├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── feature_request.md │ └── question.md └── workflows │ ├── build.yml │ ├── check-format.yml │ ├── cpack-deb.yml │ └── cpack-nsis.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── assets ├── CREDIT.md ├── notepanda.desktop └── notepanda.rc ├── images ├── banner.png ├── fluentui-icon │ ├── LICENSE │ ├── add.svg │ ├── clipboard.svg │ ├── color_background.svg │ ├── copy.svg │ ├── cut.svg │ ├── delete.svg │ ├── document.svg │ ├── document_edit.svg │ ├── info.svg │ ├── open.svg │ ├── pin.svg │ ├── preview.svg │ ├── print.svg │ ├── read_only.svg │ ├── redo.svg │ ├── save.svg │ ├── save_as.svg │ ├── settings.svg │ ├── sticker.svg │ └── undo.svg ├── notepanda-sc1.png ├── notepanda-sc2.png ├── notepanda-sc3.png ├── notepanda-sc4.png ├── notepanda.png └── panda.ico ├── makespec ├── BUILDVERSION ├── VERSION └── VERSIONSUFFIX ├── resources.qrc └── src ├── core ├── configmanager.cpp ├── configmanager.h ├── texteditor.cpp └── texteditor.h ├── main.cpp └── ui ├── aboutwindow.cpp ├── aboutwindow.h ├── aboutwindow.ui ├── linenumberarea.h ├── mainwindow.cpp ├── mainwindow.h ├── preferenceswindow.cpp ├── preferenceswindow.h └── preferenceswindow.ui /.clang-format: -------------------------------------------------------------------------------- 1 | # .clang-format 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | IndentWidth: 4 5 | BreakBeforeBraces: Custom 6 | BraceWrapping: 7 | AfterClass: true 8 | AfterNamespace: true 9 | AfterStruct: true 10 | AfterFunction: true 11 | BeforeCatch: false 12 | BeforeElse: false 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: ":bug: Bug" 6 | assignees: ChungZH 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | > A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | > A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | > If applicable, add screenshots to help explain your problem. 25 | 26 | **Affected Notepanda installation source** 27 | 28 | - [ ] ArchLinux AUR 29 | - [ ] Compile manually 30 | - [ ] Github Release 31 | 32 | 33 | **Desktop (please complete the following information):** 34 | - OS [e.g. Windows, Linux, MacOS]: 35 | - Version in the AboutWindow [e.g. 0.1.3-alpha1]: 36 | 37 | 38 | **Additional context** 39 | > Add any other context about the problem here. 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature request]" 5 | labels: ":tada: Enhancement" 6 | assignees: ChungZH 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | > A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | > A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | > A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | > Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question about Notepanda 4 | title: "[Question]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Question description** 11 | 12 | 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build CI 2 | 3 | on: 4 | push: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | build: 10 | strategy: 11 | matrix: 12 | qt_version: [5.14.2] 13 | platform: [ubuntu-16.04, macos-latest, windows-latest] 14 | arch: [x86, x64] 15 | include: 16 | - platform: windows-latest 17 | arch: x86 18 | qtarch: win32_msvc2017 19 | cmakearch: Win32 20 | - platform: windows-latest 21 | arch: x64 22 | qtarch: win64_msvc2017_64 23 | cmakearch: x64 24 | exclude: 25 | - platform: ubuntu-16.04 26 | arch: x86 27 | - platform: macos-latest 28 | arch: x86 29 | fail-fast: false 30 | runs-on: ${{ matrix.platform }} 31 | steps: 32 | - name: Checking out sources 33 | uses: actions/checkout@v2 34 | with: 35 | submodules: "recursive" 36 | - name: Install MSVC compiler 37 | uses: ilammy/msvc-dev-cmd@v1 38 | with: 39 | # 14.1 is for vs2017, 14.2 is vs2019, following the upstream vcpkg build from Qv2ray-deps repo 40 | toolset: 14.2 41 | arch: ${{ matrix.arch }} 42 | - name: Cache Qt 43 | id: cache-qt 44 | uses: actions/cache@v1 45 | with: 46 | path: ../Qt 47 | key: QtCache-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.qt_version }} 48 | - name: Installing Qt - ${{ matrix.arch }} 49 | uses: jurplel/install-qt-action@v2 50 | with: 51 | version: ${{ matrix.qt_version }} 52 | arch: ${{ matrix.qtarch }} 53 | mirror: "http://mirrors.ocf.berkeley.edu/qt/" 54 | cached: ${{ steps.cache-qt.outputs.cache-hit }} 55 | - name: Install Python 3.7 version 56 | if: matrix.platform == 'windows-latest' 57 | uses: actions/setup-python@v1 58 | with: 59 | python-version: "3.7" 60 | architecture: ${{ matrix.arch }} 61 | - name: Get the version 62 | id: get_version 63 | shell: bash 64 | run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) 65 | # Linux------------------------------------------ 66 | - name: Linux - Install packages 67 | if: matrix.platform == 'ubuntu-16.04' 68 | shell: bash 69 | env: 70 | CC: /usr/bin/gcc-9 71 | CXX: /usr/bin/g++-9 72 | run: | 73 | sudo apt install libgl-dev libx11-dev libxkbcommon-x11-dev ninja-build 74 | - name: Linux - Build ecm 75 | if: matrix.platform == 'ubuntu-16.04' 76 | shell: bash 77 | env: 78 | CC: /usr/bin/gcc-9 79 | CXX: /usr/bin/g++-9 80 | run: | 81 | cd src/3rdparty/extra-cmake-modules 82 | mkdir build 83 | cd build 84 | cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBUILD_HTML_DOCS=OFF -DBUILD_MAN_DOCS=OFF -DBUILD_QTHELP_DOCS=OFF -DBUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=/usr 85 | cmake --build . --parallel $(nproc) 86 | sudo cmake --build . --target install 87 | - name: Linux - Build SyntaxHighlighting 88 | if: matrix.platform == 'ubuntu-16.04' 89 | shell: bash 90 | env: 91 | CC: /usr/bin/gcc-9 92 | CXX: /usr/bin/g++-9 93 | run: | 94 | cd src/3rdparty/syntax-highlighting 95 | mkdir build 96 | cd build 97 | cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBUILD_HTML_DOCS=OFF -DBUILD_MAN_DOCS=OFF -DBUILD_QTHELP_DOCS=OFF -DBUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=/usr 98 | cmake --build . --parallel $(nproc) 99 | sudo cmake --build . --target install 100 | - name: Linux - ${{ matrix.qt_version }} - Generate Dependencies and Build 101 | if: matrix.platform == 'ubuntu-16.04' 102 | shell: bash 103 | env: 104 | CC: /usr/bin/gcc-9 105 | CXX: /usr/bin/g++-9 106 | run: | 107 | mkdir build 108 | cd build 109 | cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./AppDir/usr 110 | cmake --build . --parallel $(nproc) 111 | cmake --install . 112 | - name: Linux - ${{ matrix.qt_version }} - Generating AppImage 113 | if: matrix.platform == 'ubuntu-16.04' 114 | shell: bash 115 | env: 116 | CC: /usr/bin/gcc-9 117 | CXX: /usr/bin/g++-9 118 | run: | 119 | cd build 120 | wget https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage 121 | chmod +x ./linuxdeployqt-6-x86_64.AppImage 122 | ./linuxdeployqt-6-x86_64.AppImage --appimage-extract 123 | cd AppDir 124 | wget -c https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/AppRun-patched-x86_64 -O AppRun 125 | chmod a+x AppRun 126 | mkdir -p ./usr/{lib,optional}/ 127 | wget -c https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so -O ./usr/optional/exec.so 128 | mkdir -p ./usr/optional/libstdc++/ 129 | cp -fv /usr/lib/x86_64-linux-gnu/libstdc++.so.6 ./usr/optional/libstdc++/ 130 | mkdir -p ./usr/optional/libgcc_s/ 131 | cp -fv /lib/x86_64-linux-gnu/libgcc_s.so.1 ./usr/optional/libgcc_s/ 132 | cp -fv /usr/lib/x86_64-linux-gnu/{libssl.so.1.1,libcrypto.so.1.1} ./usr/lib/ 133 | cd .. 134 | squashfs-root/AppRun AppDir/usr/share/applications/notepanda.desktop -appimage -no-strip -always-overwrite 135 | mv ./Notepanda*.AppImage ./Notepanda.AppImage 136 | - name: Linux - ${{ matrix.qt_version }} - Uploading artifact 137 | if: matrix.platform == 'ubuntu-16.04' 138 | uses: actions/upload-artifact@master 139 | with: 140 | name: Notepanda-${{ github.sha }}.Linux-${{ matrix.arch }}.qt${{ matrix.qt_version }}.AppImage 141 | path: build/Notepanda.AppImage 142 | - name: Linux - ${{ matrix.qt_version }} - Upload binaries to release 143 | uses: svenstaro/upload-release-action@v1-release 144 | if: github.event_name == 'release' && matrix.platform == 'ubuntu-16.04' && matrix.qt_version == '5.14.2' 145 | with: 146 | repo_token: ${{ secrets.GITHUB_TOKEN }} 147 | file: build/Notepanda.AppImage 148 | asset_name: Notepanda.${{ steps.get_version.outputs.VERSION }}.Linux-${{ matrix.arch }}.AppImage 149 | tag: ${{ github.ref }} 150 | overwrite: true 151 | # Windows---------------------------------------------------------------------- 152 | # - name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Build preparation - Download Dependencies 153 | # if: matrix.platform == 'windows-latest' 154 | # run: | 155 | # choco install -y ninja 156 | - name: Setup Ninja 157 | if: matrix.platform == 'windows-latest' 158 | uses: ashutoshvarma/setup-ninja@master 159 | with: 160 | # ninja version to download. Default: 1.10.0 161 | version: 1.10.0 162 | - name: Win - Build ecm 163 | if: matrix.platform == 'windows-latest' 164 | env: 165 | CC: cl.exe 166 | CXX: cl.exe 167 | run: | 168 | set VCINSTALLDIR="C:\Program Files (x86)\Microsoft Visual Studio 16.0\VC" 169 | cd src/3rdparty/extra-cmake-modules 170 | mkdir build 171 | cd build 172 | cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBUILD_HTML_DOCS=OFF -DBUILD_MAN_DOCS=OFF -DBUILD_QTHELP_DOCS=OFF -DBUILD_TESTING=OFF 173 | cmake --build . --parallel $(nproc) 174 | cmake --build . --target install 175 | - name: Win - Build syntax highlight 176 | if: matrix.platform == 'windows-latest' 177 | env: 178 | CC: cl.exe 179 | CXX: cl.exe 180 | run: | 181 | cd src/3rdparty/syntax-highlighting 182 | mkdir build 183 | cd build 184 | cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBUILD_HTML_DOCS=OFF -DBUILD_MAN_DOCS=OFF -DBUILD_QTHELP_DOCS=OFF -DBUILD_TESTING=OFF 185 | cmake --build . --parallel $(nproc) 186 | cmake --build . --target install 187 | - name: Windows - ${{ matrix.qt_version }} - Generate Dependencies and Build 188 | if: matrix.platform == 'windows-latest' 189 | env: 190 | CC: cl.exe 191 | CXX: cl.exe 192 | run: | 193 | mkdir build 194 | cd build 195 | cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=Release -DKF5SyntaxHighlighting_DIR="C:/Program Files (x86)/ECM/lib/cmake/KF5SyntaxHighlighting" 196 | cmake --build . --parallel $(nproc) 197 | cmake --install . 198 | cd Release 199 | windeployqt notepanda.exe --no-translations 200 | - name: Win - ${{ matrix.arch }} - ${{ matrix.qt_version }} - Create 7z Release 201 | if: matrix.platform == 'windows-latest' 202 | uses: DuckSoft/create-7z-action@v1.0 203 | with: 204 | pathSource: ./build/Release 205 | pathTarget: ./release.7z 206 | - name: Win - ${{ matrix.arch }} - ${{ matrix.qt_version }} - Uploading artifact 207 | if: matrix.platform == 'windows-latest' 208 | uses: actions/upload-artifact@master 209 | with: 210 | name: Notepanda-${{ github.sha }}.Windows-${{ matrix.arch }}.qt${{ matrix.qt_version }}.7z 211 | path: release.7z 212 | - name: Win - ${{ matrix.arch }} - ${{ matrix.qt_version }} - Upload binaries to release 213 | uses: svenstaro/upload-release-action@v1-release 214 | if: github.event_name == 'release' && matrix.platform == 'windows-latest' && matrix.qt_version == '5.14.2' 215 | with: 216 | repo_token: ${{ secrets.GITHUB_TOKEN }} 217 | file: release.7z 218 | asset_name: Notepanda.${{ steps.get_version.outputs.VERSION }}.Windows-${{ matrix.arch }}.7z 219 | tag: ${{ github.ref }} 220 | overwrite: true 221 | # MacOS --------------------------------------------------------- 222 | - name: MacOS - Build ecm 223 | if: matrix.platform == 'macos-latest' 224 | shell: bash 225 | run: | 226 | brew install ninja 227 | cd src/3rdparty/extra-cmake-modules 228 | mkdir build 229 | cd build 230 | cmake .. -GNinja -DBUILD_HTML_DOCS=OFF -DBUILD_MAN_DOCS=OFF -DBUILD_QTHELP_DOCS=OFF -DBUILD_TESTING=OFF 231 | cmake --build . 232 | cmake --build . --target install 233 | - name: MacOS - Build syntax highlight 234 | if: matrix.platform == 'macos-latest' 235 | run: | 236 | cd src/3rdparty/syntax-highlighting 237 | mkdir build 238 | cd build 239 | cmake .. -GNinja -DBUILD_HTML_DOCS=OFF -DBUILD_MAN_DOCS=OFF -DBUILD_QTHELP_DOCS=OFF -DBUILD_TESTING=OFF 240 | cmake --build . 241 | sudo cmake --build . --target install 242 | - name: macOS - ${{ matrix.qt_version }} - Generate Dependencies and Build 243 | if: matrix.platform == 'macos-latest' 244 | run: | 245 | mkdir build 246 | cd build 247 | cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -DDS_STORE_SCRIPT=ON 248 | cmake --build . 249 | sudo cmake --install . 250 | macdeployqt notepanda.app -dmg 251 | - name: macOS - ${{ matrix.qt_version }} - Uploading Artifact 252 | if: matrix.platform == 'macos-latest' 253 | uses: actions/upload-artifact@master 254 | with: 255 | name: Notepanda-${{ github.sha }}.macOS-${{ matrix.arch }}.qt${{ matrix.qt_version }}.dmg 256 | path: build/notepanda.dmg 257 | - name: macOS - ${{ matrix.qt_version }} - Upload binaries to release 258 | uses: svenstaro/upload-release-action@v1-release 259 | if: github.event_name == 'release' && matrix.platform == 'macos-latest' && matrix.qt_version == '5.14.2' 260 | with: 261 | repo_token: ${{ secrets.GITHUB_TOKEN }} 262 | file: build/notepanda.dmg 263 | asset_name: Notepanda-${{ steps.get_version.outputs.VERSION }}.macOS-${{ matrix.arch }}.dmg 264 | tag: ${{ github.ref }} 265 | overwrite: true 266 | # END -------------------------------------------------------------------------------- /.github/workflows/check-format.yml: -------------------------------------------------------------------------------- 1 | name: "Check Clang Format" 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | format: 7 | name: "Check Clang Format" 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: "Install clang-format-9" 12 | run: | 13 | sudo apt-get update 14 | sudo apt-get install clang-format-9 15 | - name: "Format Codes" 16 | run: clang-format-9 -i src/*.cpp src/*/*.cpp src/*/*.h 17 | - name: Check diff 18 | run: git diff --exit-code HEAD 19 | - name: Create Pull Request 20 | if: failure() 21 | uses: peter-evans/create-pull-request@v2 22 | with: 23 | commit-message: "style: format codes" 24 | title: "style: format codes for ${{ github.ref }}" 25 | labels: "auto-pr/style" 26 | assignees: "${{ github.actor }}" 27 | reviewers: "${{ github.actor }}" 28 | branch: "auto-pr/clang-format/${{ github.ref }}" 29 | -------------------------------------------------------------------------------- /.github/workflows/cpack-deb.yml: -------------------------------------------------------------------------------- 1 | name: CPack - DEB 2 | 3 | on: 4 | push: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | check_commit_msg: 10 | outputs: 11 | commit_message: ${{ steps.get_message.outputs.message }} 12 | name: Check if the workflow has been disabled. 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Get commit message 17 | id: get_message 18 | run: | 19 | echo "::set-output name=message::$(git log --format=%B -n 1 ${{ github.event.after }})" 20 | echo "::set-env name=message::$(git log --format=%B -n 1 ${{ github.event.after }})" 21 | linux: 22 | strategy: 23 | matrix: 24 | distro: [stable] 25 | needs: check_commit_msg 26 | if: ${{ !contains( needs.check_commit_msg.outputs.commit_message, 'NO_DEB') }} 27 | name: Debian ${{ matrix.distro }} 28 | runs-on: ubuntu-latest 29 | container: debian:${{ matrix.distro }} 30 | 31 | steps: 32 | - name: Install git 33 | run: | 34 | apt-get update 35 | apt-get install -y git 36 | - name: Checking out sources 37 | uses: actions/checkout@v2 38 | with: 39 | submodules: 'recursive' 40 | - name: Install build dependencies 41 | run: | 42 | apt-get install -y build-essential ninja-build qtbase5-dev qttools5-dev cmake libkf5syntaxhighlighting-dev 43 | - name: Build 44 | run: | 45 | mkdir build 46 | cd build 47 | cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./AppDir/usr -DBUILD_DEB=ON 48 | cmake --build . --target package --parallel $(nproc) 49 | - name: Get package name 50 | shell: bash 51 | id: get_package 52 | run: | 53 | echo ::set-output name=NAME::$(basename build/Notepanda-*.deb) 54 | - name: Get the version 55 | id: get_version 56 | shell: bash 57 | run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) 58 | - name: Upload artifact 59 | uses: actions/upload-artifact@v2-preview 60 | with: 61 | name: ${{ steps.get_package.outputs.NAME }} 62 | path: build/${{ steps.get_package.outputs.NAME }} 63 | - name: Upload binaries to release 64 | uses: svenstaro/upload-release-action@v1-release 65 | if: github.event_name == 'release' && matrix.distro == 'stable' 66 | with: 67 | repo_token: ${{ secrets.GITHUB_TOKEN }} 68 | file: build/${{ steps.get_package.outputs.NAME }} 69 | asset_name: Notepanda.${{ steps.get_version.outputs.VERSION }}-${{ matrix.distro }}.deb 70 | tag: ${{ github.ref }} 71 | overwrite: true 72 | -------------------------------------------------------------------------------- /.github/workflows/cpack-nsis.yml: -------------------------------------------------------------------------------- 1 | name: CPack - NSIS 2 | 3 | on: 4 | push: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | build: 10 | strategy: 11 | matrix: 12 | qt_version: [5.14.2] 13 | platform: [windows-latest] 14 | arch: [x86, x64] 15 | include: 16 | - platform: windows-latest 17 | arch: x86 18 | qtarch: win32_msvc2017 19 | cmakearch: Win32 20 | - platform: windows-latest 21 | arch: x64 22 | qtarch: win64_msvc2017_64 23 | cmakearch: x64 24 | fail-fast: false 25 | 26 | runs-on: ${{ matrix.platform }} 27 | 28 | steps: 29 | - name: Get the version 30 | id: get_version 31 | shell: bash 32 | run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) 33 | - name: Checking out sources 34 | uses: actions/checkout@v2 35 | with: 36 | submodules: 'recursive' 37 | - name: Install Python 3.7 version 38 | uses: actions/setup-python@v1 39 | with: 40 | python-version: '3.7' 41 | architecture: ${{ matrix.arch }} 42 | # ========================================================================================================= 43 | - name: Install MSVC compiler 44 | uses: ilammy/msvc-dev-cmd@v1 45 | with: 46 | toolset: 14.2 47 | arch: ${{ matrix.arch }} 48 | - name: Cache Qt 49 | id: cache-qt 50 | uses: actions/cache@v1 51 | with: 52 | path: ../Qt 53 | key: QtCache-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.qt_version }} 54 | - name: Installing Qt - ${{ matrix.arch }} 55 | uses: jurplel/install-qt-action@v2.5.0 56 | with: 57 | version: ${{ matrix.qt_version }} 58 | arch: ${{ matrix.qtarch }} 59 | mirror: 'http://mirrors.ocf.berkeley.edu/qt/' 60 | cached: ${{ steps.cache-qt.outputs.cache-hit }} 61 | # Windows---------------------------------------------------------------------- 62 | # - name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Build preparation - Download Dependencies 63 | # if: matrix.platform == 'windows-latest' 64 | # run: | 65 | # choco install -y ninja 66 | - name: Setup Ninja 67 | if: matrix.platform == 'windows-latest' 68 | uses: ashutoshvarma/setup-ninja@master 69 | with: 70 | # ninja version to download. Default: 1.10.0 71 | version: 1.10.0 72 | - name: Win - Build ecm 73 | if: matrix.platform == 'windows-latest' 74 | env: 75 | CC: cl.exe 76 | CXX: cl.exe 77 | run: | 78 | set VCINSTALLDIR="C:\Program Files (x86)\Microsoft Visual Studio 16.0\VC" 79 | cd src/3rdparty/extra-cmake-modules 80 | mkdir build 81 | cd build 82 | cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBUILD_HTML_DOCS=OFF -DBUILD_MAN_DOCS=OFF -DBUILD_QTHELP_DOCS=OFF -DBUILD_TESTING=OFF 83 | cmake --build . 84 | cmake --build . --target install 85 | - name: Win - Build syntax highlight 86 | if: matrix.platform == 'windows-latest' 87 | env: 88 | CC: cl.exe 89 | CXX: cl.exe 90 | run: | 91 | cd src/3rdparty/syntax-highlighting 92 | mkdir build 93 | cd build 94 | cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBUILD_HTML_DOCS=OFF -DBUILD_MAN_DOCS=OFF -DBUILD_QTHELP_DOCS=OFF -DBUILD_TESTING=OFF 95 | cmake --build . 96 | cmake --build . --target install 97 | - name: Windows - ${{ matrix.qt_version }} - Generate Dependencies and Build 98 | if: matrix.platform == 'windows-latest' 99 | env: 100 | CC: cl.exe 101 | CXX: cl.exe 102 | run: | 103 | mkdir build 104 | cd build 105 | cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DKF5SyntaxHighlighting_DIR="C:/Program Files (x86)/ECM/lib/cmake/KF5SyntaxHighlighting" -DBUILD_NSIS=ON 106 | cmake --build . --target package --parallel $(nproc) 107 | - name: Windows - Get package name 108 | shell: bash 109 | id: get_package 110 | run: | 111 | echo ::set-output name=NAME::$(basename build/Notepanda-*.exe) 112 | - name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - uploading artifact 113 | uses: actions/upload-artifact@master 114 | with: 115 | name: ${{ steps.get_package.outputs.NAME }} 116 | path: build/${{ steps.get_package.outputs.NAME }} 117 | - name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Upload binaries to release 118 | uses: svenstaro/upload-release-action@v1-release 119 | if: github.event_name == 'release' 120 | with: 121 | repo_token: ${{ secrets.GITHUB_TOKEN }} 122 | file: build/${{ steps.get_package.outputs.NAME }} 123 | asset_name: Notepanda.${{ steps.get_version.outputs.VERSION }}.Windows-${{ matrix.arch }}.exe 124 | tag: ${{ github.ref }} 125 | overwrite: true 126 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Some files 35 | .DS_Store 36 | *.autosave 37 | *.user 38 | .vs/ 39 | *.qm 40 | .vscode/ 41 | CMakeSettings.json 42 | /makespec/qv2ray.spec 43 | 44 | build/ 45 | 46 | 47 | ## Ignore Visual Studio temporary files, build results, and 48 | ## files generated by popular Visual Studio add-ons. 49 | ## 50 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 51 | 52 | # User-specific files 53 | *.rsuser 54 | *.suo 55 | *.user 56 | *.userosscache 57 | *.sln.docstates 58 | 59 | # User-specific files (MonoDevelop/Xamarin Studio) 60 | *.userprefs 61 | 62 | # Mono auto generated files 63 | mono_crash.* 64 | 65 | # Build results 66 | [Dd]ebug/ 67 | [Dd]ebugPublic/ 68 | [Rr]elease/ 69 | [Rr]eleases/ 70 | x64/ 71 | x86/ 72 | [Ww][Ii][Nn]32/ 73 | [Aa][Rr][Mm]/ 74 | [Aa][Rr][Mm]64/ 75 | bld/ 76 | [Bb]in/ 77 | [Oo]bj/ 78 | [Ll]og/ 79 | [Ll]ogs/ 80 | 81 | # Visual Studio 2015/2017 cache/options directory 82 | .vs/ 83 | # Uncomment if you have tasks that create the project's static files in wwwroot 84 | #wwwroot/ 85 | 86 | # Visual Studio 2017 auto generated files 87 | Generated\ Files/ 88 | 89 | # MSTest test Results 90 | [Tt]est[Rr]esult*/ 91 | [Bb]uild[Ll]og.* 92 | 93 | # NUnit 94 | *.VisualState.xml 95 | TestResult.xml 96 | nunit-*.xml 97 | 98 | # Build Results of an ATL Project 99 | [Dd]ebugPS/ 100 | [Rr]eleasePS/ 101 | dlldata.c 102 | 103 | # Benchmark Results 104 | BenchmarkDotNet.Artifacts/ 105 | 106 | # .NET Core 107 | project.lock.json 108 | project.fragment.lock.json 109 | artifacts/ 110 | 111 | # ASP.NET Scaffolding 112 | ScaffoldingReadMe.txt 113 | 114 | # StyleCop 115 | StyleCopReport.xml 116 | 117 | # Files built by Visual Studio 118 | *_i.c 119 | *_p.c 120 | *_h.h 121 | *.ilk 122 | *.meta 123 | *.obj 124 | *.iobj 125 | *.pch 126 | *.pdb 127 | *.ipdb 128 | *.pgc 129 | *.pgd 130 | *.rsp 131 | *.sbr 132 | *.tlb 133 | *.tli 134 | *.tlh 135 | *.tmp 136 | *.tmp_proj 137 | *_wpftmp.csproj 138 | *.log 139 | *.vspscc 140 | *.vssscc 141 | .builds 142 | *.pidb 143 | *.svclog 144 | *.scc 145 | 146 | # Chutzpah Test files 147 | _Chutzpah* 148 | 149 | # Visual C++ cache files 150 | ipch/ 151 | *.aps 152 | *.ncb 153 | *.opendb 154 | *.opensdf 155 | *.sdf 156 | *.cachefile 157 | *.VC.db 158 | *.VC.VC.opendb 159 | 160 | # Visual Studio profiler 161 | *.psess 162 | *.vsp 163 | *.vspx 164 | *.sap 165 | 166 | # Visual Studio Trace Files 167 | *.e2e 168 | 169 | # TFS 2012 Local Workspace 170 | $tf/ 171 | 172 | # Guidance Automation Toolkit 173 | *.gpState 174 | 175 | # ReSharper is a .NET coding add-in 176 | _ReSharper*/ 177 | *.[Rr]e[Ss]harper 178 | *.DotSettings.user 179 | 180 | # TeamCity is a build add-in 181 | _TeamCity* 182 | 183 | # DotCover is a Code Coverage Tool 184 | *.dotCover 185 | 186 | # AxoCover is a Code Coverage Tool 187 | .axoCover/* 188 | !.axoCover/settings.json 189 | 190 | # Coverlet is a free, cross platform Code Coverage Tool 191 | coverage*[.json, .xml, .info] 192 | 193 | # Visual Studio code coverage results 194 | *.coverage 195 | *.coveragexml 196 | 197 | # NCrunch 198 | _NCrunch_* 199 | .*crunch*.local.xml 200 | nCrunchTemp_* 201 | 202 | # MightyMoose 203 | *.mm.* 204 | AutoTest.Net/ 205 | 206 | # Web workbench (sass) 207 | .sass-cache/ 208 | 209 | # Installshield output folder 210 | [Ee]xpress/ 211 | 212 | # DocProject is a documentation generator add-in 213 | DocProject/buildhelp/ 214 | DocProject/Help/*.HxT 215 | DocProject/Help/*.HxC 216 | DocProject/Help/*.hhc 217 | DocProject/Help/*.hhk 218 | DocProject/Help/*.hhp 219 | DocProject/Help/Html2 220 | DocProject/Help/html 221 | 222 | # Click-Once directory 223 | publish/ 224 | 225 | # Publish Web Output 226 | *.[Pp]ublish.xml 227 | *.azurePubxml 228 | # Note: Comment the next line if you want to checkin your web deploy settings, 229 | # but database connection strings (with potential passwords) will be unencrypted 230 | *.pubxml 231 | *.publishproj 232 | 233 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 234 | # checkin your Azure Web App publish settings, but sensitive information contained 235 | # in these scripts will be unencrypted 236 | PublishScripts/ 237 | 238 | # NuGet Packages 239 | *.nupkg 240 | # NuGet Symbol Packages 241 | *.snupkg 242 | # The packages folder can be ignored because of Package Restore 243 | **/[Pp]ackages/* 244 | # except build/, which is used as an MSBuild target. 245 | !**/[Pp]ackages/build/ 246 | # Uncomment if necessary however generally it will be regenerated when needed 247 | #!**/[Pp]ackages/repositories.config 248 | # NuGet v3's project.json files produces more ignorable files 249 | *.nuget.props 250 | *.nuget.targets 251 | 252 | # Microsoft Azure Build Output 253 | csx/ 254 | *.build.csdef 255 | 256 | # Microsoft Azure Emulator 257 | ecf/ 258 | rcf/ 259 | 260 | # Windows Store app package directories and files 261 | AppPackages/ 262 | BundleArtifacts/ 263 | Package.StoreAssociation.xml 264 | _pkginfo.txt 265 | *.appx 266 | *.appxbundle 267 | *.appxupload 268 | 269 | # Visual Studio cache files 270 | # files ending in .cache can be ignored 271 | *.[Cc]ache 272 | # but keep track of directories ending in .cache 273 | !?*.[Cc]ache/ 274 | 275 | # Others 276 | ClientBin/ 277 | ~$* 278 | *~ 279 | *.dbmdl 280 | *.dbproj.schemaview 281 | *.jfm 282 | *.pfx 283 | *.publishsettings 284 | orleans.codegen.cs 285 | 286 | # Including strong name files can present a security risk 287 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 288 | #*.snk 289 | 290 | # Since there are multiple workflows, uncomment next line to ignore bower_components 291 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 292 | #bower_components/ 293 | 294 | # RIA/Silverlight projects 295 | Generated_Code/ 296 | 297 | # Backup & report files from converting an old project file 298 | # to a newer Visual Studio version. Backup files are not needed, 299 | # because we have git ;-) 300 | _UpgradeReport_Files/ 301 | Backup*/ 302 | UpgradeLog*.XML 303 | UpgradeLog*.htm 304 | ServiceFabricBackup/ 305 | *.rptproj.bak 306 | 307 | # SQL Server files 308 | *.mdf 309 | *.ldf 310 | *.ndf 311 | 312 | # Business Intelligence projects 313 | *.rdl.data 314 | *.bim.layout 315 | *.bim_*.settings 316 | *.rptproj.rsuser 317 | *- [Bb]ackup.rdl 318 | *- [Bb]ackup ([0-9]).rdl 319 | *- [Bb]ackup ([0-9][0-9]).rdl 320 | 321 | # Microsoft Fakes 322 | FakesAssemblies/ 323 | 324 | # GhostDoc plugin setting file 325 | *.GhostDoc.xml 326 | 327 | # Node.js Tools for Visual Studio 328 | .ntvs_analysis.dat 329 | node_modules/ 330 | 331 | # Visual Studio 6 build log 332 | *.plg 333 | 334 | # Visual Studio 6 workspace options file 335 | *.opt 336 | 337 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 338 | *.vbw 339 | 340 | # Visual Studio LightSwitch build output 341 | **/*.HTMLClient/GeneratedArtifacts 342 | **/*.DesktopClient/GeneratedArtifacts 343 | **/*.DesktopClient/ModelManifest.xml 344 | **/*.Server/GeneratedArtifacts 345 | **/*.Server/ModelManifest.xml 346 | _Pvt_Extensions 347 | 348 | # Paket dependency manager 349 | .paket/paket.exe 350 | paket-files/ 351 | 352 | # FAKE - F# Make 353 | .fake/ 354 | 355 | # CodeRush personal settings 356 | .cr/personal 357 | 358 | # Python Tools for Visual Studio (PTVS) 359 | __pycache__/ 360 | *.pyc 361 | 362 | # Cake - Uncomment if you are using it 363 | # tools/** 364 | # !tools/packages.config 365 | 366 | # Tabs Studio 367 | *.tss 368 | 369 | # Telerik's JustMock configuration file 370 | *.jmconfig 371 | 372 | # BizTalk build output 373 | *.btp.cs 374 | *.btm.cs 375 | *.odx.cs 376 | *.xsd.cs 377 | 378 | # OpenCover UI analysis results 379 | OpenCover/ 380 | 381 | # Azure Stream Analytics local run output 382 | ASALocalRun/ 383 | 384 | # MSBuild Binary and Structured Log 385 | *.binlog 386 | 387 | # NVidia Nsight GPU debugger configuration file 388 | *.nvuser 389 | 390 | # MFractors (Xamarin productivity tool) working folder 391 | .mfractor/ 392 | 393 | # Local History for Visual Studio 394 | .localhistory/ 395 | 396 | # BeatPulse healthcheck temp database 397 | healthchecksdb 398 | 399 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 400 | MigrationBackup/ 401 | 402 | # Ionide (cross platform F# VS Code tools) working folder 403 | .ionide/ 404 | 405 | # Fody - auto-generated XML schema 406 | FodyWeavers.xsd 407 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/3rdparty/syntax-highlighting"] 2 | path = src/3rdparty/syntax-highlighting 3 | url = git@github.com:KDE/syntax-highlighting.git 4 | [submodule "src/3rdparty/extra-cmake-modules"] 5 | path = src/3rdparty/extra-cmake-modules 6 | url = git@github.com:KDE/extra-cmake-modules.git 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [UNRELEASED] 6 | 7 | ### 🌵 Features 8 | 9 | - Use `Segoe UI` font on Microsoft Windows 10 | - Opening files with Drag and Drop. 11 | - Auto complete brackets 12 | 13 | ### 🦋 Optimization 14 | 15 | - Disable PreviewPanel when not in use 16 | 17 | ### 🐞 Bug fix 18 | 19 | - PreviewPanel didn't work 20 | 21 | ## 0.1.4 22 | 23 | ### 🌵 Features 24 | 25 | - Right click menu 26 | - Syntax selection menu 27 | - Clear 28 | - Read-Only mode 29 | - Folding 30 | - Ctrl + Wheel to zoom the text 31 | - Fluent System Icons 32 | - Tab / Spaces indent 33 | - Multi-tab 34 | 35 | ## 0.1.3 36 | 37 | > If you want to track the latest news of Notepanda, welcome to our [📰 Telegram channel](https://t.me/notepanda)! 38 | 39 | ### 🌵 Features 40 | 41 | - Preview panel 42 | - JSON format settings 43 | - Pin to top 44 | - Set window modified 45 | - New logo 46 | - More available syntax highlight themes: `Breeze Dark`, `Default`, `Printing`, `Solarized Dark` and `Solarized Light` 47 | - Add normal mode icon 48 | - Sticky mode 49 | - Hide sidebar 50 | - Hide line number area 51 | - Hide statusbar 52 | - Use general font (not monospace) 53 | - Smaller window 54 | - Custom background color 55 | 56 | ### 🐞 Bug fix 57 | 58 | - Fix font settings cannot take effect 59 | - Fix file content lost on save (#30) 60 | - NSIS installer cannot found dll 61 | 62 | ### ! BREAKING CHANGE ! 63 | 64 | - The `Style` setting has been renamed to `StyleTheme` 65 | - The `ColorTheme` setting has been renamed to `EditorColorTheme` 66 | 67 | A deb package has also been added. 68 | 69 | ## 0.1.3-alpha2 70 | 71 | The main purpose of this release is to fix some bugs in the previous alpha version. 72 | 73 | > Are you confused when you see this version number? That's right, we've turned 0.0.3 into the more standardized 0.1.3. 74 | 75 | ### 🌵 Features 76 | 77 | - Pin to top 78 | 79 | ## 🐞 Bug fix 80 | 81 | - NSIS installer cannot found dll 82 | 83 | A deb package has also been added. 84 | 85 | > If you want to track the latest news of Notepanda, welcome to our [📰 Telegram channel](https://t.me/notepanda)! 86 | 87 | ## 0.0.3-alpha1 88 | 89 | ### 🌵 Features 90 | 91 | - Set window modified 92 | - New logo 93 | - More available syntax highlight themes: `Breeze Dark`, `Default`, `Printing`, `Solarized Dark` and `Solarized Light` 94 | - Add normal mode icon 95 | - Sticky mode 96 | - Hide sidebar 97 | - Hide line number area 98 | - Hide statusbar 99 | - Use general font (not monospace) 100 | - Smaller window 101 | - Custom background color 102 | 103 | ### 🐞 Bug fix 104 | 105 | - Fix font settings cannot take effect 106 | 107 | ## 0.0.2 108 | 109 | ### 🌵 Features 110 | 111 | - Custom font in Preferences Window 112 | - Change the default font to `monospace` (Different on different systems) 113 | - Syntax Highlighting 114 | - Custom theme 115 | - Launch from the shell 116 | - Persistent application settings 117 | 118 | ### 🦋 Optimization 119 | 120 | - Refactor code structure. 121 | 122 | ## 0.0.1 123 | 124 | The following is this version of changelog (including the beta/rc version): 125 | 126 | ### 🌵 Features 127 | 128 | - Basic operation 129 | - New 130 | - Open 131 | - Save 132 | - Save as 133 | - Print 134 | - Undo 135 | - Redo 136 | - Copy 137 | - Paste 138 | - Cut 139 | - Quit 140 | - Icons (from [Remix Icon - Open source icon library](https://remixicon.com/)) 141 | - Toolbar 142 | - Line numbers 143 | - Highlight current line 144 | - Beautify the interface 145 | - Show characters & line numbers on status bar 146 | - Preferences Window 147 | 148 | ### 🦋 Optimization 149 | 150 | - Refactor 151 | - Use Lambda to set action undo&redo©&cut's state. 152 | - Move all sources to `src/` dir 153 | - Move core sources to `src/core` dir, ui sources to `src/ui` dir 154 | - Rename the `notepanda` class to `MainWindow` 155 | 156 | ### 🐞 Bug fix 157 | 158 | - Fix cannot load SVG files. 159 | 160 | ## 0.0.1-rc.1 161 | 162 | ### 🌵 Features 163 | 164 | - More basic operation 165 | - Undo 166 | - Redo 167 | - Copy 168 | - Paste 169 | - Cut 170 | - Quit 171 | - Icons (from [Remix Icon - Open source icon library](https://remixicon.com/)) 172 | - Toolbar 173 | - Line numbers 174 | - Highlight current line 175 | 176 | ### 🦋 Optimization 177 | 178 | - Refactor 179 | - Move all sources to `src/` dir 180 | - Move core sources to `src/core` dir, ui sources to `src/ui` dir 181 | - Rename the `notepanda` class to `MainWindow` 182 | 183 | ## 0.0.1-alpha 184 | 185 | > First release. :beers: 186 | 187 | [GitHub Release](https://github.com/ChungZH/notepanda/releases/tag/v0.0.1-alpha) 188 | 189 | ### 🌵 Features 190 | 191 | - Basic operation 192 | - New 193 | - Open 194 | - Save 195 | - Save as 196 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeLists.txt 2 | 3 | cmake_minimum_required(VERSION 3.1.0) 4 | 5 | file(STRINGS "${CMAKE_SOURCE_DIR}/makespec/VERSION" VERSION) 6 | file(STRINGS "${CMAKE_SOURCE_DIR}/makespec/BUILDVERSION" BUILD_VERSION) 7 | file(STRINGS "${CMAKE_SOURCE_DIR}/makespec/VERSIONSUFFIX" VERSION_SUFFIX) 8 | 9 | set(CMAKE_CXX_STANDARD 11) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | project(notepanda) 13 | 14 | set(VERSION_STRING "${VERSION}${VERSION_SUFFIX}") 15 | set(VERSION_LIST ${VERSION}) 16 | string(REPLACE "." ";" VERSION_LIST ${VERSION_LIST}) 17 | separate_arguments(VERSION_LIST) 18 | 19 | list(GET VERSION_LIST 0 CMAKE_PROJECT_VERSION_MAJOR) 20 | list(GET VERSION_LIST 1 CMAKE_PROJECT_VERSION_MINOR) 21 | list(GET VERSION_LIST 2 CPACK_PACKAGE_VERSION_PATCH) 22 | 23 | add_definitions(-DVERSION_STRING="${VERSION_STRING}") 24 | add_definitions(-DXTOSTRUCT_QT) 25 | 26 | set(CMAKE_AUTOMOC ON) 27 | set(CMAKE_AUTORCC ON) 28 | set(CMAKE_AUTOUIC ON) 29 | 30 | if(CMAKE_VERSION VERSION_LESS "3.7.0") 31 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 32 | endif() 33 | 34 | # ======================= 35 | # Notepanda Build Message 36 | # ======================= 37 | 38 | message(" ") 39 | message("Notepanda Version: ${VERSION_STRING}") 40 | message("Notepanda Build Version: ${BUILD_VERSION}") 41 | message("Notepanda Build Type: ${CMAKE_BUILD_TYPE}") 42 | message("|-------------------------------------------------|") 43 | message("| Notepanda, A Cross Platform Notepad. |") 44 | message("| Based on C++ & Qt. |") 45 | message("| Licenced under MIT |") 46 | message("| |") 47 | message("| You may only use this program to the extent |") 48 | message("| permitted by local law. |") 49 | message("| |") 50 | message("| See: https://opensource.org/licenses/MIT |") 51 | message("|-------------------------------------------------|") 52 | message("| GitHub: https://github.com/ChungZH/notepanda |") 53 | message("| Welcome to contribute! |") 54 | message("|-------------------------------------------------|") 55 | message(" ") 56 | 57 | find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED) 58 | find_package(KF5SyntaxHighlighting REQUIRED) 59 | 60 | if(WIN32) 61 | set(GUI_TYPE WIN32) 62 | elseif(APPLE) 63 | set(GUI_TYPE MACOSX_BUNDLE) 64 | endif() 65 | 66 | # ============================= 67 | # Notepanda Sources and Headers 68 | # ============================= 69 | 70 | set(SOURCES 71 | src/main.cpp 72 | src/ui/aboutwindow.cpp 73 | src/ui/aboutwindow.h 74 | src/ui/aboutwindow.ui 75 | src/ui/mainwindow.cpp 76 | src/ui/mainwindow.h 77 | src/ui/linenumberarea.h 78 | src/ui/preferenceswindow.cpp 79 | src/ui/preferenceswindow.h 80 | src/ui/preferenceswindow.ui 81 | src/core/configmanager.cpp 82 | src/core/configmanager.h 83 | src/core/texteditor.cpp 84 | src/core/texteditor.h 85 | assets/notepanda.rc 86 | ${CMAKE_SOURCE_DIR}/resources.qrc 87 | ) 88 | 89 | add_executable(notepanda 90 | ${GUI_TYPE} 91 | ${SOURCES} 92 | ) 93 | 94 | if(UNIX AND NOT APPLE AND NOT WIN32) 95 | install(TARGETS notepanda RUNTIME DESTINATION bin) 96 | install(FILES assets/notepanda.desktop DESTINATION share/applications) 97 | install(FILES images/notepanda.png DESTINATION share/icons/hicolor/256x256/apps) 98 | endif() 99 | 100 | # Directories to look for dependencies 101 | set(DIRS "${CMAKE_BINARY_DIR}") 102 | 103 | # Path used for searching by FIND_XXX(), with appropriate suffixes added 104 | if(CMAKE_PREFIX_PATH) 105 | foreach(dir ${CMAKE_PREFIX_PATH}) 106 | list(APPEND DIRS "${dir}/bin" "${dir}/lib") 107 | endforeach() 108 | endif() 109 | 110 | # ========= 111 | # Packaging 112 | # ========= 113 | 114 | set(CPACK_PACKAGE_NAME "Notepanda") 115 | set(CPACK_PACKAGE_VENDOR "ChungZH") 116 | set(CPACK_PACKAGE_VERSION ${VERSION_STRING}) 117 | set(CPACK_PACKAGE_DESCRIPTION "A simple cross-platform notepad.") 118 | set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/ChungZH/notepanda") 119 | set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/images/panda.ico") 120 | set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE.md") 121 | 122 | if(MSVC) 123 | set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .) 124 | endif() 125 | 126 | include(InstallRequiredSystemLibraries) 127 | 128 | if(WIN32) 129 | install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION .) 130 | if (BUILD_NSIS) 131 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 132 | COMMAND windeployqt ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.exe --compiler-runtime --no-translations --verbose 2 --dir ${CMAKE_BINARY_DIR}/winqt/ 133 | ) 134 | install(DIRECTORY ${CMAKE_BINARY_DIR}/winqt/ DESTINATION .) 135 | endif() 136 | set(APPS "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.exe") 137 | endif() 138 | 139 | if(WIN32) 140 | if(BUILD_NSIS) 141 | set(CPACK_GENERATOR "NSIS") 142 | set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/images\\\\panda.ico") 143 | set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/images/panda.ico") 144 | set(CPACK_NSIS_MUI_UNIICON "${CMAKE_SOURCE_DIR}/images/panda.ico") 145 | set(CPACK_NSIS_DISPLAY_NAME "Notepanda") 146 | set(CPACK_NSIS_PACKAGE_NAME "Notepanda") 147 | set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS " 148 | CreateShortCut \\\"$DESKTOP\\\\Notepanda.lnk\\\" \\\"$INSTDIR\\\\notepanda.exe\\\" 149 | CreateDirectory \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Notepanda\\\" 150 | CreateShortCut \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Notepanda\\\\Notepanda.lnk\\\" \\\"$INSTDIR\\\\notepanda.exe\\\" 151 | WriteRegStr HKLM \\\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\notepanda\\\" \\\"DisplayIcon\\\" \\\"$INSTDIR\\\\notepanda.exe\\\" 152 | WriteRegStr HKLM \\\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\notepanda\\\" \\\"HelpLink\\\" \\\"https://github.com/ChungZH/notepanda\\\" 153 | WriteRegStr HKLM \\\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\notepanda\\\" \\\"InstallLocation\\\" \\\"$INSTDIR\\\" 154 | WriteRegStr HKLM \\\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\notepanda\\\" \\\"URLUpdateInfo\\\" \\\"https://github.com/ChungZH/notepanda/releases\\\" 155 | WriteRegStr HKLM \\\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\notepanda\\\" \\\"URLInfoAbout\\\" \\\"https://github.com/ChungZH/notepanda\\\" 156 | ") 157 | set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " 158 | Delete \\\"$DESKTOP\\\\Notepanda.lnk\\\" 159 | Delete \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Notepanda\\\\Notepanda.lnk\\\" 160 | RMDir \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Notepanda\\\" 161 | DeleteRegKey HKLM \\\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\notepanda\\\" 162 | ") 163 | set(CPACK_PACKAGE_INSTALL_DIRECTORY "notepanda") 164 | endif() 165 | endif() 166 | 167 | if(UNIX) 168 | if(BUILD_DEB) 169 | set(CPACK_GENERATOR "DEB") 170 | set(CPACK_DEBIAN_PACKAGE_NAME "Notepanda") 171 | set(CPACK_PACKAGE_CONTACT "chungzh07@gmail.com") 172 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "ChungZH") 173 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "qtbase5-dev, qttools5-dev, libkf5syntaxhighlighting-dev") 174 | endif() 175 | 176 | #if(BUILD_RPM) 177 | # set(CPACK_GENERATOR "RPM") 178 | # set(CPACK_RPM_PACKAGE_LICENSE "MIT") 179 | #endif() 180 | endif() 181 | 182 | if(WIN32) 183 | list(APPEND DIRS "${KF5SyntaxHighlighting_DIR}/../../../bin/") 184 | install(CODE "include(BundleUtilities) 185 | fixup_bundle(\"${APPS}\" \"\" \"${DIRS}\")") 186 | endif() 187 | 188 | include(CPack) 189 | 190 | target_link_libraries(notepanda Qt5::Core Qt5::Gui Qt5::Widgets KF5::SyntaxHighlighting) 191 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Haoshen Zhong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![banner](./images/banner.png) 2 | 3 | ###### Notepanda's icon is owned by ChungZH, any unanthorized usage will be blamed. 4 | 5 | # Notepanda 6 | 7 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a226323cc991499b9c324238949d3cb5)](https://app.codacy.com/manual/ChungZH/notepanda?utm_source=github.com&utm_medium=referral&utm_content=ChungZH/notepanda&utm_campaign=Badge_Grade_Dashboard) 8 | ![C++ & Qt](https://img.shields.io/badge/C%2B%2B%20%26%20Qt-forever-ff69b4?style=flat) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FChungZH%2Fnotepanda.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FChungZH%2Fnotepanda?ref=badge_shield) 9 | 10 | ![LICENSE](https://img.shields.io/github/license/ChungZH/notepanda) 11 | ![Made with love](https://img.shields.io/badge/Made%20with-love-red?style=flat) 12 | ![Build CI](https://github.com/ChungZH/notepanda/workflows/Build%20CI/badge.svg) 13 | 14 | 15 | > **A simple cross-platform notepad. Based on Qt and C++.** 16 | 17 | If you want to track the latest news of Notepanda, welcome to our [📰 Telegram channel](https://t.me/notepanda)! 18 | 19 | BTW you can read this post in my blog: [Notepanda 开发小结](https://blog.chungzh.cn/articles/notepanda/) 20 | 21 | ## Why write this text editor? 22 | 23 | I recently started getting started with Qt, this is my first Qt project, the first C++ project, and the first project in my life. I hope to develop my skills through this project. 24 | 25 | I'm not going to use this as a replacement for my other text editors because it's too simple and I am too poor :) 26 | 27 | So enjoy this toy 🍵️ 28 | 29 | ## Screenshots 30 | 31 | | Light | Dark | 32 | | :----------------------------------: | :---------------------------------: | 33 | | ![Light](./images/notepanda-sc1.png) | ![Dark](./images/notepanda-sc3.png) | 34 | | ![Light](./images/notepanda-sc2.png) | ![Dark](/images/notepanda-sc4.png) | 35 | 36 | ## Feature 37 | 38 | - Basic operation 39 | - Line numbers 40 | - Syntax highlight 41 | - Launch from the shell by typing: `notepanda` or `notepanda CMakeLists.txt` 42 | - Preview panel (Support Markdown & HTML) 43 | - Opening files with Drag and Drop. 44 | 45 | ## Install 46 | 47 | If you are Windows / MacOS / Linux user, you can go to [Releases](https://github.com/ChungZH/notepanda/releases) page. 48 | 49 | If you are using [Scoop](https://scoop.sh), you can get Notepanda from my sweet scoop bucket [🍑 peach](https://github.com/ChungZH/peach): 50 | 51 | ```powershell 52 | scoop bucket add peach https://github.com/ChungZH/peach 53 | scoop install notepanda 54 | ``` 55 | 56 | If you are Arch Linux (or Arch-based distros) user, you can get Notepanda from AUR. ![AUR Badge](https://img.shields.io/aur/version/notepanda) 57 | 58 | You may use an AUR helper such as `yay` to automatically handle the build process of AUR packages: 59 | 60 | ```sh 61 | yay -S notepanda 62 | ``` 63 | 64 | The latest version is `v0.1.4` now. 65 | 66 | ## Build 67 | 68 | For more information, go to [How to build - GitHub Wiki](https://github.com/ChungZH/notepanda/wiki/How-to-build---%E5%A6%82%E4%BD%95%E6%9E%84%E5%BB%BA). 69 | 70 | ### Required Tools 71 | 72 | - [CMake](https://cmake.org/) >= 3.1.0 73 | - [Ninja](https://ninja-build.org/) 74 | 75 | ### Required Dependencies 76 | 77 | - [Qt](https://www.qt.io/) >= 5.11 (recommendation 5.14) 78 | - [KSyntaxHighlighting](https://api.kde.org/frameworks/syntax-highlighting/html/index.html) 79 | - [Extra CMake Modules](https://api.kde.org/ecm/) 80 | 81 | ## Roadmap 82 | 83 | See [Projects](https://github.com/ChungZH/notepanda/projects/). 84 | 85 | 86 | ## Contributions / Bugs 87 | 88 | ![PRs Welcome](https://img.shields.io/badge/%F0%9F%A4%9DPRs-welcome-blue) 89 | 90 | You want to contribute to Notepanda? Awesome! ~~Please read the contribution guidelines for details and useful hints.~~ There is **no rules** now! 91 | 92 | If you found a bug or have a feature request, you can report it [GitHub Issue](https://github.com/ChungZH/notepanda/issues). 93 | 94 | 🙇‍♂️️ Thank you very much! 95 | 96 | ## Special Thanks 97 | 98 | - [Qv2ray](https://github.com/qv2ray/qv2ray) 99 | 100 | ## Copyright 101 | 102 | Copyright (c) ChungZH. 103 | 104 | **Libraries that have been used in Notepanda are listed below (Sorted by date added):** 105 | 106 | - Please see: [assets/CREDIT.md](assets/CREDIT.md) 107 | 108 | ## LICENSE 109 | 110 | ⚖ Released under the [MIT license](/LICENSE.Md). 111 | 112 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FChungZH%2Fnotepanda.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FChungZH%2Fnotepanda?ref=badge_large) 113 | 114 | ------ 115 | 116 | 🗒️ Notepanda © ChungZH. 117 | 118 | > [Blog](https://blog.chungzh.cn/) · [Portfolio](https://chungzh.cn/) · [GitHub](https://github.com/ChungZH) 119 | -------------------------------------------------------------------------------- /assets/CREDIT.md: -------------------------------------------------------------------------------- 1 | # Credit 2 | 3 | Copyright (c) 2020 ChungZH. 4 | 5 | **Libraries that have been used in Notepanda are listed below (Sorted by date added):** 6 | 7 | - Copyright (c) 2018 KDE : syntax-highlighting (MIT) 8 | - Copyright (c) 2020 Microsoft : Fluent System Icons (MIT) 9 | -------------------------------------------------------------------------------- /assets/notepanda.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Notepanda 3 | Type=Application 4 | Comment=A simple cross-platform notepad. Based on Qt and C++. 5 | Keywords=Notepad;Editor;Text; 6 | Terminal=false 7 | Icon=notepanda 8 | Exec=notepanda 9 | Categories=Qt; -------------------------------------------------------------------------------- /assets/notepanda.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON "../images/panda.ico" -------------------------------------------------------------------------------- /images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChungZH/notepanda/c3322084a1392af2c5055c5bd5b25fe5a19bd34b/images/banner.png -------------------------------------------------------------------------------- /images/fluentui-icon/LICENSE: -------------------------------------------------------------------------------- 1 | Fluent Mobile Icons 2 | Copyright (c) Microsoft Corporation. 3 | 4 | MIT License 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /images/fluentui-icon/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_add_24_filled 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/clipboard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_clipboard_24_regular 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/color_background.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /images/fluentui-icon/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_copy_24_regular 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/cut.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_cut_20_regular 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_delete_24_regular 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/document.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_document_24_regular 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/document_edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /images/fluentui-icon/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_info_24_regular 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_open_24_filled 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/pin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /images/fluentui-icon/preview.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /images/fluentui-icon/print.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_print_24_regular 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/read_only.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_read_only_24_regular 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/redo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_arrow_redo_24_filled 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/save.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /images/fluentui-icon/save_as.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_save_as_24_regular 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_settings_24_regular 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/sticker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_sticker_24_regular 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/fluentui-icon/undo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_arrow_undo_24_filled 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/notepanda-sc1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChungZH/notepanda/c3322084a1392af2c5055c5bd5b25fe5a19bd34b/images/notepanda-sc1.png -------------------------------------------------------------------------------- /images/notepanda-sc2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChungZH/notepanda/c3322084a1392af2c5055c5bd5b25fe5a19bd34b/images/notepanda-sc2.png -------------------------------------------------------------------------------- /images/notepanda-sc3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChungZH/notepanda/c3322084a1392af2c5055c5bd5b25fe5a19bd34b/images/notepanda-sc3.png -------------------------------------------------------------------------------- /images/notepanda-sc4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChungZH/notepanda/c3322084a1392af2c5055c5bd5b25fe5a19bd34b/images/notepanda-sc4.png -------------------------------------------------------------------------------- /images/notepanda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChungZH/notepanda/c3322084a1392af2c5055c5bd5b25fe5a19bd34b/images/notepanda.png -------------------------------------------------------------------------------- /images/panda.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChungZH/notepanda/c3322084a1392af2c5055c5bd5b25fe5a19bd34b/images/panda.ico -------------------------------------------------------------------------------- /makespec/BUILDVERSION: -------------------------------------------------------------------------------- 1 | 3300 2 | -------------------------------------------------------------------------------- /makespec/VERSION: -------------------------------------------------------------------------------- 1 | 0.1.4 2 | -------------------------------------------------------------------------------- /makespec/VERSIONSUFFIX: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | assets/CREDIT.md 4 | makespec/VERSION 5 | makespec/VERSIONSUFFIX 6 | makespec/BUILDVERSION 7 | images/panda.ico 8 | images/notepanda.png 9 | 10 | 11 | images/fluentui-icon/open.svg 12 | images/fluentui-icon/add.svg 13 | images/fluentui-icon/clipboard.svg 14 | images/fluentui-icon/info.svg 15 | images/fluentui-icon/redo.svg 16 | images/fluentui-icon/print.svg 17 | images/fluentui-icon/preview.svg 18 | images/fluentui-icon/pin.svg 19 | images/fluentui-icon/save_as.svg 20 | images/fluentui-icon/save.svg 21 | images/fluentui-icon/settings.svg 22 | images/fluentui-icon/sticker.svg 23 | images/fluentui-icon/undo.svg 24 | images/fluentui-icon/document.svg 25 | images/fluentui-icon/cut.svg 26 | images/fluentui-icon/copy.svg 27 | images/fluentui-icon/document_edit.svg 28 | images/fluentui-icon/delete.svg 29 | images/fluentui-icon/read_only.svg 30 | images/fluentui-icon/color_background.svg 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/core/configmanager.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file configmanager.cpp 9 | * @brief This file implements the ConfigManager class. 10 | * 11 | */ 12 | #include "configmanager.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | ConfigManager::ConfigManager(const QString &configuration, QObject *parent) 19 | : QObject(parent), configFile(configuration) 20 | { 21 | const QSettings::Format JsonFormat = 22 | QSettings::registerFormat("json", readJsonFile, writeJsonFile); 23 | settings = new QSettings(configFile, JsonFormat, this); 24 | 25 | readGeneralSettings(); 26 | 27 | // print all settings 28 | qDebug() << "" 29 | << "All settings:"; 30 | for (auto i : settings->allKeys()) { 31 | qDebug() << i << ":" << settings->value(i); 32 | } 33 | qDebug() << ""; 34 | } 35 | 36 | bool ConfigManager::readJsonFile(QIODevice &device, QSettings::SettingsMap &map) 37 | { 38 | QJsonParseError error; 39 | map = QJsonDocument::fromJson(device.readAll(), &error).toVariant().toMap(); 40 | return error.error == QJsonParseError::NoError; 41 | } 42 | 43 | bool ConfigManager::writeJsonFile(QIODevice &device, 44 | const QSettings::SettingsMap &map) 45 | { 46 | const auto json = QJsonDocument::fromVariant(map).toJson(); 47 | return device.write(json) == json.size(); 48 | } 49 | 50 | /** 51 | * @brief Save the settings. 52 | */ 53 | void ConfigManager::save() 54 | { 55 | settings->beginGroup("Editor"); 56 | settings->setValue("FontFamily", QVariant(editorFontFamily)); 57 | settings->setValue("FontSize", QVariant(editorFontSize)); 58 | settings->setValue("ColorTheme", QVariant(editorColorTheme)); 59 | settings->setValue("TabSize", QVariant(editorTabSize)); 60 | settings->setValue("IndentMode", QVariant(editorIndentMode)); 61 | settings->endGroup(); 62 | settings->setValue("StyleTheme", QVariant(styleTheme)); 63 | } 64 | 65 | /** 66 | * @brief Read general settings from `settings`. 67 | */ 68 | void ConfigManager::readGeneralSettings() 69 | { 70 | if (settings->contains("Editor/FontFamily")) { 71 | editorFontFamily = settings->value("Editor/FontFamily").toString(); 72 | } else { 73 | editorFontFamily = 74 | QFontDatabase::systemFont(QFontDatabase::FixedFont).toString(); 75 | } 76 | styleTheme = settings->value("StyleTheme", "Fusion").toString(); 77 | editorFontSize = settings->value("Editor/FontSize", 16).toInt(); 78 | editorColorTheme = 79 | settings->value("Editor/ColorTheme", "Default").toString(); 80 | editorTabSize = settings->value("Editor/TabSize", 4).toInt(); 81 | editorIndentMode = 82 | settings->value("Editor/IndentMode", "Spaces").toString(); 83 | } 84 | 85 | QString ConfigManager::getEditorFontFamily() const { return editorFontFamily; } 86 | void ConfigManager::setEditorFontFamily(const QString &fontname) 87 | { 88 | editorFontFamily = fontname; 89 | } 90 | 91 | QString ConfigManager::getStyleTheme() const { return styleTheme; } 92 | void ConfigManager::setStyleTheme(const QString &stylename) 93 | { 94 | styleTheme = stylename; 95 | } 96 | 97 | int ConfigManager::getEditorFontSize() const { return editorFontSize; } 98 | void ConfigManager::setEditorFontSize(const int &fontsize) 99 | { 100 | editorFontSize = fontsize; 101 | } 102 | 103 | QString ConfigManager::getEditorColorTheme() const { return editorColorTheme; } 104 | void ConfigManager::setEditorColorTheme(const QString &ctname) 105 | { 106 | editorColorTheme = ctname; 107 | } 108 | 109 | int ConfigManager::getEditorTabSize() const { return editorTabSize; } 110 | void ConfigManager::setEditorTabSize(const int &tabsize) 111 | { 112 | editorTabSize = tabsize; 113 | } 114 | 115 | QString ConfigManager::getEditorIndentMode() const { return editorIndentMode; } 116 | void ConfigManager::setEditorIndentMode(const QString &indentmode) 117 | { 118 | editorIndentMode = indentmode; 119 | } -------------------------------------------------------------------------------- /src/core/configmanager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file configmanager.h 9 | * @brief This file declares the ConfigManager class for management 10 | * configuration. 11 | */ 12 | #ifndef CONFIGMANAGER_H 13 | #define CONFIGMANAGER_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | /** 21 | * @brief The ConfigManager class 22 | */ 23 | class ConfigManager : public QObject 24 | { 25 | Q_OBJECT 26 | public: 27 | explicit ConfigManager(const QString &configuration, 28 | QObject *parent = nullptr); 29 | void save(); 30 | void readGeneralSettings(); 31 | 32 | static bool readJsonFile(QIODevice &device, QSettings::SettingsMap &map); 33 | static bool writeJsonFile(QIODevice &device, 34 | const QSettings::SettingsMap &map); 35 | 36 | // 37 | QString getEditorFontFamily() const; 38 | void setEditorFontFamily(const QString &fontname); 39 | QString getStyleTheme() const; 40 | void setStyleTheme(const QString &stylename); 41 | int getEditorFontSize() const; 42 | void setEditorFontSize(const int &fontsize); 43 | QString getEditorColorTheme() const; 44 | void setEditorColorTheme(const QString &ctname); 45 | int getEditorTabSize() const; 46 | void setEditorTabSize(const int &tabsize); 47 | QString getEditorIndentMode() const; 48 | void setEditorIndentMode(const QString &indentmode); 49 | 50 | private: 51 | QSettings *settings; 52 | QString configFile; 53 | QString editorFontFamily; 54 | QString styleTheme; 55 | int editorFontSize; 56 | QString editorColorTheme; 57 | int editorTabSize; 58 | QString editorIndentMode; 59 | }; 60 | 61 | #endif // CONFIGMANAGER_H 62 | -------------------------------------------------------------------------------- /src/core/texteditor.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file texteditor.cpp 9 | * @brief This file implements the TextEditor class. 10 | * 11 | */ 12 | #include "texteditor.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | // KSyntaxHighlighting 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "../ui/linenumberarea.h" 40 | 41 | TextEditor::TextEditor(ConfigManager *cfManager, QWidget *parent) 42 | : QPlainTextEdit(parent), 43 | configManager(cfManager), 44 | m_highlighter(new KSyntaxHighlighting::SyntaxHighlighter(document())) 45 | 46 | { 47 | const auto theme = m_repository.theme(configManager->getEditorColorTheme()); 48 | setTheme(theme); 49 | 50 | setTabStopDistance(QFontMetricsF(QFont(configManager->getEditorFontFamily(), 51 | configManager->getEditorFontSize())) 52 | .horizontalAdvance(' ') * 53 | configManager->getEditorTabSize()); 54 | 55 | // Line number area 56 | lineNumberArea = new LineNumberArea(this); 57 | 58 | connect(this, &TextEditor::blockCountChanged, this, 59 | &TextEditor::updateLineNumberAreaWidth); 60 | connect(this, &TextEditor::updateRequest, this, 61 | &TextEditor::updateLineNumberArea); 62 | connect(this, &TextEditor::cursorPositionChanged, this, 63 | &TextEditor::highlightCurrentLine); 64 | 65 | updateLineNumberAreaWidth(0); 66 | highlightCurrentLine(); 67 | 68 | currentMode = 0; 69 | 70 | TextEditor::setFont(QFont(configManager->getEditorFontFamily(), 71 | configManager->getEditorFontSize())); 72 | setCurrentFile(QString()); 73 | lineNumberArea->resize(0, 0); 74 | } 75 | 76 | bool TextEditor::maybeSave() 77 | { 78 | if (!QPlainTextEdit::document()->isModified()) return true; 79 | const QMessageBox::StandardButton ret = QMessageBox::warning( 80 | this, tr("Application"), 81 | tr("The document has been modified.\n" 82 | "Do you want to save your changes?"), 83 | QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); 84 | switch (ret) { 85 | case QMessageBox::Save: 86 | save(); 87 | return true; 88 | case QMessageBox::Cancel: 89 | return false; 90 | default: 91 | break; 92 | } 93 | return true; 94 | } 95 | 96 | bool TextEditor::newDocument() 97 | { 98 | clear(); 99 | setCurrentFile(QString()); 100 | emit changeTitle(); 101 | return true; 102 | } 103 | 104 | bool TextEditor::open() 105 | { 106 | if (maybeSave()) { 107 | QString fileName = 108 | QFileDialog::getOpenFileName(this, tr("Open the file")); 109 | QFile file(fileName); 110 | if (!file.open(QFile::ReadOnly | QFile::Text)) { 111 | QMessageBox::warning(this, tr("Warning"), 112 | tr("Cannot open file: ") + file.errorString()); 113 | qWarning() << "[WARN 1] Failed to open" << fileName << ":" 114 | << file.errorString(); 115 | return false; 116 | } 117 | 118 | clear(); 119 | 120 | auto def = m_repository.definitionForFileName(fileName); 121 | if (currentMode == 1) def = m_repository.definitionForName("Markdown"); 122 | m_highlighter->setDefinition(def); 123 | 124 | QTextStream in(&file); 125 | 126 | #ifndef QT_NO_CURSOR 127 | QGuiApplication::setOverrideCursor(Qt::WaitCursor); 128 | #endif 129 | 130 | setPlainText(in.readAll()); 131 | 132 | #ifndef QT_NO_CURSOR 133 | QGuiApplication::restoreOverrideCursor(); 134 | #endif 135 | file.close(); 136 | 137 | setCurrentFile(fileName); 138 | emit changeTitle(); 139 | return true; 140 | } 141 | return false; 142 | } 143 | 144 | /** 145 | * @brief Open from command line like `notepanda texteditor.cpp`. 146 | * It doesn't need `maybeSave()` because at this time notepanda opens for 147 | * the first time. 148 | */ 149 | void TextEditor::openFile(const QString &fileName) 150 | { 151 | QFile file(fileName); 152 | if (!file.open(QIODevice::ReadOnly | QFile::Text)) { 153 | QMessageBox::warning(this, tr("Warning"), 154 | tr("Cannot open file: ") + file.errorString()); 155 | qWarning() << "[WARN 2] Failed to open" << fileName << ":" 156 | << file.errorString(); 157 | return; 158 | } 159 | 160 | const auto def = m_repository.definitionForFileName(fileName); 161 | m_highlighter->setDefinition(def); 162 | 163 | QTextStream in(&file); 164 | 165 | #ifndef QT_NO_CURSOR 166 | QGuiApplication::setOverrideCursor(Qt::WaitCursor); 167 | #endif 168 | setPlainText(in.readAll()); 169 | #ifndef QT_NO_CURSOR 170 | QGuiApplication::restoreOverrideCursor(); 171 | #endif 172 | setCurrentFile(fileName); 173 | emit changeTitle(); 174 | } 175 | 176 | void TextEditor::save() 177 | { 178 | QGuiApplication::setOverrideCursor(Qt::WaitCursor); 179 | QString fileName, errorMessage; 180 | if (currentFile.isEmpty()) 181 | fileName = QFileDialog::getSaveFileName(this, tr("Save")); 182 | else 183 | fileName = currentFile; 184 | 185 | QSaveFile file(fileName); 186 | 187 | if (file.open(QFile::WriteOnly | QFile::Text)) { 188 | setCurrentFile(fileName); 189 | emit changeTitle(); 190 | 191 | QTextStream out(&file); 192 | out << toPlainText(); 193 | 194 | if (!file.commit()) 195 | errorMessage = "[WARN 5] Cannot save file: " + file.errorString(); 196 | } else { 197 | errorMessage = "[WARN 3] Cannot save file: " + file.errorString(); 198 | } 199 | 200 | if (!errorMessage.isEmpty()) { 201 | qWarning() << errorMessage; 202 | QMessageBox::warning(this, tr("Warning"), 203 | tr("Cannot save file: ") + file.errorString()); 204 | } 205 | 206 | QGuiApplication::restoreOverrideCursor(); 207 | } 208 | 209 | void TextEditor::saveAs() 210 | { 211 | QGuiApplication::setOverrideCursor(Qt::WaitCursor); 212 | QString fileName = QFileDialog::getSaveFileName(this, tr("Save as")); 213 | QSaveFile file(fileName); 214 | 215 | if (!file.open(QFile::WriteOnly | QFile::Text)) { 216 | QMessageBox::warning(this, tr("Warning"), 217 | tr("Cannot save file: ") + file.errorString()); 218 | qWarning() << "[WARN 4] Failed to save" << fileName << ":" 219 | << file.errorString(); 220 | return; 221 | } 222 | 223 | setCurrentFile(fileName); 224 | 225 | emit changeTitle(); 226 | QTextStream out(&file); 227 | out << toPlainText(); 228 | 229 | QGuiApplication::restoreOverrideCursor(); 230 | 231 | if (!file.commit()) { 232 | QMessageBox::warning( 233 | this, tr("Warning"), 234 | tr("Cannot save file: ") + fileName + file.errorString()); 235 | qWarning() << "[WARN 6] Failed to save" << fileName << ":" 236 | << file.errorString(); 237 | } 238 | } 239 | 240 | void TextEditor::print() 241 | { 242 | #if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer) 243 | QPrinter printDev; 244 | #if QT_CONFIG(printdialog) 245 | QPrintDialog dialog(&printDev, this); 246 | if (dialog.exec() == QDialog::Rejected) return; 247 | #endif // QT_CONFIG(printdialog) 248 | QPlainTextEdit: 249 | print(&printDev); 250 | #endif // QT_CONFIG(printer) 251 | } 252 | 253 | void TextEditor::undo() { QPlainTextEdit::undo(); } 254 | 255 | void TextEditor::redo() { QPlainTextEdit::redo(); } 256 | 257 | void TextEditor::copy() 258 | { 259 | #if QT_CONFIG(clipboard) 260 | QPlainTextEdit::copy(); 261 | #endif 262 | } 263 | 264 | void TextEditor::paste() 265 | { 266 | #if QT_CONFIG(clipboard) 267 | QPlainTextEdit::paste(); 268 | #endif 269 | } 270 | 271 | void TextEditor::cut() 272 | { 273 | #if QT_CONFIG(clipboard) 274 | QPlainTextEdit::cut(); 275 | #endif 276 | } 277 | 278 | int TextEditor::lineNumberAreaWidth() 279 | { 280 | int digits = 1; 281 | int max = qMax(1, blockCount()); 282 | while (max >= 10) { 283 | max /= 10; 284 | ++digits; 285 | } 286 | 287 | return 15 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits + 288 | fontMetrics().lineSpacing(); 289 | } 290 | 291 | void TextEditor::updateLineNumberAreaWidth(int /* newBlockCount */) 292 | { 293 | if (currentMode == 1) 294 | setViewportMargins(0, 0, 0, 0); 295 | else 296 | setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); 297 | } 298 | 299 | void TextEditor::updateLineNumberArea(const QRect &rect, int dy) 300 | { 301 | if (dy) 302 | lineNumberArea->scroll(0, dy); 303 | else 304 | lineNumberArea->update(0, rect.y(), lineNumberArea->sizeHint().width(), 305 | rect.height()); 306 | if (rect.contains(viewport()->rect())) updateLineNumberAreaWidth(0); 307 | } 308 | 309 | void TextEditor::resizeEvent(QResizeEvent *e) 310 | { 311 | QPlainTextEdit::resizeEvent(e); 312 | 313 | QRect cr = contentsRect(); 314 | lineNumberArea->setGeometry( 315 | QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); 316 | } 317 | 318 | void TextEditor::contextMenuEvent(QContextMenuEvent *event) 319 | { 320 | // if you want to **extend** the standard context menu, use 321 | // auto menu = createStandardContextMenu(event->pos()); 322 | 323 | // if you want to customize the context menu, use 324 | QMenu *menu = new QMenu; 325 | 326 | // Read only 327 | QAction *readOnlyAct = 328 | new QAction(QIcon(":/icons/read_only.svg"), tr("Read-Only mode"), this); 329 | readOnlyAct->setCheckable(true); 330 | readOnlyAct->setChecked(isReadOnly()); 331 | menu->addAction(readOnlyAct); 332 | connect(readOnlyAct, &QAction::triggered, [&]() { 333 | setReadOnly(!isReadOnly()); 334 | readOnlyAct->setChecked(isReadOnly()); 335 | emit readOnlyChanged(); 336 | }); 337 | 338 | // syntax selection 339 | auto hlActionGroup = new QActionGroup(menu); 340 | hlActionGroup->setExclusive(true); 341 | auto hlGroupMenu = menu->addMenu(QStringLiteral("Syntax")); 342 | QMenu *hlSubMenu = hlGroupMenu; 343 | QString currentGroup; 344 | for (const auto &def : m_repository.definitions()) { 345 | if (def.isHidden()) continue; 346 | if (currentGroup != def.section()) { 347 | currentGroup = def.section(); 348 | hlSubMenu = hlGroupMenu->addMenu(def.translatedSection()); 349 | } 350 | 351 | auto action = hlSubMenu->addAction(def.translatedName()); 352 | action->setCheckable(true); 353 | action->setData(def.name()); 354 | hlActionGroup->addAction(action); 355 | if (def.name() == m_highlighter->definition().name()) 356 | action->setChecked(true); 357 | } 358 | connect(hlActionGroup, &QActionGroup::triggered, this, 359 | [this](QAction *action) { 360 | const auto defName = action->data().toString(); 361 | const auto def = m_repository.definitionForName(defName); 362 | m_highlighter->setDefinition(def); 363 | }); 364 | 365 | // clear 366 | QAction *clearAct = new QAction(tr("Clear"), this); 367 | menu->addAction(clearAct); 368 | connect(clearAct, &QAction::triggered, [&]() { newDocument(); }); 369 | 370 | menu->exec(event->globalPos()); 371 | delete menu; 372 | } 373 | 374 | void TextEditor::keyPressEvent(QKeyEvent *e) 375 | { 376 | if (e->key() == Qt::Key_Tab) { 377 | if (this->textCursor().hasSelection()) { 378 | qDebug() << "wtf"; 379 | addInEachLineOfSelection( 380 | QRegularExpression("^"), 381 | configManager->getEditorIndentMode() == "Spaces" 382 | ? QString(configManager->getEditorTabSize(), ' ') 383 | : "\t"); 384 | return; 385 | } else if (configManager->getEditorIndentMode() == "Spaces") { 386 | this->insertPlainText( 387 | QString(configManager->getEditorTabSize(), ' ')); 388 | e->accept(); 389 | } 390 | } else if (e->key() == '(') { 391 | this->insertPlainText("()"); 392 | this->moveCursor(QTextCursor::MoveOperation::Left, 393 | QTextCursor::MoveAnchor); 394 | e->accept(); 395 | } else if (e->key() == '{') { 396 | this->insertPlainText("{}"); 397 | this->moveCursor(QTextCursor::MoveOperation::Left, 398 | QTextCursor::MoveAnchor); 399 | e->accept(); 400 | } else if (e->key() == '[') { 401 | this->insertPlainText("[]"); 402 | this->moveCursor(QTextCursor::MoveOperation::Left, 403 | QTextCursor::MoveAnchor); 404 | e->accept(); 405 | } 406 | QPlainTextEdit::keyPressEvent(e); 407 | } 408 | 409 | void TextEditor::addInEachLineOfSelection(const QRegularExpression ®ex, 410 | const QString &str) 411 | { 412 | auto cursor = textCursor(); 413 | auto lines = toPlainText().remove('\r').split('\n'); 414 | int selectionStart = cursor.selectionStart(); 415 | int selectionEnd = cursor.selectionEnd(); 416 | bool cursorAtEnd = cursor.position() == selectionEnd; 417 | cursor.setPosition(selectionStart); 418 | int lineStart = cursor.blockNumber(); 419 | cursor.setPosition(selectionEnd); 420 | int lineEnd = cursor.blockNumber(); 421 | QString newText; 422 | QTextStream stream(&newText); 423 | for (int i = lineStart; i <= lineEnd; ++i) { 424 | auto line = lines[i]; 425 | stream << line.insert(line.indexOf(regex), str); 426 | if (i != lineEnd) 427 | #if QT_VERSION >= 0x50E00 428 | stream << Qt::endl; 429 | #else 430 | stream << endl; 431 | #endif 432 | } 433 | cursor.movePosition(QTextCursor::Start); 434 | cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, 435 | lineStart); 436 | cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor, 437 | lineEnd - lineStart); 438 | cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); 439 | cursor.insertText(newText); 440 | int pos = selectionStart + str.length(); 441 | int pos2 = selectionEnd + str.length() * (lineEnd - lineStart + 1); 442 | if (cursorAtEnd) { 443 | cursor.setPosition(pos); 444 | cursor.setPosition(pos2, QTextCursor::KeepAnchor); 445 | } else { 446 | cursor.setPosition(pos2); 447 | cursor.setPosition(pos, QTextCursor::KeepAnchor); 448 | } 449 | setTextCursor(cursor); 450 | } 451 | 452 | void TextEditor::highlightCurrentLine() 453 | { 454 | QList extraSelections; 455 | 456 | if (!isReadOnly()) { 457 | QTextEdit::ExtraSelection selection; 458 | 459 | selection.format.setBackground( 460 | QColor(m_highlighter->theme().editorColor( 461 | KSyntaxHighlighting::Theme::CurrentLine))); 462 | selection.format.setProperty(QTextFormat::FullWidthSelection, true); 463 | selection.cursor = textCursor(); 464 | selection.cursor.clearSelection(); 465 | extraSelections.append(selection); 466 | } 467 | 468 | if (currentMode == 1) extraSelections.clear(); 469 | 470 | setExtraSelections(extraSelections); 471 | } 472 | 473 | void TextEditor::lineNumberAreaPaintEvent(QPaintEvent *event) 474 | { 475 | QPainter painter(lineNumberArea); 476 | painter.fillRect(event->rect(), 477 | m_highlighter->theme().editorColor( 478 | KSyntaxHighlighting::Theme::IconBorder)); 479 | 480 | auto block = firstVisibleBlock(); 481 | auto blockNumber = block.blockNumber(); 482 | int top = blockBoundingGeometry(block).translated(contentOffset()).top(); 483 | int bottom = top + blockBoundingRect(block).height(); 484 | const int currentBlockNumber = textCursor().blockNumber(); 485 | 486 | const auto foldingMarkerSize = fontMetrics().lineSpacing(); 487 | 488 | while (block.isValid() && top <= event->rect().bottom()) { 489 | if (block.isVisible() && bottom >= event->rect().top()) { 490 | const auto number = QString::number(blockNumber + 1); 491 | painter.setPen(m_highlighter->theme().editorColor( 492 | (blockNumber == currentBlockNumber) 493 | ? KSyntaxHighlighting::Theme::CurrentLineNumber 494 | : KSyntaxHighlighting::Theme::LineNumbers)); 495 | painter.drawText(0, top, 496 | lineNumberArea->width() - foldingMarkerSize + 3, 497 | fontMetrics().height(), Qt::AlignRight, number); 498 | } 499 | 500 | // folding marker 501 | if (block.isVisible() && isFoldable(block)) { 502 | QPolygonF polygon; 503 | if (isFolded(block)) { 504 | polygon << QPointF(foldingMarkerSize * 0.4, 505 | foldingMarkerSize * 0.25); 506 | polygon << QPointF(foldingMarkerSize * 0.4, 507 | foldingMarkerSize * 0.75); 508 | polygon << QPointF(foldingMarkerSize * 0.8, 509 | foldingMarkerSize * 0.5); 510 | } else { 511 | polygon << QPointF(foldingMarkerSize * 0.25, 512 | foldingMarkerSize * 0.4); 513 | polygon << QPointF(foldingMarkerSize * 0.75, 514 | foldingMarkerSize * 0.4); 515 | polygon << QPointF(foldingMarkerSize * 0.5, 516 | foldingMarkerSize * 0.8); 517 | } 518 | painter.save(); 519 | painter.setRenderHint(QPainter::Antialiasing); 520 | painter.setPen(Qt::NoPen); 521 | painter.setBrush(QColor(m_highlighter->theme().editorColor( 522 | KSyntaxHighlighting::Theme::CodeFolding))); 523 | painter.translate(lineNumberArea->width() - foldingMarkerSize + 3, 524 | top); 525 | painter.drawPolygon(polygon); 526 | painter.restore(); 527 | } 528 | 529 | block = block.next(); 530 | top = bottom; 531 | bottom = top + blockBoundingRect(block).height(); 532 | ++blockNumber; 533 | } 534 | } 535 | 536 | void TextEditor::dropEvent(QDropEvent *e) 537 | { 538 | QList urls = e->mimeData()->urls(); 539 | 540 | for (auto iUrl : urls) { 541 | QString filename = iUrl.toLocalFile(); 542 | qDebug() << filename; 543 | emit openFileInNewTab(filename); 544 | } 545 | 546 | e->accept(); 547 | } 548 | 549 | void LineNumberArea::mouseReleaseEvent(QMouseEvent *event) 550 | { 551 | if (event->x() >= width() - textEditor->fontMetrics().lineSpacing()) { 552 | auto block = textEditor->blockAtPosition(event->y()); 553 | if (!block.isValid() || !textEditor->isFoldable(block)) return; 554 | textEditor->toggleFold(block); 555 | } 556 | QWidget::mouseReleaseEvent(event); 557 | } 558 | 559 | QTextBlock TextEditor::blockAtPosition(int y) const 560 | { 561 | auto block = firstVisibleBlock(); 562 | if (!block.isValid()) return QTextBlock(); 563 | 564 | int top = blockBoundingGeometry(block).translated(contentOffset()).top(); 565 | int bottom = top + blockBoundingRect(block).height(); 566 | do { 567 | if (top <= y && y <= bottom) return block; 568 | block = block.next(); 569 | top = bottom; 570 | bottom = top + blockBoundingRect(block).height(); 571 | } while (block.isValid()); 572 | return QTextBlock(); 573 | } 574 | 575 | bool TextEditor::isFoldable(const QTextBlock &block) const 576 | { 577 | return m_highlighter->startsFoldingRegion(block); 578 | } 579 | 580 | bool TextEditor::isFolded(const QTextBlock &block) const 581 | { 582 | if (!block.isValid()) return false; 583 | const auto nextBlock = block.next(); 584 | if (!nextBlock.isValid()) return false; 585 | return !nextBlock.isVisible(); 586 | } 587 | 588 | void TextEditor::toggleFold(const QTextBlock &startBlock) 589 | { 590 | // we also want to fold the last line of the region, therefore the ".next()" 591 | const auto endBlock = 592 | m_highlighter->findFoldingRegionEnd(startBlock).next(); 593 | 594 | if (isFolded(startBlock)) { 595 | // unfold 596 | auto block = startBlock.next(); 597 | while (block.isValid() && !block.isVisible()) { 598 | block.setVisible(true); 599 | block.setLineCount(block.layout()->lineCount()); 600 | block = block.next(); 601 | } 602 | 603 | } else { 604 | // fold 605 | auto block = startBlock.next(); 606 | while (block.isValid() && block != endBlock) { 607 | block.setVisible(false); 608 | block.setLineCount(0); 609 | block = block.next(); 610 | } 611 | } 612 | 613 | // redraw document 614 | document()->markContentsDirty( 615 | startBlock.position(), endBlock.position() - startBlock.position() + 1); 616 | 617 | // update scrollbars 618 | emit document()->documentLayout()->documentSizeChanged( 619 | document()->documentLayout()->documentSize()); 620 | } 621 | 622 | void TextEditor::setTheme(const KSyntaxHighlighting::Theme &theme) 623 | { 624 | auto pal = qApp->palette(); 625 | if (theme.isValid()) { 626 | pal.setColor( 627 | QPalette::Base, 628 | theme.editorColor(KSyntaxHighlighting::Theme::BackgroundColor)); 629 | pal.setColor( 630 | QPalette::Highlight, 631 | theme.editorColor(KSyntaxHighlighting::Theme::TextSelection)); 632 | } 633 | 634 | setPalette(pal); 635 | 636 | m_highlighter->setTheme(theme); 637 | m_highlighter->rehighlight(); 638 | highlightCurrentLine(); 639 | } 640 | 641 | void TextEditor::setEditorFont(const QFont &font) 642 | { 643 | QFont f = QFont(font.family(), configManager->getEditorFontSize()); 644 | QPlainTextEdit::setFont(f); 645 | configManager->setEditorFontFamily(font.family()); 646 | lineNumberArea->setFont(f); 647 | } 648 | 649 | void TextEditor::setEditorFontSize(const int &size) 650 | { 651 | QFont font = configManager->getEditorFontFamily(); 652 | font.setPointSize(size); 653 | QPlainTextEdit::setFont(font); 654 | configManager->setEditorFontSize(size); 655 | } 656 | 657 | void TextEditor::setEditorColorTheme(const QString &ctname) 658 | { 659 | const auto theme = m_repository.theme(ctname); 660 | setTheme(theme); 661 | } 662 | 663 | void TextEditor::setCurrentFile(const QString &fileName) 664 | { 665 | currentFile = fileName; 666 | if (!fileName.isEmpty()) currentFileName = QFileInfo(fileName).fileName(); 667 | document()->setModified(false); 668 | setWindowModified(false); 669 | emit modifiedFalse(); 670 | } 671 | 672 | void TextEditor::switchMode(const int &mode) 673 | { 674 | if (mode == 0) { 675 | lineNumberArea->show(); 676 | currentMode = mode; 677 | QPlainTextEdit::setFont(QFont(configManager->getEditorFontFamily(), 678 | configManager->getEditorFontSize())); 679 | setStyleSheet("background: " + 680 | QString::number(m_highlighter->theme().editorColor( 681 | KSyntaxHighlighting::Theme::BackgroundColor))); 682 | setEditorFont(configManager->getEditorFontFamily()); 683 | } else if (mode == 1) { 684 | lineNumberArea->hide(); 685 | setViewportMargins(0, 0, 0, 0); 686 | currentMode = mode; 687 | setFont(QFont( 688 | QFontDatabase::systemFont(QFontDatabase::GeneralFont).toString(), 689 | configManager->getEditorFontSize())); 690 | auto def = m_repository.definitionForName("Markdown"); 691 | m_highlighter->setDefinition(def); 692 | } 693 | } 694 | 695 | // QPlainTextEdit can only zoomIn in Read-Only mode 696 | void TextEditor::wheelEvent(QWheelEvent *e) 697 | { 698 | // Qt: if (!(d->control->textInteractionFlags() & Qt::TextEditable)) 699 | if (e->modifiers() & Qt::ControlModifier) { 700 | float delta = e->angleDelta().y() / 120.f; 701 | zoomInF(delta); 702 | return; 703 | } 704 | 705 | QAbstractScrollArea::wheelEvent(e); 706 | updateMicroFocus(); 707 | } 708 | 709 | void TextEditor::updateSyntaxHighlight() 710 | { 711 | const auto def = m_repository.definitionForFileName(currentFile); 712 | m_highlighter->setDefinition(def); 713 | } 714 | -------------------------------------------------------------------------------- /src/core/texteditor.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file texteditor.h 9 | * @brief This file declares the TextEditor class. 10 | * 11 | */ 12 | #ifndef TEXTEDITOR_H 13 | #define TEXTEDITOR_H 14 | 15 | #include 16 | 17 | #include 18 | 19 | #include "configmanager.h" 20 | 21 | QT_BEGIN_NAMESPACE 22 | class QPaintEvent; 23 | class QResizeEvent; 24 | class QSize; 25 | class QWidget; 26 | class QFileDialog; 27 | QT_END_NAMESPACE 28 | 29 | namespace KSyntaxHighlighting 30 | { 31 | class SyntaxHighlighter; 32 | } 33 | 34 | class TextEditor : public QPlainTextEdit 35 | { 36 | Q_OBJECT 37 | 38 | public: 39 | TextEditor(ConfigManager *cfManager, QWidget *parent = nullptr); 40 | QString currentFile, currentFileName; 41 | void openFile(const QString &fileName); 42 | 43 | void lineNumberAreaPaintEvent(QPaintEvent *event); 44 | int lineNumberAreaWidth(); 45 | QTextBlock blockAtPosition(int y) const; 46 | bool isFoldable(const QTextBlock &block) const; 47 | bool isFolded(const QTextBlock &block) const; 48 | void toggleFold(const QTextBlock &block); 49 | 50 | protected: 51 | void resizeEvent(QResizeEvent *event) override; 52 | void contextMenuEvent(QContextMenuEvent *event) override; 53 | void wheelEvent(QWheelEvent *e) override; 54 | void keyPressEvent(QKeyEvent *e) override; 55 | void dropEvent(QDropEvent *e) override; 56 | 57 | public slots: 58 | bool maybeSave(); 59 | bool newDocument(); 60 | bool open(); 61 | void save(); 62 | void saveAs(); 63 | void print(); 64 | void undo(); 65 | void redo(); 66 | void copy(); 67 | void paste(); 68 | void cut(); 69 | void updateLineNumberAreaWidth(int newBlockCount); 70 | void highlightCurrentLine(); 71 | void updateLineNumberArea(const QRect &rect, int dy); 72 | void setEditorFont(const QFont &font); 73 | void setEditorFontSize(const int &size); 74 | void setEditorColorTheme(const QString &ctname); 75 | void setCurrentFile(const QString &fileName); 76 | void updateSyntaxHighlight(); 77 | 78 | /** 79 | * @brief switchMode 80 | * @param mode 81 | * 0: normal mode 82 | * 1: sticky mode 83 | */ 84 | void switchMode(const int &mode); 85 | 86 | private: 87 | ConfigManager *configManager; 88 | 89 | KSyntaxHighlighting::Repository m_repository; 90 | KSyntaxHighlighting::SyntaxHighlighter *m_highlighter; 91 | void setTheme(const KSyntaxHighlighting::Theme &theme); 92 | 93 | QWidget *lineNumberArea; 94 | int currentMode; 95 | 96 | void addInEachLineOfSelection(const QRegularExpression ®ex, 97 | const QString &str); 98 | 99 | signals: 100 | void changeTitle(); 101 | void modifiedFalse(); 102 | void readOnlyChanged(); 103 | void openFileInNewTab(const QString &file); 104 | }; 105 | 106 | #endif // TEXTEDITOR_H 107 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file main.cpp 9 | * @brief This file is main of the project. 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "./core/configmanager.h" 22 | #include "./ui/mainwindow.h" 23 | 24 | int main(int argc, char *argv[]) 25 | { 26 | QApplication App(argc, argv); 27 | 28 | App.setOrganizationName("ChungZH"); 29 | App.setApplicationName("Notepanda"); 30 | App.setApplicationVersion("0.1.4"); 31 | 32 | QCommandLineParser parser; 33 | parser.addHelpOption(); 34 | parser.addVersionOption(); 35 | parser.addPositionalArgument("source", "The source file to open."); 36 | QCommandLineOption configFileOption("c", "specify configuration file.", 37 | "config.json"); 38 | parser.addOption(configFileOption); 39 | parser.process(App); 40 | 41 | QString configFile = parser.value(configFileOption); 42 | if (configFile.isEmpty()) { 43 | #ifdef Q_OS_WIN 44 | 45 | /* 46 | if (QLocale::system().country() == QLocale::China || 47 | QLocale::system().language() == QLocale::Chinese) 48 | App.setFont(QFont("Microsoft Yahei", 9, QFont::Normal, false)); 49 | else */ 50 | App.setFont(QFont("Segoe UI", 9, QFont::Normal, false)); 51 | 52 | if (QDir(App.applicationDirPath() + "/config").exists()) { 53 | configFile = App.applicationDirPath() + "/config/notepanda.json"; 54 | } else { 55 | QDir configDir = QStandardPaths::writableLocation( 56 | QStandardPaths::StandardLocation::AppConfigLocation); 57 | configFile = configDir.absolutePath() + "/config.json"; 58 | } 59 | #else 60 | QDir configDir = QDir::homePath() + "/.config/notepanda"; 61 | configFile = configDir.absolutePath() + "/config.json"; 62 | if (!configDir.exists()) { 63 | configDir.mkpath(configDir.absolutePath()); 64 | } 65 | #endif 66 | } 67 | qDebug() << configFile; 68 | 69 | ConfigManager *configManager; 70 | configManager = new ConfigManager(configFile); 71 | 72 | App.setStyle(QStyleFactory::create(configManager->getStyleTheme())); 73 | 74 | MainWindow notepanda(configManager); 75 | notepanda.show(); 76 | if (parser.positionalArguments().size() == 1) 77 | notepanda.plainTextEdit->openFile(parser.positionalArguments().at(0)); 78 | 79 | qInfo() << QStringLiteral("Welcome to Notepanda!") << ""; 80 | 81 | return App.exec(); 82 | } 83 | -------------------------------------------------------------------------------- /src/ui/aboutwindow.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file aboutwindow.cpp 9 | * @brief This file implements the AboutWindow class. 10 | * 11 | */ 12 | #include "aboutwindow.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "ui_aboutwindow.h" 23 | 24 | AboutWindow::AboutWindow(QWidget *parent) 25 | : QDialog(parent), ui(new Ui::AboutWindow) 26 | { 27 | ui->setupUi(this); 28 | setWindowTitle("About - Notepanda"); 29 | 30 | QFile ver(":/asset/makespec/VERSION"); 31 | QFile verSuf(":/asset/makespec/VERSIONSUFFIX"); 32 | QFile buildVer(":/asset/makespec/BUILDVERSION"); 33 | ver.open(QFile::ReadOnly); 34 | verSuf.open(QFile::ReadOnly); 35 | buildVer.open(QFile::ReadOnly); 36 | 37 | QTextStream t_ver(&ver); 38 | QTextStream t_vSf(&verSuf); 39 | QTextStream t_bVe(&buildVer); 40 | 41 | QString verString = 42 | t_ver.readAll() + t_vSf.readAll() + " BV" + t_bVe.readAll(); 43 | verString.remove("\n"); 44 | 45 | ui->label_5->setText(verString); 46 | ui->label_2->setFont(QFont( 47 | QFontDatabase::systemFont(QFontDatabase::FixedFont).toString(), 15)); 48 | ui->label_3->setFont(QFont( 49 | QFontDatabase::systemFont(QFontDatabase::FixedFont).toString(), 15)); 50 | ui->label_4->setFont(QFont( 51 | QFontDatabase::systemFont(QFontDatabase::FixedFont).toString(), 15)); 52 | ui->label_5->setFont(QFont( 53 | QFontDatabase::systemFont(QFontDatabase::FixedFont).toString(), 15)); 54 | ui->label_6->setFont(QFont( 55 | QFontDatabase::systemFont(QFontDatabase::FixedFont).toString(), 15)); 56 | ui->label_7->setFont(QFont( 57 | QFontDatabase::systemFont(QFontDatabase::FixedFont).toString(), 15)); 58 | } 59 | -------------------------------------------------------------------------------- /src/ui/aboutwindow.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file aboutwindow.h 9 | * @brief This file declares the AboutWindow class. 10 | */ 11 | #ifndef ABOUTWINDOW_H 12 | #define ABOUTWINDOW_H 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "../core/configmanager.h" 22 | 23 | class AboutWindow : public QDialog, private Ui::AboutWindow 24 | { 25 | Q_OBJECT 26 | 27 | public: 28 | explicit AboutWindow(QWidget *parent = nullptr); 29 | 30 | private: 31 | Ui::AboutWindow *ui; 32 | }; 33 | 34 | #endif // ABOUTWINDOW_H 35 | -------------------------------------------------------------------------------- /src/ui/aboutwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | AboutWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 827 10 | 472 11 | 12 | 13 | 14 | 15 | Arial 16 | 17 | 18 | 19 | Dialog 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | image: url(:/asset/images/notepanda.png) 33 | 34 | 35 | QFrame::StyledPanel 36 | 37 | 38 | QFrame::Raised 39 | 40 | 41 | 42 | 43 | 44 | 45 | Qt::Vertical 46 | 47 | 48 | 49 | 20 50 | 40 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 26 66 | 67 | 68 | 69 | Notepanda 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 16 82 | 83 | 84 | 85 | Version: 86 | 87 | 88 | Qt::AutoText 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 16 97 | 98 | 99 | 100 | <a href="https://github.com/ChungZH/notepanda">ChungZH/notepanda</a> 101 | 102 | 103 | Qt::AutoText 104 | 105 | 106 | true 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 16 115 | 116 | 117 | 118 | MIT 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 16 127 | 128 | 129 | 130 | License: 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 16 139 | 140 | 141 | 142 | GitHub: 143 | 144 | 145 | Qt::AutoText 146 | 147 | 148 | true 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 16 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | qrc:/asset/assets/CREDIT.md 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /src/ui/linenumberarea.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file linenumberarea.h 9 | * @brief This file declares the LineNumberArea class. 10 | * 11 | */ 12 | #ifndef LINENUMBERAREA_H 13 | #define LINENUMBERAREA_H 14 | 15 | #include 16 | 17 | #include "../core/texteditor.h" 18 | 19 | class LineNumberArea : public QWidget 20 | { 21 | Q_OBJECT 22 | 23 | public: 24 | LineNumberArea(TextEditor *editor) : QWidget(editor), textEditor(editor) {} 25 | QSize sizeHint() const override 26 | { 27 | return QSize(textEditor->lineNumberAreaWidth(), 0); 28 | } 29 | 30 | protected: 31 | void mouseReleaseEvent(QMouseEvent *event) override; 32 | void paintEvent(QPaintEvent *event) override 33 | { 34 | textEditor->lineNumberAreaPaintEvent(event); 35 | } 36 | 37 | private: 38 | TextEditor *textEditor; 39 | }; 40 | 41 | #endif // LINENUMBERAREA_H 42 | -------------------------------------------------------------------------------- /src/ui/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file mainwindow.cpp 9 | * @brief This file implements the MainWindow class. 10 | * It is the main window of notepanda. 11 | * 12 | */ 13 | 14 | #include "mainwindow.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "aboutwindow.h" 27 | #include "preferenceswindow.h" 28 | #include "ui_preferenceswindow.h" 29 | 30 | MainWindow::MainWindow(ConfigManager *cfManager, QWidget *parent) 31 | : QMainWindow(parent), 32 | TabBar(new QTabBar), 33 | SToolBar(new QToolBar), 34 | ColorDialog(new QColorDialog), 35 | previewPanel(new QTextBrowser), 36 | ToolBar(new QToolBar), 37 | configManager(cfManager), 38 | curTabIndex(0), 39 | prevTabIndex(0), 40 | lastTabRemoveFlag(0), 41 | doNotSaveDataFlag(0) 42 | { 43 | resize(800, 600); 44 | setupUi(); 45 | setBaseSize(size()); 46 | 47 | ToolBar->setIconSize(QSize(26, 26)); 48 | 49 | isPintotop = 0; 50 | 51 | // Sticky note mode 52 | changeBgColor = new QAction(QIcon(":/icons/color_background.svg"), 53 | tr("Change background color"), this); 54 | changeBgColor->setToolTip(tr("Change background color")); 55 | currentColor = "#AAFFFF"; 56 | 57 | SToolBar->addAction(changeBgColor); 58 | SToolBar->addAction(actionNormalmode); 59 | SToolBar->addAction(actionPin_to_top); 60 | 61 | addToolBar(Qt::ToolBarArea::BottomToolBarArea, SToolBar); 62 | SToolBar->setVisible(0); 63 | 64 | connect(changeBgColor, &QAction::triggered, [&]() { ColorDialog->open(); }); 65 | connect(ColorDialog, &QColorDialog::currentColorChanged, 66 | [&](const QColor &color) { 67 | currentColor = color; 68 | plainTextEdit->setStyleSheet("background-color: " + 69 | currentColor.name()); 70 | }); 71 | 72 | // Sticky note mode END 73 | 74 | this->addToolBar(Qt::LeftToolBarArea, ToolBar); 75 | 76 | normalMode(1); 77 | 78 | /** TODO: TabBar->setMovable(true); 79 | If move, index will change. So I must adjust `tabData`. 80 | */ 81 | 82 | TabBar->setTabsClosable(true); 83 | TabToolBar = new QToolBar; 84 | TabToolBar->addWidget(TabBar); 85 | TabToolBar->setMovable(false); 86 | TabToolBar->setFloatable(false); 87 | this->addToolBar(Qt::TopToolBarArea, TabToolBar); 88 | 89 | plainTextEdit = new TextEditor(configManager); 90 | this->setCentralWidget(plainTextEdit); 91 | 92 | TabBar->addTab("Untitled.txt"); 93 | tabData.push_back(TabData{"", "", 0}); 94 | 95 | connect(TabBar, &QTabBar::currentChanged, [&](const int index) { 96 | prevTabIndex = curTabIndex; 97 | curTabIndex = index; 98 | 99 | if (curTabIndex == -1) curTabIndex = 0; 100 | if (prevTabIndex == -1) prevTabIndex = 0; 101 | 102 | if (TabBar->count() == 0) { 103 | prevTabIndex = 0; 104 | tabData.push_back(TabData{"", "", 0}); 105 | TabBar->addTab("Untitled.txt"); 106 | } 107 | 108 | if (lastTabRemoveFlag == true) { 109 | prevTabIndex = curTabIndex = index; 110 | lastTabRemoveFlag = false; 111 | } else if (doNotSaveDataFlag == true) { 112 | doNotSaveDataFlag = false; 113 | } else { 114 | saveTabData(prevTabIndex); 115 | } 116 | 117 | plainTextEdit->clear(); 118 | if (index != -1) { 119 | plainTextEdit->setCurrentFile(tabData[index].fileName); 120 | plainTextEdit->setPlainText(tabData[index].plainText); 121 | } 122 | 123 | plainTextEdit->updateSyntaxHighlight(); 124 | plainTextEdit->document()->setModified(tabData[index].isModified); 125 | changeWindowTitle(); 126 | }); 127 | 128 | connect(TabBar, &QTabBar::tabCloseRequested, [&](int index) { 129 | tabData.remove(index); 130 | lastTabRemoveFlag = true; 131 | TabBar->removeTab(index); 132 | 133 | if (TabBar->count() != 0) { 134 | TabData tempTD = tabData[TabBar->currentIndex()]; 135 | plainTextEdit->setPlainText(tempTD.plainText); 136 | plainTextEdit->setCurrentFile(tempTD.fileName); 137 | plainTextEdit->document()->setModified(tempTD.isModified); 138 | } 139 | changeWindowTitle(); 140 | }); 141 | 142 | connect(actionNew, &QAction::triggered, [&]() { 143 | saveTabData(TabBar->currentIndex()); 144 | 145 | if (plainTextEdit->newDocument()) { 146 | doNotSaveDataFlag = true; 147 | const int newIndex = TabBar->addTab("Untitled.txt"); 148 | tabData.push_back(TabData{"", "", 0}); 149 | TabBar->setCurrentIndex(newIndex); 150 | } 151 | }); 152 | connect(actionOpen, &QAction::triggered, [&]() { 153 | saveTabData(TabBar->currentIndex()); 154 | if (plainTextEdit->open()) { 155 | doNotSaveDataFlag = true; 156 | const int newIndex = TabBar->addTab(plainTextEdit->currentFileName); 157 | tabData.push_back(TabData{plainTextEdit->toPlainText(), 158 | plainTextEdit->currentFileName, 0}); 159 | TabBar->setCurrentIndex(newIndex); 160 | } 161 | }); 162 | connect(actionSave, &QAction::triggered, plainTextEdit, &TextEditor::save); 163 | connect(actionSave_As, &QAction::triggered, plainTextEdit, 164 | &TextEditor::saveAs); 165 | connect(actionPrint, &QAction::triggered, plainTextEdit, 166 | &TextEditor::print); 167 | connect(actionRedo, &QAction::triggered, plainTextEdit, &TextEditor::redo); 168 | connect(actionUndo, &QAction::triggered, plainTextEdit, &TextEditor::undo); 169 | connect(actionReadOnlyMode, &QAction::triggered, [&]() { 170 | plainTextEdit->setReadOnly(!plainTextEdit->isReadOnly()); 171 | updateStatusBar(); 172 | }); 173 | /** 174 | if user change Read-Only mode in right-click menu of TextEditor 175 | than setChecked in MainWindow::actionReadOnlyMode 176 | */ 177 | connect(plainTextEdit, &TextEditor::readOnlyChanged, [&]() { 178 | actionReadOnlyMode->setChecked(plainTextEdit->isReadOnly()); 179 | updateStatusBar(); 180 | }); 181 | 182 | DockWidget = new QDockWidget(tr("Preview panel"), this); 183 | DockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | 184 | Qt::RightDockWidgetArea); 185 | DockWidget->setWidget(previewPanel); 186 | addDockWidget(Qt::RightDockWidgetArea, DockWidget); 187 | DockWidget->setVisible(0); 188 | 189 | previewPanel->setEnabled(0); 190 | 191 | connect(DockWidget, &QDockWidget::visibilityChanged, [&](bool visible) { 192 | actionPreview_panel->setChecked(visible); 193 | previewPanel->setEnabled(visible); 194 | }); 195 | 196 | // PreferencesWindow START 197 | 198 | pfWindow = new PreferencesWindow(configManager, this); 199 | 200 | connect(actionPreferences, &QAction::triggered, 201 | [&]() { pfWindow->exec(); }); 202 | 203 | connect(pfWindow->ui->themeCombo, &QComboBox::currentTextChanged, 204 | [&](const QString &curTheme) { 205 | QApplication::setStyle(QStyleFactory::create(curTheme)); 206 | configManager->setStyleTheme(curTheme); 207 | }); 208 | connect(pfWindow->ui->fontComboBox, &QFontComboBox::currentFontChanged, 209 | [&](const QFont font) { plainTextEdit->setEditorFont(font); }); 210 | connect(pfWindow->ui->spinBox, QOverload::of(&QSpinBox::valueChanged), 211 | [=](const int &value) { plainTextEdit->setEditorFontSize(value); }); 212 | connect(pfWindow->ui->highlightThemeCombo, &QComboBox::currentTextChanged, 213 | [&](const QString &ctname) { 214 | plainTextEdit->setEditorColorTheme(ctname); 215 | configManager->setEditorColorTheme(ctname); 216 | }); 217 | 218 | // User accepted, so change the `settings`. 219 | connect(pfWindow->ui->buttonBox, &QDialogButtonBox::accepted, 220 | [&]() { configManager->save(); }); 221 | 222 | // User rejected, so read general settings, do not change the `settings` !!! 223 | // Otherwise it will not be restored 224 | connect(pfWindow->ui->buttonBox, &QDialogButtonBox::rejected, [&]() { 225 | // Restore the variables 226 | configManager->readGeneralSettings(); 227 | 228 | // Restore TextEditor 229 | plainTextEdit->setEditorFont(configManager->getEditorFontFamily()); 230 | plainTextEdit->setEditorFontSize(configManager->getEditorFontSize()); 231 | plainTextEdit->setEditorColorTheme( 232 | configManager->getEditorColorTheme()); 233 | 234 | // Restore MainWindow 235 | QApplication::setStyle( 236 | QStyleFactory::create(configManager->getStyleTheme())); 237 | 238 | // Restore PreferencesWindow 239 | pfWindow->resetAllValues(0); 240 | }); 241 | 242 | // PW END 243 | 244 | connect(plainTextEdit, &TextEditor::changeTitle, this, 245 | &MainWindow::changeWindowTitle); 246 | connect(plainTextEdit->document(), &QTextDocument::contentsChanged, this, 247 | &MainWindow::documentWasModified); 248 | connect(plainTextEdit, &TextEditor::modifiedFalse, 249 | [=]() { setWindowModified(false); }); 250 | connect(plainTextEdit, &TextEditor::undoAvailable, 251 | [=](bool undoIsAvailable) { 252 | actionUndo->setDisabled(!undoIsAvailable); 253 | }); 254 | connect(plainTextEdit, &TextEditor::redoAvailable, 255 | [=](bool redoIsAvailable) { 256 | actionRedo->setDisabled(!redoIsAvailable); 257 | }); 258 | 259 | connect(plainTextEdit, &TextEditor::textChanged, this, 260 | &MainWindow::updateStatusBar); 261 | connect( 262 | plainTextEdit, &TextEditor::openFileInNewTab, [&](const QString &file) { 263 | saveTabData(TabBar->currentIndex()); 264 | 265 | const int newIndex = TabBar->addTab(plainTextEdit->currentFileName); 266 | plainTextEdit->openFile(file); 267 | tabData.push_back(TabData{plainTextEdit->toPlainText(), 268 | plainTextEdit->currentFileName, 0}); 269 | doNotSaveDataFlag = true; 270 | TabBar->setCurrentIndex(newIndex); 271 | changeWindowTitle(); 272 | }); 273 | 274 | updateStatusBar(); 275 | changeWindowTitle(); 276 | actionUndo->setDisabled(1); 277 | actionRedo->setDisabled(1); 278 | 279 | // Disable menu actions for unavailable features 280 | #if !defined(QT_PRINTSUPPORT_LIB) || !QT_CONFIG(printer) 281 | actionPrint->setEnabled(false); 282 | #endif 283 | #if !QT_CONFIG(clipboard) 284 | ui->actionCut->setEnabled(false); 285 | ui->actionCopy->setEnabled(false); 286 | ui->actionPaste->setEnabled(false); 287 | #else 288 | connect(actionCopy, &QAction::triggered, plainTextEdit, &TextEditor::copy); 289 | connect(actionPaste, &QAction::triggered, plainTextEdit, 290 | &TextEditor::paste); 291 | connect(actionCut, &QAction::triggered, plainTextEdit, &TextEditor::cut); 292 | connect(plainTextEdit, &TextEditor::copyAvailable, 293 | [&](bool isCopyAvailable) { 294 | actionCopy->setDisabled(!isCopyAvailable); 295 | }); 296 | connect( 297 | plainTextEdit, &TextEditor::copyAvailable, 298 | [&](bool isCutAvailable) { actionCut->setDisabled(!isCutAvailable); }); 299 | actionCopy->setDisabled(1); 300 | actionCut->setDisabled(1); 301 | #endif 302 | } 303 | 304 | MainWindow::~MainWindow() { delete plainTextEdit; } 305 | 306 | void MainWindow::setupUi() 307 | { 308 | actionNew = new QAction(QIcon(":/icons/add.svg"), tr("&New"), this); 309 | actionNew->setShortcut(QKeySequence::New); 310 | 311 | actionOpen = new QAction(QIcon(":/icons/open.svg"), tr("&Open"), this); 312 | actionOpen->setShortcut(QKeySequence::Open); 313 | 314 | actionSave = new QAction(QIcon(":/icons/save.svg"), tr("&Save"), this); 315 | actionSave->setShortcut(QKeySequence::Save); 316 | 317 | actionSave_As = 318 | new QAction(QIcon(":/icons/save_as.svg"), tr("&Save as"), this); 319 | actionSave_As->setShortcut(QKeySequence::SaveAs); 320 | 321 | actionUndo = new QAction(QIcon(":/icons/undo.svg"), tr("&Undo"), this); 322 | actionUndo->setShortcut(QKeySequence::Undo); 323 | 324 | actionRedo = new QAction(QIcon(":/icons/redo.svg"), tr("&Redo"), this); 325 | actionRedo->setShortcut(QKeySequence::Redo); 326 | 327 | actionCopy = new QAction(QIcon(":/icons/copy.svg"), tr("&Copy"), this); 328 | actionCopy->setShortcut(QKeySequence::Copy); 329 | 330 | actionPaste = 331 | new QAction(QIcon(":/icons/clipboard.svg"), tr("&Paste"), this); 332 | actionPaste->setShortcut(QKeySequence::Paste); 333 | 334 | actionQuit = new QAction(QIcon(":/icons/delete.svg"), tr("&Quit"), this); 335 | actionQuit->setShortcut(QKeySequence::Quit); 336 | connect(actionQuit, &QAction::triggered, this, &QCoreApplication::quit); 337 | 338 | actionCut = new QAction(QIcon(":/icons/cut.svg"), tr("Cut"), this); 339 | actionCut->setShortcut(QKeySequence::Cut); 340 | 341 | actionPrint = new QAction(QIcon(":/icons/print.svg"), tr("Print"), this); 342 | actionPrint->setShortcut(QKeySequence::Print); 343 | 344 | actionPreferences = 345 | new QAction(QIcon(":/icons/settings.svg"), tr("Preferences"), this); 346 | actionPreferences->setShortcut(QKeySequence::Preferences); 347 | 348 | actionSticky_note_mode = 349 | new QAction(QIcon(":/icons/sticker.svg"), tr("Sticky note mode"), this); 350 | actionSticky_note_mode->setToolTip(tr("Switch to sticky note mode")); 351 | connect(actionSticky_note_mode, &QAction::triggered, [&]() { 352 | stickyNoteMode(); 353 | actionSticky_note_mode->setDisabled(1); 354 | actionNormalmode->setEnabled(1); 355 | }); 356 | 357 | actionNormalmode = new QAction(QIcon(":/icons/document_edit.svg"), 358 | tr("Normal mode"), this); 359 | actionNormalmode->setToolTip(tr("Switch to normal mode")); 360 | connect(actionNormalmode, &QAction::triggered, [&]() { 361 | normalMode(0); 362 | actionNormalmode->setDisabled(1); 363 | actionSticky_note_mode->setEnabled(1); 364 | }); 365 | 366 | actionPin_to_top = 367 | new QAction(QIcon(":/icons/pin.svg"), tr("Pin to top"), this); 368 | connect(actionPin_to_top, &QAction::triggered, [&]() { 369 | Qt::WindowFlags flags = this->windowFlags(); 370 | if (!isPintotop) { 371 | this->setWindowFlags(flags | Qt::CustomizeWindowHint | 372 | Qt::WindowStaysOnTopHint); 373 | this->show(); 374 | } else { 375 | this->setWindowFlags( 376 | flags ^ (Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint)); 377 | this->show(); 378 | } 379 | isPintotop = !isPintotop; 380 | }); 381 | 382 | actionPreview_panel = 383 | new QAction(QIcon(":/icons/preview.svg"), tr("Preview panel"), this); 384 | actionPreview_panel->setCheckable(true); 385 | connect(actionPreview_panel, &QAction::triggered, [&]() { 386 | previewPanel->setEnabled(1); 387 | DockWidget->setVisible(!DockWidget->isVisible()); 388 | }); 389 | 390 | actionAbout = new QAction(QIcon(":/icons/info.svg"), tr("&About"), this); 391 | connect(actionAbout, &QAction::triggered, 392 | [&]() { AboutWindow(this).exec(); }); 393 | 394 | actionAboutQt = new QAction(tr("&About Qt"), this); 395 | connect(actionAboutQt, &QAction::triggered, this, &QApplication::aboutQt); 396 | 397 | actionReadOnlyMode = 398 | new QAction(QIcon(":/icons/read_only.svg"), tr("Read-Only mode"), this); 399 | actionReadOnlyMode->setCheckable(true); 400 | actionReadOnlyMode->setChecked(0); 401 | 402 | menuFile = menuBar()->addMenu(tr("&File")); 403 | menuFile->addAction(actionNew); 404 | menuFile->addAction(actionOpen); 405 | menuFile->addAction(actionSave); 406 | menuFile->addAction(actionSave_As); 407 | menuFile->addSeparator(); 408 | menuFile->addAction(actionPreferences); 409 | menuFile->addSeparator(); 410 | menuFile->addAction(actionPrint); 411 | menuFile->addSeparator(); 412 | menuFile->addAction(actionQuit); 413 | 414 | menuEdit = menuBar()->addMenu(tr("&Edit")); 415 | menuEdit->addAction(actionUndo); 416 | menuEdit->addAction(actionRedo); 417 | menuEdit->addSeparator(); 418 | menuEdit->addAction(actionCopy); 419 | menuEdit->addAction(actionPaste); 420 | menuEdit->addAction(actionCut); 421 | menuEdit->addSeparator(); 422 | menuEdit->addAction(actionReadOnlyMode); 423 | 424 | menuView = menuBar()->addMenu(tr("&View")); 425 | menuView->addAction(actionNormalmode); 426 | menuView->addAction(actionSticky_note_mode); 427 | menuView->addSeparator(); 428 | menuView->addAction(actionPin_to_top); 429 | menuView->addSeparator(); 430 | menuView->addAction(actionPreview_panel); 431 | 432 | menuHelp = menuBar()->addMenu(tr("&Help")); 433 | menuHelp->addAction(actionAbout); 434 | menuHelp->addAction(actionAboutQt); 435 | 436 | menuBar()->addMenu(menuFile); 437 | menuBar()->addMenu(menuEdit); 438 | menuBar()->addMenu(menuView); 439 | menuBar()->addMenu(menuHelp); 440 | } 441 | 442 | void MainWindow::closeEvent(QCloseEvent *event) 443 | { 444 | if (plainTextEdit->maybeSave()) { 445 | event->accept(); 446 | } else { 447 | event->ignore(); 448 | } 449 | } 450 | 451 | void MainWindow::changeWindowTitle() 452 | { 453 | QString showName; 454 | if (!plainTextEdit->currentFile.isEmpty()) 455 | showName = plainTextEdit->currentFileName; 456 | else 457 | showName = "Untitled"; 458 | 459 | for (int i = 0; i < tabData.size(); i++) { 460 | if (tabData[i].fileName.isEmpty()) 461 | TabBar->setTabText(i, "Untitled"); 462 | else 463 | TabBar->setTabText(i, tabData[i].fileName); 464 | } 465 | setWindowTitle(showName + "[*] - Notepanda"); 466 | } 467 | 468 | void MainWindow::updateStatusBar() 469 | { 470 | if (currentMode != 1) { 471 | QString flags; 472 | if (plainTextEdit->isReadOnly() == 1) { 473 | flags += "[Read-Only]"; 474 | } 475 | 476 | statusBar()->showMessage( 477 | tr("Characters:") + 478 | QString::number(plainTextEdit->document()->characterCount() - 1) + 479 | tr(" Lines:") + 480 | QString::number(plainTextEdit->document()->lineCount()) + flags); 481 | } 482 | } 483 | 484 | void MainWindow::normalMode(bool isFirst) 485 | { 486 | if (!isFirst) { 487 | resize(baseSize()); 488 | plainTextEdit->switchMode(0); 489 | ToolBar->setVisible(1); 490 | SToolBar->setVisible(0); 491 | } else { 492 | ToolBar->addAction(actionNew); 493 | ToolBar->addAction(actionOpen); 494 | ToolBar->addAction(actionSave); 495 | ToolBar->addAction(actionSave_As); 496 | ToolBar->addAction(actionPreferences); 497 | ToolBar->addAction(actionUndo); 498 | ToolBar->addAction(actionRedo); 499 | ToolBar->addAction(actionQuit); 500 | ToolBar->addAction(actionAbout); 501 | ToolBar->addAction(actionSticky_note_mode); 502 | ToolBar->addAction(actionPreview_panel); 503 | actionNormalmode->setDisabled(1); 504 | } 505 | actionPreferences->setEnabled(1); 506 | currentMode = 0; 507 | } 508 | 509 | /** 510 | * @brief Sticky Note Mode, like Microsoft Sticky Notes. For more details, see 511 | * 512 | */ 513 | void MainWindow::stickyNoteMode() 514 | { 515 | resize(baseSize() * 0.7); 516 | plainTextEdit->switchMode(1); 517 | plainTextEdit->setStyleSheet("background: " + currentColor.name()); 518 | statusBar()->clearMessage(); 519 | actionPreferences->setDisabled(1); 520 | 521 | ToolBar->setVisible(0); 522 | SToolBar->setVisible(1); 523 | 524 | currentMode = 1; 525 | } 526 | 527 | void MainWindow::documentWasModified() 528 | { 529 | setWindowModified(plainTextEdit->document()->isModified()); 530 | 531 | if (previewPanel->isEnabled()) { 532 | previewPanel->reload(); 533 | 534 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) 535 | const QString fileSuffix = 536 | plainTextEdit->currentFileName.split('.').last(); 537 | 538 | if (fileSuffix == "md" || fileSuffix == "markdown" || 539 | fileSuffix == "mdown") 540 | previewPanel->setMarkdown(plainTextEdit->toPlainText()); 541 | else 542 | #endif 543 | previewPanel->setText(plainTextEdit->toPlainText()); 544 | } 545 | } 546 | 547 | void MainWindow::saveTabData(const int index) 548 | { 549 | tabData[index].fileName = plainTextEdit->currentFileName; 550 | tabData[index].isModified = plainTextEdit->document()->isModified(); 551 | tabData[index].plainText = plainTextEdit->toPlainText(); 552 | } 553 | -------------------------------------------------------------------------------- /src/ui/mainwindow.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file mainwindow.h 9 | * @brief This file declares the MainWindow class. 10 | * It is the main window of notepanda. 11 | * 12 | */ 13 | #ifndef MAINWINDOW_H 14 | #define MAINWINDOW_H 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "../core/configmanager.h" 29 | #include "../core/texteditor.h" 30 | #include "preferenceswindow.h" 31 | 32 | QT_BEGIN_NAMESPACE 33 | class TextEditor; 34 | QT_END_NAMESPACE 35 | 36 | struct TabData 37 | { 38 | QString plainText; 39 | QString fileName; 40 | bool isModified; 41 | }; 42 | 43 | class MainWindow : public QMainWindow 44 | { 45 | Q_OBJECT 46 | 47 | public: 48 | MainWindow(ConfigManager *confManager, QWidget *parent = nullptr); 49 | ~MainWindow(); 50 | TextEditor *plainTextEdit; 51 | 52 | protected: 53 | void closeEvent(QCloseEvent *event) override; 54 | 55 | private slots: 56 | void changeWindowTitle(); 57 | void updateStatusBar(); 58 | void normalMode(bool isFirst); 59 | void stickyNoteMode(); 60 | void documentWasModified(); 61 | 62 | private: 63 | void setupUi(); 64 | 65 | // TODO: use QTabBar::tabData 66 | QVector tabData; 67 | 68 | QToolBar *ToolBar; 69 | ConfigManager *configManager; 70 | PreferencesWindow *pfWindow; 71 | QTextBrowser *previewPanel; 72 | QDockWidget *DockWidget; 73 | QTabBar *TabBar; 74 | QToolBar *TabToolBar; 75 | 76 | bool lastTabRemoveFlag, doNotSaveDataFlag; 77 | int curTabIndex, prevTabIndex; 78 | void saveTabData(const int index); 79 | 80 | /** 81 | * @brief currentMode 82 | * 0: normal mode 83 | * 1: sticky mode 84 | */ 85 | int currentMode; 86 | bool isPintotop; 87 | 88 | // Sticky note mode 89 | 90 | QToolBar *SToolBar; 91 | QAction *changeBgColor; 92 | QColorDialog *ColorDialog; 93 | QColor currentColor; 94 | 95 | // SNM end 96 | 97 | // UI start 98 | QAction *actionNew; 99 | QAction *actionOpen; 100 | QAction *actionSave; 101 | QAction *actionSave_As; 102 | QAction *actionUndo; 103 | QAction *actionRedo; 104 | QAction *actionCopy; 105 | QAction *actionPaste; 106 | QAction *actionQuit; 107 | QAction *actionCut; 108 | QAction *actionPrint; 109 | QAction *actionPreferences; 110 | QAction *actionSticky_note_mode; 111 | QAction *actionNormalmode; 112 | QAction *actionPin_to_top; 113 | QAction *actionPreview_panel; 114 | QAction *actionReadOnlyMode; 115 | QAction *actionAbout; 116 | QAction *actionAboutQt; 117 | QMenu *menuFile; 118 | QMenu *menuEdit; 119 | QMenu *menuView; 120 | QMenu *menuHelp; 121 | // UI end 122 | }; 123 | #endif // MAINWINDOW_H 124 | -------------------------------------------------------------------------------- /src/ui/preferenceswindow.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file preferenceswindow.cpp 9 | * @brief This file implements the PreferencesWindow class. 10 | * All persistent application settings should be in PreferencesWindow. 11 | * 12 | */ 13 | #include "preferenceswindow.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "../core/texteditor.h" 21 | #include "ui_preferenceswindow.h" 22 | 23 | PreferencesWindow::PreferencesWindow(ConfigManager *cfManager, QWidget *parent) 24 | : QDialog(parent), ui(new Ui::PreferencesWindow), configManager(cfManager) 25 | { 26 | ui->setupUi(this); 27 | setWindowTitle(tr("Preferences - Notepanda")); 28 | 29 | resetAllValues(1); 30 | } 31 | 32 | void PreferencesWindow::resetAllValues(const bool isFirst) 33 | { 34 | if (isFirst) { 35 | ui->themeCombo->addItems(QStyleFactory::keys()); 36 | 37 | /* 38 | * Another implementation (better but harder) : 39 | for (const auto &theme : m_repository.themes()) addItem(...); 40 | * There is currently no plan to customize the theme of highlighting 41 | * So this implementation is temporarily used 42 | */ 43 | 44 | ui->highlightThemeCombo->addItem("Breeze Dark"); 45 | ui->highlightThemeCombo->addItem("Default"); 46 | ui->highlightThemeCombo->addItem("Printing"); 47 | ui->highlightThemeCombo->addItem("Solarized Dark"); 48 | ui->highlightThemeCombo->addItem("Solarized Light"); 49 | } 50 | ui->themeCombo->setCurrentText(configManager->getStyleTheme()); 51 | ui->fontComboBox->setCurrentFont( 52 | QFont(configManager->getEditorFontFamily())); 53 | ui->spinBox->setValue(configManager->getEditorFontSize()); 54 | ui->highlightThemeCombo->setCurrentText( 55 | configManager->getEditorColorTheme()); 56 | } 57 | -------------------------------------------------------------------------------- /src/ui/preferenceswindow.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 ChungZH. ALl Rights Reserved. 3 | * Licensed under the MIT license. 4 | * See file LICENSE for detail or copy at 5 | * 6 | * This file is a part of Notepanda. 7 | * 8 | * @file preferenceswindow.h 9 | * @brief This file declares the PreferencesWindow class. 10 | * All persistent application settings should be in PreferencesWindow. 11 | * 12 | */ 13 | #ifndef PREFERENCESWINDOW_H 14 | #define PREFERENCESWINDOW_H 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "../core/configmanager.h" 21 | #include "../core/texteditor.h" 22 | #include "ui_preferenceswindow.h" 23 | 24 | class PreferencesWindow : public QDialog, public Ui::PreferencesWindow 25 | { 26 | Q_OBJECT 27 | public: 28 | PreferencesWindow(ConfigManager *cfManager, QWidget *parent = nullptr); 29 | void resetAllValues(const bool isFirst); 30 | Ui::PreferencesWindow *ui; 31 | 32 | private: 33 | ConfigManager *configManager; 34 | }; 35 | 36 | #endif // PREFERENCESWINDOW_H 37 | -------------------------------------------------------------------------------- /src/ui/preferenceswindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | PreferencesWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 678 10 | 335 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | Qt::Horizontal 21 | 22 | 23 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 24 | 25 | 26 | 27 | 28 | 29 | 30 | 0 31 | 32 | 33 | 34 | General Settings 35 | 36 | 37 | 38 | 39 | 10 40 | 10 41 | 169 42 | 129 43 | 44 | 45 | 46 | 47 | 48 | 49 | Appearance 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 61 | 76 62 | 63 | 64 | 65 | UI Theme 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | Editor Settings 85 | 86 | 87 | 88 | 89 | 10 90 | 10 91 | 580 92 | 129 93 | 94 | 95 | 96 | 97 | 98 | 99 | Font and Color 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 6 108 | 109 | 110 | 111 | 112 | Font Family 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 144 121 | 16777215 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 110 135 | 0 136 | 137 | 138 | 139 | Font Size 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 6 152 | 153 | 154 | 155 | 156 | 157 | 78 158 | 76 159 | 160 | 161 | 162 | Theme 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | buttonBox 187 | accepted() 188 | PreferencesWindow 189 | accept() 190 | 191 | 192 | 248 193 | 254 194 | 195 | 196 | 157 197 | 274 198 | 199 | 200 | 201 | 202 | buttonBox 203 | rejected() 204 | PreferencesWindow 205 | reject() 206 | 207 | 208 | 316 209 | 260 210 | 211 | 212 | 286 213 | 274 214 | 215 | 216 | 217 | 218 | 219 | --------------------------------------------------------------------------------