mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-06 22:39:53 +08:00
Compare commits
300 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
856d2597df | ||
![]() |
44a0d75996 | ||
![]() |
62f6b7f3c5 | ||
![]() |
6150d327da | ||
![]() |
522ccfecc4 | ||
![]() |
4788ae4ccf | ||
![]() |
c3191e8f88 | ||
![]() |
4952c88b2e | ||
![]() |
ebd4489adf | ||
![]() |
d9aee037ad | ||
![]() |
87e87619fb | ||
![]() |
8494a714cd | ||
![]() |
3ec78f4c36 | ||
![]() |
a27485c021 | ||
![]() |
713b98f29e | ||
![]() |
82b0ec751d | ||
![]() |
a7348c0aa0 | ||
![]() |
85d2ee950d | ||
![]() |
b0b51570b0 | ||
![]() |
a5a9b32ea3 | ||
![]() |
ac20b7f80c | ||
![]() |
b462337824 | ||
![]() |
bbb88ded57 | ||
![]() |
0f0995d4fe | ||
![]() |
0881dd581a | ||
![]() |
c4512bb69a | ||
![]() |
5e4a6fa72a | ||
![]() |
5da9268aa7 | ||
![]() |
f1af78573a | ||
![]() |
a7600fa7f7 | ||
![]() |
78de724757 | ||
![]() |
05078a7857 | ||
![]() |
84d396f6c4 | ||
![]() |
b2fb5b1664 | ||
![]() |
3477469b66 | ||
![]() |
bed95b1757 | ||
![]() |
f83761d95f | ||
![]() |
84cb60d882 | ||
![]() |
313764b533 | ||
![]() |
44b5251045 | ||
![]() |
743e63edbf | ||
![]() |
74e20dcb3e | ||
![]() |
9a971860e0 | ||
![]() |
b1a1bf05d2 | ||
![]() |
06c712cb64 | ||
![]() |
84891fb2f2 | ||
![]() |
889026bece | ||
![]() |
2ebb210c57 | ||
![]() |
bfb0e5bd29 | ||
![]() |
61528de505 | ||
![]() |
f62a82364c | ||
![]() |
f461c706d1 | ||
![]() |
898c82ec8f | ||
![]() |
f5b69b73cf | ||
![]() |
5a01b4bb35 | ||
![]() |
918aa15e74 | ||
![]() |
522b37425f | ||
![]() |
a04968913c | ||
![]() |
3b755287e7 | ||
![]() |
25b556ae14 | ||
![]() |
daa2fe1785 | ||
![]() |
30712745bc | ||
![]() |
15d14359d7 | ||
![]() |
75f7250ed1 | ||
![]() |
853e9c08e0 | ||
![]() |
2f6afb2f97 | ||
![]() |
cbd3956cdc | ||
![]() |
4ab2033a81 | ||
![]() |
e8fe0726ff | ||
![]() |
f773bc0348 | ||
![]() |
d208d4f9a7 | ||
![]() |
a5f6d5a142 | ||
![]() |
cfce10b0ca | ||
![]() |
2ccfb5ca9f | ||
![]() |
c7f471cb8f | ||
![]() |
c255cd54ed | ||
![]() |
075380749c | ||
![]() |
f1a03554f0 | ||
![]() |
9353c9ef40 | ||
![]() |
d664c5a1c6 | ||
![]() |
d499b0d7be | ||
![]() |
f764fec458 | ||
![]() |
0cb502520f | ||
![]() |
b4e38409a9 | ||
![]() |
03ffb12ff1 | ||
![]() |
31d4ad151e | ||
![]() |
5229be4687 | ||
![]() |
e76c6829f7 | ||
![]() |
e7a7895c6e | ||
![]() |
7f2cfe4f52 | ||
![]() |
7f27745303 | ||
![]() |
4002cbca1b | ||
![]() |
f2c7abcc6f | ||
![]() |
e47b813b69 | ||
![]() |
0f635fba7d | ||
![]() |
9a10b210ec | ||
![]() |
038de48d54 | ||
![]() |
89cd40d0cf | ||
![]() |
3d77c29393 | ||
![]() |
aea0da5570 | ||
![]() |
7a88d1ef99 | ||
![]() |
ee2dc9ccc5 | ||
![]() |
73bb88e61f | ||
![]() |
2846e590d5 | ||
![]() |
cdf7091ae1 | ||
![]() |
bed49cf038 | ||
![]() |
4120ea2601 | ||
![]() |
0e9b6894c7 | ||
![]() |
ff6031209f | ||
![]() |
7e5ea84f37 | ||
![]() |
e87be24e5d | ||
![]() |
8141d58652 | ||
![]() |
d6631280e5 | ||
![]() |
0da7200898 | ||
![]() |
ed87ea3df1 | ||
![]() |
749c4006a2 | ||
![]() |
66ed2cdda1 | ||
![]() |
3739cfd0ef | ||
![]() |
815ef927cb | ||
![]() |
1d94617837 | ||
![]() |
a25b75f828 | ||
![]() |
e92ea5061a | ||
![]() |
d9f009953f | ||
![]() |
08fe335a80 | ||
![]() |
73ebcaeb90 | ||
![]() |
6de3663269 | ||
![]() |
12302d375d | ||
![]() |
490634b85a | ||
![]() |
cc0689d946 | ||
![]() |
fe4b6ca72e | ||
![]() |
791a5da245 | ||
![]() |
2a91577521 | ||
![]() |
08c597513c | ||
![]() |
3c64b86297 | ||
![]() |
a37d03fd02 | ||
![]() |
12eda471fc | ||
![]() |
5877366b77 | ||
![]() |
9b78d38726 | ||
![]() |
b78bd5abef | ||
![]() |
5865519402 | ||
![]() |
4f1959501d | ||
![]() |
12e18934df | ||
![]() |
05e8648d25 | ||
![]() |
e9c8225255 | ||
![]() |
14475330da | ||
![]() |
53e2b3bfa8 | ||
![]() |
8197012bcb | ||
![]() |
719c7339cf | ||
![]() |
21893fdff8 | ||
![]() |
9a3c69870c | ||
![]() |
30f73cb004 | ||
![]() |
3025e8e01c | ||
![]() |
e5f7a23157 | ||
![]() |
8c0c056c47 | ||
![]() |
17af3b8dc0 | ||
![]() |
82ec6beeb5 | ||
![]() |
1318427bb7 | ||
![]() |
29b2093ef0 | ||
![]() |
c08296bc3b | ||
![]() |
cd53eedc7f | ||
![]() |
e0b07b8aba | ||
![]() |
8678468998 | ||
![]() |
d33b539659 | ||
![]() |
09776f07cd | ||
![]() |
f762204512 | ||
![]() |
06b9269b84 | ||
![]() |
243a03e142 | ||
![]() |
dfa4e59737 | ||
![]() |
cf0146050e | ||
![]() |
18080d174c | ||
![]() |
34f3bb7a08 | ||
![]() |
09789590a2 | ||
![]() |
0cadea231a | ||
![]() |
bbab5cc223 | ||
![]() |
fe3280e02e | ||
![]() |
a31a7a32a5 | ||
![]() |
b13771e34f | ||
![]() |
cc8ee46efe | ||
![]() |
1ca899d8ab | ||
![]() |
8a1bd930eb | ||
![]() |
ecce8d13c1 | ||
![]() |
5a8eb1e7aa | ||
![]() |
cadbab25bb | ||
![]() |
dba9fb30e8 | ||
![]() |
2908148c00 | ||
![]() |
8be34ade30 | ||
![]() |
52d389a5e7 | ||
![]() |
f9767bf7f7 | ||
![]() |
d25c142bfa | ||
![]() |
bdd935d9c2 | ||
![]() |
eab367cf6f | ||
![]() |
486950c1aa | ||
![]() |
0e4442f513 | ||
![]() |
401b1934e2 | ||
![]() |
6ea5995c12 | ||
![]() |
b3a385693c | ||
![]() |
737f9e51d8 | ||
![]() |
ef7b28b6b3 | ||
![]() |
993d522e15 | ||
![]() |
969db5c3db | ||
![]() |
8b0210eb5d | ||
![]() |
5da6a81d4b | ||
![]() |
36d4248a32 | ||
![]() |
b1dc1eefe6 | ||
![]() |
2630999acf | ||
![]() |
a4a5dea3d7 | ||
![]() |
09dba18994 | ||
![]() |
a5671e20e5 | ||
![]() |
5ba1ba5b97 | ||
![]() |
eff1a81125 | ||
![]() |
727fade948 | ||
![]() |
126091c080 | ||
![]() |
7a017e30ea | ||
![]() |
293d04e2c3 | ||
![]() |
70c984353c | ||
![]() |
87ed9250ef | ||
![]() |
ba9a0ba459 | ||
![]() |
f50a834ae7 | ||
![]() |
301dace730 | ||
![]() |
b369612070 | ||
![]() |
911392deab | ||
![]() |
fa238b3e22 | ||
![]() |
956842f102 | ||
![]() |
04a93de5a5 | ||
![]() |
04451257d2 | ||
![]() |
2d4d4609da | ||
![]() |
ff7cf0f24e | ||
![]() |
54576543e9 | ||
![]() |
14c54ab275 | ||
![]() |
ecf1f8dbbe | ||
![]() |
6884e1ff6a | ||
![]() |
258adf10da | ||
![]() |
3a4334ffd2 | ||
![]() |
4d41421fb8 | ||
![]() |
e2fda18dbd | ||
![]() |
6b1f00880e | ||
![]() |
86df5c39a2 | ||
![]() |
47a49b7bb2 | ||
![]() |
0b58669e39 | ||
![]() |
9fca1cad12 | ||
![]() |
077cd6562e | ||
![]() |
d7bfb956ec | ||
![]() |
dc0a9b1a7d | ||
![]() |
0b624eeefd | ||
![]() |
8de8b0dda8 | ||
![]() |
964dfbb085 | ||
![]() |
c362facdd7 | ||
![]() |
2b5076584d | ||
![]() |
79993814a8 | ||
![]() |
1802b00525 | ||
![]() |
5848b5e737 | ||
![]() |
313d362ed3 | ||
![]() |
9e9a6bd584 | ||
![]() |
810438b5e5 | ||
![]() |
5bc48be5d0 | ||
![]() |
0a2bdc7033 | ||
![]() |
3b03fd2189 | ||
![]() |
b8dd91e353 | ||
![]() |
6689e8c84c | ||
![]() |
bb1598dde2 | ||
![]() |
2740983308 | ||
![]() |
2436adc257 | ||
![]() |
e4355eeb48 | ||
![]() |
606dcef16f | ||
![]() |
dc0c0ca849 | ||
![]() |
8973f40020 | ||
![]() |
d8635e957c | ||
![]() |
e4edee269b | ||
![]() |
8391014b38 | ||
![]() |
54a71ae977 | ||
![]() |
5c2a14e6a7 | ||
![]() |
2bb475a0fd | ||
![]() |
0d8bb7eebd | ||
![]() |
ed8cd503b5 | ||
![]() |
8326d3c702 | ||
![]() |
c9be7a7a7f | ||
![]() |
fa7dd8c541 | ||
![]() |
0f1be2883a | ||
![]() |
bcec79fe17 | ||
![]() |
e017c0f475 | ||
![]() |
6109737b9d | ||
![]() |
7cc31a5d6e | ||
![]() |
f4aabc1f2d | ||
![]() |
280144f4eb | ||
![]() |
a9005bef23 | ||
![]() |
0530422a9b | ||
![]() |
aaee9d00e4 | ||
![]() |
721e821ca5 | ||
![]() |
e9799990f0 | ||
![]() |
a0594dcc2b | ||
![]() |
0cf0da06f8 | ||
![]() |
c03884c429 | ||
![]() |
f1d931c276 | ||
![]() |
30a9d0ecc7 | ||
![]() |
0784fd43f3 | ||
![]() |
4defcc7aea | ||
![]() |
500eba29fa | ||
![]() |
9cf015a676 | ||
![]() |
5ba425ae95 | ||
![]() |
516aaa95ca |
128
.github/workflows/ci-linux.yml
vendored
128
.github/workflows/ci-linux.yml
vendored
@ -8,14 +8,21 @@ on:
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab.
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
debug_enabled:
|
||||
type: boolean
|
||||
description: 'Run the build with tmate debugging enabled'
|
||||
required: false
|
||||
default: false
|
||||
|
||||
env:
|
||||
VNOTE_VER: 3.5.0
|
||||
VNOTE_VER: 3.19.2
|
||||
CMAKE_VER: 3.24.3
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
name: Build On Ubuntu
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
|
||||
steps:
|
||||
@ -30,75 +37,86 @@ jobs:
|
||||
git submodule sync --recursive
|
||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
||||
|
||||
- name: Install a Fresh CMake
|
||||
run: |
|
||||
wget --no-verbose https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-Linux-x86_64.sh
|
||||
chmod +x cmake-${CMAKE_VER}-Linux-x86_64.sh
|
||||
mkdir ${{runner.workspace}}/cmake
|
||||
sudo ./cmake-${CMAKE_VER}-Linux-x86_64.sh --skip-license --prefix=${{runner.workspace}}/cmake
|
||||
sudo rm -f /usr/local/bin/cmake /usr/local/bin/cpack
|
||||
sudo ln -s ${{runner.workspace}}/cmake/bin/cmake /usr/local/bin/cmake
|
||||
sudo ln -s ${{runner.workspace}}/cmake/bin/cpack /usr/local/bin/cpack
|
||||
|
||||
- name: Install linuxdeploy
|
||||
uses: miurahr/install-linuxdeploy-action@v1
|
||||
with:
|
||||
plugins: qt appimage
|
||||
|
||||
- name: Install dependencies
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get update
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y libfcitx-qt5-dev tree
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y libfcitx5-qt-dev fcitx-libs-dev extra-cmake-modules libxkbcommon-dev
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tree
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y fuse libxcb-cursor-dev
|
||||
python3 -m pip config set global.break-system-packages true
|
||||
|
||||
- name: Cache Qt
|
||||
id: cache-qt
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{runner.workspace}}/Qt
|
||||
key: ${{ runner.os }}-QtCache-6.8
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: 5.12.10
|
||||
version: 6.8.3
|
||||
target: desktop
|
||||
modules: qtwebchannel qtwebengine qtsvg qtlocation qttools qttranslations
|
||||
tools: tools_openssl_x64,1.1.1-4,qt.tools.openssl.gcc_64
|
||||
modules: 'qtwebengine qtwebchannel qtpositioning qtpdf qtimageformats qt5compat qtserialport'
|
||||
tools: 'tools_opensslv3_src'
|
||||
cache: 'true'
|
||||
|
||||
- name: Compile OpenSSLV3
|
||||
run: |
|
||||
cd ${Qt6_DIR}/../../Tools/OpenSSLv3/src
|
||||
./Configure
|
||||
make -j2
|
||||
sudo make install
|
||||
|
||||
- name: Create Build Dir
|
||||
run: mkdir build
|
||||
working-directory: ${{runner.workspace}}
|
||||
|
||||
- name: Compile qt5ct
|
||||
- name: Compile fcitxqt5
|
||||
run: |
|
||||
wget -c https://excellmedia.dl.sourceforge.net/project/qt5ct/qt5ct-1.1.tar.bz2
|
||||
tar xf qt5ct-1.*.tar.bz2
|
||||
cd qt5ct-1.*/
|
||||
QT_SELECT=5 qmake
|
||||
git clone https://github.com/fcitx/fcitx-qt5
|
||||
cd fcitx-qt5
|
||||
mkdir build && cd build
|
||||
cmake -DENABLE_QT5=OFF -DENABLE_QT6=ON ..
|
||||
make -j2
|
||||
sudo make install
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Compile qt6ct
|
||||
run: |
|
||||
git clone https://github.com/trialuser02/qt6ct qt6ct.git
|
||||
cd qt6ct.git
|
||||
qmake
|
||||
make -j$(nproc) && sudo make install
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Configure Project
|
||||
run: |
|
||||
qmake -v
|
||||
cp /usr/lib/x86_64-linux-gnu/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so ./
|
||||
sudo chmod +rwx ./libfcitxplatforminputcontextplugin.so
|
||||
cp ./libfcitxplatforminputcontextplugin.so $Qt5_Dir/plugins/platforminputcontexts
|
||||
qmake CONFIG+=release -spec linux-g++-64 ${GITHUB_WORKSPACE}/vnote.pro
|
||||
cmake --version
|
||||
cmake ${GITHUB_WORKSPACE}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Build Project
|
||||
run: make -j$(nproc)
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Install Project
|
||||
run: |
|
||||
mkdir AppDir
|
||||
make install INSTALL_ROOT=${{runner.workspace}}/build/AppDir
|
||||
tree AppDir
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Package Project
|
||||
run: |
|
||||
# Move the lib out to avoid duplication
|
||||
mv AppDir/usr/lib ./
|
||||
LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH
|
||||
# Copy translations
|
||||
mkdir -p AppDir/usr/translations
|
||||
cp $Qt5_Dir/translations/qt_zh_CN.qm AppDir/usr/translations
|
||||
# Package qt5ct (EXTRA_QT_PLUGINS seems not work)
|
||||
# EXTRA_QT_PLUGINS="platformthemes/libqt5ct.so;styles/libqt5ct-style.so"
|
||||
mkdir -p AppDir/usr/plugins/platformthemes
|
||||
mkdir -p AppDir/usr/plugins/styles
|
||||
cp $Qt5_Dir/plugins/platformthemes/* AppDir/usr/plugins/platformthemes/
|
||||
cp $Qt5_Dir/plugins/styles/* AppDir/usr/plugins/styles/
|
||||
# Package libssl.so and libcrypto.so
|
||||
Qt5_Tools=$Qt5_Dir/../../Tools
|
||||
linuxdeploy-x86_64.AppImage --appdir ./AppDir --plugin qt --output appimage -l $Qt5_Tools/OpenSSL/binary/lib/libcrypto.so.1.1 -l $Qt5_Tools/OpenSSL/binary/lib/libssl.so.1.1
|
||||
tree AppDir
|
||||
# Remove the libqsqlmimer.so as libmimerapi.so is not deployed with Qt6
|
||||
rm ${{runner.workspace}}/Qt/6.*/gcc_64/plugins/sqldrivers/libqsqlmimer.so
|
||||
cmake --build . --target pack
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Fix Package
|
||||
@ -115,15 +133,14 @@ jobs:
|
||||
linuxdeploy-plugin-appimage-x86_64.AppImage --appdir=./squashfs-root
|
||||
mv VNote*.AppImage ../
|
||||
popd
|
||||
mv VNote*.AppImage vnote-linux-x64_v${{env.VNOTE_VER}}.AppImage
|
||||
cp vnote-linux-x64_v${{env.VNOTE_VER}}.AppImage vnote-linux-x64.AppImage
|
||||
mv VNote*.AppImage VNote-${{env.VNOTE_VER}}-linux-x64.AppImage
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Archive Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: vnote-linux-x64_v${{env.VNOTE_VER}}
|
||||
path: ${{runner.workspace}}/build/vnote-linux-x64_v${{env.VNOTE_VER}}.AppImage
|
||||
name: VNote-${{env.VNOTE_VER}}-linux-x64.AppImage
|
||||
path: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-linux-x64.AppImage
|
||||
|
||||
- name: Update Tag
|
||||
if: github.ref == 'refs/heads/master'
|
||||
@ -136,6 +153,17 @@ jobs:
|
||||
uses: johnwbyrd/update-release@v1.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: ${{runner.workspace}}/build/vnote-linux-x64.AppImage
|
||||
files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-linux-x64.AppImage
|
||||
release: Continuous Build
|
||||
tag: continuous-build
|
||||
|
||||
- name: Release
|
||||
if: github.ref == 'refs/heads/master' && startsWith(github.event.head_commit.message, '[Release]')
|
||||
uses: ncipollo/release-action@v1.11.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-linux-x64.AppImage
|
||||
commit: master
|
||||
tag: v${{env.VNOTE_VER}}
|
||||
allowUpdates: true
|
||||
draft: true
|
||||
|
220
.github/workflows/ci-macos.yml
vendored
220
.github/workflows/ci-macos.yml
vendored
@ -8,16 +8,34 @@ on:
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab.
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
debug_enabled:
|
||||
type: boolean
|
||||
description: 'Run the build with tmate debugging enabled'
|
||||
required: false
|
||||
default: false
|
||||
|
||||
env:
|
||||
VNOTE_VER: 3.5.0
|
||||
VNOTE_VER: 3.19.2
|
||||
CMAKE_VER: 3.24.3
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
build:
|
||||
environment: Mac-code-sign
|
||||
name: Build On MacOS
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 120
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- name: "Build on Arm64"
|
||||
os: macos-latest
|
||||
arch: universal
|
||||
qt: 6.8.3
|
||||
|
||||
runs-on: ${{matrix.config.os}}
|
||||
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE.
|
||||
- uses: actions/checkout@v2
|
||||
@ -32,19 +50,50 @@ jobs:
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
brew install tree
|
||||
brew install tree libiodbc libpq
|
||||
|
||||
- name: Fix SQL
|
||||
run: |
|
||||
sudo mkdir -p /usr/local/opt/libiodbc/lib
|
||||
sudo ln -s /opt/homebrew/opt/libiodbc/lib/libiodbc.2.dylib /usr/local/opt/libiodbc/lib/libiodbc.2.dylib
|
||||
sudo mkdir -p /Applications/Postgres.app/Contents/Versions/14/lib
|
||||
sudo ln -s /opt/homebrew/Cellar/libpq/16.3/lib/libpq.5.dylib /Applications/Postgres.app/Contents/Versions/14/lib/libpq.5.dylib
|
||||
|
||||
- name: Install a fresh CMake
|
||||
run: |
|
||||
wget --no-verbose https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-macos-universal.tar.gz
|
||||
tar xzf cmake-${CMAKE_VER}-macos-universal.tar.gz
|
||||
sudo rm -f /usr/local/bin/cmake /usr/local/bin/cpack
|
||||
sudo ln -s ${{runner.workspace}}/cmake-${CMAKE_VER}-macos-universal/CMake.app/Contents/bin/cmake /usr/local/bin/cmake
|
||||
sudo ln -s ${{runner.workspace}}/cmake-${CMAKE_VER}-macos-universal/CMake.app/Contents/bin/cpack /usr/local/bin/cpack
|
||||
working-directory: ${{runner.workspace}}
|
||||
|
||||
- name: Install macdeployqtfix
|
||||
run: |
|
||||
git clone https://github.com/tamlok/macdeployqtfix.git macdeployqtfix --depth=1
|
||||
working-directory: ${{runner.workspace}}
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
- name: Install optool
|
||||
run: |
|
||||
wget --no-verbose https://github.com/alexzielenski/optool/releases/download/0.1/optool.zip
|
||||
unzip ./optool.zip
|
||||
sudo ln -s ./optool /usr/local/bin/optool
|
||||
working-directory: ${{runner.workspace}}
|
||||
|
||||
- name: Cache Qt
|
||||
id: cache-qt
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
version: 5.12.10
|
||||
path: ${{runner.workspace}}/Qt
|
||||
key: ${{ runner.os }}-QtCache-6.8
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: ${{matrix.config.qt}}
|
||||
target: desktop
|
||||
modules: qtwebchannel qtwebengine qtsvg qtlocation qttools qttranslations
|
||||
modules: 'qtwebengine qtwebchannel qtpositioning qtpdf qtimageformats qt5compat qtserialport'
|
||||
cache: 'true'
|
||||
|
||||
- name: Create Build Dir
|
||||
run: mkdir build
|
||||
@ -53,71 +102,111 @@ jobs:
|
||||
- name: Configure Project
|
||||
run: |
|
||||
qmake -v
|
||||
qmake CONFIG+=release ${GITHUB_WORKSPACE}/vnote.pro
|
||||
cmake --version
|
||||
cmake -DMACDEPLOYQTFIX_EXECUTABLE=${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" ${GITHUB_WORKSPACE}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Build Project
|
||||
run: make -j4
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Deploy VSyntaxHighlighting Framework
|
||||
run: |
|
||||
my_lib_name=VSyntaxHighlighting
|
||||
my_lib_framework=${my_lib_name}.framework
|
||||
my_lib_dir=./libs/vtextedit/src/libs/syntax-highlighting
|
||||
frameworks_dir=./src/vnote.app/Contents/Frameworks
|
||||
mkdir -p ${frameworks_dir}
|
||||
cp -R ${my_lib_dir}/${my_lib_framework} ${frameworks_dir}
|
||||
# Keep only required SQL drivers
|
||||
rm ${{env.Qt6_DIR}}/plugins/sqldrivers/libqsqlmimer.dylib
|
||||
rm ${{env.Qt6_DIR}}/plugins/sqldrivers/libqsqlodbc.dylib
|
||||
rm ${{env.Qt6_DIR}}/plugins/sqldrivers/libqsqlpsql.dylib
|
||||
|
||||
# Build the project
|
||||
cmake --build . --target pack
|
||||
|
||||
# Fix Qt frameworks
|
||||
python3 ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py ./src/VNote.app/Contents/MacOS/VNote ${{env.Qt6_DIR}}/../..
|
||||
|
||||
# Only delete rpaths that exist to avoid errors
|
||||
for rpath in $(otool -l ./src/VNote.app/Contents/MacOS/VNote | awk '/LC_RPATH/ {getline; getline; print $2}' | grep 'vnote'); do
|
||||
echo "Checking rpath: $rpath"
|
||||
if otool -l ./src/VNote.app/Contents/MacOS/VNote | grep -q "$rpath"; then
|
||||
echo "Deleting rpath: $rpath"
|
||||
install_name_tool -delete_rpath "$rpath" ./src/VNote.app/Contents/MacOS/VNote
|
||||
else
|
||||
echo "Rpath not found: $rpath"
|
||||
fi
|
||||
done
|
||||
for rpath in $(otool -l ./src/VNote.app/Contents/Frameworks/libVTextEdit.dylib | awk '/LC_RPATH/ {getline; getline; print $2}' | grep 'vnote'); do
|
||||
echo "Checking rpath: $rpath"
|
||||
if otool -l ./src/VNote.app/Contents/Frameworks/libVTextEdit.dylib | grep -q "$rpath"; then
|
||||
echo "Deleting rpath: $rpath"
|
||||
install_name_tool -delete_rpath "$rpath" ./src/VNote.app/Contents/Frameworks/libVTextEdit.dylib
|
||||
else
|
||||
echo "Rpath not found: $rpath"
|
||||
fi
|
||||
done
|
||||
|
||||
# Run macdeployqtfix again to ensure all dependencies are properly fixed
|
||||
python3 ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py ./src/VNote.app/Contents/MacOS/VNote ${{env.Qt6_DIR}}/../..
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Deploy VTextEdit Framework
|
||||
- name: Codesign Bundle
|
||||
# Extract the secrets we defined earlier as environment variables
|
||||
env:
|
||||
MACOS_CERTIFICATE: ${{ secrets.CLI_MACOS_CERTIFICATE }}
|
||||
MACOS_CERTIFICATE_PWD: ${{ secrets.CLI_MACOS_CERTIFICATE_PWD }}
|
||||
MACOS_CERTIFICATE_NAME: ${{ secrets.CLI_MACOS_CERTIFICATE_NAME }}
|
||||
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.CLI_MACOS_CERTIFICATE }}
|
||||
run: |
|
||||
my_lib_name=VTextEdit
|
||||
my_lib_framework=${my_lib_name}.framework
|
||||
my_lib_dir=./libs/vtextedit/src/editor
|
||||
frameworks_dir=./src/vnote.app/Contents/Frameworks
|
||||
mkdir -p ${frameworks_dir}
|
||||
cp -R ${my_lib_dir}/${my_lib_framework} ${frameworks_dir}
|
||||
# Turn our base64-encoded certificate back to a regular .p12 file
|
||||
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
|
||||
|
||||
# We need to create a new keychain, otherwise using the certificate will prompt
|
||||
# with a UI dialog asking for the certificate password, which we can't
|
||||
# use in a headless CI environment
|
||||
security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
||||
security default-keychain -s build.keychain
|
||||
security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
||||
security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
||||
|
||||
echo "Codesigning main app bundle"
|
||||
codesign --force --deep -s "$MACOS_CERTIFICATE_NAME" --entitlements ${{github.workspace}}/package/entitlements.xml --options runtime ./src/VNote.app
|
||||
codesign -v -vvv ./src/VNote.app
|
||||
|
||||
hdiutil create -volname "VNote" -srcfolder ./src/VNote.app -ov -format UDZO VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
|
||||
codesign --force --deep -s "$MACOS_CERTIFICATE_NAME" --entitlements ${{github.workspace}}/package/entitlements.xml --options runtime ./VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
|
||||
codesign -v -vvv ./VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
|
||||
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Cleanup rpath
|
||||
- name: "Notarize Bundle"
|
||||
# Extract the secrets we defined earlier as environment variables
|
||||
env:
|
||||
PROD_MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.CLI_MACOS_NOTARY_USER }}
|
||||
PROD_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.CLI_MACOS_TEAM_ID }}
|
||||
PROD_MACOS_NOTARIZATION_PWD: ${{ secrets.CLI_MACOS_NOTARY_PWD }}
|
||||
run: |
|
||||
app_target=./src/vnote.app/Contents/MacOS/vnote
|
||||
install_name_tool -delete_rpath ${PWD}/src/../libs/vtextedit/src/editor ${app_target}
|
||||
install_name_tool -delete_rpath ${PWD}/src/../libs/vtextedit/src/libs/syntax-highlighting ${app_target}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
# Store the notarization credentials so that we can prevent a UI password dialog
|
||||
# from blocking the CI
|
||||
echo "Create keychain profile"
|
||||
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" --password "$PROD_MACOS_NOTARIZATION_PWD"
|
||||
|
||||
- name: Mac Deploy
|
||||
run: |
|
||||
pushd src
|
||||
macdeployqt vnote.app
|
||||
python ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py vnote.app/Contents/MacOS/vnote $Qt5_Dir
|
||||
# Fix Helpers/QtWebEngineProcess.app
|
||||
pushd vnote.app/Contents/Frameworks/QtWebEngineCore.framework/Versions/5/Helpers
|
||||
macdeployqt QtWebEngineProcess.app
|
||||
python ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess $Qt5_Dir
|
||||
popd
|
||||
popd
|
||||
tree ./
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
# Here we send the notarization request to the Apple's Notarization service, waiting for the result.
|
||||
# This typically takes a few seconds inside a CI environment, but it might take more depending on the App
|
||||
# characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if
|
||||
# you're curious
|
||||
echo "Notarize app"
|
||||
xcrun notarytool submit "${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg" --keychain-profile "notarytool-profile" --wait
|
||||
|
||||
- name: Package Project
|
||||
run: |
|
||||
mkdir -p distrib/vnote
|
||||
pushd distrib/vnote
|
||||
mv ../../src/vnote.app ./
|
||||
ln -s /Applications ./Applications
|
||||
popd
|
||||
sleep 1m
|
||||
hdiutil create -srcfolder ./distrib/vnote -format UDBZ vnote-mac-x64_v${{env.VNOTE_VER}}.dmg
|
||||
cp vnote-mac-x64_v${{env.VNOTE_VER}}.dmg vnote-mac-x64.dmg
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
# Finally, we need to "attach the staple" to our executable, which will allow our app to be
|
||||
# validated by macOS even when an internet connection is not available.
|
||||
echo "Attach staple"
|
||||
xcrun stapler staple "${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg"
|
||||
|
||||
- name: Archive Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
# Enable tmate debugging of manually-triggered workflows if the input option was provided
|
||||
- name: Setup tmate session
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
|
||||
|
||||
- name: Archive DMG
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: vnote-mac-x64_v${{env.VNOTE_VER}}
|
||||
path: ${{runner.workspace}}/build/vnote-mac-x64_v${{env.VNOTE_VER}}.dmg
|
||||
name: VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}
|
||||
path: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
|
||||
|
||||
- name: Update Tag
|
||||
if: github.ref == 'refs/heads/master'
|
||||
@ -130,6 +219,17 @@ jobs:
|
||||
uses: johnwbyrd/update-release@v1.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: ${{runner.workspace}}/build/vnote-mac-x64.dmg
|
||||
files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
|
||||
release: Continuous Build
|
||||
tag: continuous-build
|
||||
|
||||
- name: Release
|
||||
if: github.ref == 'refs/heads/master' && startsWith(github.event.head_commit.message, '[Release]')
|
||||
uses: ncipollo/release-action@v1.11.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
|
||||
commit: master
|
||||
tag: v${{env.VNOTE_VER}}
|
||||
allowUpdates: true
|
||||
draft: true
|
||||
|
131
.github/workflows/ci-win.yml
vendored
131
.github/workflows/ci-win.yml
vendored
@ -8,29 +8,44 @@ on:
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab.
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
debug_enabled:
|
||||
type: boolean
|
||||
description: 'Run the build with tmate debugging enabled'
|
||||
required: false
|
||||
default: false
|
||||
|
||||
env:
|
||||
VNOTE_VER: 3.5.0
|
||||
VNOTE_VER: 3.19.2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.config.name }}
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-${{ matrix.config.vs_version }}
|
||||
timeout-minutes: 120
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {
|
||||
name: "Build On Win64",
|
||||
arch: win64_msvc2017_64,
|
||||
platform: x64
|
||||
}
|
||||
- {
|
||||
name: "Build On Win32",
|
||||
arch: win32_msvc2017,
|
||||
platform: x86
|
||||
}
|
||||
- name: "Build on Win64 Qt 5.15"
|
||||
arch: win64_msvc2019_64
|
||||
vs_version: 2019
|
||||
vs_cmd: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Auxiliary\\Build\\vcvars64.bat"
|
||||
qt: 5.15.2
|
||||
qt_modules: qtwebengine
|
||||
qt_tools: tools_opensslv3_x64
|
||||
qt_major: 5
|
||||
suffix: "-windows7"
|
||||
- name: "Build on Win64 Qt 6"
|
||||
arch: win64_msvc2022_64
|
||||
vs_version: 2022
|
||||
vs_cmd: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Auxiliary\\Build\\vcvars64.bat"
|
||||
qt: 6.8.3
|
||||
qt_modules: "qtwebengine qtwebchannel qtpositioning qtpdf qtimageformats qt5compat"
|
||||
qt_tools: tools_opensslv3_x64
|
||||
qt_major: 6
|
||||
suffix: ""
|
||||
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE.
|
||||
@ -45,65 +60,72 @@ jobs:
|
||||
git submodule sync --recursive
|
||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
- name: Cache Qt
|
||||
id: cache-qt
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
version: 5.12.10
|
||||
path: ${{runner.workspace}}/Qt
|
||||
key: ${{runner.os}}-${{matrix.config.arch}}-QtCache-${{matrix.config.qt}}
|
||||
|
||||
- name: Install Qt Official Build
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: ${{matrix.config.qt}}
|
||||
target: desktop
|
||||
arch: ${{matrix.config.arch}}
|
||||
modules: qtwebchannel qtwebengine qtsvg qtlocation qttools qttranslations
|
||||
tools: tools_openssl_${{matrix.config.platform}},1.1.1-4,qt.tools.openssl.win_${{matrix.config.platform}}
|
||||
modules: ${{matrix.config.qt_modules}}
|
||||
tools: ${{matrix.config.qt_tools}}
|
||||
cache: 'true'
|
||||
|
||||
- name: Create Build Dir
|
||||
shell: bash
|
||||
run: mkdir build
|
||||
working-directory: ${{runner.workspace}}
|
||||
|
||||
- name: Clone OpenSSL
|
||||
- name: Clone OpenSSL on 5.15
|
||||
shell: bash
|
||||
if: ${{startsWith(matrix.config.qt, '5.15')}}
|
||||
run: |
|
||||
git clone https://github.com/tamlok/openssl-utils.git openssl-utils.git --depth=1
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
# Enable tmate debugging of manually-triggered workflows if the input option was provided
|
||||
- name: Setup tmate session
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
|
||||
|
||||
- name: Configure and Build Project
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" ${{matrix.config.platform}}
|
||||
qmake -r -spec win32-msvc CONFIG-=debug CONFIG+=release %GITHUB_WORKSPACE%\vnote.pro
|
||||
nmake
|
||||
cmake --version
|
||||
call "${{matrix.config.vs_cmd}}"
|
||||
cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DQT_DEFAULT_MAJOR_VERSION=${{matrix.config.qt_major}} -DOPENSSL_EXTRA_LIB_DIR=${{runner.workspace}}\build\openssl-utils.git\1.1.1j\Win_x64 %GITHUB_WORKSPACE%
|
||||
cmake --build .
|
||||
cmake --build . --target=pack
|
||||
7z x VNote*.zip -o*
|
||||
dir
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Package Project
|
||||
shell: cmd
|
||||
- name: Rename on 5.15
|
||||
shell: bash
|
||||
if: ${{startsWith(matrix.config.qt, '5.15')}}
|
||||
run: |
|
||||
mkdir "%DISTRIB_PATH%"
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" ${{matrix.config.platform}}
|
||||
windeployqt.exe --dir "%DISTRIB_PATH%" .\src\release\vnote.exe
|
||||
copy .\src\release\vnote.exe "%DISTRIB_PATH%\vnote.exe"
|
||||
copy .\src\release\vnote_extra.rcc "%DISTRIB_PATH%\vnote_extra.rcc"
|
||||
copy .\libs\vtextedit\src\libs\syntax-highlighting\release\VSyntaxHighlighting.dll "%DISTRIB_PATH%\VSyntaxHighlighting.dll"
|
||||
copy .\libs\vtextedit\src\editor\release\VTextEdit.dll "%DISTRIB_PATH%\VTextEdit.dll"
|
||||
rem set qt_dir=%Qt5_Dir:/=\%
|
||||
rem for %%I in ("%qt_dir%\..\..") do set "qt_topdir=%%~fI"
|
||||
rem set openssl_dir=%qt_topdir%\Tools\OpenSSL\Win_${{matrix.config.platform}}\bin
|
||||
set openssl_dir=openssl-utils.git\1.1.1g\Win_${{matrix.config.platform}}
|
||||
copy %openssl_dir%\lib*.dll "%DISTRIB_PATH%\"
|
||||
copy "%GITHUB_WORKSPACE%\README.md" "%DISTRIB_PATH%\README.md"
|
||||
copy "%GITHUB_WORKSPACE%\COPYING.LESSER" "%DISTRIB_PATH%\COPYING.LESSER"
|
||||
echo %GITHUB_SHA% > "%DISTRIB_PATH%\commit"
|
||||
del /F /Q "%DISTRIB_PATH%\translations\qt_*.qm"
|
||||
7z a vnote-win-${{matrix.config.platform}}.zip "%DISTRIB_PATH%"
|
||||
mv VNote-${{env.VNOTE_VER}}-win64 VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}
|
||||
mv VNote-${{env.VNOTE_VER}}-win64.zip VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}.zip
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
env:
|
||||
DISTRIB_PATH: ${{runner.workspace}}/build/distrib/vnote
|
||||
|
||||
- name: Archive Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: vnote-win-${{matrix.config.platform}}_v${{env.VNOTE_VER}}
|
||||
path: ${{env.DISTRIB_PATH}}
|
||||
env:
|
||||
DISTRIB_PATH: ${{runner.workspace}}/build/distrib/vnote
|
||||
name: VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}
|
||||
path: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}
|
||||
|
||||
- name: Archive Installer
|
||||
if: ${{!startsWith(matrix.config.qt, '5.15')}}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}.msi
|
||||
path: ${{runner.workspace}}/build/VNote*.msi
|
||||
|
||||
- name: Update Tag
|
||||
if: github.ref == 'refs/heads/master'
|
||||
@ -117,6 +139,19 @@ jobs:
|
||||
uses: johnwbyrd/update-release@v1.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: ${{runner.workspace}}/build/vnote-win-${{matrix.config.platform}}.zip
|
||||
# glob not supported
|
||||
files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}.zip
|
||||
release: Continuous Build
|
||||
tag: continuous-build
|
||||
|
||||
- name: Release
|
||||
if: github.ref == 'refs/heads/master' && startsWith(github.event.head_commit.message, '[Release]')
|
||||
uses: ncipollo/release-action@v1.11.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# glob not supported
|
||||
artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}
|
||||
commit: master
|
||||
tag: v${{env.VNOTE_VER}}
|
||||
allowUpdates: true
|
||||
draft: true
|
||||
|
12
.gitignore
vendored
12
.gitignore
vendored
@ -2,4 +2,16 @@
|
||||
*.pro.user.*
|
||||
.ccls
|
||||
compile_commands.json
|
||||
compile_commands.json.*
|
||||
compile_flags.txt
|
||||
.cache
|
||||
.tasks
|
||||
.vimspector.json
|
||||
GPATH
|
||||
GRTAGS
|
||||
GTAGS
|
||||
aqtinstall.log
|
||||
tags
|
||||
CMakeLists.txt.user
|
||||
build
|
||||
.DS_Store
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
||||
[submodule "libs/vtextedit"]
|
||||
path = libs/vtextedit
|
||||
url = https://github.com/vnotex/vtextedit.git
|
||||
[submodule "libs/QHotkey"]
|
||||
path = libs/QHotkey
|
||||
url = https://github.com/vnotex/QHotkey.git
|
||||
|
26
CMakeLists.txt
Normal file
26
CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
cmake_minimum_required (VERSION 3.20)
|
||||
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.1" CACHE STRING "Minimum OS X deployment version")
|
||||
|
||||
project(VNote
|
||||
VERSION 3.19.2
|
||||
DESCRIPTION "A pleasant note-taking platform"
|
||||
HOMEPAGE_URL "https://app.vnote.fun"
|
||||
LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type, defaults to Release")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(QHOTKEY_INSTALL OFF CACHE BOOL "Disable installing QHotKey" FORCE)
|
||||
add_subdirectory(libs)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
# TODO: find a better way to organize tests
|
||||
# add_subdirectory(tests)
|
28
README.md
28
README.md
@ -1,11 +1,13 @@
|
||||
# VNote
|
||||
  
|
||||
  
|
||||
|
||||
[简体中文](README_zh_CN.md)
|
||||
|
||||
[Project on Gitee](https://gitee.com/vnotex/vnote)
|
||||
|
||||
A pleasant note-taking platform.
|
||||
|
||||
For more information, please visit [**VNote's Home Page**](https://vnotex.github.io/vnote) or [Home Page on Gitee](https://tamlok.gitee.io/vnote).
|
||||
For more information, please visit [**VNote's Home Page**](https://vnotex.github.io/vnote).
|
||||
|
||||

|
||||
|
||||
@ -18,38 +20,22 @@ Utilizing Qt, VNote could run on **Linux**, **Windows**, and **macOS**.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Downloads
|
||||
Continuous builds on `master` branch could be found at the [Continuous Build](https://github.com/vnotex/vnote/releases/tag/continuous-build) release.
|
||||
|
||||
Latest stable builds could be found at the [latest release](https://github.com/vnotex/vnote/releases/latest). Alternative download services are available:
|
||||
|
||||
* [Tianyi Netdisk](https://cloud.189.cn/t/Av67NvmEJVBv)
|
||||
* [Baidu Netdisk](https://pan.baidu.com/s/1lX69oMBw8XuJshQDN3HiHw?pwd=f8fk)
|
||||
|
||||
## Supports
|
||||
* [GitHub Issues](https://github.com/vnotex/vnote/issues);
|
||||
* Email: `tamlokveer at gmail.com`;
|
||||
* [Slack](https://join.slack.com/t/vnote/shared_invite/enQtNDg2MzY0NDg3NzI4LTVhMzBlOTY0YzVhMmQyMTFmZDdhY2M3MDQxYTBjOTA2Y2IxOGRiZjg2NzdhMjkzYmUyY2VkMWJlZTNhMTQyODU);
|
||||
* [Telegram](https://t.me/vnotex);
|
||||
* WeChat Public Account: vnotex;
|
||||
|
||||
## Donate
|
||||
You could help VNote's development in many ways.
|
||||
|
||||
* Keep monitoring VNote and sending feedback for improvement.
|
||||
* Spread and promote VNote to your friends. Popularity is a strong power to drive developers.
|
||||
* Participate in the development of VNote and send [Pull Request](https://github.com/vnotex/vnote/pulls) to make VNote perfect.
|
||||
* Last, really appreciate your donations to VNote if VNote does help.
|
||||
|
||||
**PayPal**: [PayPal.Me/vnotemd](https://www.paypal.me/vnotemd)
|
||||
|
||||
**Alipay**: `tamlokveer@gmail.com`
|
||||
|
||||
<img src="pics/alipay.png" width="256px" height="256px" />
|
||||
|
||||
**WeChat**
|
||||
|
||||
<img src="pics/wechat_pay.png" width="256px" height="256px" />
|
||||
|
||||
Thank [users who donated to VNote](https://github.com/vnotex/vnote/wiki/Donate-List)!
|
||||
|
||||
## License
|
||||
|
@ -1,56 +1,42 @@
|
||||
# VNote
|
||||
  
|
||||
  
|
||||
|
||||
[English](README.md)
|
||||
|
||||
[Gitee托管项目](https://gitee.com/vnotex/vnote)
|
||||
|
||||
一个舒适的笔记平台!
|
||||
|
||||
更多信息,请访问 [VNote 主页](https://tamlok.gitee.io/vnote) 或者[由 Gitee 托管的主页](https://tamlok.gitee.io/vnote) 。
|
||||
更多信息,请访问[VNote主页](https://vnotex.github.io/vnote)。
|
||||
|
||||

|
||||
|
||||
## 简介
|
||||
**VNote** 是一个专注于 Markdown 的基于 Qt 的开源免费的笔记应用。VNote 希望能提供一个拥有完美编辑体验的舒适的笔记平台。
|
||||
**VNote**是一个专注于Markdown的基于Qt的开源免费的笔记应用。VNote希望能提供一个拥有完美编辑体验的舒适的笔记平台。
|
||||
|
||||
VNote 不是一个简单的 Markdown 编辑器。通过提供强大的笔记管理,VNote 使得使用 Markdown 记笔记更轻松简单。将来,VNote 会支持更多的文档格式。
|
||||
VNote不是一个简单的Markdown编辑器。通过提供强大的笔记管理,VNote使得使用Markdown记笔记更轻松简单。将来,VNote会支持更多的文档格式。
|
||||
|
||||
得益于 Qt,VNote 当前可以高效地运行在 **Linux** , **Windows** ,以及 **macOS** 平台上。
|
||||
得益于Qt,VNote当前可以高效地运行在**Linux**,**Windows**,以及**macOS**平台上。
|
||||
|
||||

|
||||
|
||||
## 下载
|
||||
基于 `master` 分支的 [持续构建版本发布](https://github.com/vnotex/vnote/releases/tag/continuous-build) 。
|
||||

|
||||
|
||||
最新的 [稳定版本发布](https://github.com/vnotex/vnote/releases/latest) 。 其他下载选项:
|
||||
## 下载
|
||||
基于`master`分支的[持续构建版本发布](https://github.com/vnotex/vnote/releases/tag/continuous-build)。
|
||||
|
||||
最新的[稳定版本发布](https://github.com/vnotex/vnote/releases/latest)。其他下载选项:
|
||||
|
||||
* [天翼云盘](https://cloud.189.cn/t/Av67NvmEJVBv)
|
||||
* [百度云盘](https://pan.baidu.com/s/1lX69oMBw8XuJshQDN3HiHw?pwd=f8fk)
|
||||
|
||||
## 支持
|
||||
* [GitHub Issues](https://github.com/vnotex/vnote/issues) ;
|
||||
* 邮件: `tamlokveer at gmail.com` ;
|
||||
* [Slack](https://join.slack.com/t/vnote/shared_invite/enQtNDg2MzY0NDg3NzI4LTVhMzBlOTY0YzVhMmQyMTFmZDdhY2M3MDQxYTBjOTA2Y2IxOGRiZjg2NzdhMjkzYmUyY2VkMWJlZTNhMTQyODU) ;
|
||||
* [Telegram](https://t.me/vnotex) ;
|
||||
* 微信公众号: vnotex ;
|
||||
* [GitHub Issues](https://github.com/vnotex/vnote/issues);
|
||||
* 邮件:`tamlokveer at gmail.com`;
|
||||
* [Telegram](https://t.me/vnotex);
|
||||
* 微信公众号:`vnotex`;
|
||||
|
||||
## 捐赠
|
||||
有很多方式可以帮助 VNote 的开发:
|
||||
|
||||
* 持续关注 VNote 并反馈问题以帮助改进;
|
||||
* 推荐 VNote 给朋友,口碑传播;
|
||||
* 参与 VNote 的开发,发起 [拉取请求](https://github.com/vnotex/vnote/pulls) 一起改进 VNote;
|
||||
* 如果 VNote 真的好用,可以考虑捐赠;
|
||||
|
||||
**PayPal**: [PayPal.Me/vnotemd](https://www.paypal.me/vnotemd)
|
||||
|
||||
**支付宝**: `tamlokveer@gmail.com`
|
||||
|
||||
<img src="pics/alipay.png" width="256px" height="256px" />
|
||||
|
||||
**微信**
|
||||
|
||||
<img src="pics/wechat_pay.png" width="256px" height="256px" />
|
||||
|
||||
感谢这些 [捐赠用户](https://github.com/vnotex/vnote/wiki/Donate-List) !
|
||||
感谢这些[捐赠用户](https://github.com/vnotex/vnote/wiki/Donate-List)!
|
||||
|
||||
## 许可
|
||||
VNote 遵循 [GNU LGPLv3](https://opensource.org/licenses/LGPL-3.0) 许可。VNote 项目的代码可以自由给 VNoteX 项目使用。
|
||||
VNote遵循[GNU LGPLv3](https://opensource.org/licenses/LGPL-3.0)许可。VNote项目的代码可以自由给VNoteX项目使用。
|
||||
|
15
SECURITY.md
Normal file
15
SECURITY.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Only the latest version is supported with security updates.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please [contact support](mailto:tamlokveer@gmail.com) **with a proof of concept** that shows the security vulnerability. Please do not contact us without this proof of concept, as we cannot fix anything without this.
|
||||
|
||||
For general opinions on what makes an app more or less secure, please use the forum.
|
||||
|
||||
## Bounty
|
||||
|
||||
We **do not** offer a bounty for discovering vulnerabilities, please do not ask. We can however credit you and link to your website/profile in the changelog and release announcement.
|
139
changes.md
139
changes.md
@ -1,4 +1,143 @@
|
||||
# Changes
|
||||
## v3.19.2
|
||||
* Codesign MacOS Bundle
|
||||
* Fix toolbar expansion button style
|
||||
* Support hot-reloading of theme via --watch-themes option
|
||||
|
||||
## v3.19.1
|
||||
* Fix toolbar button in Qt 6.8
|
||||
|
||||
## v3.19.0
|
||||
* Add VSCode-sytle editor shortcuts
|
||||
|
||||
## v3.18.1
|
||||
* Fix crash caused by Qt6 change
|
||||
* Fix XSS protection exemption
|
||||
* Check link before open
|
||||
|
||||
## v3.18.0
|
||||
* Upgrade to Qt6
|
||||
* Support MacOS universal build
|
||||
* Upgrade Mermaid, Flowchart.js, and markdown-it
|
||||
* Markdown-it
|
||||
* Fix XSS protection and turn it on by default
|
||||
* Support mark by `==xx==`
|
||||
|
||||
## v3.17.0
|
||||
* Quick note: create note in given scheme (@feloxx)
|
||||
* MarkdownEditor: support inserting multiple images (@feloxx)
|
||||
* Mermaid: upgrade and fix preview issue (@ygcaicn)
|
||||
* Flowchart.js: upgrade
|
||||
|
||||
## v3.16.0
|
||||
* Support reading PDF format
|
||||
* Support Ming Map editor in suffix `*.emind`
|
||||
* Support "View By" for notebooks selector
|
||||
* ViewWindow: add shortcut Ctrl+G,V to alternate among view modes
|
||||
* Bug fixes
|
||||
|
||||
## v3.15.1
|
||||
* Add two themes
|
||||
* Bug fixes
|
||||
|
||||
## v3.15.0
|
||||
* Editor supports Word Count
|
||||
* Add Open Windows panel
|
||||
* Theme: add Vue-light theme
|
||||
* Support default open mode
|
||||
* NotebookSelector: support dynamic icons for notebooks
|
||||
|
||||
## v3.14.0
|
||||
* Theme: support custom icons
|
||||
* Theme: refine icons
|
||||
* NavigationMode: fix issue for input method
|
||||
|
||||
## v3.13.1
|
||||
* Shortcuts for Copy/Paste/Properties in node explorer
|
||||
* Global shortcut to call out main window
|
||||
* UnitedEntry: bug fix for macOS
|
||||
|
||||
## v3.13.0
|
||||
* United Entry: migration of Universal Entry
|
||||
|
||||
## v3.12.888
|
||||
* Fix shortcuts in key sequence with input method (like `Ctrl+G, E`)
|
||||
* Add line ending settings for config files
|
||||
* FindAndReplace: fix zero-length search
|
||||
* QuickAccess: support folders
|
||||
* Upgrade to Qt 5.15.2
|
||||
* Support file associations
|
||||
* NewNoteDialog: remember default file type
|
||||
|
||||
## v3.12.0
|
||||
* NotebookExplorer: support separate node explorer
|
||||
* Theme: add user-provided VSCode-Dark theme
|
||||
* MarkdownEditor: use web to highlight code blocks
|
||||
* MarkdownViewWindow
|
||||
* Add switch for code block line number
|
||||
* Fix ParseToMarkdown `<style>` issue
|
||||
* Add config for overridding MathJax script
|
||||
* SortDialog: fix sorting issue of date
|
||||
* FramelessMainWindow: fix StayOnTop issue
|
||||
|
||||
## v3.11.0
|
||||
* Task: support a simple task system (@tootal)
|
||||
* Theme: add user-provided Solarized-Dark and Solarized-Light themes
|
||||
* Export: fix wkhtmltopdf table-of-contents translation
|
||||
* Support equation begin in MathJax
|
||||
* MainWindow: decide DPI on the screen vnote starts
|
||||
* Settings: support searching
|
||||
* Fix crash caused by Youdao Dict
|
||||
|
||||
## v3.10.1
|
||||
* MarkdownEditor: fix view mode issue
|
||||
* Support print
|
||||
* Refine icons
|
||||
|
||||
## v3.10.0
|
||||
* MarkdownEditor
|
||||
* Support side-by-side edit with preview
|
||||
* Support config for highlighting whitespace
|
||||
* Tag: fix input method issue on macOS
|
||||
|
||||
## v3.9.0
|
||||
* Remove recycle bin node (now recycle bin is just a simple folder)
|
||||
* Quick Access: support removing items directly
|
||||
* MarkdownEditor
|
||||
* Support centering images in read mode
|
||||
* Add user.css for user styles in read mode
|
||||
* Add debugger by F12
|
||||
* Support context-sensitive context menu for images and links
|
||||
|
||||
## v3.8.0
|
||||
* Support tags
|
||||
* Introduce notebook database using SQLITE
|
||||
* A perfect frameless main window on Windows
|
||||
* Add switch to control whether store history in notebook
|
||||
* Refine dock widgets of main window
|
||||
* NotebookExplorer: support scan notebook and import external files
|
||||
|
||||
## v3.7.0
|
||||
* PlantUml/Graphviz: support relative path executable
|
||||
* macOS: support opening file with VNote in Finder
|
||||
* Sort notes by name case-insensitively
|
||||
* Export
|
||||
* Support All-in-One in PDF format
|
||||
* Support Custom export format (like Pandoc)
|
||||
* Allow minimizing the export dialog and doing export at background
|
||||
* MainWindow: use icon-only bar for docks
|
||||
* Support update check
|
||||
* Add shortcuts for CloseOtherTabs and CloseTabsToTheRight
|
||||
* Search: highlight matched items in opened files
|
||||
* Editor: support specifying line ending
|
||||
|
||||
## v3.6.0
|
||||
* Support **Image Host**: GitHub and Gitee
|
||||
* Add config page for Vi
|
||||
|
||||
## v3.5.1
|
||||
* LocationList: fix recently introduced regression when highlighting segments of text
|
||||
|
||||
## v3.5.0
|
||||
* Support History
|
||||
* ViewArea
|
||||
|
2
libs/CMakeLists.txt
Normal file
2
libs/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
add_subdirectory(QHotkey)
|
||||
add_subdirectory(vtextedit)
|
1
libs/QHotkey
Submodule
1
libs/QHotkey
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 8abe0b2280533af57f423f5785acc4d9d4d73ab8
|
@ -1,4 +0,0 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += \
|
||||
vtextedit
|
@ -1 +1 @@
|
||||
Subproject commit 922084a388e1f135e25297ba84a9d0ca0078ed06
|
||||
Subproject commit 50b1421793af3882ddc62ad4e6cf5537e1d7906f
|
24
package/QtWebEngineProcess.entitlements.xml
Normal file
24
package/QtWebEngineProcess.entitlements.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.get-task-allow</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.debugger</key>
|
||||
<true/>
|
||||
<key>com.apple.security.inherit</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
BIN
package/banner_text.png
Normal file
BIN
package/banner_text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
24
package/entitlements.xml
Normal file
24
package/entitlements.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
||||
<true/>
|
||||
<key>com.apple.security.get-task-allow</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.debugger</key>
|
||||
<true/>
|
||||
<key>com.apple.security.inherit</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
110
package/lgpl-3.0.rtf
Normal file
110
package/lgpl-3.0.rtf
Normal file
@ -0,0 +1,110 @@
|
||||
{\rtf1\ansi\deff3\adeflang1025
|
||||
{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Liberation Serif{\*\falt Times New Roman};}{\f4\fswiss\fprq2\fcharset0 Liberation Sans{\*\falt Arial};}{\f5\froman\fprq0\fcharset128 Helvetica{\*\falt Arial};}{\f6\fnil\fprq2\fcharset0 Droid Sans Fallback;}{\f7\fnil\fprq2\fcharset0 FreeSans;}{\f8\fswiss\fprq0\fcharset128 FreeSans;}}
|
||||
{\colortbl;\red0\green0\blue0;\red0\green0\blue128;\red128\green128\blue128;}
|
||||
{\stylesheet{\s0\snext0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033 Normal;}
|
||||
{\*\cs15\snext15\cf2\ul\ulc0\langfe255\alang255\lang255 Internet Link;}
|
||||
{\s16\sbasedon0\snext17\sb240\sa120\keepn\dbch\af6\dbch\af7\afs28\loch\f4\fs28 Heading;}
|
||||
{\s17\sbasedon0\snext17\sl288\slmult1\sb0\sa140 Text Body;}
|
||||
{\s18\sbasedon17\snext18\sl288\slmult1\sb0\sa140\dbch\af8 List;}
|
||||
{\s19\sbasedon0\snext19\sb120\sa120\noline\i\dbch\af8\afs24\ai\fs24 Caption;}
|
||||
{\s20\sbasedon0\snext20\noline\dbch\af8 Index;}
|
||||
}{\info{\creatim\yr0\mo0\dy0\hr0\min0}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}{\comment LibreOffice}{\vern67241986}}\deftab720
|
||||
\viewscale100
|
||||
{\*\pgdsctbl
|
||||
{\pgdsc0\pgdscuse451\pgwsxn12240\pghsxn15840\marglsxn1800\margrsxn1800\margtsxn1440\margbsxn1440\pgdscnxt0 Default Style;}}
|
||||
\formshade{\*\pgdscno0}\paperh15840\paperw12240\margl1800\margr1800\margt1440\margb1440\sectd\sbknone\sectunlocked1\pgndec\pgwsxn12240\pghsxn15840\marglsxn1800\margrsxn1800\margtsxn1440\margbsxn1440\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc
|
||||
\pgndec\pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs28\loch\f5
|
||||
GNU LESSER GENERAL PUBLIC LICENSE}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
Version 3, 29 June 2007}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
Copyright \u169\'3f 2007 Free Software Foundation, Inc. <}{{\field{\*\fldinst HYPERLINK "https://fsf.org/" }{\fldrslt {\cf2\ul\ulc0\langfe255\alang255\lang255\ul\ulc0\rtlch \ltrch\loch\loch\f5
|
||||
https://fsf.org/}{}}}\rtlch \ltrch\loch\loch\f5
|
||||
>}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
|
||||
0. Additional Definitions.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
As used herein, \uc2 \u8220\'81\'67this License\u8221\'81\'68 refers to version 3 of the GNU Lesser General Public License, and the \u8220\'81\'67GNU GPL\u8221\'81\'68 refers to version 3 of the GNU General Public License.\uc1 }
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\f5
|
||||
\uc2 \u8220\'81\'67\uc1 }{\rtlch \ltrch\loch\loch\f5
|
||||
The Library\uc2 \u8221\'81\'68 refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.\uc1 }
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
An \uc2 \u8220\'81\'67Application\u8221\'81\'68 is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.\uc1 }
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
A \uc2 \u8220\'81\'67Combined Work\u8221\'81\'68 is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the \u8220\'81\'67Linked Version\u8221\'81\'68.\uc1 }
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
The \uc2 \u8220\'81\'67Minimal Corresponding Source\u8221\'81\'68 for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.\uc1 }
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
The \uc2 \u8220\'81\'67Corresponding Application Code\u8221\'81\'68 for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.\uc1 }
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
|
||||
1. Exception to Section 3 of the GNU GPL.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
|
||||
2. Conveying Modified Versions.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
|
||||
3. Object Code Incorporating Material from Library Header Files.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab b) Accompany the object code with a copy of the GNU GPL and this license document.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
|
||||
4. Combined Works.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab b) Accompany the Combined Work with a copy of the GNU GPL and this license document.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab d) Do one of the following:}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li720\ri0\lin720\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8211\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li720\ri0\lin720\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8211\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
|
||||
5. Combined Libraries.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
|
||||
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
|
||||
\tab b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
|
||||
6. Revised Versions of the GNU Lesser General Public License.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License \uc2 \u8220\'81\'67or any later version\u8221\'81\'68 applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.\uc1 }
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
|
||||
If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.}
|
||||
\par }
|
2
package/qt.conf
Normal file
2
package/qt.conf
Normal file
@ -0,0 +1,2 @@
|
||||
[Platforms]
|
||||
WindowsArguments = fontengine=freetype
|
BIN
package/wix_banner.png
Normal file
BIN
package/wix_banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
package/wix_dialog.png
Normal file
BIN
package/wix_dialog.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
pics/main.png
BIN
pics/main.png
Binary file not shown.
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 269 KiB |
BIN
pics/main2.png
Normal file
BIN
pics/main2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 KiB |
26
privacy_policy.md
Normal file
26
privacy_policy.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Privacy Policy
|
||||
|
||||
## Introduction
|
||||
Welcome to VNote! We value your privacy and are committed to protecting your personal information. This Privacy Policy explains how we handle your information when you use our application.
|
||||
|
||||
## Information We Collect
|
||||
VNote does not collect any personal or usage data from its users. Your interactions with the app remain private and are not stored or shared.
|
||||
|
||||
## How We Use Your Information
|
||||
Since VNote does not collect any user data, we do not use, store, or process any personal information.
|
||||
|
||||
## Sharing Your Information
|
||||
As VNote does not collect any user data, we do not share any personal information with third parties.
|
||||
|
||||
## Security
|
||||
While VNote does not collect any user data, we still implement security measures to ensure the integrity and safety of the application itself.
|
||||
|
||||
## Your Choices
|
||||
Since no data is collected, there are no choices or actions required from users regarding their personal information.
|
||||
|
||||
## Children's Privacy
|
||||
Our services are not intended for children under the age of 13. We do not knowingly collect personal information from children under 13.
|
||||
|
||||
## Changes to This Privacy Policy
|
||||
We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on our app. You are advised to review this Privacy Policy periodically for any changes.
|
||||
|
@ -1,33 +0,0 @@
|
||||
@echo off
|
||||
rem Update .ccls project file for ccls LPS and compile_flags.txt for clangd
|
||||
|
||||
if "%~1"=="" (
|
||||
echo missing argument: the location of Qt's include directory
|
||||
EXIT /B 0
|
||||
)
|
||||
|
||||
set qt_inc=%~1
|
||||
set qt_inc=%qt_inc:\=\\%
|
||||
|
||||
(
|
||||
echo clang
|
||||
echo -fcxx-exceptions
|
||||
echo -std=c++14
|
||||
echo -Isrc\\core
|
||||
echo -Isrc
|
||||
echo -Ilibs\\vtextedit\\src\\editor\\include
|
||||
echo -Ilibs\\vtitlebar\\src
|
||||
echo -I%qt_inc%
|
||||
echo -I%qt_inc%\\QtCore
|
||||
echo -I%qt_inc%\\QtWebEngineWidgets
|
||||
echo -I%qt_inc%\\QtSvg
|
||||
echo -I%qt_inc%\\QtPrintSupport
|
||||
echo -I%qt_inc%\\QtWidgets
|
||||
echo -I%qt_inc%\\QtWebEngineCore
|
||||
echo -I%qt_inc%\\QtGui
|
||||
echo -I%qt_inc%\\QtWebChannel
|
||||
echo -I%qt_inc%\\QtNetwork
|
||||
echo -I%qt_inc%\\QtTest
|
||||
) > ".ccls"
|
||||
|
||||
copy /Y .ccls compile_flags.txt
|
@ -1,30 +0,0 @@
|
||||
#!/bin/sh
|
||||
if [ -n "$1" ]; then
|
||||
echo Qt include directory: $1
|
||||
else
|
||||
echo Please specify the Qt include directory.
|
||||
exit
|
||||
fi
|
||||
|
||||
ccls_file=".ccls"
|
||||
|
||||
echo clang > $ccls_file
|
||||
echo -fcxx-exceptions >> $ccls_file
|
||||
echo -std=c++14 >> $ccls_file
|
||||
echo -Isrc/core >> $ccls_file
|
||||
echo -Isrc >> $ccls_file
|
||||
echo -Ilibs/vtextedit/src/editor/include >> $ccls_file
|
||||
echo -Ilibs/vtitlebar/src >> $ccls_file
|
||||
echo -I$1 >> $ccls_file
|
||||
echo -I$1/QtCore >> $ccls_file
|
||||
echo -I$1/QtWebEngineWidgets >> $ccls_file
|
||||
echo -I$1/QtSvg >> $ccls_file
|
||||
echo -I$1/QtPrintSupport >> $ccls_file
|
||||
echo -I$1/QtWidgets >> $ccls_file
|
||||
echo -I$1/QtWebEngineCore >> $ccls_file
|
||||
echo -I$1/QtGui >> $ccls_file
|
||||
echo -I$1/QtWebChannel >> $ccls_file
|
||||
echo -I$1/QtNetwork >> $ccls_file
|
||||
echo -I$1/QtTest >> $ccls_file
|
||||
|
||||
cp -f .ccls compile_flags.txt
|
@ -7,8 +7,14 @@ if len(sys.argv) < 2:
|
||||
exit
|
||||
|
||||
newVersion = sys.argv[1]
|
||||
shortVersion = re.match('^(\\d+\\.\\d+).', newVersion).group(1)
|
||||
print("New version: {0}".format(newVersion))
|
||||
|
||||
# CMakeList
|
||||
regExp = re.compile('(\\s+)VERSION \\S+')
|
||||
for line in fileinput.input(['CMakeLists.txt'], inplace = True):
|
||||
print(regExp.sub('\\1VERSION ' + newVersion, line), end='')
|
||||
|
||||
# vnotex.json
|
||||
regExp = re.compile('(\\s+)"version" : "\\S+"')
|
||||
for line in fileinput.input(['src/data/core/vnotex.json'], inplace = True):
|
||||
@ -18,3 +24,16 @@ for line in fileinput.input(['src/data/core/vnotex.json'], inplace = True):
|
||||
regExp = re.compile('(\\s+)VNOTE_VER: \\S+')
|
||||
for line in fileinput.input(['.github/workflows/ci-win.yml', '.github/workflows/ci-linux.yml', '.github/workflows/ci-macos.yml'], inplace = True):
|
||||
print(regExp.sub('\\1VNOTE_VER: ' + newVersion, line), end='')
|
||||
|
||||
# Info.plist
|
||||
regExp = re.compile('(\\s+)<string>(?!10\\.15)\\d+\\.\\d+</string>')
|
||||
for line in fileinput.input(['src/data/core/Info.plist'], inplace = True):
|
||||
print(regExp.sub('\\1<string>' + shortVersion + '</string>', line), end='')
|
||||
|
||||
regExp = re.compile('(\\s+)<string>\\d+\\.\\d+\\.\\d+</string>')
|
||||
for line in fileinput.input(['src/data/core/Info.plist'], inplace = True):
|
||||
print(regExp.sub('\\1<string>' + newVersion + '</string>', line), end='')
|
||||
|
||||
regExp = re.compile('(\\s+)<string>\\d+\\.\\d+\\.\\d+\\.\\d+</string>')
|
||||
for line in fileinput.input(['src/data/core/Info.plist'], inplace = True):
|
||||
print(regExp.sub('\\1<string>' + newVersion + '.1</string>', line), end='')
|
||||
|
179
src/CMakeLists.txt
Normal file
179
src/CMakeLists.txt
Normal file
@ -0,0 +1,179 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
set(CMAKE_INSTALL_BINDIR "." CACHE STRING "Binary dir for install")
|
||||
|
||||
set(QT_DEFAULT_MAJOR_VERSION 6 CACHE STRING "Qt version to use (5 or 6), defaults to 6")
|
||||
find_package(Qt${QT_DEFAULT_MAJOR_VERSION} REQUIRED COMPONENTS Core Gui Network PrintSupport Sql Svg Widgets WebChannel WebEngineWidgets LinguistTools)
|
||||
find_package(Qt${QT_DEFAULT_MAJOR_VERSION} OPTIONAL_COMPONENTS Core5Compat)
|
||||
|
||||
if ((QT_DEFAULT_MAJOR_VERSION GREATER 5))
|
||||
qt_standard_project_setup()
|
||||
else()
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
endif()
|
||||
|
||||
# Application icon on Windows
|
||||
set(VX_APP_ICON_RC_WIN data/core/icons/vnote.rc)
|
||||
|
||||
# The MACOSX_BUNDLE_ICON_FILE variable is added to the Info.plist
|
||||
# generated by CMake. This variable contains the .icns file name,
|
||||
# without the path.
|
||||
set(MACOSX_BUNDLE_ICON_FILE vnote.icns)
|
||||
# And the following tells CMake where to find and install the file itself.
|
||||
set(VX_APP_ICON_MACOS data/core/icons/vnote.icns)
|
||||
set_source_files_properties(${VX_APP_ICON_MACOS} PROPERTIES
|
||||
MACOSX_PACKAGE_LOCATION "Resources")
|
||||
|
||||
# Translations
|
||||
set(VX_TS_FILES data/core/translations/vnote_zh_CN.ts
|
||||
data/core/translations/vnote_ja.ts)
|
||||
if((QT_DEFAULT_MAJOR_VERSION EQUAL 6))
|
||||
if((Qt6Widgets_VERSION VERSION_GREATER_EQUAL 6.7.0))
|
||||
qt_add_lupdate(TS_FILES ${VX_TS_FILES}
|
||||
SOURCE_TARGETS vnote)
|
||||
else()
|
||||
qt_add_lupdate(vnote TS_FILES ${VX_TS_FILES})
|
||||
endif()
|
||||
endif()
|
||||
# Generate .qm files from .ts files (lrelease)
|
||||
set_source_files_properties(${VX_TS_FILES} PROPERTIES
|
||||
OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
|
||||
qt_add_translation(VX_QM_FILES ${VX_TS_FILES})
|
||||
add_custom_target(lrelease DEPENDS ${VX_QM_FILES})
|
||||
|
||||
# TODO: VTextEdit translations
|
||||
list(APPEND VX_QM_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qdialogbuttonbox_zh_CN.qm
|
||||
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtbase_ja.qm
|
||||
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtbase_zh_CN.qm
|
||||
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtv_ja.qm
|
||||
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtv_zh_CN.qm
|
||||
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qwebengine_zh_CN.qm
|
||||
)
|
||||
|
||||
# Resources
|
||||
set(VX_RESOURCE_FILES data/core/core.qrc)
|
||||
set(VX_EXTRA_RESOURCE_FILES_RCC ${CMAKE_CURRENT_BINARY_DIR}/vnote_extra.rcc)
|
||||
qt_add_binary_resources(VX_EXTRA_RESOURCE data/extra/extra.qrc DESTINATION ${VX_EXTRA_RESOURCE_FILES_RCC} OPTIONS -compress 9)
|
||||
|
||||
add_executable(vnote WIN32 MACOSX_BUNDLE
|
||||
application.cpp application.h
|
||||
commandlineoptions.cpp commandlineoptions.h
|
||||
fakeaccessible.cpp fakeaccessible.h
|
||||
main.cpp
|
||||
${VX_APP_ICON_RC_WIN} ${VX_APP_ICON_MACOS} ${VX_RESOURCE_FILES}
|
||||
)
|
||||
|
||||
add_dependencies(vnote VX_EXTRA_RESOURCE)
|
||||
|
||||
set(VX_LIBS_FOLDER ../libs)
|
||||
target_include_directories(vnote PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
find_program(GOLD_LINKER "ld.gold")
|
||||
if (NOT ${GOLD_LINKER} STREQUAL GOLD_LINKER-NOTFOUND)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold")
|
||||
endif()
|
||||
|
||||
target_compile_definitions(vnote PRIVATE
|
||||
QT_MESSAGELOGCONTEXT
|
||||
)
|
||||
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(export)
|
||||
add_subdirectory(imagehost)
|
||||
add_subdirectory(search)
|
||||
add_subdirectory(snippet)
|
||||
add_subdirectory(task)
|
||||
add_subdirectory(unitedentry)
|
||||
add_subdirectory(utils)
|
||||
add_subdirectory(widgets)
|
||||
|
||||
target_link_libraries(vnote PRIVATE
|
||||
Qt::Core
|
||||
Qt::Gui
|
||||
Qt::Network
|
||||
Qt::PrintSupport
|
||||
Qt::Sql
|
||||
Qt::Svg
|
||||
Qt::WebChannel
|
||||
Qt::WebEngineWidgets
|
||||
Qt::Widgets
|
||||
VTextEdit
|
||||
qhotkey
|
||||
)
|
||||
|
||||
if((QT_DEFAULT_MAJOR_VERSION GREATER 5))
|
||||
target_link_libraries(vnote PRIVATE
|
||||
Qt::Core5Compat
|
||||
)
|
||||
endif()
|
||||
|
||||
# Copy the qt.conf on Windows
|
||||
if(WIN32)
|
||||
add_custom_command(TARGET vnote POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${PROJECT_SOURCE_DIR}/package/qt.conf" $<TARGET_FILE_DIR:vnote>)
|
||||
endif()
|
||||
|
||||
# Installation
|
||||
if (WIN32)
|
||||
install(TARGETS vnote RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/package/qt.conf" DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(FILES ${VX_EXTRA_RESOURCE_FILES_RCC} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(FILES ${VX_QM_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/translations" OPTIONAL)
|
||||
elseif(APPLE)
|
||||
set(CMAKE_MACOSX_RPATH ON)
|
||||
|
||||
# TODO: declare install for macOS if necessary. For packing, we will manually copy files into
|
||||
# the src/vnote.app bundle.
|
||||
|
||||
# The generated Info.plist will be overridden.
|
||||
set_target_properties(vnote
|
||||
PROPERTIES
|
||||
OUTPUT_NAME "${PROJECT_NAME}"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}"
|
||||
MACOSX_BUNDLE_INFO_STRING "${PROJECT_DESCRIPTION}"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "fun.vnote.vnote"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_COPYRIGHT "Distributed under LGPL-3.0 license. Copyright (c) 2024 app.vnote.fun"
|
||||
)
|
||||
else()
|
||||
install(TARGETS vnote
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
install(FILES ${VX_EXTRA_RESOURCE_FILES_RCC} DESTINATION ${CMAKE_INSTALL_DATADIR})
|
||||
install(FILES ${VX_QM_FILES} DESTINATION "${CMAKE_INSTALL_DATADIR}/translations" OPTIONAL)
|
||||
|
||||
set(desktop.path applications)
|
||||
set(desktop.files data/core/vnote.desktop)
|
||||
set(icon16.path icons/hicolor/16x16/apps)
|
||||
set(icon16.files data/core/logo/16x16/vnote.png)
|
||||
set(icon32.path icons/hicolor/32x32/apps)
|
||||
set(icon32.files data/core/logo/32x32/vnote.png)
|
||||
set(icon48.path icons/hicolor/48x48/apps)
|
||||
set(icon48.files data/core/logo/48x48/vnote.png)
|
||||
set(icon64.path icons/hicolor/64x64/apps)
|
||||
set(icon64.files data/core/logo/64x64/vnote.png)
|
||||
set(icon128.path icons/hicolor/128x128/apps)
|
||||
set(icon128.files data/core/logo/128x128/vnote.png)
|
||||
set(icon256.path icons/hicolor/256x256/apps)
|
||||
set(icon256.files data/core/logo/256x256/vnote.png)
|
||||
set(iconsvg.path icons/hicolor/scalable/apps)
|
||||
set(iconsvg.files data/core/logo/vnote.svg)
|
||||
foreach(item IN ITEMS desktop icon16 icon32 icon48 icon64 icon128 icon256 iconsvg)
|
||||
install(FILES ${CMAKE_CURRENT_LIST_DIR}/${${item}.files}
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/${${item}.path}
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/Packaging.cmake)
|
16
src/CPackLinuxDeployQt.cmake.in
Normal file
16
src/CPackLinuxDeployQt.cmake.in
Normal file
@ -0,0 +1,16 @@
|
||||
message(STATUS "VX_APPIMAGE_DEST_DIR ${VX_APPIMAGE_DEST_DIR}")
|
||||
message(STATUS "VX_APPIMAGE_DESKTOP_FILE ${VX_APPIMAGE_DESKTOP_FILE}")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_MAKE_PROGRAM} DESTDIR=${VX_APPIMAGE_DEST_DIR} install
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
execute_process(
|
||||
COMMAND env QMAKE=${QMAKE_EXECUTABLE} LD_LIBRARY_PATH=/usr/local/lib64:$ENV{LD_LIBRARY_PATH} "${LINUXDEPLOY_EXECUTABLE}" --plugin=qt --output=appimage
|
||||
--appdir=${VX_APPIMAGE_DEST_DIR} -e ${CMAKE_CURRENT_BINARY_DIR}/vnote -d ${VX_APPIMAGE_DESKTOP_FILE}
|
||||
-i ${CMAKE_CURRENT_LIST_DIR}/data/core/logo/64x64/vnote.png
|
||||
-l ${QT_PLUGINS_DIR}/platformthemes/libqgtk3.so
|
||||
-l /usr/local/lib64/libcrypto.so.3
|
||||
-l /usr/local/lib64/libssl.so.3
|
||||
# --exclude-library option does not work as expected
|
||||
# --exclude-library=libssl.so.1.1,libcrypto.so.1.1,libnss3.so,libnssutil3.so
|
||||
WORKING_DIRECTORY ${CPACK_PACKAGE_DIRECTORY})
|
6
src/CPackMacDeployQt.cmake.in
Normal file
6
src/CPackMacDeployQt.cmake.in
Normal file
@ -0,0 +1,6 @@
|
||||
execute_process(COMMAND "optool" strip -t ${CMAKE_CURRENT_BINARY_DIR}/VNote.app
|
||||
WORKING_DIRECTORY ${CPACK_PACKAGE_DIRECTORY}
|
||||
)
|
||||
execute_process(COMMAND "${MACDEPLOYQT_EXECUTABLE}" ${CMAKE_CURRENT_BINARY_DIR}/VNote.app -always-overwrite -verbose=1
|
||||
WORKING_DIRECTORY ${CPACK_PACKAGE_DIRECTORY}
|
||||
)
|
165
src/Packaging.cmake
Normal file
165
src/Packaging.cmake
Normal file
@ -0,0 +1,165 @@
|
||||
# from: https://github.com/miurahr/cmake-qt-packaging-example
|
||||
find_package(Qt${QT_DEFAULT_MAJOR_VERSION} REQUIRED COMPONENTS Core)
|
||||
|
||||
get_target_property(QMAKE_EXECUTABLE Qt::qmake IMPORTED_LOCATION)
|
||||
get_filename_component(QT_BIN_DIR "${QMAKE_EXECUTABLE}" DIRECTORY)
|
||||
execute_process(COMMAND ${QMAKE_EXECUTABLE} -query QT_VERSION OUTPUT_VARIABLE QT_VERSION)
|
||||
|
||||
set(QT_TOOLS_DIR "${QT_BIN_DIR}/../../../Tools")
|
||||
cmake_path(NORMAL_PATH QT_TOOLS_DIR OUTPUT_VARIABLE QT_TOOLS_DIR)
|
||||
|
||||
set(QT_PLUGINS_DIR "${QT_BIN_DIR}/../plugins")
|
||||
cmake_path(NORMAL_PATH QT_PLUGINS_DIR OUTPUT_VARIABLE QT_PLUGINS_DIR)
|
||||
|
||||
# To use the specific version of Qt
|
||||
set(WINDEPLOYQT_EXECUTABLE "${QT_BIN_DIR}/windeployqt.exe")
|
||||
|
||||
find_program(LINUXDEPLOY_EXECUTABLE linuxdeploy linuxdeploy-x86_64.AppImage HINTS "${QT_BIN_DIR}")
|
||||
find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${QT_BIN_DIR}")
|
||||
find_program(MACDEPLOYQTFIX_EXECUTABLE macdeployqtfix.py HINTS "${QT_BIN_DIR}")
|
||||
find_package(Python)
|
||||
|
||||
function(windeployqt target)
|
||||
# Bundle Library Files
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER)
|
||||
|
||||
if ((QT_DEFAULT_MAJOR_VERSION GREATER 5))
|
||||
if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG")
|
||||
set(WINDEPLOYQT_ARGS --debug)
|
||||
else()
|
||||
set(WINDEPLOYQT_ARGS --release)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_custom_target(deploy
|
||||
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/"
|
||||
COMMAND "${CMAKE_COMMAND}" -E
|
||||
env PATH="${QT_BIN_DIR}" "${WINDEPLOYQT_EXECUTABLE}"
|
||||
${WINDEPLOYQT_ARGS}
|
||||
--no-quick-import
|
||||
--no-opengl-sw
|
||||
--no-compiler-runtime
|
||||
--translations zh_CN,ja
|
||||
--dir "${CMAKE_CURRENT_BINARY_DIR}/winqt/"
|
||||
$<TARGET_FILE:${target}>
|
||||
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/generic/"
|
||||
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/styles/"
|
||||
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/qmltooling/"
|
||||
COMMENT "Deploying Qt..."
|
||||
DEPENDS vnote lrelease
|
||||
)
|
||||
|
||||
add_dependencies(pack deploy)
|
||||
|
||||
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/winqt/" DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
|
||||
|
||||
set(OPENSSL_ROOT_DIR "${QT_TOOLS_DIR}/OpenSSL/Win_x64" CACHE STRING "OpenSSL dir")
|
||||
file(GLOB OPENSSL_LIBS_FILES "${OPENSSL_ROOT_DIR}/bin/lib*.dll")
|
||||
cmake_path(NORMAL_PATH OPENSSL_LIBS_FILES OUTPUT_VARIABLE OPENSSL_LIBS_FILES)
|
||||
install(FILES ${OPENSSL_LIBS_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
|
||||
|
||||
message(STATUS "OpenSSLExtraLIBDIR:${OPENSSL_EXTRA_LIB_DIR}")
|
||||
file(GLOB OPENSSL_EXTRA_LIB_FILES "${OPENSSL_EXTRA_LIB_DIR}/lib*.dll")
|
||||
cmake_path(NORMAL_PATH OPENSSL_EXTRA_LIB_FILES OUTPUT_VARIABLE OPENSSL_EXTRA_LIB_FILES)
|
||||
message(STATUS "OpenSSLExtraLibFiles:${OPENSSL_EXTRA_LIB_FILES}")
|
||||
install(FILES ${OPENSSL_EXTRA_LIB_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
|
||||
|
||||
set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE)
|
||||
include(InstallRequiredSystemLibraries)
|
||||
endfunction()
|
||||
|
||||
set(CPACK_PACKAGE_VENDOR "VNoteX")
|
||||
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
|
||||
set(CPACK_PACKAGE_CONTACT "Le Tan <tamlokveer@gmail.com>")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_DESCRIPTION}")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.md")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING.LESSER")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
||||
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}")
|
||||
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
|
||||
# Start menu entry on Windows
|
||||
set(CPACK_PACKAGE_EXECUTABLES "vnote" "VNote")
|
||||
# Desktop link on Windows
|
||||
set(CPACK_CREATE_DESKTOP_LINKS "vnote")
|
||||
|
||||
set(CPACK_STRIP_FILES TRUE)
|
||||
|
||||
# WIX generator
|
||||
set(CPACK_WIX_UPGRADE_GUID BA25F337-991A-4893-9D8A-AD5E89BAF5C4)
|
||||
set(CPACK_WIX_PRODUCT_GUID BA25F337-991A-4893-9D8A-AD5E89BAF5C4)
|
||||
set(CPACK_WIX_LICENSE_RTF "${PROJECT_SOURCE_DIR}/package/lgpl-3.0.rtf")
|
||||
set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_LIST_DIR}/data/core/icons/vnote.ico")
|
||||
set(CPACK_WIX_UI_BANNER "${PROJECT_SOURCE_DIR}/package/wix_banner.png")
|
||||
set(CPACK_WIX_UI_DIALOG "${PROJECT_SOURCE_DIR}/package/wix_dialog.png")
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# include CPack, so we get target for packages
|
||||
set(CPACK_OUTPUT_CONFIG_FILE "${CMAKE_BINARY_DIR}/BundleConfig.cmake")
|
||||
|
||||
add_custom_target(pack
|
||||
COMMAND ${CMAKE_CPACK_COMMAND} "--config" "${CMAKE_BINARY_DIR}/BundleConfig.cmake" "--verbose"
|
||||
COMMENT "Running CPACK. Please wait..."
|
||||
DEPENDS vnote)
|
||||
add_dependencies(pack lrelease)
|
||||
|
||||
set(CPACK_GENERATOR)
|
||||
|
||||
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_LIST_DIR}/data/core/logo/64x64/vnote.png")
|
||||
|
||||
if(WIN32)
|
||||
find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${QT_BIN_DIR}" DOC "Path to the windeployqt utility")
|
||||
|
||||
list(APPEND CPACK_GENERATOR ZIP)
|
||||
message(STATUS "Package generation - Windows - Zip")
|
||||
|
||||
find_program(WIX_EXECUTABLE wix HINTS "${QT_BIN_DIR}" DOC "Path to the WiX utility")
|
||||
|
||||
if (NOT WIX_EXECUTABLE-NOTFOUND)
|
||||
list(APPEND CPACK_GENERATOR WIX)
|
||||
message(STATUS "Package generation - Windows - WiX")
|
||||
endif()
|
||||
|
||||
windeployqt(vnote)
|
||||
elseif(APPLE)
|
||||
# Manually copy resources.
|
||||
set(VX_BUNDLE_CONTENTS_DIR $<TARGET_FILE_DIR:vnote>/..)
|
||||
add_custom_target(deploy
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_CURRENT_LIST_DIR}/data/core/Info.plist" ${VX_BUNDLE_CONTENTS_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${VX_EXTRA_RESOURCE_FILES_RCC} ${VX_BUNDLE_CONTENTS_DIR}/Resources
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${VX_BUNDLE_CONTENTS_DIR}/Resources/translations
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${VX_QM_FILES} ${VX_BUNDLE_CONTENTS_DIR}/Resources/translations
|
||||
COMMENT "Copying resources into bundle Contents ${VX_BUNDLE_CONTENTS_DIR}"
|
||||
DEPENDS vnote lrelease
|
||||
)
|
||||
add_dependencies(pack deploy)
|
||||
|
||||
message(STATUS "MACDeployQtExecutable: ${MACDEPLOYQT_EXECUTABLE}")
|
||||
if (MACDEPLOYQT_EXECUTABLE)
|
||||
message(STATUS "Package generation - MacOS - DMG")
|
||||
|
||||
list(APPEND CPACK_GENERATOR External)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CPackMacDeployQt.cmake.in "${CMAKE_BINARY_DIR}/CPackExternal.cmake")
|
||||
set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_BINARY_DIR}/CPackExternal.cmake")
|
||||
include(InstallRequiredSystemLibraries)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "LinuxDeployExecutable: ${LINUXDEPLOY_EXECUTABLE}")
|
||||
if(LINUXDEPLOY_EXECUTABLE)
|
||||
message(STATUS "Package generation - Linux - AppImage")
|
||||
|
||||
list(APPEND CPACK_GENERATOR External)
|
||||
set(VX_APPIMAGE_DEST_DIR "${CPACK_PACKAGE_DIRECTORY}/_CPack_Packages/Linux/External/AppImage")
|
||||
set(VX_APPIMAGE_DESKTOP_FILE "${VX_APPIMAGE_DEST_DIR}${CMAKE_INSTALL_PREFIX}/share/applications/vnote.desktop")
|
||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/CPackLinuxDeployQt.cmake.in "${CMAKE_BINARY_DIR}/CPackExternal.cmake")
|
||||
set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_BINARY_DIR}/CPackExternal.cmake")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CPack)
|
75
src/application.cpp
Normal file
75
src/application.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
#include "application.h"
|
||||
|
||||
#include <QFileOpenEvent>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QStyle>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QTimer>
|
||||
#include <core/vnotex.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
Application::Application(int &p_argc, char **p_argv)
|
||||
: QApplication(p_argc, p_argv)
|
||||
{
|
||||
}
|
||||
|
||||
void Application::watchThemeFolder(const QString &p_themeFolderPath)
|
||||
{
|
||||
if (p_themeFolderPath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize watchers only when needed
|
||||
if (!m_styleWatcher) {
|
||||
m_styleWatcher = new QFileSystemWatcher(this);
|
||||
}
|
||||
if (!m_reloadTimer) {
|
||||
m_reloadTimer = new QTimer(this);
|
||||
m_reloadTimer->setSingleShot(true);
|
||||
m_reloadTimer->setInterval(500); // 500ms debounce delay
|
||||
connect(m_reloadTimer, &QTimer::timeout,
|
||||
this, &Application::reloadThemeResources);
|
||||
|
||||
// Connect file watcher to timer
|
||||
connect(m_styleWatcher, &QFileSystemWatcher::directoryChanged,
|
||||
m_reloadTimer, qOverload<>(&QTimer::start));
|
||||
connect(m_styleWatcher, &QFileSystemWatcher::fileChanged,
|
||||
m_reloadTimer, qOverload<>(&QTimer::start));
|
||||
}
|
||||
|
||||
// Watch the theme folder and its files
|
||||
m_styleWatcher->addPath(p_themeFolderPath);
|
||||
|
||||
// Also watch individual files in the theme folder
|
||||
QDir themeDir(p_themeFolderPath);
|
||||
QStringList files = themeDir.entryList(QDir::Files);
|
||||
for (const QString &file : files) {
|
||||
m_styleWatcher->addPath(themeDir.filePath(file));
|
||||
}
|
||||
}
|
||||
|
||||
void Application::reloadThemeResources()
|
||||
{
|
||||
VNoteX::getInst().getThemeMgr().refreshCurrentTheme();
|
||||
|
||||
auto stylesheet = VNoteX::getInst().getThemeMgr().fetchQtStyleSheet();
|
||||
if (!stylesheet.isEmpty()) {
|
||||
setStyleSheet(stylesheet);
|
||||
style()->unpolish(this);
|
||||
style()->polish(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::event(QEvent *p_event)
|
||||
{
|
||||
// On macOS, we need this to open file from Finder.
|
||||
if (p_event->type() == QEvent::FileOpen) {
|
||||
QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(p_event);
|
||||
qDebug() << "request to open file" << openEvent->file();
|
||||
emit openFileRequested(openEvent->file());
|
||||
}
|
||||
|
||||
return QApplication::event(p_event);
|
||||
}
|
34
src/application.h
Normal file
34
src/application.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef APPLICATION_H
|
||||
#define APPLICATION_H
|
||||
#include <QApplication>
|
||||
|
||||
class QFileSystemWatcher;
|
||||
class QTimer;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Application : public QApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Application(int &p_argc, char **p_argv);
|
||||
|
||||
// Set up theme folder watcher for hot-reload
|
||||
void watchThemeFolder(const QString &p_themeFolderPath);
|
||||
|
||||
// Reload the theme resources (stylesheet, icons, etc)
|
||||
void reloadThemeResources();
|
||||
|
||||
signals:
|
||||
void openFileRequested(const QString &p_filePath);
|
||||
|
||||
protected:
|
||||
bool event(QEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
QFileSystemWatcher *m_styleWatcher = nullptr;
|
||||
QTimer *m_reloadTimer = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // APPLICATION_H
|
@ -22,18 +22,28 @@ CommandLineOptions::ParseResult CommandLineOptions::parse(const QStringList &p_a
|
||||
const QCommandLineOption verboseOpt("verbose", MainWindow::tr("Print more logs."));
|
||||
parser.addOption(verboseOpt);
|
||||
|
||||
const QCommandLineOption logStderrOpt("log-stderr", MainWindow::tr("Log to stderr."));
|
||||
parser.addOption(logStderrOpt);
|
||||
|
||||
const QCommandLineOption watchThemesOpt("watch-themes", MainWindow::tr("Watch theme folder for changes."));
|
||||
parser.addOption(watchThemesOpt);
|
||||
|
||||
// WebEngine options.
|
||||
// No need to handle them. Just add them to the parser to avoid parse error.
|
||||
{
|
||||
QCommandLineOption webRemoteDebuggingPortOpt("remote-debugging-port",
|
||||
MainWindow::tr("WebEngine remote debugging port."),
|
||||
"port_number");
|
||||
MainWindow::tr("port_number"));
|
||||
webRemoteDebuggingPortOpt.setFlags(QCommandLineOption::HiddenFromHelp);
|
||||
parser.addOption(webRemoteDebuggingPortOpt);
|
||||
|
||||
QCommandLineOption webNoSandboxOpt("no-sandbox", MainWindow::tr("WebEngine without sandbox."));
|
||||
webNoSandboxOpt.setFlags(QCommandLineOption::HiddenFromHelp);
|
||||
parser.addOption(webNoSandboxOpt);
|
||||
|
||||
QCommandLineOption webDisableGpu("disable-gpu", MainWindow::tr("WebEngine with GPU disabled."));
|
||||
webDisableGpu.setFlags(QCommandLineOption::HiddenFromHelp);
|
||||
parser.addOption(webDisableGpu);
|
||||
}
|
||||
|
||||
if (!parser.parse(p_arguments)) {
|
||||
@ -59,5 +69,13 @@ CommandLineOptions::ParseResult CommandLineOptions::parse(const QStringList &p_a
|
||||
m_verbose = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(logStderrOpt)) {
|
||||
m_logToStderr = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(watchThemesOpt)) {
|
||||
m_watchThemes = true;
|
||||
}
|
||||
|
||||
return ParseResult::Ok;
|
||||
}
|
||||
|
@ -25,6 +25,11 @@ public:
|
||||
QStringList m_pathsToOpen;
|
||||
|
||||
bool m_verbose = false;
|
||||
|
||||
bool m_logToStderr = false;
|
||||
|
||||
// Whether to watch theme folder for changes
|
||||
bool m_watchThemes = false;
|
||||
};
|
||||
|
||||
#endif // COMMANDLINEOPTIONS_H
|
||||
|
87
src/core/CMakeLists.txt
Normal file
87
src/core/CMakeLists.txt
Normal file
@ -0,0 +1,87 @@
|
||||
target_sources(vnote PRIVATE
|
||||
buffer/buffer.cpp buffer/buffer.h
|
||||
buffer/bufferprovider.cpp buffer/bufferprovider.h
|
||||
buffer/filebufferprovider.cpp buffer/filebufferprovider.h
|
||||
buffer/filetypehelper.cpp buffer/filetypehelper.h
|
||||
buffer/ibufferfactory.h
|
||||
buffer/markdownbuffer.cpp buffer/markdownbuffer.h
|
||||
buffer/markdownbufferfactory.cpp buffer/markdownbufferfactory.h
|
||||
buffer/mindmapbuffer.cpp buffer/mindmapbuffer.h
|
||||
buffer/mindmapbufferfactory.cpp buffer/mindmapbufferfactory.h
|
||||
buffer/nodebufferprovider.cpp buffer/nodebufferprovider.h
|
||||
buffer/pdfbuffer.cpp buffer/pdfbuffer.h
|
||||
buffer/pdfbufferfactory.cpp buffer/pdfbufferfactory.h
|
||||
buffer/textbuffer.cpp buffer/textbuffer.h
|
||||
buffer/textbufferfactory.cpp buffer/textbufferfactory.h
|
||||
buffer/urlbasedbufferprovider.h
|
||||
buffermgr.cpp buffermgr.h
|
||||
clipboarddata.cpp clipboarddata.h
|
||||
configmgr.cpp configmgr.h
|
||||
coreconfig.cpp coreconfig.h
|
||||
editorconfig.cpp editorconfig.h
|
||||
events.h
|
||||
exception.h
|
||||
externalfile.cpp externalfile.h
|
||||
file.cpp file.h
|
||||
filelocator.h
|
||||
fileopenparameters.h
|
||||
global.cpp global.h
|
||||
historyitem.cpp historyitem.h
|
||||
historymgr.cpp historymgr.h
|
||||
htmltemplatehelper.cpp htmltemplatehelper.h
|
||||
iconfig.h
|
||||
location.h
|
||||
logger.cpp logger.h
|
||||
mainconfig.cpp mainconfig.h
|
||||
markdowneditorconfig.cpp markdowneditorconfig.h
|
||||
mindmapeditorconfig.cpp mindmapeditorconfig.h
|
||||
namebasedserver.h
|
||||
noncopyable.h
|
||||
notebook/bundlenotebook.cpp notebook/bundlenotebook.h
|
||||
notebook/bundlenotebookfactory.cpp notebook/bundlenotebookfactory.h
|
||||
notebook/externalnode.cpp notebook/externalnode.h
|
||||
notebook/historyi.h
|
||||
notebook/inotebookfactory.h
|
||||
notebook/node.cpp notebook/node.h
|
||||
notebook/nodeparameters.cpp notebook/nodeparameters.h
|
||||
notebook/notebook.cpp notebook/notebook.h
|
||||
notebook/notebookdatabaseaccess.cpp notebook/notebookdatabaseaccess.h
|
||||
notebook/notebookparameters.cpp notebook/notebookparameters.h
|
||||
notebook/notebooktagmgr.cpp notebook/notebooktagmgr.h
|
||||
notebook/tag.cpp notebook/tag.h
|
||||
notebook/tagi.h
|
||||
notebook/vxnode.cpp notebook/vxnode.h
|
||||
notebook/vxnodefile.cpp notebook/vxnodefile.h
|
||||
notebookbackend/inotebookbackend.cpp notebookbackend/inotebookbackend.h
|
||||
notebookbackend/inotebookbackendfactory.h
|
||||
notebookbackend/localnotebookbackend.cpp notebookbackend/localnotebookbackend.h
|
||||
notebookbackend/localnotebookbackendfactory.cpp notebookbackend/localnotebookbackendfactory.h
|
||||
notebookconfigmgr/bundlenotebookconfigmgr.cpp notebookconfigmgr/bundlenotebookconfigmgr.h
|
||||
notebookconfigmgr/inotebookconfigmgr.cpp notebookconfigmgr/inotebookconfigmgr.h
|
||||
notebookconfigmgr/inotebookconfigmgrfactory.h
|
||||
notebookconfigmgr/notebookconfig.cpp notebookconfigmgr/notebookconfig.h
|
||||
notebookconfigmgr/vxnodeconfig.cpp notebookconfigmgr/vxnodeconfig.h
|
||||
notebookconfigmgr/vxnotebookconfigmgr.cpp notebookconfigmgr/vxnotebookconfigmgr.h
|
||||
notebookconfigmgr/vxnotebookconfigmgrfactory.cpp notebookconfigmgr/vxnotebookconfigmgrfactory.h
|
||||
notebookmgr.cpp notebookmgr.h
|
||||
pdfviewerconfig.cpp pdfviewerconfig.h
|
||||
quickaccesshelper.cpp quickaccesshelper.h
|
||||
sessionconfig.cpp sessionconfig.h
|
||||
singleinstanceguard.cpp singleinstanceguard.h
|
||||
templatemgr.cpp templatemgr.h
|
||||
texteditorconfig.cpp texteditorconfig.h
|
||||
theme.cpp theme.h
|
||||
thememgr.cpp thememgr.h
|
||||
versioncontroller/dummyversioncontroller.cpp versioncontroller/dummyversioncontroller.h
|
||||
versioncontroller/dummyversioncontrollerfactory.cpp versioncontroller/dummyversioncontrollerfactory.h
|
||||
versioncontroller/iversioncontroller.h
|
||||
versioncontroller/iversioncontrollerfactory.h
|
||||
versioncontroller/versioncontrollerserver.cpp versioncontroller/versioncontrollerserver.h
|
||||
vnotex.cpp vnotex.h
|
||||
webresource.h
|
||||
widgetconfig.cpp widgetconfig.h
|
||||
)
|
||||
|
||||
target_include_directories(vnote PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
@ -61,6 +61,7 @@ void Buffer::attachViewWindow(ViewWindow *p_win)
|
||||
|
||||
void Buffer::detachViewWindow(ViewWindow *p_win)
|
||||
{
|
||||
Q_UNUSED(p_win);
|
||||
Q_ASSERT(p_win != m_viewWindowToSync);
|
||||
|
||||
--m_attachedViewWindowCount;
|
||||
@ -320,6 +321,8 @@ void Buffer::autoSave()
|
||||
case EditorConfig::AutoSavePolicy::AutoSave:
|
||||
if (save(false) != OperationCode::Success) {
|
||||
qWarning() << "AutoSave failed to save buffer, retry later";
|
||||
} else {
|
||||
emit autoSaved();
|
||||
}
|
||||
break;
|
||||
|
||||
@ -355,7 +358,7 @@ void Buffer::writeBackupFile()
|
||||
|
||||
QString Buffer::generateBackupFileHead() const
|
||||
{
|
||||
return QString("vnotex_backup_file %1|").arg(getContentPath());
|
||||
return QStringLiteral("vnotex_backup_file %1|").arg(getContentPath());
|
||||
}
|
||||
|
||||
void Buffer::checkBackupFileOfPreviousSession()
|
||||
@ -370,7 +373,7 @@ void Buffer::checkBackupFileOfPreviousSession()
|
||||
QDir backupDir(backupDirPath);
|
||||
QStringList backupFiles;
|
||||
{
|
||||
const QString nameFilter = QString("%1*%2").arg(getName(), config.getBackupFileExtension());
|
||||
const QString nameFilter = QStringLiteral("%1*%2").arg(getName(), config.getBackupFileExtension());
|
||||
backupFiles = backupDir.entryList(QStringList(nameFilter),
|
||||
QDir::Files | QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot);
|
||||
}
|
||||
@ -526,6 +529,11 @@ bool Buffer::isAttachment(const QString &p_path) const
|
||||
return PathUtils::pathContains(getAttachmentFolderPath(), p_path);
|
||||
}
|
||||
|
||||
bool Buffer::isTagSupported() const
|
||||
{
|
||||
return m_provider->isTagSupported();
|
||||
}
|
||||
|
||||
Buffer::ProviderType Buffer::getProviderType() const
|
||||
{
|
||||
return m_provider->getType();
|
||||
|
@ -77,7 +77,7 @@ namespace vnotex
|
||||
|
||||
QString getPath() const;
|
||||
|
||||
// In some cases, getPath() may point to a ocntainer containting all the stuffs.
|
||||
// In some cases, getPath() may point to a container containting all the stuffs.
|
||||
// getContentPath() will return the real path to the file providing the content.
|
||||
QString getContentPath() const;
|
||||
|
||||
@ -165,6 +165,8 @@ namespace vnotex
|
||||
// Judge whether file @p_path is attachment.
|
||||
bool isAttachment(const QString &p_path) const;
|
||||
|
||||
bool isTagSupported() const;
|
||||
|
||||
ProviderType getProviderType() const;
|
||||
|
||||
bool checkFileExistsOnDisk();
|
||||
@ -186,6 +188,9 @@ namespace vnotex
|
||||
|
||||
void attachmentChanged();
|
||||
|
||||
// This buffer is AutoSavePolicy::AutoSave.
|
||||
void autoSaved();
|
||||
|
||||
protected:
|
||||
virtual ViewWindow *createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent) = 0;
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
SOURCES += \
|
||||
$$PWD/buffer.cpp \
|
||||
$$PWD/bufferprovider.cpp \
|
||||
$$PWD/filebufferprovider.cpp \
|
||||
$$PWD/markdownbuffer.cpp \
|
||||
$$PWD/markdownbufferfactory.cpp \
|
||||
$$PWD/filetypehelper.cpp \
|
||||
$$PWD/nodebufferprovider.cpp \
|
||||
$$PWD/textbuffer.cpp \
|
||||
$$PWD/textbufferfactory.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/bufferprovider.h \
|
||||
$$PWD/buffer.h \
|
||||
$$PWD/filebufferprovider.h \
|
||||
$$PWD/ibufferfactory.h \
|
||||
$$PWD/markdownbuffer.h \
|
||||
$$PWD/markdownbufferfactory.h \
|
||||
$$PWD/filetypehelper.h \
|
||||
$$PWD/nodebufferprovider.h \
|
||||
$$PWD/textbuffer.h \
|
||||
$$PWD/textbufferfactory.h
|
@ -16,6 +16,7 @@ QDateTime BufferProvider::getLastModifiedFromFile() const
|
||||
|
||||
bool BufferProvider::checkFileChangedOutside() const
|
||||
{
|
||||
// TODO: support non-local URLs.
|
||||
QFileInfo info(getContentPath());
|
||||
if (!info.exists() || m_lastModified != info.lastModified()) {
|
||||
return true;
|
||||
|
@ -68,6 +68,8 @@ namespace vnotex
|
||||
|
||||
virtual bool isAttachmentSupported() const = 0;
|
||||
|
||||
virtual bool isTagSupported() const = 0;
|
||||
|
||||
virtual bool checkFileExistsOnDisk() const;
|
||||
|
||||
virtual bool checkFileChangedOutside() const;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <utils/fileutils.h>
|
||||
#include <notebook/node.h>
|
||||
#include <core/file.h>
|
||||
#include <core/exception.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -160,7 +161,11 @@ void FileBufferProvider::removeImage(const QString &p_imagePath)
|
||||
{
|
||||
auto file = m_file->getImageInterface();
|
||||
if (file) {
|
||||
file->removeImage(p_imagePath);
|
||||
try {
|
||||
file->removeImage(p_imagePath);
|
||||
} catch (Exception &e) {
|
||||
qWarning() << "failed to remove image" << p_imagePath << e.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,6 +174,11 @@ bool FileBufferProvider::isAttachmentSupported() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileBufferProvider::isTagSupported() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Node *FileBufferProvider::getNode() const
|
||||
{
|
||||
return c_nodeAttachedTo;
|
||||
|
@ -63,6 +63,8 @@ namespace vnotex
|
||||
|
||||
bool isAttachmentSupported() const Q_DECL_OVERRIDE;
|
||||
|
||||
bool isTagSupported() const Q_DECL_OVERRIDE;
|
||||
|
||||
bool isReadOnly() const Q_DECL_OVERRIDE;
|
||||
|
||||
QSharedPointer<File> getFile() const Q_DECL_OVERRIDE;
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include "buffer.h"
|
||||
#include <core/configmgr.h>
|
||||
#include <core/coreconfig.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -18,26 +20,42 @@ bool FileType::isMarkdown() const
|
||||
return m_type == Type::Markdown;
|
||||
}
|
||||
|
||||
QString FileTypeHelper::s_systemDefaultProgram = QStringLiteral("System");
|
||||
|
||||
FileTypeHelper::FileTypeHelper()
|
||||
{
|
||||
setupBuiltInTypes();
|
||||
reload();
|
||||
}
|
||||
|
||||
// TODO: read configuration file.
|
||||
void FileTypeHelper::reload()
|
||||
{
|
||||
setupBuiltInTypes();
|
||||
|
||||
setupSuffixTypeMap();
|
||||
}
|
||||
|
||||
void FileTypeHelper::setupBuiltInTypes()
|
||||
{
|
||||
m_fileTypes.clear();
|
||||
|
||||
const auto &coreConfig = ConfigMgr::getInst().getCoreConfig();
|
||||
|
||||
{
|
||||
FileType type;
|
||||
type.m_type = FileType::Markdown;
|
||||
type.m_displayName = Buffer::tr("Markdown");
|
||||
type.m_typeName = QStringLiteral("Markdown");
|
||||
type.m_suffixes << QStringLiteral("md")
|
||||
<< QStringLiteral("mkd")
|
||||
<< QStringLiteral("rmd")
|
||||
<< QStringLiteral("markdown");
|
||||
type.m_displayName = Buffer::tr("Markdown");
|
||||
|
||||
auto suffixes = coreConfig.findFileTypeSuffix(type.m_typeName);
|
||||
if (suffixes && !suffixes->isEmpty()) {
|
||||
type.m_suffixes = *suffixes;
|
||||
} else {
|
||||
type.m_suffixes << QStringLiteral("md")
|
||||
<< QStringLiteral("mkd")
|
||||
<< QStringLiteral("rmd")
|
||||
<< QStringLiteral("markdown");
|
||||
}
|
||||
|
||||
m_fileTypes.push_back(type);
|
||||
}
|
||||
|
||||
@ -46,7 +64,47 @@ void FileTypeHelper::setupBuiltInTypes()
|
||||
type.m_type = FileType::Text;
|
||||
type.m_typeName = QStringLiteral("Text");
|
||||
type.m_displayName = Buffer::tr("Text");
|
||||
type.m_suffixes << QStringLiteral("txt") << QStringLiteral("text") << QStringLiteral("log");
|
||||
|
||||
auto suffixes = coreConfig.findFileTypeSuffix(type.m_typeName);
|
||||
if (suffixes && !suffixes->isEmpty()) {
|
||||
type.m_suffixes = *suffixes;
|
||||
} else {
|
||||
type.m_suffixes << QStringLiteral("txt") << QStringLiteral("text") << QStringLiteral("log");
|
||||
}
|
||||
|
||||
m_fileTypes.push_back(type);
|
||||
}
|
||||
|
||||
{
|
||||
FileType type;
|
||||
type.m_type = FileType::Pdf;
|
||||
type.m_typeName = QStringLiteral("PDF");
|
||||
type.m_displayName = Buffer::tr("Portable Document Format");
|
||||
type.m_isNewable = false;
|
||||
|
||||
auto suffixes = coreConfig.findFileTypeSuffix(type.m_typeName);
|
||||
if (suffixes && !suffixes->isEmpty()) {
|
||||
type.m_suffixes = *suffixes;
|
||||
} else {
|
||||
type.m_suffixes << QStringLiteral("pdf");
|
||||
}
|
||||
|
||||
m_fileTypes.push_back(type);
|
||||
}
|
||||
|
||||
{
|
||||
FileType type;
|
||||
type.m_type = FileType::MindMap;
|
||||
type.m_typeName = QStringLiteral("MindMap");
|
||||
type.m_displayName = Buffer::tr("Mind Map");
|
||||
|
||||
auto suffixes = coreConfig.findFileTypeSuffix(type.m_typeName);
|
||||
if (suffixes && !suffixes->isEmpty()) {
|
||||
type.m_suffixes = *suffixes;
|
||||
} else {
|
||||
type.m_suffixes << QStringLiteral("emind");
|
||||
}
|
||||
|
||||
m_fileTypes.push_back(type);
|
||||
}
|
||||
|
||||
@ -88,10 +146,10 @@ const FileType &FileTypeHelper::getFileTypeBySuffix(const QString &p_suffix) con
|
||||
}
|
||||
}
|
||||
|
||||
#define ADD(x, y) m_suffixTypeMap.insert((x), (y))
|
||||
|
||||
void FileTypeHelper::setupSuffixTypeMap()
|
||||
{
|
||||
m_suffixTypeMap.clear();
|
||||
|
||||
for (int i = 0; i < m_fileTypes.size(); ++i) {
|
||||
for (const auto &suffix : m_fileTypes[i].m_suffixes) {
|
||||
if (m_suffixTypeMap.contains(suffix)) {
|
||||
@ -109,11 +167,13 @@ const QVector<FileType> &FileTypeHelper::getAllFileTypes() const
|
||||
|
||||
const FileType &FileTypeHelper::getFileType(int p_type) const
|
||||
{
|
||||
Q_ASSERT(p_type < m_fileTypes.size());
|
||||
if (p_type >= m_fileTypes.size()) {
|
||||
p_type = FileType::Others;
|
||||
}
|
||||
return m_fileTypes[p_type];
|
||||
}
|
||||
|
||||
const FileTypeHelper &FileTypeHelper::getInst()
|
||||
FileTypeHelper &FileTypeHelper::getInst()
|
||||
{
|
||||
static FileTypeHelper helper;
|
||||
return helper;
|
||||
|
@ -15,9 +15,15 @@ namespace vnotex
|
||||
{
|
||||
Markdown = 0,
|
||||
Text,
|
||||
Pdf,
|
||||
MindMap,
|
||||
Others
|
||||
};
|
||||
|
||||
QString preferredSuffix() const;
|
||||
|
||||
bool isMarkdown() const;
|
||||
|
||||
// Type.
|
||||
int m_type = -1;
|
||||
|
||||
@ -27,11 +33,11 @@ namespace vnotex
|
||||
|
||||
QStringList m_suffixes;
|
||||
|
||||
QString preferredSuffix() const;
|
||||
|
||||
bool isMarkdown() const;
|
||||
// Whether we can new this type of file.
|
||||
bool m_isNewable = true;
|
||||
};
|
||||
|
||||
// Only handle built-in editors.
|
||||
class FileTypeHelper
|
||||
{
|
||||
public:
|
||||
@ -47,7 +53,11 @@ namespace vnotex
|
||||
|
||||
bool checkFileType(const QString &p_filePath, int p_type) const;
|
||||
|
||||
static const FileTypeHelper &getInst();
|
||||
void reload();
|
||||
|
||||
static FileTypeHelper &getInst();
|
||||
|
||||
static QString s_systemDefaultProgram;
|
||||
|
||||
private:
|
||||
FileTypeHelper();
|
||||
|
@ -18,6 +18,8 @@ namespace vnotex
|
||||
|
||||
virtual Buffer *createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent) = 0;
|
||||
|
||||
virtual bool isBufferCreatedByFactory(const Buffer *p_buffer) const = 0;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -35,9 +35,11 @@ QString MarkdownBuffer::insertImage(const QImage &p_image, const QString &p_imag
|
||||
void MarkdownBuffer::fetchInitialImages()
|
||||
{
|
||||
Q_ASSERT(m_initialImages.isEmpty());
|
||||
// There is compilation error on Linux and macOS using TypeFlags directly.
|
||||
int linkFlags = vte::MarkdownLink::TypeFlag::LocalRelativeInternal | vte::MarkdownLink::TypeFlag::Remote;
|
||||
m_initialImages = vte::MarkdownUtils::fetchImagesFromMarkdownText(getContent(),
|
||||
getResourcePath(),
|
||||
vte::MarkdownLink::TypeFlag::LocalRelativeInternal);
|
||||
static_cast<vte::MarkdownLink::TypeFlags>(linkFlags));
|
||||
}
|
||||
|
||||
void MarkdownBuffer::addInsertedImage(const QString &p_imagePath, const QString &p_urlInLink)
|
||||
@ -45,41 +47,51 @@ void MarkdownBuffer::addInsertedImage(const QString &p_imagePath, const QString
|
||||
vte::MarkdownLink link;
|
||||
link.m_path = p_imagePath;
|
||||
link.m_urlInLink = p_urlInLink;
|
||||
link.m_type = vte::MarkdownLink::TypeFlag::LocalRelativeInternal;
|
||||
// There are two types: local internal and remote for image host.
|
||||
link.m_type = PathUtils::isLocalFile(p_imagePath) ? vte::MarkdownLink::TypeFlag::LocalRelativeInternal : vte::MarkdownLink::TypeFlag::Remote;
|
||||
m_insertedImages.append(link);
|
||||
}
|
||||
|
||||
QSet<QString> MarkdownBuffer::clearObsoleteImages()
|
||||
QHash<QString, bool> MarkdownBuffer::clearObsoleteImages()
|
||||
{
|
||||
QSet<QString> obsoleteImages;
|
||||
QHash<QString, bool> obsoleteImages;
|
||||
|
||||
Q_ASSERT(!isModified());
|
||||
const bool discarded = state() & StateFlag::Discarded;
|
||||
const int linkFlags = vte::MarkdownLink::TypeFlag::LocalRelativeInternal | vte::MarkdownLink::TypeFlag::Remote;
|
||||
const auto latestImages =
|
||||
vte::MarkdownUtils::fetchImagesFromMarkdownText(!discarded ? getContent() : m_provider->read(),
|
||||
getResourcePath(),
|
||||
vte::MarkdownLink::TypeFlag::LocalRelativeInternal);
|
||||
static_cast<vte::MarkdownLink::TypeFlags>(linkFlags));
|
||||
QSet<QString> latestImagesPath;
|
||||
for (const auto &link : latestImages) {
|
||||
latestImagesPath.insert(PathUtils::normalizePath(link.m_path));
|
||||
if (link.m_type & vte::MarkdownLink::TypeFlag::Remote) {
|
||||
latestImagesPath.insert(link.m_path);
|
||||
} else {
|
||||
latestImagesPath.insert(PathUtils::normalizePath(link.m_path));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &link : m_insertedImages) {
|
||||
if (!(link.m_type & vte::MarkdownLink::TypeFlag::LocalRelativeInternal)) {
|
||||
if (!(link.m_type & linkFlags)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!latestImagesPath.contains(PathUtils::normalizePath(link.m_path))) {
|
||||
obsoleteImages.insert(link.m_path);
|
||||
const bool isRemote = link.m_type & vte::MarkdownLink::TypeFlag::Remote;
|
||||
const auto linkPath = isRemote ? link.m_path : PathUtils::normalizePath(link.m_path);
|
||||
if (!latestImagesPath.contains(linkPath)) {
|
||||
obsoleteImages.insert(link.m_path, isRemote);
|
||||
}
|
||||
}
|
||||
|
||||
m_insertedImages.clear();
|
||||
|
||||
for (const auto &link : m_initialImages) {
|
||||
Q_ASSERT(link.m_type & vte::MarkdownLink::TypeFlag::LocalRelativeInternal);
|
||||
if (!latestImagesPath.contains(PathUtils::normalizePath(link.m_path))) {
|
||||
obsoleteImages.insert(link.m_path);
|
||||
Q_ASSERT(link.m_type & linkFlags);
|
||||
const bool isRemote = link.m_type & vte::MarkdownLink::TypeFlag::Remote;
|
||||
const auto linkPath = isRemote ? link.m_path : PathUtils::normalizePath(link.m_path);
|
||||
if (!latestImagesPath.contains(linkPath)) {
|
||||
obsoleteImages.insert(link.m_path, isRemote);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "buffer.h"
|
||||
|
||||
#include <QVector>
|
||||
#include <QSet>
|
||||
#include <QHash>
|
||||
|
||||
#include <vtextedit/markdownutils.h>
|
||||
|
||||
@ -28,7 +28,8 @@ namespace vnotex
|
||||
// Clear obsolete images.
|
||||
// Won't delete images, just return a list of obsolete images path.
|
||||
// Will re-init m_initialImages and clear m_insertedImages.
|
||||
QSet<QString> clearObsoleteImages();
|
||||
// Return [ImagePath] -> IsRemote.
|
||||
QHash<QString, bool> clearObsoleteImages();
|
||||
|
||||
protected:
|
||||
ViewWindow *createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent) Q_DECL_OVERRIDE;
|
||||
|
@ -9,3 +9,8 @@ Buffer *MarkdownBufferFactory::createBuffer(const BufferParameters &p_parameters
|
||||
{
|
||||
return new MarkdownBuffer(p_parameters, p_parent);
|
||||
}
|
||||
|
||||
bool MarkdownBufferFactory::isBufferCreatedByFactory(const Buffer *p_buffer) const
|
||||
{
|
||||
return dynamic_cast<const MarkdownBuffer *>(p_buffer) != nullptr;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ namespace vnotex
|
||||
public:
|
||||
Buffer *createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent) Q_DECL_OVERRIDE;
|
||||
|
||||
bool isBufferCreatedByFactory(const Buffer *p_buffer) const Q_DECL_OVERRIDE;
|
||||
};
|
||||
} // vnotex
|
||||
|
||||
|
17
src/core/buffer/mindmapbuffer.cpp
Normal file
17
src/core/buffer/mindmapbuffer.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "mindmapbuffer.h"
|
||||
|
||||
#include <widgets/mindmapviewwindow.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
MindMapBuffer::MindMapBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent)
|
||||
: Buffer(p_parameters, p_parent)
|
||||
{
|
||||
}
|
||||
|
||||
ViewWindow *MindMapBuffer::createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent)
|
||||
{
|
||||
Q_UNUSED(p_paras);
|
||||
return new MindMapViewWindow(p_parent);
|
||||
}
|
21
src/core/buffer/mindmapbuffer.h
Normal file
21
src/core/buffer/mindmapbuffer.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef MINDMAPBUFFER_H
|
||||
#define MINDMAPBUFFER_H
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class MindMapBuffer : public Buffer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MindMapBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
protected:
|
||||
ViewWindow *createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras,
|
||||
QWidget *p_parent) Q_DECL_OVERRIDE;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MINDMAPBUFFER_H
|
16
src/core/buffer/mindmapbufferfactory.cpp
Normal file
16
src/core/buffer/mindmapbufferfactory.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "mindmapbufferfactory.h"
|
||||
|
||||
#include "mindmapbuffer.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
Buffer *MindMapBufferFactory::createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent)
|
||||
{
|
||||
return new MindMapBuffer(p_parameters, p_parent);
|
||||
}
|
||||
|
||||
bool MindMapBufferFactory::isBufferCreatedByFactory(const Buffer *p_buffer) const
|
||||
{
|
||||
return dynamic_cast<const MindMapBuffer *>(p_buffer) != nullptr;
|
||||
}
|
19
src/core/buffer/mindmapbufferfactory.h
Normal file
19
src/core/buffer/mindmapbufferfactory.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef MINDMAPBUFFERFACTORY_H
|
||||
#define MINDMAPBUFFERFACTORY_H
|
||||
|
||||
#include "ibufferfactory.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// Buffer factory for MindMap file.
|
||||
class MindMapBufferFactory : public IBufferFactory
|
||||
{
|
||||
public:
|
||||
Buffer *createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent) Q_DECL_OVERRIDE;
|
||||
|
||||
bool isBufferCreatedByFactory(const Buffer *p_buffer) const Q_DECL_OVERRIDE;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MINDMAPBUFFERFACTORY_H
|
@ -3,8 +3,10 @@
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <notebook/node.h>
|
||||
#include <notebook/notebook.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <core/file.h>
|
||||
#include <core/exception.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -139,7 +141,11 @@ void NodeBufferProvider::removeImage(const QString &p_imagePath)
|
||||
{
|
||||
auto file = m_nodeFile->getImageInterface();
|
||||
if (file) {
|
||||
file->removeImage(p_imagePath);
|
||||
try {
|
||||
file->removeImage(p_imagePath);
|
||||
} catch (Exception &e) {
|
||||
qWarning() << "failed to remove image" << p_imagePath << e.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,6 +154,11 @@ bool NodeBufferProvider::isAttachmentSupported() const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NodeBufferProvider::isTagSupported() const
|
||||
{
|
||||
return m_node->getNotebook()->tag() != nullptr;
|
||||
}
|
||||
|
||||
Node *NodeBufferProvider::getNode() const
|
||||
{
|
||||
return m_node.data();
|
||||
|
@ -65,6 +65,8 @@ namespace vnotex
|
||||
|
||||
bool isAttachmentSupported() const Q_DECL_OVERRIDE;
|
||||
|
||||
bool isTagSupported() const Q_DECL_OVERRIDE;
|
||||
|
||||
bool isReadOnly() const Q_DECL_OVERRIDE;
|
||||
|
||||
QSharedPointer<File> getFile() const Q_DECL_OVERRIDE;
|
||||
|
17
src/core/buffer/pdfbuffer.cpp
Normal file
17
src/core/buffer/pdfbuffer.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "pdfbuffer.h"
|
||||
|
||||
#include <widgets/pdfviewwindow.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
PdfBuffer::PdfBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent)
|
||||
: Buffer(p_parameters, p_parent)
|
||||
{
|
||||
}
|
||||
|
||||
ViewWindow *PdfBuffer::createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent)
|
||||
{
|
||||
Q_UNUSED(p_paras);
|
||||
return new PdfViewWindow(p_parent);
|
||||
}
|
21
src/core/buffer/pdfbuffer.h
Normal file
21
src/core/buffer/pdfbuffer.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef PDFBUFFER_H
|
||||
#define PDFBUFFER_H
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class PdfBuffer : public Buffer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PdfBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
protected:
|
||||
ViewWindow *createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras,
|
||||
QWidget *p_parent) Q_DECL_OVERRIDE;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // PDFBUFFER_H
|
19
src/core/buffer/pdfbufferfactory.cpp
Normal file
19
src/core/buffer/pdfbufferfactory.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "pdfbufferfactory.h"
|
||||
|
||||
#include "pdfbuffer.h"
|
||||
#include "urlbasedbufferprovider.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
Buffer *PdfBufferFactory::createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent)
|
||||
{
|
||||
BufferParameters paras;
|
||||
paras.m_provider = QSharedPointer<UrlBasedBufferProvider>::create(p_parameters.m_provider);
|
||||
return new PdfBuffer(paras, p_parent);
|
||||
}
|
||||
|
||||
bool PdfBufferFactory::isBufferCreatedByFactory(const Buffer *p_buffer) const
|
||||
{
|
||||
return dynamic_cast<const PdfBuffer *>(p_buffer) != nullptr;
|
||||
}
|
19
src/core/buffer/pdfbufferfactory.h
Normal file
19
src/core/buffer/pdfbufferfactory.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef PDFBUFFERFACTORY_H
|
||||
#define PDFBUFFERFACTORY_H
|
||||
|
||||
#include "ibufferfactory.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// Buffer factory for Pdf file.
|
||||
class PdfBufferFactory : public IBufferFactory
|
||||
{
|
||||
public:
|
||||
Buffer *createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent) Q_DECL_OVERRIDE;
|
||||
|
||||
bool isBufferCreatedByFactory(const Buffer *p_buffer) const Q_DECL_OVERRIDE;
|
||||
};
|
||||
} // vnotex
|
||||
|
||||
#endif // PDFBUFFERFACTORY_H
|
@ -9,3 +9,8 @@ Buffer *TextBufferFactory::createBuffer(const BufferParameters &p_parameters,
|
||||
{
|
||||
return new TextBuffer(p_parameters, p_parent);
|
||||
}
|
||||
|
||||
bool TextBufferFactory::isBufferCreatedByFactory(const Buffer *p_buffer) const
|
||||
{
|
||||
return dynamic_cast<const TextBuffer *>(p_buffer) != nullptr;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ namespace vnotex
|
||||
public:
|
||||
Buffer *createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent) Q_DECL_OVERRIDE;
|
||||
|
||||
bool isBufferCreatedByFactory(const Buffer *p_buffer) const Q_DECL_OVERRIDE;
|
||||
};
|
||||
}
|
||||
|
||||
|
132
src/core/buffer/urlbasedbufferprovider.h
Normal file
132
src/core/buffer/urlbasedbufferprovider.h
Normal file
@ -0,0 +1,132 @@
|
||||
#ifndef URLBASEDBUFFERPROVIDER_H
|
||||
#define URLBASEDBUFFERPROVIDER_H
|
||||
|
||||
#include "bufferprovider.h"
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// A wrapper provider to provide URL-based buffer (instead of content-based).
|
||||
class UrlBasedBufferProvider : public BufferProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Will own @p_provider.
|
||||
UrlBasedBufferProvider(const QSharedPointer<BufferProvider> &p_provider, QObject *p_parent = nullptr)
|
||||
: BufferProvider(p_parent),
|
||||
m_provider(p_provider)
|
||||
{
|
||||
}
|
||||
|
||||
Buffer::ProviderType getType() const Q_DECL_OVERRIDE {
|
||||
return m_provider->getType();
|
||||
}
|
||||
|
||||
bool match(const Node *p_node) const Q_DECL_OVERRIDE {
|
||||
return m_provider->match(p_node);
|
||||
}
|
||||
|
||||
bool match(const QString &p_filePath) const Q_DECL_OVERRIDE {
|
||||
return m_provider->match(p_filePath);
|
||||
}
|
||||
|
||||
QString getName() const Q_DECL_OVERRIDE {
|
||||
return m_provider->getName();
|
||||
}
|
||||
|
||||
QString getPath() const Q_DECL_OVERRIDE {
|
||||
return m_provider->getPath();
|
||||
}
|
||||
|
||||
QString getContentPath() const Q_DECL_OVERRIDE {
|
||||
return m_provider->getContentPath();
|
||||
}
|
||||
|
||||
QString getResourcePath() const Q_DECL_OVERRIDE {
|
||||
return m_provider->getResourcePath();
|
||||
}
|
||||
|
||||
void write(const QString &p_content) Q_DECL_OVERRIDE {
|
||||
Q_UNUSED(p_content);
|
||||
}
|
||||
|
||||
QString read() const Q_DECL_OVERRIDE {
|
||||
const_cast<UrlBasedBufferProvider *>(this)->m_lastModified = getLastModifiedFromFile();
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString fetchImageFolderPath() Q_DECL_OVERRIDE {
|
||||
return m_provider->fetchImageFolderPath();
|
||||
}
|
||||
|
||||
bool isChildOf(const Node *p_node) const Q_DECL_OVERRIDE {
|
||||
return m_provider->isChildOf(p_node);
|
||||
}
|
||||
|
||||
Node *getNode() const Q_DECL_OVERRIDE {
|
||||
return m_provider->getNode();
|
||||
}
|
||||
|
||||
QString getAttachmentFolder() const Q_DECL_OVERRIDE {
|
||||
return m_provider->getAttachmentFolder();
|
||||
}
|
||||
|
||||
QString fetchAttachmentFolderPath() Q_DECL_OVERRIDE {
|
||||
return m_provider->fetchAttachmentFolderPath();
|
||||
}
|
||||
|
||||
QStringList addAttachment(const QString &p_destFolderPath, const QStringList &p_files) Q_DECL_OVERRIDE {
|
||||
return m_provider->addAttachment(p_destFolderPath, p_files);
|
||||
}
|
||||
|
||||
QString newAttachmentFile(const QString &p_destFolderPath, const QString &p_name) Q_DECL_OVERRIDE {
|
||||
return m_provider->newAttachmentFile(p_destFolderPath, p_name);
|
||||
}
|
||||
|
||||
QString newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name) Q_DECL_OVERRIDE {
|
||||
return m_provider->newAttachmentFolder(p_destFolderPath, p_name);
|
||||
}
|
||||
|
||||
QString renameAttachment(const QString &p_path, const QString &p_name) Q_DECL_OVERRIDE {
|
||||
return m_provider->renameAttachment(p_path, p_name);
|
||||
}
|
||||
|
||||
void removeAttachment(const QStringList &p_paths) Q_DECL_OVERRIDE {
|
||||
m_provider->removeAttachment(p_paths);
|
||||
}
|
||||
|
||||
QString insertImage(const QString &p_srcImagePath, const QString &p_imageFileName) Q_DECL_OVERRIDE {
|
||||
return m_provider->insertImage(p_srcImagePath, p_imageFileName);
|
||||
}
|
||||
|
||||
QString insertImage(const QImage &p_image, const QString &p_imageFileName) Q_DECL_OVERRIDE {
|
||||
return m_provider->insertImage(p_image, p_imageFileName);
|
||||
}
|
||||
|
||||
void removeImage(const QString &p_imagePath) Q_DECL_OVERRIDE {
|
||||
m_provider->removeImage(p_imagePath);
|
||||
}
|
||||
|
||||
bool isAttachmentSupported() const Q_DECL_OVERRIDE {
|
||||
return m_provider->isAttachmentSupported();
|
||||
}
|
||||
|
||||
bool isTagSupported() const Q_DECL_OVERRIDE {
|
||||
return m_provider->isTagSupported();
|
||||
}
|
||||
|
||||
bool isReadOnly() const Q_DECL_OVERRIDE {
|
||||
return true;
|
||||
}
|
||||
|
||||
QSharedPointer<File> getFile() const Q_DECL_OVERRIDE {
|
||||
return m_provider->getFile();
|
||||
}
|
||||
|
||||
private:
|
||||
QSharedPointer<BufferProvider> m_provider;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // URLBASEDBUFFERPROVIDER_H
|
@ -7,18 +7,25 @@
|
||||
#include <buffer/filetypehelper.h>
|
||||
#include <buffer/markdownbufferfactory.h>
|
||||
#include <buffer/textbufferfactory.h>
|
||||
#include <buffer/pdfbufferfactory.h>
|
||||
#include <buffer/mindmapbufferfactory.h>
|
||||
#include <buffer/buffer.h>
|
||||
#include <buffer/nodebufferprovider.h>
|
||||
#include <buffer/filebufferprovider.h>
|
||||
#include <utils/widgetutils.h>
|
||||
#include <utils/processutils.h>
|
||||
#include "notebookmgr.h"
|
||||
#include "vnotex.h"
|
||||
#include "externalfile.h"
|
||||
#include "sessionconfig.h"
|
||||
#include "configmgr.h"
|
||||
|
||||
#include "fileopenparameters.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
QMap<QString, QString> BufferMgr::s_suffixToFileType;
|
||||
|
||||
BufferMgr::BufferMgr(QObject *p_parent)
|
||||
: QObject(p_parent)
|
||||
{
|
||||
@ -47,6 +54,14 @@ void BufferMgr::initBufferServer()
|
||||
// Text.
|
||||
auto textFactory = QSharedPointer<TextBufferFactory>::create();
|
||||
m_bufferServer->registerItem(helper.getFileType(FileType::Text).m_typeName, textFactory);
|
||||
|
||||
// Pdf.
|
||||
auto pdfFactory = QSharedPointer<PdfBufferFactory>::create();
|
||||
m_bufferServer->registerItem(helper.getFileType(FileType::Pdf).m_typeName, pdfFactory);
|
||||
|
||||
// MindMap.
|
||||
auto mindMapFactory = QSharedPointer<MindMapBufferFactory>::create();
|
||||
m_bufferServer->registerItem(helper.getFileType(FileType::MindMap).m_typeName, mindMapFactory);
|
||||
}
|
||||
|
||||
void BufferMgr::open(Node *p_node, const QSharedPointer<FileOpenParameters> &p_paras)
|
||||
@ -60,18 +75,33 @@ void BufferMgr::open(Node *p_node, const QSharedPointer<FileOpenParameters> &p_p
|
||||
}
|
||||
|
||||
if (!p_node->checkExists()) {
|
||||
auto msg = QString("Failed to open node that does not exist (%1)").arg(p_node->fetchAbsolutePath());
|
||||
auto msg = QStringLiteral("Failed to open node that does not exist (%1)").arg(p_node->fetchAbsolutePath());
|
||||
qWarning() << msg;
|
||||
VNoteX::getInst().showStatusMessageShort(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto nodePath = p_node->fetchAbsolutePath();
|
||||
|
||||
auto fileType = p_paras->m_fileType;
|
||||
if (fileType.isEmpty()) {
|
||||
// Check if we need to open it with external program by default according to the suffix.
|
||||
fileType = findFileTypeByFile(nodePath);
|
||||
if (openWithExternalProgram(nodePath, fileType)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto buffer = findBuffer(p_node);
|
||||
if (!buffer) {
|
||||
auto nodePath = p_node->fetchAbsolutePath();
|
||||
if (!buffer || !isSameTypeBuffer(buffer, fileType)) {
|
||||
auto nodeFile = p_node->getContentFile();
|
||||
Q_ASSERT(nodeFile);
|
||||
auto fileType = nodeFile->getContentType().m_typeName;
|
||||
if (fileType.isEmpty()) {
|
||||
fileType = nodeFile->getContentType().m_typeName;
|
||||
} else if (fileType != nodeFile->getContentType().m_typeName) {
|
||||
nodeFile->setContentType(FileTypeHelper::getInst().getFileTypeByName(fileType).m_type);
|
||||
}
|
||||
|
||||
auto factory = m_bufferServer->getItem(fileType);
|
||||
if (!factory) {
|
||||
// No factory to open this file type.
|
||||
@ -96,11 +126,17 @@ void BufferMgr::open(const QString &p_filePath, const QSharedPointer<FileOpenPar
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it is requested to open with external program.
|
||||
if (openWithExternalProgram(p_filePath, p_paras->m_fileType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFileInfo finfo(p_filePath);
|
||||
if (!finfo.exists()) {
|
||||
auto msg = QString("Failed to open file that does not exist (%1)").arg(p_filePath);
|
||||
auto msg = QStringLiteral("Failed to open file that does not exist (%1)").arg(p_filePath);
|
||||
qWarning() << msg;
|
||||
VNoteX::getInst().showStatusMessageShort(msg);
|
||||
WidgetUtils::openUrlByDesktop(QUrl::fromUserInput(p_filePath));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -122,11 +158,25 @@ void BufferMgr::open(const QString &p_filePath, const QSharedPointer<FileOpenPar
|
||||
return;
|
||||
}
|
||||
|
||||
auto fileType = p_paras->m_fileType;
|
||||
if (fileType.isEmpty()) {
|
||||
// Check if we need to open it with external program by default according to the suffix.
|
||||
fileType = findFileTypeByFile(p_filePath);
|
||||
if (openWithExternalProgram(p_filePath, fileType)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto buffer = findBuffer(p_filePath);
|
||||
if (!buffer) {
|
||||
if (!buffer || !isSameTypeBuffer(buffer, fileType)) {
|
||||
// Open it as external file.
|
||||
auto externalFile = QSharedPointer<ExternalFile>::create(p_filePath);
|
||||
auto fileType = externalFile->getContentType().m_typeName;
|
||||
if (fileType.isEmpty()) {
|
||||
fileType = externalFile->getContentType().m_typeName;
|
||||
} else if (fileType != externalFile->getContentType().m_typeName) {
|
||||
externalFile->setContentType(FileTypeHelper::getInst().getFileTypeByName(fileType).m_type);
|
||||
}
|
||||
|
||||
auto factory = m_bufferServer->getItem(fileType);
|
||||
if (!factory) {
|
||||
// No factory to open this file type.
|
||||
@ -187,3 +237,72 @@ void BufferMgr::addBuffer(Buffer *p_buffer)
|
||||
p_buffer->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
bool BufferMgr::openWithExternalProgram(const QString &p_filePath, const QString &p_name) const
|
||||
{
|
||||
if (p_name.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_name == FileTypeHelper::s_systemDefaultProgram) {
|
||||
// Open it by system default program.
|
||||
qInfo() << "file will be opened by default program" << p_filePath;
|
||||
WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(p_filePath));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto pro = ConfigMgr::getInst().getSessionConfig().findExternalProgram(p_name)) {
|
||||
const auto command = pro->fetchCommand(p_filePath);
|
||||
qDebug() << "external program" << command;
|
||||
if (!command.isEmpty()) {
|
||||
ProcessUtils::startDetached(command);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BufferMgr::isSameTypeBuffer(const Buffer *p_buffer, const QString &p_typeName) const
|
||||
{
|
||||
if (p_typeName.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto factory = m_bufferServer->getItem(p_typeName);
|
||||
Q_ASSERT(factory);
|
||||
if (factory) {
|
||||
return factory->isBufferCreatedByFactory(p_buffer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BufferMgr::updateSuffixToFileType(const QVector<CoreConfig::FileTypeSuffix> &p_fileTypeSuffixes)
|
||||
{
|
||||
s_suffixToFileType.clear();
|
||||
|
||||
for (const auto &fts : p_fileTypeSuffixes) {
|
||||
for (const auto &suf : fts.m_suffixes) {
|
||||
auto it = s_suffixToFileType.find(suf);
|
||||
if (it != s_suffixToFileType.end()) {
|
||||
qWarning() << "suffix conflicts for file types" << fts.m_name << it.value();
|
||||
it.value() = fts.m_name;
|
||||
} else {
|
||||
s_suffixToFileType.insert(suf, fts.m_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString BufferMgr::findFileTypeByFile(const QString &p_filePath)
|
||||
{
|
||||
QFileInfo fi(p_filePath);
|
||||
auto suffix = fi.suffix().toLower();
|
||||
auto it = s_suffixToFileType.find(suffix);
|
||||
if (it != s_suffixToFileType.end()) {
|
||||
return it.value();
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,10 @@
|
||||
#include <QScopedPointer>
|
||||
#include <QSharedPointer>
|
||||
#include <QVector>
|
||||
#include <QMap>
|
||||
|
||||
#include "namebasedserver.h"
|
||||
#include "coreconfig.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
@ -30,6 +32,8 @@ namespace vnotex
|
||||
|
||||
void open(const QString &p_filePath, const QSharedPointer<FileOpenParameters> &p_paras);
|
||||
|
||||
static void updateSuffixToFileType(const QVector<CoreConfig::FileTypeSuffix> &p_fileTypeSuffixes);
|
||||
|
||||
signals:
|
||||
void bufferRequested(Buffer *p_buffer, const QSharedPointer<FileOpenParameters> &p_paras);
|
||||
|
||||
@ -42,10 +46,19 @@ namespace vnotex
|
||||
|
||||
void addBuffer(Buffer *p_buffer);
|
||||
|
||||
bool openWithExternalProgram(const QString &p_filePath, const QString &p_name) const;
|
||||
|
||||
bool isSameTypeBuffer(const Buffer *p_buffer, const QString &p_typeName) const;
|
||||
|
||||
static QString findFileTypeByFile(const QString &p_filePath);
|
||||
|
||||
QSharedPointer<NameBasedServer<IBufferFactory>> m_bufferServer;
|
||||
|
||||
// Managed by QObject.
|
||||
QVector<Buffer *> m_buffers;
|
||||
|
||||
// Mapping from suffix to file type or external program name.
|
||||
static QMap<QString, QString> s_suffixToFileType;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -63,14 +63,14 @@ void ClipboardData::fromJson(const QJsonObject &p_jobj)
|
||||
|| !p_jobj.contains(c_action)
|
||||
|| !p_jobj.contains(c_data)) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
|
||||
QStringLiteral("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
|
||||
return;
|
||||
}
|
||||
|
||||
auto idRet = stringToID(p_jobj[c_instanceId].toString());
|
||||
if (!idRet.first) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
|
||||
QStringLiteral("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
|
||||
return;
|
||||
}
|
||||
m_instanceId = idRet.second;
|
||||
@ -79,7 +79,7 @@ void ClipboardData::fromJson(const QJsonObject &p_jobj)
|
||||
m_action = intToAction(act);
|
||||
if (m_action == Action::Invalid) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
|
||||
QStringLiteral("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <QPixmap>
|
||||
#include <QSplashScreen>
|
||||
#include <QScopedPointer>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
#include <utils/pathutils.h>
|
||||
#include <utils/fileutils.h>
|
||||
@ -24,7 +25,7 @@
|
||||
using namespace vnotex;
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
// #define VX_DEBUG_WEB
|
||||
// #define VX_DEBUG_WEB
|
||||
#endif
|
||||
|
||||
const QString ConfigMgr::c_orgName = "VNote";
|
||||
@ -35,6 +36,10 @@ const QString ConfigMgr::c_configFileName = "vnotex.json";
|
||||
|
||||
const QString ConfigMgr::c_sessionFileName = "session.json";
|
||||
|
||||
const QString ConfigMgr::c_userFilesFolder = "user_files";
|
||||
|
||||
const QString ConfigMgr::c_appFilesFolder = "vnotex_files";
|
||||
|
||||
const QJsonObject &ConfigMgr::Settings::getJson() const
|
||||
{
|
||||
return m_jobj;
|
||||
@ -56,14 +61,33 @@ void ConfigMgr::Settings::writeToFile(const QString &p_jsonFilePath) const
|
||||
FileUtils::writeFile(p_jsonFilePath, QJsonDocument(this->m_jobj).toJson());
|
||||
}
|
||||
|
||||
ConfigMgr::ConfigMgr(QObject *p_parent)
|
||||
ConfigMgr::ConfigMgr(bool p_isUnitTest, QObject *p_parent)
|
||||
: QObject(p_parent),
|
||||
m_config(new MainConfig(this)),
|
||||
m_sessionConfig(new SessionConfig(this))
|
||||
{
|
||||
if (p_isUnitTest) {
|
||||
m_dirForUnitTest.reset(new QTemporaryDir());
|
||||
if (!m_dirForUnitTest->isValid()) {
|
||||
qWarning() << "failed to init ConfigMgr for UnitTest";
|
||||
return;
|
||||
}
|
||||
|
||||
QDir dir(m_dirForUnitTest->path());
|
||||
dir.mkdir(c_appFilesFolder);
|
||||
dir.mkdir(c_userFilesFolder);
|
||||
|
||||
m_appConfigFolderPath = m_dirForUnitTest->filePath(c_appFilesFolder);
|
||||
m_userConfigFolderPath = m_dirForUnitTest->filePath(c_userFilesFolder);
|
||||
return;
|
||||
}
|
||||
|
||||
locateConfigFolder();
|
||||
|
||||
checkAppConfig();
|
||||
bool needUpdate = checkAppConfig();
|
||||
if (needUpdate) {
|
||||
checkUserConfig();
|
||||
}
|
||||
|
||||
m_config->init();
|
||||
m_sessionConfig->init();
|
||||
@ -73,14 +97,24 @@ ConfigMgr::~ConfigMgr()
|
||||
{
|
||||
}
|
||||
|
||||
ConfigMgr &ConfigMgr::getInst(bool p_isUnitTest)
|
||||
{
|
||||
static ConfigMgr inst(p_isUnitTest);
|
||||
return inst;
|
||||
}
|
||||
|
||||
void ConfigMgr::initForUnitTest()
|
||||
{
|
||||
getInst(true);
|
||||
}
|
||||
|
||||
void ConfigMgr::locateConfigFolder()
|
||||
{
|
||||
const auto appDirPath = getApplicationDirPath();
|
||||
qInfo() << "app folder" << appDirPath;
|
||||
// Check app config.
|
||||
{
|
||||
const QString configFolderName("vnotex_files");
|
||||
QString folderPath(appDirPath + '/' + configFolderName);
|
||||
QString folderPath(appDirPath + '/' + c_appFilesFolder);
|
||||
if (QDir(folderPath).exists()) {
|
||||
// Config folder in app/.
|
||||
m_appConfigFolderPath = PathUtils::cleanPath(folderPath);
|
||||
@ -91,8 +125,7 @@ void ConfigMgr::locateConfigFolder()
|
||||
|
||||
// Check user config.
|
||||
{
|
||||
const QString configFolderName("user_files");
|
||||
QString folderPath(appDirPath + '/' + configFolderName);
|
||||
QString folderPath(appDirPath + '/' + c_userFilesFolder);
|
||||
if (QDir(folderPath).exists()) {
|
||||
// Config folder in app/.
|
||||
m_userConfigFolderPath = PathUtils::cleanPath(folderPath);
|
||||
@ -110,7 +143,7 @@ void ConfigMgr::locateConfigFolder()
|
||||
qInfo() << "user config folder" << m_userConfigFolderPath;
|
||||
}
|
||||
|
||||
void ConfigMgr::checkAppConfig()
|
||||
bool ConfigMgr::checkAppConfig()
|
||||
{
|
||||
bool needUpdate = false;
|
||||
QDir appConfigDir(m_appConfigFolderPath);
|
||||
@ -144,7 +177,7 @@ void ConfigMgr::checkAppConfig()
|
||||
|
||||
#ifndef VX_DEBUG_WEB
|
||||
if (!needUpdate) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -158,12 +191,11 @@ void ConfigMgr::checkAppConfig()
|
||||
|
||||
// Load extra data.
|
||||
splash->showMessage("Loading extra resource data");
|
||||
const QString extraRcc(PathUtils::concatenateFilePath(QCoreApplication::applicationDirPath(),
|
||||
QStringLiteral("vnote_extra.rcc")));
|
||||
const QString extraRcc("app:vnote_extra.rcc");
|
||||
bool ret = QResource::registerResource(extraRcc);
|
||||
if (!ret) {
|
||||
Exception::throwOne(Exception::Type::FailToReadFile,
|
||||
QString("failed to register resource file %1").arg(extraRcc));
|
||||
QStringLiteral("failed to register resource file %1").arg(extraRcc));
|
||||
}
|
||||
auto cleanup = qScopeGuard([extraRcc]() {
|
||||
QResource::unregisterResource(extraRcc);
|
||||
@ -188,7 +220,7 @@ void ConfigMgr::checkAppConfig()
|
||||
FileUtils::copyFile(getConfigFilePath(Source::Default), mainConfigFilePath);
|
||||
FileUtils::copyDir(extraDataRoot + QStringLiteral("/web"),
|
||||
appConfigDir.filePath(QStringLiteral("web")));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
Q_ASSERT(needUpdate);
|
||||
@ -200,6 +232,12 @@ void ConfigMgr::checkAppConfig()
|
||||
FileUtils::copyDir(extraDataRoot + QStringLiteral("/themes"),
|
||||
appConfigDir.filePath(QStringLiteral("themes")));
|
||||
|
||||
// Copy tasks.
|
||||
qApp->processEvents();
|
||||
splash->showMessage("Copying tasks");
|
||||
FileUtils::copyDir(extraDataRoot + QStringLiteral("/tasks"),
|
||||
appConfigDir.filePath(QStringLiteral("tasks")));
|
||||
|
||||
// Copy docs.
|
||||
qApp->processEvents();
|
||||
splash->showMessage("Copying docs");
|
||||
@ -227,6 +265,27 @@ void ConfigMgr::checkAppConfig()
|
||||
// Main config file.
|
||||
FileUtils::copyFile(getConfigFilePath(Source::Default), appConfigDir.filePath(c_configFileName));
|
||||
|
||||
return needUpdate;
|
||||
}
|
||||
|
||||
void ConfigMgr::checkUserConfig()
|
||||
{
|
||||
// Mainly check if the user config and session config is read-only.
|
||||
const auto userFile = getConfigFilePath(Source::User);
|
||||
if (QFileInfo::exists(userFile)) {
|
||||
if (!(QFile::permissions(userFile) & QFile::WriteUser)) {
|
||||
qDebug() << "make user config file writable" << userFile;
|
||||
QFile::setPermissions(userFile, QFile::WriteUser);
|
||||
}
|
||||
}
|
||||
|
||||
const auto sessionFile = getConfigFilePath(Source::Session);
|
||||
if (QFileInfo::exists(sessionFile)) {
|
||||
if (!(QFile::permissions(sessionFile) & QFile::WriteUser)) {
|
||||
qDebug() << "make session config file writable" << sessionFile;
|
||||
QFile::setPermissions(sessionFile, QFile::WriteUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString ConfigMgr::getConfigFilePath(Source p_src) const
|
||||
@ -329,6 +388,18 @@ QString ConfigMgr::getUserThemeFolder() const
|
||||
return folderPath;
|
||||
}
|
||||
|
||||
QString ConfigMgr::getAppTaskFolder() const
|
||||
{
|
||||
return PathUtils::concatenateFilePath(m_appConfigFolderPath, QStringLiteral("tasks"));
|
||||
}
|
||||
|
||||
QString ConfigMgr::getUserTaskFolder() const
|
||||
{
|
||||
auto folderPath = PathUtils::concatenateFilePath(m_userConfigFolderPath, QStringLiteral("tasks"));
|
||||
QDir().mkpath(folderPath);
|
||||
return folderPath;
|
||||
}
|
||||
|
||||
QString ConfigMgr::getAppWebStylesFolder() const
|
||||
{
|
||||
return PathUtils::concatenateFilePath(m_appConfigFolderPath, QStringLiteral("web-styles"));
|
||||
@ -395,6 +466,17 @@ QString ConfigMgr::getUserSnippetFolder() const
|
||||
return folderPath;
|
||||
}
|
||||
|
||||
QString ConfigMgr::getUserMarkdownUserStyleFile() const
|
||||
{
|
||||
auto folderPath = PathUtils::concatenateFilePath(m_userConfigFolderPath, QStringLiteral("web/css"));
|
||||
auto filePath = PathUtils::concatenateFilePath(folderPath, QStringLiteral("user.css"));
|
||||
if (!QFileInfo::exists(filePath)) {
|
||||
QDir().mkpath(folderPath);
|
||||
FileUtils::writeFile(filePath, QByteArray());
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
QString ConfigMgr::getUserOrAppFile(const QString &p_filePath) const
|
||||
{
|
||||
QFileInfo fi(p_filePath);
|
||||
@ -487,3 +569,46 @@ QString ConfigMgr::getApplicationVersion()
|
||||
|
||||
return appVersion;
|
||||
}
|
||||
|
||||
void ConfigMgr::initAppPrefixPath()
|
||||
{
|
||||
// Support QFile("app:abc.txt").
|
||||
QStringList potential_dirs;
|
||||
auto app_dir_path = QCoreApplication::applicationDirPath();
|
||||
qInfo() << "app prefix path: " << app_dir_path;
|
||||
potential_dirs << app_dir_path;
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
QDir localBinDir(app_dir_path);
|
||||
if (localBinDir.exists("../local/bin/vnote")) {
|
||||
auto app_dir_path2 = localBinDir.cleanPath(localBinDir.filePath("../local/share"));
|
||||
qInfo() << "app prefix path: " << app_dir_path2;
|
||||
potential_dirs << app_dir_path2;
|
||||
}
|
||||
if (localBinDir.exists("../share")) {
|
||||
auto app_dir_path3 = localBinDir.cleanPath(localBinDir.filePath("../share"));
|
||||
qInfo() << "app prefix path: " << app_dir_path3;
|
||||
potential_dirs << app_dir_path3;
|
||||
}
|
||||
#elif defined(Q_OS_MACOS)
|
||||
QDir localBinDir(app_dir_path);
|
||||
if (localBinDir.exists("../Resources")) {
|
||||
auto app_dir_path2 = localBinDir.cleanPath(localBinDir.filePath("../Resources"));
|
||||
qInfo() << "app prefix path: " << app_dir_path2;
|
||||
potential_dirs << app_dir_path2;
|
||||
}
|
||||
#endif
|
||||
|
||||
QDir::setSearchPaths("app", potential_dirs);
|
||||
}
|
||||
|
||||
QJsonValue ConfigMgr::parseAndReadConfig(const QString &p_exp) const
|
||||
{
|
||||
if (p_exp.startsWith(QStringLiteral("main."))) {
|
||||
return Utils::parseAndReadJson(m_config->toJson(), p_exp.mid(5));
|
||||
} else if (p_exp.startsWith(QStringLiteral("session."))) {
|
||||
return Utils::parseAndReadJson(m_sessionConfig->toJson(), p_exp.mid(8));
|
||||
} else {
|
||||
return QJsonValue();
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include "noncopyable.h"
|
||||
|
||||
class QTemporaryDir;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class MainConfig;
|
||||
@ -48,14 +50,10 @@ namespace vnotex
|
||||
QJsonObject m_jobj;
|
||||
};
|
||||
|
||||
static ConfigMgr &getInst()
|
||||
{
|
||||
static ConfigMgr inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
~ConfigMgr();
|
||||
|
||||
static ConfigMgr &getInst(bool p_isUnitTest = false);
|
||||
|
||||
MainConfig &getConfig();
|
||||
|
||||
SessionConfig &getSessionConfig();
|
||||
@ -76,6 +74,10 @@ namespace vnotex
|
||||
|
||||
QString getUserThemeFolder() const;
|
||||
|
||||
QString getAppTaskFolder() const;
|
||||
|
||||
QString getUserTaskFolder() const;
|
||||
|
||||
QString getAppWebStylesFolder() const;
|
||||
|
||||
QString getUserWebStylesFolder() const;
|
||||
@ -95,12 +97,18 @@ namespace vnotex
|
||||
|
||||
QString getUserSnippetFolder() const;
|
||||
|
||||
// web/css/user.css.
|
||||
QString getUserMarkdownUserStyleFile() const;
|
||||
|
||||
// If @p_filePath is absolute, just return it.
|
||||
// Otherwise, first try to find it in user folder, then in app folder.
|
||||
QString getUserOrAppFile(const QString &p_filePath) const;
|
||||
|
||||
QString getConfigFilePath(Source p_src) const;
|
||||
|
||||
// Parse exp like "[main|session].core.shortcuts.FullScreen" and return the config value.
|
||||
QJsonValue parseAndReadConfig(const QString &p_exp) const;
|
||||
|
||||
// Called at boostrap without QApplication instance.
|
||||
static QString locateSessionConfigFilePathAtBootstrap();
|
||||
|
||||
@ -112,6 +120,10 @@ namespace vnotex
|
||||
|
||||
static QString getApplicationVersion();
|
||||
|
||||
static void initAppPrefixPath();
|
||||
|
||||
static void initForUnitTest();
|
||||
|
||||
static const QString c_orgName;
|
||||
|
||||
static const QString c_appName;
|
||||
@ -128,14 +140,17 @@ namespace vnotex
|
||||
void editorConfigChanged();
|
||||
|
||||
private:
|
||||
explicit ConfigMgr(QObject *p_parent = nullptr);
|
||||
ConfigMgr(bool p_isUnitTest, QObject *p_parent = nullptr);
|
||||
|
||||
// Locate the folder path where the config file exists.
|
||||
void locateConfigFolder();
|
||||
|
||||
// Check if app config exists and is updated.
|
||||
// Update it if in need.
|
||||
void checkAppConfig();
|
||||
// Return true if there is update.
|
||||
bool checkAppConfig();
|
||||
|
||||
void checkUserConfig();
|
||||
|
||||
static QString getDefaultConfigFilePath();
|
||||
|
||||
@ -150,11 +165,18 @@ namespace vnotex
|
||||
// Absolute path of the user config folder.
|
||||
QString m_userConfigFolderPath;
|
||||
|
||||
// In UnitTest, we use a temp dir to hold the user files and app files.
|
||||
QScopedPointer<QTemporaryDir> m_dirForUnitTest;
|
||||
|
||||
// Name of the core config file.
|
||||
static const QString c_configFileName;
|
||||
|
||||
// Name of the session config file.
|
||||
static const QString c_sessionFileName;
|
||||
|
||||
static const QString c_userFilesFolder;
|
||||
|
||||
static const QString c_appFilesFolder;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -1,71 +0,0 @@
|
||||
INCLUDEPATH *= $$PWD
|
||||
|
||||
include($$PWD/notebookbackend/notebookbackend.pri)
|
||||
|
||||
include($$PWD/versioncontroller/versioncontroller.pri)
|
||||
|
||||
include($$PWD/notebookconfigmgr/notebookconfigmgr.pri)
|
||||
|
||||
include($$PWD/notebook/notebook.pri)
|
||||
|
||||
include($$PWD/buffer/buffer.pri)
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/buffermgr.cpp \
|
||||
$$PWD/configmgr.cpp \
|
||||
$$PWD/coreconfig.cpp \
|
||||
$$PWD/editorconfig.cpp \
|
||||
$$PWD/externalfile.cpp \
|
||||
$$PWD/file.cpp \
|
||||
$$PWD/historyitem.cpp \
|
||||
$$PWD/historymgr.cpp \
|
||||
$$PWD/htmltemplatehelper.cpp \
|
||||
$$PWD/logger.cpp \
|
||||
$$PWD/mainconfig.cpp \
|
||||
$$PWD/markdowneditorconfig.cpp \
|
||||
$$PWD/quickaccesshelper.cpp \
|
||||
$$PWD/singleinstanceguard.cpp \
|
||||
$$PWD/templatemgr.cpp \
|
||||
$$PWD/texteditorconfig.cpp \
|
||||
$$PWD/vnotex.cpp \
|
||||
$$PWD/thememgr.cpp \
|
||||
$$PWD/notebookmgr.cpp \
|
||||
$$PWD/theme.cpp \
|
||||
$$PWD/sessionconfig.cpp \
|
||||
$$PWD/clipboarddata.cpp \
|
||||
$$PWD/widgetconfig.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/buffermgr.h \
|
||||
$$PWD/configmgr.h \
|
||||
$$PWD/coreconfig.h \
|
||||
$$PWD/editorconfig.h \
|
||||
$$PWD/events.h \
|
||||
$$PWD/externalfile.h \
|
||||
$$PWD/file.h \
|
||||
$$PWD/filelocator.h \
|
||||
$$PWD/fileopenparameters.h \
|
||||
$$PWD/historyitem.h \
|
||||
$$PWD/historymgr.h \
|
||||
$$PWD/htmltemplatehelper.h \
|
||||
$$PWD/location.h \
|
||||
$$PWD/logger.h \
|
||||
$$PWD/mainconfig.h \
|
||||
$$PWD/markdowneditorconfig.h \
|
||||
$$PWD/noncopyable.h \
|
||||
$$PWD/quickaccesshelper.h \
|
||||
$$PWD/singleinstanceguard.h \
|
||||
$$PWD/iconfig.h \
|
||||
$$PWD/templatemgr.h \
|
||||
$$PWD/texteditorconfig.h \
|
||||
$$PWD/vnotex.h \
|
||||
$$PWD/thememgr.h \
|
||||
$$PWD/global.h \
|
||||
$$PWD/namebasedserver.h \
|
||||
$$PWD/exception.h \
|
||||
$$PWD/notebookmgr.h \
|
||||
$$PWD/theme.h \
|
||||
$$PWD/sessionconfig.h \
|
||||
$$PWD/clipboarddata.h \
|
||||
$$PWD/webresource.h \
|
||||
$$PWD/widgetconfig.h
|
@ -3,6 +3,9 @@
|
||||
#include <QMetaEnum>
|
||||
#include <QLocale>
|
||||
|
||||
#include <utils/utils.h>
|
||||
#include <buffer/filetypehelper.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
#define READSTR(key) readString(appObj, userObj, (key))
|
||||
@ -10,6 +13,17 @@ using namespace vnotex;
|
||||
#define READBOOL(key) readBool(appObj, userObj, (key))
|
||||
#define READSTRLIST(key) readStringList(appObj, userObj, (key))
|
||||
|
||||
CoreConfig::FileTypeSuffix::FileTypeSuffix(const QString &p_name, const QStringList &p_suffixes)
|
||||
: m_name(p_name),
|
||||
m_suffixes(p_suffixes)
|
||||
{
|
||||
}
|
||||
|
||||
bool CoreConfig::FileTypeSuffix::operator==(const FileTypeSuffix &p_other) const
|
||||
{
|
||||
return m_name == p_other.m_name && m_suffixes == p_other.m_suffixes;
|
||||
}
|
||||
|
||||
QStringList CoreConfig::s_availableLocales;
|
||||
|
||||
CoreConfig::CoreConfig(ConfigMgr *p_mgr, IConfig *p_topConfig)
|
||||
@ -43,19 +57,44 @@ void CoreConfig::init(const QJsonObject &p_app,
|
||||
|
||||
loadShortcuts(appObj, userObj);
|
||||
|
||||
m_shortcutLeaderKey = READSTR(QStringLiteral("shortcut_leader_key"));
|
||||
|
||||
m_toolBarIconSize = READINT(QStringLiteral("toolbar_icon_size"));
|
||||
if (m_toolBarIconSize <= 0) {
|
||||
m_toolBarIconSize = 16;
|
||||
m_toolBarIconSize = 18;
|
||||
}
|
||||
|
||||
m_docksTabBarIconSize = READINT(QStringLiteral("docks_tabbar_icon_size"));
|
||||
if (m_docksTabBarIconSize <= 0) {
|
||||
m_docksTabBarIconSize = 18;
|
||||
}
|
||||
|
||||
loadNoteManagement(appObj, userObj);
|
||||
|
||||
m_recoverLastSessionOnStartEnabled = READBOOL(QStringLiteral("recover_last_session_on_start"));
|
||||
|
||||
m_checkForUpdatesOnStartEnabled = READBOOL(QStringLiteral("check_for_updates_on_start"));
|
||||
|
||||
m_historyMaxCount = READINT(QStringLiteral("history_max_count"));
|
||||
if (m_historyMaxCount < 0) {
|
||||
m_historyMaxCount = 100;
|
||||
}
|
||||
|
||||
m_perNotebookHistoryEnabled = READBOOL(QStringLiteral("per_notebook_history"));
|
||||
|
||||
{
|
||||
auto lineEnding = READSTR(QStringLiteral("line_ending"));
|
||||
m_lineEnding = stringToLineEndingPolicy(lineEnding);
|
||||
}
|
||||
|
||||
{
|
||||
auto mode = READSTR(QStringLiteral("default_open_mode"));
|
||||
m_defaultOpenMode = stringToViewWindowMode(mode);
|
||||
}
|
||||
|
||||
loadFileTypeSuffixes(appObj, userObj);
|
||||
|
||||
loadUnitedEntry(appObj, userObj);
|
||||
}
|
||||
|
||||
QJsonObject CoreConfig::toJson() const
|
||||
@ -64,9 +103,17 @@ QJsonObject CoreConfig::toJson() const
|
||||
obj[QStringLiteral("theme")] = m_theme;
|
||||
obj[QStringLiteral("locale")] = m_locale;
|
||||
obj[QStringLiteral("shortcuts")] = saveShortcuts();
|
||||
obj[QStringLiteral("shortcut_leader_key")] = m_shortcutLeaderKey;
|
||||
obj[QStringLiteral("toolbar_icon_size")] = m_toolBarIconSize;
|
||||
obj[QStringLiteral("docks_tabbar_icon_size")] = m_docksTabBarIconSize;
|
||||
obj[QStringLiteral("recover_last_session_on_start")] = m_recoverLastSessionOnStartEnabled;
|
||||
obj[QStringLiteral("check_for_updates_on_start")] = m_checkForUpdatesOnStartEnabled;
|
||||
obj[QStringLiteral("history_max_count")] = m_historyMaxCount;
|
||||
obj[QStringLiteral("per_notebook_history")] = m_perNotebookHistoryEnabled;
|
||||
obj[QStringLiteral("line_ending")] = lineEndingPolicyToString(m_lineEnding);
|
||||
obj[QStringLiteral("file_type_suffixes")] = saveFileTypeSuffixes();
|
||||
obj[QStringLiteral("united_entry")] = saveUnitedEntry();
|
||||
obj[QStringLiteral("default_open_mode")] = viewWindowModeToString(m_defaultOpenMode);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -154,6 +201,17 @@ void CoreConfig::setToolBarIconSize(int p_size)
|
||||
updateConfig(m_toolBarIconSize, p_size, this);
|
||||
}
|
||||
|
||||
int CoreConfig::getDocksTabBarIconSize() const
|
||||
{
|
||||
return m_docksTabBarIconSize;
|
||||
}
|
||||
|
||||
void CoreConfig::setDocksTabBarIconSize(int p_size)
|
||||
{
|
||||
Q_ASSERT(p_size > 0);
|
||||
updateConfig(m_docksTabBarIconSize, p_size, this);
|
||||
}
|
||||
|
||||
const QStringList &CoreConfig::getExternalNodeExcludePatterns() const
|
||||
{
|
||||
return m_externalNodeExcludePatterns;
|
||||
@ -169,7 +227,173 @@ void CoreConfig::setRecoverLastSessionOnStartEnabled(bool p_enabled)
|
||||
updateConfig(m_recoverLastSessionOnStartEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
bool CoreConfig::isCheckForUpdatesOnStartEnabled() const
|
||||
{
|
||||
return m_checkForUpdatesOnStartEnabled;
|
||||
}
|
||||
|
||||
void CoreConfig::setCheckForUpdatesOnStartEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_checkForUpdatesOnStartEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
int CoreConfig::getHistoryMaxCount() const
|
||||
{
|
||||
return m_historyMaxCount;
|
||||
}
|
||||
|
||||
bool CoreConfig::isPerNotebookHistoryEnabled() const
|
||||
{
|
||||
return m_perNotebookHistoryEnabled;
|
||||
}
|
||||
|
||||
void CoreConfig::setPerNotebookHistoryEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_perNotebookHistoryEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
const QString &CoreConfig::getShortcutLeaderKey() const
|
||||
{
|
||||
return m_shortcutLeaderKey;
|
||||
}
|
||||
|
||||
LineEndingPolicy CoreConfig::getLineEndingPolicy() const
|
||||
{
|
||||
return m_lineEnding;
|
||||
}
|
||||
|
||||
void CoreConfig::setLineEndingPolicy(LineEndingPolicy p_ending)
|
||||
{
|
||||
updateConfig(m_lineEnding, p_ending, this);
|
||||
}
|
||||
|
||||
void CoreConfig::loadFileTypeSuffixes(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
{
|
||||
m_fileTypeSuffixes.clear();
|
||||
|
||||
QJsonArray arr;
|
||||
if (p_user.contains(QStringLiteral("file_type_suffixes"))) {
|
||||
arr = p_user[QStringLiteral("file_type_suffixes")].toArray();
|
||||
} else {
|
||||
arr = p_app[QStringLiteral("file_type_suffixes")].toArray();
|
||||
}
|
||||
|
||||
m_fileTypeSuffixes.reserve(arr.size());
|
||||
|
||||
bool hasSystemDefined = false;
|
||||
|
||||
for (int i = 0; i < arr.size(); ++i) {
|
||||
const auto obj = arr[i].toObject();
|
||||
const auto name = obj[QStringLiteral("name")].toString();
|
||||
if (name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hasSystemDefined && name == FileTypeHelper::s_systemDefaultProgram) {
|
||||
hasSystemDefined = true;
|
||||
}
|
||||
|
||||
const auto suffixes = readStringList(obj, QStringLiteral("suffixes"));
|
||||
m_fileTypeSuffixes.push_back(FileTypeSuffix(name, Utils::toLower(suffixes)));
|
||||
}
|
||||
|
||||
if (!hasSystemDefined) {
|
||||
m_fileTypeSuffixes.push_back(FileTypeSuffix(FileTypeHelper::s_systemDefaultProgram, QStringList()));
|
||||
}
|
||||
}
|
||||
|
||||
QJsonArray CoreConfig::saveFileTypeSuffixes() const
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const auto &fts : m_fileTypeSuffixes) {
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("name")] = fts.m_name;
|
||||
writeStringList(obj, QStringLiteral("suffixes"), fts.m_suffixes);
|
||||
arr.push_back(obj);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
void CoreConfig::loadUnitedEntry(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
{
|
||||
QJsonObject unitedObj;
|
||||
if (p_user.contains(QStringLiteral("united_entry"))) {
|
||||
unitedObj = p_user[QStringLiteral("united_entry")].toObject();
|
||||
} else {
|
||||
unitedObj = p_app[QStringLiteral("united_entry")].toObject();
|
||||
}
|
||||
|
||||
m_unitedEntryAlias = unitedObj[QStringLiteral("alias")].toArray();
|
||||
}
|
||||
|
||||
QJsonObject CoreConfig::saveUnitedEntry() const
|
||||
{
|
||||
QJsonObject unitedObj;
|
||||
unitedObj[QStringLiteral("alias")] = m_unitedEntryAlias;
|
||||
return unitedObj;
|
||||
}
|
||||
|
||||
const QVector<CoreConfig::FileTypeSuffix> &CoreConfig::getFileTypeSuffixes() const
|
||||
{
|
||||
return m_fileTypeSuffixes;
|
||||
}
|
||||
|
||||
void CoreConfig::setFileTypeSuffixes(const QVector<CoreConfig::FileTypeSuffix> &p_fileTypeSuffixes)
|
||||
{
|
||||
updateConfig(m_fileTypeSuffixes, p_fileTypeSuffixes, this);
|
||||
}
|
||||
|
||||
const QStringList *CoreConfig::findFileTypeSuffix(const QString &p_name) const
|
||||
{
|
||||
if (p_name.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const auto &fts : m_fileTypeSuffixes) {
|
||||
if (fts.m_name == p_name) {
|
||||
return &fts.m_suffixes;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const QJsonArray &CoreConfig::getUnitedEntryAlias() const
|
||||
{
|
||||
return m_unitedEntryAlias;
|
||||
}
|
||||
|
||||
void CoreConfig::setUnitedEntryAlias(const QJsonArray &p_alias)
|
||||
{
|
||||
updateConfig(m_unitedEntryAlias, p_alias, this);
|
||||
}
|
||||
|
||||
ViewWindowMode CoreConfig::getDefaultOpenMode() const
|
||||
{
|
||||
return m_defaultOpenMode;
|
||||
}
|
||||
|
||||
void CoreConfig::setDefaultOpenMode(ViewWindowMode p_mode)
|
||||
{
|
||||
updateConfig(m_defaultOpenMode, p_mode, this);
|
||||
}
|
||||
|
||||
ViewWindowMode CoreConfig::stringToViewWindowMode(const QString &p_mode)
|
||||
{
|
||||
if (p_mode == "edit") {
|
||||
return ViewWindowMode::Edit;
|
||||
}
|
||||
|
||||
return ViewWindowMode::Read;
|
||||
}
|
||||
|
||||
QString CoreConfig::viewWindowModeToString(ViewWindowMode p_mode)
|
||||
{
|
||||
switch (p_mode) {
|
||||
case ViewWindowMode::Edit:
|
||||
return "edit";
|
||||
|
||||
default:
|
||||
return "read";
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "global.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class CoreConfig : public IConfig
|
||||
@ -20,13 +22,21 @@ namespace vnotex
|
||||
ExpandContentArea,
|
||||
Settings,
|
||||
NewNote,
|
||||
NewQuickNote,
|
||||
NewFolder,
|
||||
CloseTab,
|
||||
CloseAllTabs,
|
||||
CloseOtherTabs,
|
||||
CloseTabsToTheLeft,
|
||||
CloseTabsToTheRight,
|
||||
NavigationDock,
|
||||
OutlineDock,
|
||||
SearchDock,
|
||||
SnippetDock,
|
||||
LocationListDock,
|
||||
HistoryDock,
|
||||
WindowsDock,
|
||||
TagDock,
|
||||
Search,
|
||||
NavigationMode,
|
||||
LocateNode,
|
||||
@ -63,10 +73,29 @@ namespace vnotex
|
||||
MoveOneSplitUp,
|
||||
MoveOneSplitRight,
|
||||
OpenLastClosedFile,
|
||||
UnitedEntry,
|
||||
Copy,
|
||||
Paste,
|
||||
Cut,
|
||||
Properties,
|
||||
Global_WakeUp,
|
||||
MaxShortcut
|
||||
};
|
||||
Q_ENUM(Shortcut)
|
||||
|
||||
struct FileTypeSuffix
|
||||
{
|
||||
FileTypeSuffix() = default;
|
||||
|
||||
FileTypeSuffix(const QString &p_name, const QStringList &p_suffixes);
|
||||
|
||||
bool operator==(const FileTypeSuffix &p_other) const;
|
||||
|
||||
QString m_name;
|
||||
|
||||
QStringList m_suffixes;
|
||||
};
|
||||
|
||||
CoreConfig(ConfigMgr *p_mgr, IConfig *p_topConfig);
|
||||
|
||||
void init(const QJsonObject &p_app, const QJsonObject &p_user) Q_DECL_OVERRIDE;
|
||||
@ -87,6 +116,9 @@ namespace vnotex
|
||||
int getToolBarIconSize() const;
|
||||
void setToolBarIconSize(int p_size);
|
||||
|
||||
int getDocksTabBarIconSize() const;
|
||||
void setDocksTabBarIconSize(int p_size);
|
||||
|
||||
const QStringList &getExternalNodeExcludePatterns() const;
|
||||
|
||||
static const QStringList &getAvailableLocales();
|
||||
@ -94,8 +126,30 @@ namespace vnotex
|
||||
bool isRecoverLastSessionOnStartEnabled() const;
|
||||
void setRecoverLastSessionOnStartEnabled(bool p_enabled);
|
||||
|
||||
bool isCheckForUpdatesOnStartEnabled() const;
|
||||
void setCheckForUpdatesOnStartEnabled(bool p_enabled);
|
||||
|
||||
int getHistoryMaxCount() const;
|
||||
|
||||
bool isPerNotebookHistoryEnabled() const;
|
||||
void setPerNotebookHistoryEnabled(bool p_enabled);
|
||||
|
||||
const QString &getShortcutLeaderKey() const;
|
||||
|
||||
LineEndingPolicy getLineEndingPolicy() const;
|
||||
void setLineEndingPolicy(LineEndingPolicy p_ending);
|
||||
|
||||
const QVector<FileTypeSuffix> &getFileTypeSuffixes() const;
|
||||
void setFileTypeSuffixes(const QVector<FileTypeSuffix> &p_fileTypeSuffixes);
|
||||
|
||||
const QStringList *findFileTypeSuffix(const QString &p_name) const;
|
||||
|
||||
const QJsonArray &getUnitedEntryAlias() const;
|
||||
void setUnitedEntryAlias(const QJsonArray &p_alias);
|
||||
|
||||
ViewWindowMode getDefaultOpenMode() const;
|
||||
void setDefaultOpenMode(ViewWindowMode p_mode);
|
||||
|
||||
private:
|
||||
friend class MainConfig;
|
||||
|
||||
@ -105,6 +159,17 @@ namespace vnotex
|
||||
|
||||
QJsonObject saveShortcuts() const;
|
||||
|
||||
void loadFileTypeSuffixes(const QJsonObject &p_app, const QJsonObject &p_user);
|
||||
|
||||
QJsonArray saveFileTypeSuffixes() const;
|
||||
|
||||
void loadUnitedEntry(const QJsonObject &p_app, const QJsonObject &p_user);
|
||||
|
||||
QJsonObject saveUnitedEntry() const;
|
||||
|
||||
static ViewWindowMode stringToViewWindowMode(const QString &p_mode);
|
||||
static QString viewWindowModeToString(ViewWindowMode p_mode);
|
||||
|
||||
// Theme name.
|
||||
QString m_theme;
|
||||
|
||||
@ -114,17 +179,36 @@ namespace vnotex
|
||||
|
||||
QString m_shortcuts[Shortcut::MaxShortcut];
|
||||
|
||||
// Leader key of shortcuts defined in m_shortctus.
|
||||
QString m_shortcutLeaderKey;
|
||||
|
||||
// Icon size of MainWindow tool bar.
|
||||
int m_toolBarIconSize = 16;
|
||||
int m_toolBarIconSize = 18;
|
||||
|
||||
// Icon size of MainWindow QDockWidgets tab bar.
|
||||
int m_docksTabBarIconSize = 20;
|
||||
|
||||
QStringList m_externalNodeExcludePatterns;
|
||||
|
||||
// Whether recover last session on start.
|
||||
bool m_recoverLastSessionOnStartEnabled = true;
|
||||
|
||||
bool m_checkForUpdatesOnStartEnabled = true;
|
||||
|
||||
// Max count of the history items for each notebook and session config.
|
||||
int m_historyMaxCount = 100;
|
||||
|
||||
// Whether store history in each notebook.
|
||||
bool m_perNotebookHistoryEnabled = false;
|
||||
|
||||
LineEndingPolicy m_lineEnding = LineEndingPolicy::LF;
|
||||
|
||||
QVector<FileTypeSuffix> m_fileTypeSuffixes;
|
||||
|
||||
QJsonArray m_unitedEntryAlias;
|
||||
|
||||
ViewWindowMode m_defaultOpenMode = ViewWindowMode::Read;
|
||||
|
||||
static QStringList s_availableLocales;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
@ -5,6 +5,10 @@
|
||||
|
||||
#include "texteditorconfig.h"
|
||||
#include "markdowneditorconfig.h"
|
||||
#include "pdfviewerconfig.h"
|
||||
#include "mindmapeditorconfig.h"
|
||||
|
||||
#include <vtextedit/viconfig.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -12,10 +16,36 @@ using namespace vnotex;
|
||||
#define READSTR(key) readString(appObj, userObj, (key))
|
||||
#define READBOOL(key) readBool(appObj, userObj, (key))
|
||||
|
||||
bool EditorConfig::ImageHostItem::operator==(const ImageHostItem &p_other) const
|
||||
{
|
||||
return m_type == p_other.m_type
|
||||
&& m_name == p_other.m_name
|
||||
&& m_config == p_other.m_config;
|
||||
}
|
||||
|
||||
void EditorConfig::ImageHostItem::fromJson(const QJsonObject &p_jobj)
|
||||
{
|
||||
m_type = p_jobj[QStringLiteral("type")].toInt();
|
||||
m_name = p_jobj[QStringLiteral("name")].toString();
|
||||
m_config = p_jobj[QStringLiteral("config")].toObject();
|
||||
}
|
||||
|
||||
QJsonObject EditorConfig::ImageHostItem::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("type")] = m_type;
|
||||
obj[QStringLiteral("name")] = m_name;
|
||||
obj[QStringLiteral("config")] = m_config;
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
EditorConfig::EditorConfig(ConfigMgr *p_mgr, IConfig *p_topConfig)
|
||||
: IConfig(p_mgr, p_topConfig),
|
||||
m_textEditorConfig(new TextEditorConfig(p_mgr, p_topConfig)),
|
||||
m_markdownEditorConfig(new MarkdownEditorConfig(p_mgr, p_topConfig, m_textEditorConfig))
|
||||
m_markdownEditorConfig(new MarkdownEditorConfig(p_mgr, p_topConfig, m_textEditorConfig)),
|
||||
m_pdfViewerConfig(new PdfViewerConfig(p_mgr, p_topConfig)),
|
||||
m_mindMapEditorConfig(new MindMapEditorConfig(p_mgr, p_topConfig))
|
||||
{
|
||||
m_sessionName = QStringLiteral("editor");
|
||||
}
|
||||
@ -32,8 +62,15 @@ void EditorConfig::init(const QJsonObject &p_app,
|
||||
|
||||
loadCore(appObj, userObj);
|
||||
|
||||
loadImageHost(appObj, userObj);
|
||||
|
||||
m_viConfig = QSharedPointer<vte::ViConfig>::create();
|
||||
m_viConfig->fromJson(read(appObj, userObj, QStringLiteral("vi")).toObject());
|
||||
|
||||
m_textEditorConfig->init(appObj, userObj);
|
||||
m_markdownEditorConfig->init(appObj, userObj);
|
||||
m_pdfViewerConfig->init(appObj, userObj);
|
||||
m_mindMapEditorConfig->init(appObj, userObj);
|
||||
}
|
||||
|
||||
void EditorConfig::loadCore(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
@ -64,6 +101,11 @@ void EditorConfig::loadCore(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
if (m_spellCheckDefaultDictionary.isEmpty()) {
|
||||
m_spellCheckDefaultDictionary = QStringLiteral("en_US");
|
||||
}
|
||||
|
||||
{
|
||||
auto lineEnding = READSTR(QStringLiteral("line_ending"));
|
||||
m_lineEnding = stringToLineEndingPolicy(lineEnding);
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject EditorConfig::saveCore() const
|
||||
@ -76,6 +118,7 @@ QJsonObject EditorConfig::saveCore() const
|
||||
obj[QStringLiteral("shortcuts")] = saveShortcuts();
|
||||
obj[QStringLiteral("spell_check_auto_detect_language")] = m_spellCheckAutoDetectLanguageEnabled;
|
||||
obj[QStringLiteral("spell_check_default_dictionary")] = m_spellCheckDefaultDictionary;
|
||||
obj[QStringLiteral("line_ending")] = lineEndingPolicyToString(m_lineEnding);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -111,7 +154,15 @@ QJsonObject EditorConfig::toJson() const
|
||||
QJsonObject obj;
|
||||
obj[m_textEditorConfig->getSessionName()] = m_textEditorConfig->toJson();
|
||||
obj[m_markdownEditorConfig->getSessionName()] = m_markdownEditorConfig->toJson();
|
||||
obj[m_pdfViewerConfig->getSessionName()] = m_pdfViewerConfig->toJson();
|
||||
obj[m_mindMapEditorConfig->getSessionName()] = m_mindMapEditorConfig->toJson();
|
||||
obj[QStringLiteral("core")] = saveCore();
|
||||
obj[QStringLiteral("image_host")] = saveImageHost();
|
||||
|
||||
// In UT, it may be nullptr.
|
||||
if (m_viConfig) {
|
||||
obj[QStringLiteral("vi")] = m_viConfig->toJson();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -135,6 +186,26 @@ const MarkdownEditorConfig &EditorConfig::getMarkdownEditorConfig() const
|
||||
return *m_markdownEditorConfig;
|
||||
}
|
||||
|
||||
PdfViewerConfig &EditorConfig::getPdfViewerConfig()
|
||||
{
|
||||
return *m_pdfViewerConfig;
|
||||
}
|
||||
|
||||
const PdfViewerConfig &EditorConfig::getPdfViewerConfig() const
|
||||
{
|
||||
return *m_pdfViewerConfig;
|
||||
}
|
||||
|
||||
MindMapEditorConfig &EditorConfig::getMindMapEditorConfig()
|
||||
{
|
||||
return *m_mindMapEditorConfig;
|
||||
}
|
||||
|
||||
const MindMapEditorConfig &EditorConfig::getMindMapEditorConfig() const
|
||||
{
|
||||
return *m_mindMapEditorConfig;
|
||||
}
|
||||
|
||||
int EditorConfig::getToolBarIconSize() const
|
||||
{
|
||||
return m_toolBarIconSize;
|
||||
@ -188,6 +259,16 @@ void EditorConfig::setAutoSavePolicy(EditorConfig::AutoSavePolicy p_policy)
|
||||
updateConfig(m_autoSavePolicy, p_policy, this);
|
||||
}
|
||||
|
||||
LineEndingPolicy EditorConfig::getLineEndingPolicy() const
|
||||
{
|
||||
return m_lineEnding;
|
||||
}
|
||||
|
||||
void EditorConfig::setLineEndingPolicy(LineEndingPolicy p_ending)
|
||||
{
|
||||
updateConfig(m_lineEnding, p_ending, this);
|
||||
}
|
||||
|
||||
const QString &EditorConfig::getBackupFileDirectory() const
|
||||
{
|
||||
return m_backupFileDirectory;
|
||||
@ -212,3 +293,73 @@ void EditorConfig::setSpellCheckDefaultDictionary(const QString &p_dict)
|
||||
{
|
||||
updateConfig(m_spellCheckDefaultDictionary, p_dict, this);
|
||||
}
|
||||
|
||||
void EditorConfig::loadImageHost(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
{
|
||||
const auto appObj = p_app.value(QStringLiteral("image_host")).toObject();
|
||||
const auto userObj = p_user.value(QStringLiteral("image_host")).toObject();
|
||||
|
||||
{
|
||||
auto arr = read(appObj, userObj, QStringLiteral("hosts")).toArray();
|
||||
m_imageHosts.resize(arr.size());
|
||||
for (int i = 0; i < arr.size(); ++i) {
|
||||
m_imageHosts[i].fromJson(arr[i].toObject());
|
||||
}
|
||||
}
|
||||
|
||||
m_defaultImageHost = READSTR(QStringLiteral("default_image_host"));
|
||||
m_clearObsoleteImageAtImageHost = READBOOL(QStringLiteral("clear_obsolete_image"));
|
||||
}
|
||||
|
||||
QJsonObject EditorConfig::saveImageHost() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const auto &item : m_imageHosts) {
|
||||
arr.append(item.toJson());
|
||||
}
|
||||
obj[QStringLiteral("hosts")] = arr;
|
||||
}
|
||||
|
||||
obj[QStringLiteral("default_image_host")] = m_defaultImageHost;
|
||||
obj[QStringLiteral("clear_obsolete_image")] = m_clearObsoleteImageAtImageHost;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
const QVector<EditorConfig::ImageHostItem> &EditorConfig::getImageHosts() const
|
||||
{
|
||||
return m_imageHosts;
|
||||
}
|
||||
|
||||
void EditorConfig::setImageHosts(const QVector<ImageHostItem> &p_hosts)
|
||||
{
|
||||
updateConfig(m_imageHosts, p_hosts, this);
|
||||
}
|
||||
|
||||
const QString &EditorConfig::getDefaultImageHost() const
|
||||
{
|
||||
return m_defaultImageHost;
|
||||
}
|
||||
|
||||
void EditorConfig::setDefaultImageHost(const QString &p_host)
|
||||
{
|
||||
updateConfig(m_defaultImageHost, p_host, this);
|
||||
}
|
||||
|
||||
bool EditorConfig::isClearObsoleteImageAtImageHostEnabled() const
|
||||
{
|
||||
return m_clearObsoleteImageAtImageHost;
|
||||
}
|
||||
|
||||
void EditorConfig::setClearObsoleteImageAtImageHostEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_clearObsoleteImageAtImageHost, p_enabled, this);
|
||||
}
|
||||
|
||||
const QSharedPointer<vte::ViConfig> &EditorConfig::getViConfig() const
|
||||
{
|
||||
return m_viConfig;
|
||||
}
|
||||
|
@ -6,11 +6,21 @@
|
||||
#include <QScopedPointer>
|
||||
#include <QSharedPointer>
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
#include "global.h"
|
||||
|
||||
namespace vte
|
||||
{
|
||||
class ViConfig;
|
||||
}
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class TextEditorConfig;
|
||||
class MarkdownEditorConfig;
|
||||
class PdfViewerConfig;
|
||||
class MindMapEditorConfig;
|
||||
|
||||
class EditorConfig : public IConfig
|
||||
{
|
||||
@ -45,11 +55,19 @@ namespace vnotex
|
||||
TypeTable,
|
||||
TypeMark,
|
||||
Outline,
|
||||
RichPaste,
|
||||
AltPaste,
|
||||
FindAndReplace,
|
||||
FindNext,
|
||||
FindPrevious,
|
||||
ApplySnippet,
|
||||
Tag,
|
||||
Debug,
|
||||
Print,
|
||||
ClearHighlights,
|
||||
WordCount,
|
||||
Attachment,
|
||||
AlternateViewMode,
|
||||
ParseToMarkdownAndPaste,
|
||||
MaxShortcut
|
||||
};
|
||||
Q_ENUM(Shortcut)
|
||||
@ -62,6 +80,23 @@ namespace vnotex
|
||||
};
|
||||
Q_ENUM(AutoSavePolicy)
|
||||
|
||||
struct ImageHostItem
|
||||
{
|
||||
ImageHostItem() = default;
|
||||
|
||||
bool operator==(const ImageHostItem &p_other) const;
|
||||
|
||||
void fromJson(const QJsonObject &p_jobj);
|
||||
|
||||
QJsonObject toJson() const;
|
||||
|
||||
int m_type = 0;
|
||||
|
||||
QString m_name;
|
||||
|
||||
QJsonObject m_config;
|
||||
};
|
||||
|
||||
EditorConfig(ConfigMgr *p_mgr, IConfig *p_topConfig);
|
||||
|
||||
~EditorConfig();
|
||||
@ -72,6 +107,12 @@ namespace vnotex
|
||||
MarkdownEditorConfig &getMarkdownEditorConfig();
|
||||
const MarkdownEditorConfig &getMarkdownEditorConfig() const;
|
||||
|
||||
PdfViewerConfig &getPdfViewerConfig();
|
||||
const PdfViewerConfig &getPdfViewerConfig() const;
|
||||
|
||||
MindMapEditorConfig &getMindMapEditorConfig();
|
||||
const MindMapEditorConfig &getMindMapEditorConfig() const;
|
||||
|
||||
void init(const QJsonObject &p_app, const QJsonObject &p_user) Q_DECL_OVERRIDE;
|
||||
|
||||
QJsonObject toJson() const Q_DECL_OVERRIDE;
|
||||
@ -93,6 +134,20 @@ namespace vnotex
|
||||
const QString &getSpellCheckDefaultDictionary() const;
|
||||
void setSpellCheckDefaultDictionary(const QString &p_dict);
|
||||
|
||||
const QVector<ImageHostItem> &getImageHosts() const;
|
||||
void setImageHosts(const QVector<ImageHostItem> &p_hosts);
|
||||
|
||||
const QString &getDefaultImageHost() const;
|
||||
void setDefaultImageHost(const QString &p_host);
|
||||
|
||||
bool isClearObsoleteImageAtImageHostEnabled() const;
|
||||
void setClearObsoleteImageAtImageHostEnabled(bool p_enabled);
|
||||
|
||||
const QSharedPointer<vte::ViConfig> &getViConfig() const;
|
||||
|
||||
LineEndingPolicy getLineEndingPolicy() const;
|
||||
void setLineEndingPolicy(LineEndingPolicy p_ending);
|
||||
|
||||
private:
|
||||
friend class MainConfig;
|
||||
|
||||
@ -107,6 +162,10 @@ namespace vnotex
|
||||
QString autoSavePolicyToString(AutoSavePolicy p_policy) const;
|
||||
AutoSavePolicy stringToAutoSavePolicy(const QString &p_str) const;
|
||||
|
||||
void loadImageHost(const QJsonObject &p_app, const QJsonObject &p_user);
|
||||
|
||||
QJsonObject saveImageHost() const;
|
||||
|
||||
// Icon size of editor tool bar.
|
||||
int m_toolBarIconSize = 16;
|
||||
|
||||
@ -125,9 +184,23 @@ namespace vnotex
|
||||
|
||||
QScopedPointer<MarkdownEditorConfig> m_markdownEditorConfig;
|
||||
|
||||
QScopedPointer<PdfViewerConfig> m_pdfViewerConfig;
|
||||
|
||||
QScopedPointer<MindMapEditorConfig> m_mindMapEditorConfig;
|
||||
|
||||
bool m_spellCheckAutoDetectLanguageEnabled = false;
|
||||
|
||||
QString m_spellCheckDefaultDictionary;
|
||||
|
||||
QVector<ImageHostItem> m_imageHosts;
|
||||
|
||||
QString m_defaultImageHost;
|
||||
|
||||
bool m_clearObsoleteImageAtImageHost = false;
|
||||
|
||||
QSharedPointer<vte::ViConfig> m_viConfig;
|
||||
|
||||
LineEndingPolicy m_lineEnding = LineEndingPolicy::LF;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,8 @@ namespace vnotex
|
||||
FailToRemoveDir,
|
||||
FileMissingOnDisk,
|
||||
EssentialFileMissing,
|
||||
FileExistsOnCreate,
|
||||
DirExistsOnCreate,
|
||||
InvalidArgument
|
||||
};
|
||||
|
||||
@ -45,40 +47,46 @@ namespace vnotex
|
||||
{
|
||||
switch (p_type) {
|
||||
case Type::InvalidPath:
|
||||
return QString("InvalidPath");
|
||||
return QStringLiteral("InvalidPath");
|
||||
|
||||
case Type::FailToCreateDir:
|
||||
return QString("FailToCreateDir");
|
||||
return QStringLiteral("FailToCreateDir");
|
||||
|
||||
case Type::FailToWriteFile:
|
||||
return QString("FailToWriteFile");
|
||||
return QStringLiteral("FailToWriteFile");
|
||||
|
||||
case Type::FailToReadFile:
|
||||
return QString("FailToReadFile");
|
||||
return QStringLiteral("FailToReadFile");
|
||||
|
||||
case Type::FailToRenameFile:
|
||||
return QString("FailToRenameFile");
|
||||
return QStringLiteral("FailToRenameFile");
|
||||
|
||||
case Type::FailToCopyFile:
|
||||
return QString("FailToCopyFile");
|
||||
return QStringLiteral("FailToCopyFile");
|
||||
|
||||
case Type::FailToCopyDir:
|
||||
return QString("FailToCopyDir");
|
||||
return QStringLiteral("FailToCopyDir");
|
||||
|
||||
case Type::FailToRemoveFile:
|
||||
return QString("FailToRemoveFile");
|
||||
return QStringLiteral("FailToRemoveFile");
|
||||
|
||||
case Type::FailToRemoveDir:
|
||||
return QString("FailToRemoveDir");
|
||||
return QStringLiteral("FailToRemoveDir");
|
||||
|
||||
case Type::FileMissingOnDisk:
|
||||
return QString("FileMissingOnDisk");
|
||||
return QStringLiteral("FileMissingOnDisk");
|
||||
|
||||
case Type::EssentialFileMissing:
|
||||
return QString("EssentialFileMissing");
|
||||
return QStringLiteral("EssentialFileMissing");
|
||||
|
||||
case Type::FileExistsOnCreate:
|
||||
return QStringLiteral("FileExistsOnCreate");
|
||||
|
||||
case Type::DirExistsOnCreate:
|
||||
return QStringLiteral("DirExistsOnCreate");
|
||||
|
||||
case Type::InvalidArgument:
|
||||
return QString("InvalidArgument");
|
||||
return QStringLiteral("InvalidArgument");
|
||||
}
|
||||
|
||||
return QString::number(static_cast<int>(p_type));
|
||||
|
@ -59,7 +59,6 @@ namespace vnotex
|
||||
|
||||
const FileType &getContentType() const;
|
||||
|
||||
protected:
|
||||
void setContentType(int p_type);
|
||||
|
||||
private:
|
||||
|
@ -1,14 +1,25 @@
|
||||
#ifndef FILEOPENPARAMETERS_H
|
||||
#define FILEOPENPARAMETERS_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "global.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Node;
|
||||
class SearchToken;
|
||||
|
||||
struct FileOpenParameters
|
||||
{
|
||||
enum Hook
|
||||
{
|
||||
PostSave,
|
||||
MaxHook
|
||||
};
|
||||
|
||||
ViewWindowMode m_mode = ViewWindowMode::Read;
|
||||
|
||||
// Force to enter m_mode.
|
||||
@ -32,6 +43,17 @@ namespace vnotex
|
||||
|
||||
// Whether always open a new window for file.
|
||||
bool m_alwaysNewWindow = false;
|
||||
|
||||
// If not empty, use this token to do a search text highlight.
|
||||
QSharedPointer<SearchToken> m_searchToken;
|
||||
|
||||
// Whether should save this file into session.
|
||||
bool m_sessionEnabled = true;
|
||||
|
||||
// Whether specify the built-in file type to open as or the external program to open with.
|
||||
QString m_fileType;
|
||||
|
||||
std::function<void()> m_hooks[Hook::MaxHook];
|
||||
};
|
||||
}
|
||||
|
||||
|
3
src/core/global.cpp
Normal file
3
src/core/global.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include "global.h"
|
||||
|
||||
using namespace vnotex;
|
@ -94,8 +94,6 @@ namespace vnotex
|
||||
{
|
||||
Read,
|
||||
Edit,
|
||||
FullPreview,
|
||||
FocusPreview,
|
||||
Invalid
|
||||
};
|
||||
|
||||
@ -133,8 +131,77 @@ namespace vnotex
|
||||
int m_length = -1;
|
||||
};
|
||||
|
||||
enum class LineEndingPolicy
|
||||
{
|
||||
Platform,
|
||||
File,
|
||||
LF,
|
||||
CRLF,
|
||||
CR
|
||||
};
|
||||
|
||||
inline QString lineEndingPolicyToString(LineEndingPolicy p_ending)
|
||||
{
|
||||
switch (p_ending) {
|
||||
case LineEndingPolicy::Platform:
|
||||
return QStringLiteral("platform");
|
||||
|
||||
case LineEndingPolicy::File:
|
||||
return QStringLiteral("file");
|
||||
|
||||
case LineEndingPolicy::LF:
|
||||
return QStringLiteral("lf");
|
||||
|
||||
case LineEndingPolicy::CRLF:
|
||||
return QStringLiteral("crlf");
|
||||
|
||||
case LineEndingPolicy::CR:
|
||||
return QStringLiteral("cr");
|
||||
}
|
||||
|
||||
return QStringLiteral("platform");
|
||||
}
|
||||
|
||||
inline LineEndingPolicy stringToLineEndingPolicy(const QString &p_str)
|
||||
{
|
||||
auto ending = p_str.toLower();
|
||||
if (ending == QStringLiteral("file")) {
|
||||
return LineEndingPolicy::File;
|
||||
} else if (ending == QStringLiteral("lf")) {
|
||||
return LineEndingPolicy::LF;
|
||||
} else if (ending == QStringLiteral("crlf")) {
|
||||
return LineEndingPolicy::CRLF;
|
||||
} else if (ending == QStringLiteral("cr")) {
|
||||
return LineEndingPolicy::CR;
|
||||
} else {
|
||||
return LineEndingPolicy::Platform;
|
||||
}
|
||||
}
|
||||
|
||||
enum Role
|
||||
{
|
||||
// Qt::UserRole = 0x0100
|
||||
UserRole2 = 0x0101,
|
||||
HighlightsRole = 0x0102,
|
||||
// Used for comparison.
|
||||
ComparisonRole = 0x0103
|
||||
};
|
||||
|
||||
enum ViewOrder
|
||||
{
|
||||
OrderedByConfiguration = 0,
|
||||
OrderedByName,
|
||||
OrderedByNameReversed,
|
||||
OrderedByCreatedTime,
|
||||
OrderedByCreatedTimeReversed,
|
||||
OrderedByModifiedTime,
|
||||
OrderedByModifiedTimeReversed,
|
||||
ViewOrderMax
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(vnotex::FindOptions);
|
||||
|
||||
Q_DECLARE_METATYPE(vnotex::Segment);
|
||||
|
||||
#endif // GLOBAL_H
|
||||
|
@ -8,6 +8,9 @@
|
||||
#include "vnotex.h"
|
||||
#include "notebookmgr.h"
|
||||
#include <notebook/notebook.h>
|
||||
#include <notebook/historyi.h>
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
#include "exception.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -23,12 +26,9 @@ bool HistoryItemFull::operator<(const HistoryItemFull &p_other) const
|
||||
}
|
||||
|
||||
|
||||
int HistoryMgr::s_maxHistoryCount = 100;
|
||||
|
||||
HistoryMgr::HistoryMgr()
|
||||
: m_perNotebookHistoryEnabled(ConfigMgr::getInst().getCoreConfig().isPerNotebookHistoryEnabled())
|
||||
{
|
||||
s_maxHistoryCount = ConfigMgr::getInst().getCoreConfig().getHistoryMaxCount();
|
||||
|
||||
connect(&VNoteX::getInst().getNotebookMgr(), &NotebookMgr::notebooksUpdated,
|
||||
this, &HistoryMgr::loadHistory);
|
||||
|
||||
@ -55,13 +55,27 @@ void HistoryMgr::loadHistory()
|
||||
}
|
||||
|
||||
// Load from notebooks.
|
||||
{
|
||||
if (m_perNotebookHistoryEnabled) {
|
||||
const auto ¬ebooks = VNoteX::getInst().getNotebookMgr().getNotebooks();
|
||||
for (const auto &nb : notebooks) {
|
||||
const auto &history = nb->getHistory();
|
||||
auto historyI = nb->history();
|
||||
if (!historyI) {
|
||||
continue;
|
||||
}
|
||||
const auto &history = historyI->getHistory();
|
||||
const auto &backend = nb->getBackend();
|
||||
for (const auto &item : history) {
|
||||
auto fullItem = QSharedPointer<HistoryItemFull>::create();
|
||||
fullItem->m_item = item;
|
||||
|
||||
// We saved the absolute path by mistake in previous version.
|
||||
try {
|
||||
fullItem->m_item.m_path = backend->getFullPath(item.m_path);
|
||||
} catch (Exception &p_e) {
|
||||
qWarning() << "skipped loading history item" << item.m_path << "from notebook" << nb->getName() << p_e.what();
|
||||
continue;
|
||||
}
|
||||
|
||||
fullItem->m_notebookName = nb->getName();
|
||||
m_history.push_back(fullItem);
|
||||
}
|
||||
@ -80,20 +94,31 @@ const QVector<QSharedPointer<HistoryItemFull>> &HistoryMgr::getHistory() const
|
||||
return m_history;
|
||||
}
|
||||
|
||||
void HistoryMgr::removeFromHistory(const QString &p_itemPath)
|
||||
{
|
||||
for (int i = m_history.size() - 1; i >= 0; --i) {
|
||||
if (m_history[i]->m_item.m_path == p_itemPath) {
|
||||
m_history.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryMgr::add(const QString &p_path,
|
||||
int p_lineNumber,
|
||||
ViewWindowMode p_mode,
|
||||
bool p_readOnly,
|
||||
Notebook *p_notebook)
|
||||
{
|
||||
if (p_path.isEmpty() || s_maxHistoryCount == 0) {
|
||||
const int maxHistoryCount = ConfigMgr::getInst().getCoreConfig().getHistoryMaxCount();
|
||||
if (p_path.isEmpty() || maxHistoryCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
HistoryItem item(p_path, p_lineNumber, QDateTime::currentDateTimeUtc());
|
||||
|
||||
if (p_notebook) {
|
||||
p_notebook->addHistory(item);
|
||||
if (p_notebook && m_perNotebookHistoryEnabled && p_notebook->history()) {
|
||||
p_notebook->history()->addHistory(item);
|
||||
} else {
|
||||
auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
|
||||
sessionConfig.addHistory(item);
|
||||
@ -101,13 +126,7 @@ void HistoryMgr::add(const QString &p_path,
|
||||
|
||||
// Maintain the combined queue.
|
||||
{
|
||||
for (int i = m_history.size() - 1; i >= 0; --i) {
|
||||
if (m_history[i]->m_item.m_path == item.m_path) {
|
||||
// Erase it.
|
||||
m_history.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
removeFromHistory(item.m_path);
|
||||
|
||||
auto fullItem = QSharedPointer<HistoryItemFull>::create();
|
||||
fullItem->m_item = item;
|
||||
@ -133,28 +152,48 @@ void HistoryMgr::add(const QString &p_path,
|
||||
file.m_mode = p_mode;
|
||||
file.m_readOnly = p_readOnly;
|
||||
|
||||
if (m_lastClosedFiles.size() > 100) {
|
||||
m_lastClosedFiles.remove(0, m_lastClosedFiles.size() - 100);
|
||||
if (m_lastClosedFiles.size() > maxHistoryCount) {
|
||||
m_lastClosedFiles.remove(0, m_lastClosedFiles.size() - maxHistoryCount);
|
||||
}
|
||||
}
|
||||
|
||||
emit historyUpdated();
|
||||
}
|
||||
|
||||
void HistoryMgr::insertHistoryItem(QVector<HistoryItem> &p_history, const HistoryItem &p_item)
|
||||
void HistoryMgr::remove(const QVector<QString> &p_paths, Notebook *p_notebook)
|
||||
{
|
||||
for(const QString &p_itemPath : p_paths) {
|
||||
if (p_notebook && m_perNotebookHistoryEnabled && p_notebook->history()) {
|
||||
p_notebook->history()->removeHistory(p_itemPath);
|
||||
} else {
|
||||
auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
|
||||
sessionConfig.removeHistory(p_itemPath);
|
||||
}
|
||||
|
||||
removeFromHistory(p_itemPath);
|
||||
}
|
||||
|
||||
emit historyUpdated();
|
||||
}
|
||||
|
||||
void HistoryMgr::removeHistoryItem(QVector<HistoryItem> &p_history, const QString &p_itemPath)
|
||||
{
|
||||
for (int i = p_history.size() - 1; i >= 0; --i) {
|
||||
if (p_history[i].m_path == p_item.m_path) {
|
||||
// Erase it.
|
||||
if (p_history[i].m_path == p_itemPath) {
|
||||
p_history.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryMgr::insertHistoryItem(QVector<HistoryItem> &p_history, const HistoryItem &p_item)
|
||||
{
|
||||
removeHistoryItem(p_history, p_item.m_path);
|
||||
p_history.append(p_item);
|
||||
|
||||
if (p_history.size() > s_maxHistoryCount) {
|
||||
p_history.remove(0, p_history.size() - s_maxHistoryCount);
|
||||
const int maxHistoryCount = ConfigMgr::getInst().getCoreConfig().getHistoryMaxCount();
|
||||
if (p_history.size() > maxHistoryCount) {
|
||||
p_history.remove(0, p_history.size() - maxHistoryCount);
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,9 +201,13 @@ void HistoryMgr::clear()
|
||||
{
|
||||
ConfigMgr::getInst().getSessionConfig().clearHistory();
|
||||
|
||||
const auto ¬ebooks = VNoteX::getInst().getNotebookMgr().getNotebooks();
|
||||
for (const auto &nb : notebooks) {
|
||||
nb->clearHistory();
|
||||
if (m_perNotebookHistoryEnabled) {
|
||||
const auto ¬ebooks = VNoteX::getInst().getNotebookMgr().getNotebooks();
|
||||
for (const auto &nb : notebooks) {
|
||||
if (auto historyI = nb->history()) {
|
||||
historyI->clearHistory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadHistory();
|
||||
|
@ -55,10 +55,14 @@ namespace vnotex
|
||||
bool p_readOnly,
|
||||
Notebook *p_notebook);
|
||||
|
||||
void remove(const QVector<QString> &p_paths, Notebook *p_notebook);
|
||||
|
||||
void clear();
|
||||
|
||||
LastClosedFile popLastClosedFile();
|
||||
|
||||
static void removeHistoryItem(QVector<HistoryItem> &p_history, const QString &p_itemPath);
|
||||
|
||||
static void insertHistoryItem(QVector<HistoryItem> &p_history, const HistoryItem &p_item);
|
||||
|
||||
signals:
|
||||
@ -72,9 +76,11 @@ namespace vnotex
|
||||
// Sorted by last accessed time ascendingly.
|
||||
QVector<QSharedPointer<HistoryItemFull>> m_history;
|
||||
|
||||
void removeFromHistory(const QString &p_itemPath);
|
||||
|
||||
QVector<LastClosedFile> m_lastClosedFiles;
|
||||
|
||||
static int s_maxHistoryCount;
|
||||
const bool m_perNotebookHistoryEnabled = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include <QDebug>
|
||||
|
||||
#include <core/markdowneditorconfig.h>
|
||||
#include <core/pdfviewerconfig.h>
|
||||
#include <core/mindmapeditorconfig.h>
|
||||
#include <core/configmgr.h>
|
||||
#include <utils/utils.h>
|
||||
#include <utils/fileutils.h>
|
||||
@ -10,32 +12,40 @@
|
||||
#include <utils/htmlutils.h>
|
||||
#include <core/thememgr.h>
|
||||
#include <core/vnotex.h>
|
||||
#include <core/exception.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
HtmlTemplateHelper::Template HtmlTemplateHelper::s_markdownViewerTemplate;
|
||||
|
||||
static const QString c_globalStylesPlaceholder = "/* VX_GLOBAL_STYLES_PLACEHOLDER */";
|
||||
HtmlTemplateHelper::Template HtmlTemplateHelper::s_pdfViewerTemplate;
|
||||
|
||||
QString WebGlobalOptions::toJavascriptObject() const
|
||||
HtmlTemplateHelper::Template HtmlTemplateHelper::s_mindMapEditorTemplate;
|
||||
|
||||
QString MarkdownWebGlobalOptions::toJavascriptObject() const
|
||||
{
|
||||
return QStringLiteral("window.vxOptions = {\n")
|
||||
+ QString("webPlantUml: %1,\n").arg(Utils::boolToString(m_webPlantUml))
|
||||
+ QString("webGraphviz: %1,\n").arg(Utils::boolToString(m_webGraphviz))
|
||||
+ QString("constrainImageWidthEnabled: %1,\n").arg(Utils::boolToString(m_constrainImageWidthEnabled))
|
||||
+ QString("protectFromXss: %1,\n").arg(Utils::boolToString(m_protectFromXss))
|
||||
+ QString("htmlTagEnabled: %1,\n").arg(Utils::boolToString(m_htmlTagEnabled))
|
||||
+ QString("autoBreakEnabled: %1,\n").arg(Utils::boolToString(m_autoBreakEnabled))
|
||||
+ QString("linkifyEnabled: %1,\n").arg(Utils::boolToString(m_linkifyEnabled))
|
||||
+ QString("indentFirstLineEnabled: %1,\n").arg(Utils::boolToString(m_indentFirstLineEnabled))
|
||||
+ QString("sectionNumberEnabled: %1,\n").arg(Utils::boolToString(m_sectionNumberEnabled))
|
||||
+ QString("transparentBackgroundEnabled: %1,\n").arg(Utils::boolToString(m_transparentBackgroundEnabled))
|
||||
+ QString("scrollable: %1,\n").arg(Utils::boolToString(m_scrollable))
|
||||
+ QString("bodyWidth: %1,\n").arg(m_bodyWidth)
|
||||
+ QString("bodyHeight: %1,\n").arg(m_bodyHeight)
|
||||
+ QString("transformSvgToPngEnabled: %1,\n").arg(Utils::boolToString(m_transformSvgToPngEnabled))
|
||||
+ QString("mathJaxScale: %1,\n").arg(m_mathJaxScale)
|
||||
+ QString("sectionNumberBaseLevel: %1\n").arg(m_sectionNumberBaseLevel)
|
||||
+ QStringLiteral("webPlantUml: %1,\n").arg(Utils::boolToString(m_webPlantUml))
|
||||
+ QStringLiteral("plantUmlWebService: '%1',\n").arg(m_plantUmlWebService)
|
||||
+ QStringLiteral("webGraphviz: %1,\n").arg(Utils::boolToString(m_webGraphviz))
|
||||
+ QStringLiteral("mathJaxScript: '%1',\n").arg(m_mathJaxScript)
|
||||
+ QStringLiteral("constrainImageWidthEnabled: %1,\n").arg(Utils::boolToString(m_constrainImageWidthEnabled))
|
||||
+ QStringLiteral("imageAlignCenterEnabled: %1,\n").arg(Utils::boolToString(m_imageAlignCenterEnabled))
|
||||
+ QStringLiteral("protectFromXss: %1,\n").arg(Utils::boolToString(m_protectFromXss))
|
||||
+ QStringLiteral("htmlTagEnabled: %1,\n").arg(Utils::boolToString(m_htmlTagEnabled))
|
||||
+ QStringLiteral("autoBreakEnabled: %1,\n").arg(Utils::boolToString(m_autoBreakEnabled))
|
||||
+ QStringLiteral("linkifyEnabled: %1,\n").arg(Utils::boolToString(m_linkifyEnabled))
|
||||
+ QStringLiteral("indentFirstLineEnabled: %1,\n").arg(Utils::boolToString(m_indentFirstLineEnabled))
|
||||
+ QStringLiteral("codeBlockLineNumberEnabled: %1,\n").arg(Utils::boolToString(m_codeBlockLineNumberEnabled))
|
||||
+ QStringLiteral("sectionNumberEnabled: %1,\n").arg(Utils::boolToString(m_sectionNumberEnabled))
|
||||
+ QStringLiteral("transparentBackgroundEnabled: %1,\n").arg(Utils::boolToString(m_transparentBackgroundEnabled))
|
||||
+ QStringLiteral("scrollable: %1,\n").arg(Utils::boolToString(m_scrollable))
|
||||
+ QStringLiteral("bodyWidth: %1,\n").arg(m_bodyWidth)
|
||||
+ QStringLiteral("bodyHeight: %1,\n").arg(m_bodyHeight)
|
||||
+ QStringLiteral("transformSvgToPngEnabled: %1,\n").arg(Utils::boolToString(m_transformSvgToPngEnabled))
|
||||
+ QStringLiteral("mathJaxScale: %1,\n").arg(m_mathJaxScale)
|
||||
+ QStringLiteral("removeCodeToolBarEnabled: %1,\n").arg(Utils::boolToString(m_removeCodeToolBarEnabled))
|
||||
+ QStringLiteral("sectionNumberBaseLevel: %1\n").arg(m_sectionNumberBaseLevel)
|
||||
+ QStringLiteral("}");
|
||||
}
|
||||
|
||||
@ -49,7 +59,11 @@ static void fillGlobalStyles(QString &p_template, const WebResource &p_resource,
|
||||
for (const auto &style : ele.m_styles) {
|
||||
// Read the style file content.
|
||||
auto styleFile = ConfigMgr::getInst().getUserOrAppFile(style);
|
||||
styles += FileUtils::readTextFile(styleFile);
|
||||
try {
|
||||
styles += FileUtils::readTextFile(styleFile);
|
||||
} catch (Exception &p_e) {
|
||||
qWarning() << "failed to read global styles" << ele.m_name << styleFile << p_e.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -59,7 +73,7 @@ static void fillGlobalStyles(QString &p_template, const WebResource &p_resource,
|
||||
styles += p_additionalStyles;
|
||||
|
||||
if (!styles.isEmpty()) {
|
||||
p_template.replace(c_globalStylesPlaceholder, styles);
|
||||
p_template.replace("/* VX_GLOBAL_STYLES_PLACEHOLDER */", styles);
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +83,7 @@ static QString fillStyleTag(const QString &p_styleFile)
|
||||
return "";
|
||||
}
|
||||
auto url = PathUtils::pathToUrl(p_styleFile);
|
||||
return QString("<link rel=\"stylesheet\" type=\"text/css\" href=\"%1\">\n").arg(url.toString());
|
||||
return QStringLiteral("<link rel=\"stylesheet\" type=\"text/css\" href=\"%1\">\n").arg(url.toString());
|
||||
}
|
||||
|
||||
static QString fillScriptTag(const QString &p_scriptFile)
|
||||
@ -78,7 +92,7 @@ static QString fillScriptTag(const QString &p_scriptFile)
|
||||
return "";
|
||||
}
|
||||
auto url = PathUtils::pathToUrl(p_scriptFile);
|
||||
return QString("<script type=\"text/javascript\" src=\"%1\"></script>\n").arg(url.toString());
|
||||
return QStringLiteral("<script type=\"text/javascript\" src=\"%1\"></script>\n").arg(url.toString());
|
||||
}
|
||||
|
||||
static void fillThemeStyles(QString &p_template, const QString &p_webStyleSheetFile, const QString &p_highlightStyleSheetFile)
|
||||
@ -93,7 +107,7 @@ static void fillThemeStyles(QString &p_template, const QString &p_webStyleSheetF
|
||||
}
|
||||
}
|
||||
|
||||
static void fillGlobalOptions(QString &p_template, const WebGlobalOptions &p_opts)
|
||||
static void fillGlobalOptions(QString &p_template, const MarkdownWebGlobalOptions &p_opts)
|
||||
{
|
||||
p_template.replace(QStringLiteral("/* VX_GLOBAL_OPTIONS_PLACEHOLDER */"),
|
||||
p_opts.toJavascriptObject());
|
||||
@ -139,16 +153,20 @@ static void fillResourcesByContent(QString &p_template, const WebResource &p_res
|
||||
|
||||
for (const auto &ele : p_resource.m_resources) {
|
||||
if (ele.m_enabled && !ele.isGlobal()) {
|
||||
// Styles.
|
||||
for (const auto &style : ele.m_styles) {
|
||||
auto styleFile = ConfigMgr::getInst().getUserOrAppFile(style);
|
||||
styles += FileUtils::readTextFile(styleFile);
|
||||
}
|
||||
try {
|
||||
// Styles.
|
||||
for (const auto &style : ele.m_styles) {
|
||||
auto styleFile = ConfigMgr::getInst().getUserOrAppFile(style);
|
||||
styles += FileUtils::readTextFile(styleFile);
|
||||
}
|
||||
|
||||
// Scripts.
|
||||
for (const auto &script : ele.m_scripts) {
|
||||
auto scriptFile = ConfigMgr::getInst().getUserOrAppFile(script);
|
||||
scripts += FileUtils::readTextFile(scriptFile);
|
||||
// Scripts.
|
||||
for (const auto &script : ele.m_scripts) {
|
||||
auto scriptFile = ConfigMgr::getInst().getUserOrAppFile(script);
|
||||
scripts += FileUtils::readTextFile(scriptFile);
|
||||
}
|
||||
} catch (Exception &p_e) {
|
||||
qWarning() << "failed to read resource" << ele.m_name << p_e.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,57 +185,62 @@ const QString &HtmlTemplateHelper::getMarkdownViewerTemplate()
|
||||
return s_markdownViewerTemplate.m_template;
|
||||
}
|
||||
|
||||
void HtmlTemplateHelper::updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config)
|
||||
void HtmlTemplateHelper::updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, bool p_force)
|
||||
{
|
||||
if (p_config.revision() == s_markdownViewerTemplate.m_revision) {
|
||||
if (!p_force && p_config.revision() == s_markdownViewerTemplate.m_revision) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_markdownViewerTemplate.m_revision = p_config.revision();
|
||||
|
||||
MarkdownParas paras;
|
||||
const auto &themeMgr = VNoteX::getInst().getThemeMgr();
|
||||
s_markdownViewerTemplate.m_template =
|
||||
generateMarkdownViewerTemplate(p_config,
|
||||
themeMgr.getFile(Theme::File::WebStyleSheet),
|
||||
themeMgr.getFile(Theme::File::HighlightStyleSheet));
|
||||
paras.m_webStyleSheetFile = themeMgr.getFile(Theme::File::WebStyleSheet);
|
||||
paras.m_highlightStyleSheetFile = themeMgr.getFile(Theme::File::HighlightStyleSheet);
|
||||
|
||||
s_markdownViewerTemplate.m_template = generateMarkdownViewerTemplate(p_config, paras);
|
||||
}
|
||||
|
||||
QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config,
|
||||
const QString &p_webStyleSheetFile,
|
||||
const QString &p_highlightStyleSheetFile,
|
||||
bool p_useTransparentBg,
|
||||
bool p_scrollable,
|
||||
int p_bodyWidth,
|
||||
int p_bodyHeight,
|
||||
bool p_transformSvgToPng,
|
||||
qreal p_mathJaxScale)
|
||||
const MarkdownParas &p_paras)
|
||||
{
|
||||
const auto &viewerResource = p_config.getViewerResource();
|
||||
const auto templateFile = ConfigMgr::getInst().getUserOrAppFile(viewerResource.m_template);
|
||||
auto htmlTemplate = FileUtils::readTextFile(templateFile);
|
||||
QString htmlTemplate;
|
||||
try {
|
||||
htmlTemplate = FileUtils::readTextFile(templateFile);
|
||||
} catch (Exception &p_e) {
|
||||
qWarning() << "failed to read HTML template" << templateFile << p_e.what();
|
||||
return errorPage();
|
||||
}
|
||||
|
||||
fillGlobalStyles(htmlTemplate, viewerResource, "");
|
||||
|
||||
fillThemeStyles(htmlTemplate, p_webStyleSheetFile, p_highlightStyleSheetFile);
|
||||
fillThemeStyles(htmlTemplate, p_paras.m_webStyleSheetFile, p_paras.m_highlightStyleSheetFile);
|
||||
|
||||
{
|
||||
WebGlobalOptions opts;
|
||||
MarkdownWebGlobalOptions opts;
|
||||
opts.m_webPlantUml = p_config.getWebPlantUml();
|
||||
opts.m_plantUmlWebService = p_config.getPlantUmlWebService();
|
||||
opts.m_webGraphviz = p_config.getWebGraphviz();
|
||||
opts.m_mathJaxScript = p_config.getMathJaxScript();
|
||||
opts.m_sectionNumberEnabled = p_config.getSectionNumberMode() == MarkdownEditorConfig::SectionNumberMode::Read;
|
||||
opts.m_sectionNumberBaseLevel = p_config.getSectionNumberBaseLevel();
|
||||
opts.m_constrainImageWidthEnabled = p_config.getConstrainImageWidthEnabled();
|
||||
opts.m_imageAlignCenterEnabled = p_config.getImageAlignCenterEnabled();
|
||||
opts.m_protectFromXss = p_config.getProtectFromXss();
|
||||
opts.m_htmlTagEnabled = p_config.getHtmlTagEnabled();
|
||||
opts.m_autoBreakEnabled = p_config.getAutoBreakEnabled();
|
||||
opts.m_linkifyEnabled = p_config.getLinkifyEnabled();
|
||||
opts.m_indentFirstLineEnabled = p_config.getIndentFirstLineEnabled();
|
||||
opts.m_transparentBackgroundEnabled = p_useTransparentBg;
|
||||
opts.m_scrollable = p_scrollable;
|
||||
opts.m_bodyWidth = p_bodyWidth;
|
||||
opts.m_bodyHeight = p_bodyHeight;
|
||||
opts.m_transformSvgToPngEnabled = p_transformSvgToPng;
|
||||
opts.m_mathJaxScale = p_mathJaxScale;
|
||||
opts.m_codeBlockLineNumberEnabled = p_config.getCodeBlockLineNumberEnabled();
|
||||
opts.m_transparentBackgroundEnabled = p_paras.m_transparentBackgroundEnabled;
|
||||
opts.m_scrollable = p_paras.m_scrollable;
|
||||
opts.m_bodyWidth = p_paras.m_bodyWidth;
|
||||
opts.m_bodyHeight = p_paras.m_bodyHeight;
|
||||
opts.m_transformSvgToPngEnabled = p_paras.m_transformSvgToPngEnabled;
|
||||
opts.m_mathJaxScale = p_paras.m_mathJaxScale;
|
||||
opts.m_removeCodeToolBarEnabled = p_paras.m_removeCodeToolBarEnabled;
|
||||
fillGlobalOptions(htmlTemplate, opts);
|
||||
}
|
||||
|
||||
@ -226,33 +249,58 @@ QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorC
|
||||
return htmlTemplate;
|
||||
}
|
||||
|
||||
QString HtmlTemplateHelper::generateExportTemplate(const MarkdownEditorConfig &p_config,
|
||||
bool p_addOutlinePanel)
|
||||
QString HtmlTemplateHelper::generateMarkdownExportTemplate(const MarkdownEditorConfig &p_config,
|
||||
bool p_addOutlinePanel)
|
||||
{
|
||||
auto exportResource = p_config.getExportResource();
|
||||
const auto templateFile = ConfigMgr::getInst().getUserOrAppFile(exportResource.m_template);
|
||||
auto htmlTemplate = FileUtils::readTextFile(templateFile);
|
||||
QString htmlTemplate;
|
||||
try {
|
||||
htmlTemplate = FileUtils::readTextFile(templateFile);
|
||||
} catch (Exception &p_e) {
|
||||
qWarning() << "failed to read Markdown export HTML template" << templateFile << p_e.what();
|
||||
return errorPage();
|
||||
}
|
||||
|
||||
fillGlobalStyles(htmlTemplate, exportResource, "");
|
||||
|
||||
// Outline panel.
|
||||
for (auto &ele : exportResource.m_resources) {
|
||||
if (ele.m_name == QStringLiteral("outline")) {
|
||||
ele.m_enabled = p_addOutlinePanel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fillOutlinePanel(htmlTemplate, exportResource, p_addOutlinePanel);
|
||||
|
||||
fillResourcesByContent(htmlTemplate, exportResource);
|
||||
|
||||
return htmlTemplate;
|
||||
}
|
||||
|
||||
void HtmlTemplateHelper::fillOutlinePanel(QString &p_template, WebResource &p_exportResource, bool p_addOutlinePanel)
|
||||
{
|
||||
for (auto &ele : p_exportResource.m_resources) {
|
||||
if (ele.m_name == QStringLiteral("outline")) {
|
||||
ele.m_enabled = p_addOutlinePanel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove static content to make the page clean.
|
||||
if (!p_addOutlinePanel) {
|
||||
int startIdx = p_template.indexOf("<!-- VX_OUTLINE_PANEL_START -->");
|
||||
QString endMark("<!-- VX_OUTLINE_PANEL_END -->");
|
||||
int endIdx = p_template.lastIndexOf(endMark);
|
||||
Q_ASSERT(startIdx > -1 && endIdx > startIdx);
|
||||
p_template.remove(startIdx, endIdx + endMark.size() - startIdx);
|
||||
|
||||
startIdx = p_template.indexOf("<!-- VX_OUTLINE_BUTTON_START -->");
|
||||
endMark = "<!-- VX_OUTLINE_BUTTON_END -->";
|
||||
endIdx = p_template.lastIndexOf(endMark);
|
||||
Q_ASSERT(startIdx > -1 && endIdx > startIdx);
|
||||
p_template.remove(startIdx, endIdx + endMark.size() - startIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void HtmlTemplateHelper::fillTitle(QString &p_template, const QString &p_title)
|
||||
{
|
||||
if (!p_title.isEmpty()) {
|
||||
p_template.replace("<!-- VX_TITLE_PLACEHOLDER -->",
|
||||
QString("<title>%1</title>").arg(HtmlUtils::escapeHtml(p_title)));
|
||||
QStringLiteral("<title>%1</title>").arg(HtmlUtils::escapeHtml(p_title)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -275,3 +323,81 @@ void HtmlTemplateHelper::fillBodyClassList(QString &p_template, const QString &p
|
||||
{
|
||||
p_template.replace("<!-- VX_BODY_CLASS_LIST_PLACEHOLDER -->", p_classList);
|
||||
}
|
||||
|
||||
QString HtmlTemplateHelper::errorPage()
|
||||
{
|
||||
return VNoteX::tr("Failed to load HTML template. Check the logs for details. "
|
||||
"Try deleting the user configuration file and the default configuration file.");
|
||||
}
|
||||
|
||||
const QString &HtmlTemplateHelper::getPdfViewerTemplate()
|
||||
{
|
||||
return s_pdfViewerTemplate.m_template;
|
||||
}
|
||||
|
||||
const QString &HtmlTemplateHelper::getPdfViewerTemplatePath()
|
||||
{
|
||||
return s_pdfViewerTemplate.m_templatePath;
|
||||
}
|
||||
|
||||
void HtmlTemplateHelper::updatePdfViewerTemplate(const PdfViewerConfig &p_config, bool p_force)
|
||||
{
|
||||
if (!p_force && p_config.revision() == s_pdfViewerTemplate.m_revision) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_pdfViewerTemplate.m_revision = p_config.revision();
|
||||
generatePdfViewerTemplate(p_config, s_pdfViewerTemplate);
|
||||
}
|
||||
|
||||
void HtmlTemplateHelper::generatePdfViewerTemplate(const PdfViewerConfig &p_config, Template& p_template)
|
||||
{
|
||||
const auto &viewerResource = p_config.getViewerResource();
|
||||
p_template.m_templatePath = ConfigMgr::getInst().getUserOrAppFile(viewerResource.m_template);
|
||||
try {
|
||||
p_template.m_template = FileUtils::readTextFile(p_template.m_templatePath);
|
||||
} catch (Exception &p_e) {
|
||||
qWarning() << "failed to read HTML template" << p_template.m_templatePath << p_e.what();
|
||||
p_template.m_template = errorPage();
|
||||
return;
|
||||
}
|
||||
|
||||
fillResources(p_template.m_template, viewerResource);
|
||||
}
|
||||
|
||||
const QString &HtmlTemplateHelper::getMindMapEditorTemplate()
|
||||
{
|
||||
return s_mindMapEditorTemplate.m_template;
|
||||
}
|
||||
|
||||
void HtmlTemplateHelper::updateMindMapEditorTemplate(const MindMapEditorConfig &p_config, bool p_force)
|
||||
{
|
||||
if (!p_force && p_config.revision() == s_mindMapEditorTemplate.m_revision) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_mindMapEditorTemplate.m_revision = p_config.revision();
|
||||
|
||||
generateMindMapEditorTemplate(p_config,
|
||||
QString() /* Use empty theme style for now */,
|
||||
s_mindMapEditorTemplate);
|
||||
}
|
||||
|
||||
void HtmlTemplateHelper::generateMindMapEditorTemplate(const MindMapEditorConfig &p_config,
|
||||
const QString &p_webStyleSheetFile,
|
||||
Template& p_template)
|
||||
{
|
||||
const auto &editorResource = p_config.getEditorResource();
|
||||
p_template.m_templatePath = ConfigMgr::getInst().getUserOrAppFile(editorResource.m_template);
|
||||
try {
|
||||
p_template.m_template = FileUtils::readTextFile(p_template.m_templatePath);
|
||||
} catch (Exception &p_e) {
|
||||
qWarning() << "failed to read HTML template" << p_template.m_templatePath << p_e.what();
|
||||
p_template.m_template = errorPage();
|
||||
return;
|
||||
}
|
||||
|
||||
fillThemeStyles(p_template.m_template, p_webStyleSheetFile, QString());
|
||||
|
||||
fillResources(p_template.m_template, editorResource);
|
||||
}
|
||||
|
@ -6,20 +6,29 @@
|
||||
namespace vnotex
|
||||
{
|
||||
class MarkdownEditorConfig;
|
||||
class PdfViewerConfig;
|
||||
class MindMapEditorConfig;
|
||||
struct WebResource;
|
||||
|
||||
// Global options to be passed to Web side at the very beginning.
|
||||
struct WebGlobalOptions
|
||||
// Global options to be passed to Web side at the very beginning for Markdown.
|
||||
struct MarkdownWebGlobalOptions
|
||||
{
|
||||
bool m_webPlantUml = true;
|
||||
|
||||
QString m_plantUmlWebService;
|
||||
|
||||
bool m_webGraphviz = true;
|
||||
|
||||
QString m_mathJaxScript;
|
||||
|
||||
bool m_sectionNumberEnabled = true;
|
||||
|
||||
int m_sectionNumberBaseLevel = 2;
|
||||
|
||||
bool m_constrainImageWidthEnabled = true;
|
||||
|
||||
bool m_imageAlignCenterEnabled = true;
|
||||
|
||||
bool m_protectFromXss = false;
|
||||
|
||||
bool m_htmlTagEnabled = true;
|
||||
@ -30,6 +39,8 @@ namespace vnotex
|
||||
|
||||
bool m_indentFirstLineEnabled = false;
|
||||
|
||||
bool m_codeBlockLineNumberEnabled = true;
|
||||
|
||||
// Force to use transparent background.
|
||||
bool m_transparentBackgroundEnabled = false;
|
||||
|
||||
@ -48,6 +59,9 @@ namespace vnotex
|
||||
// wkhtmltopdf will make the MathJax formula too small.
|
||||
qreal m_mathJaxScale = -1;
|
||||
|
||||
// Whether remove the tool bar of code blocks added by Prism.js.
|
||||
bool m_removeCodeToolBarEnabled = false;
|
||||
|
||||
QString toJavascriptObject() const;
|
||||
};
|
||||
|
||||
@ -55,24 +69,39 @@ namespace vnotex
|
||||
class HtmlTemplateHelper
|
||||
{
|
||||
public:
|
||||
struct MarkdownParas
|
||||
{
|
||||
QString m_webStyleSheetFile;
|
||||
|
||||
QString m_highlightStyleSheetFile;
|
||||
|
||||
bool m_transparentBackgroundEnabled = false;
|
||||
|
||||
bool m_scrollable = true;
|
||||
|
||||
int m_bodyWidth = -1;
|
||||
|
||||
int m_bodyHeight = -1;
|
||||
|
||||
bool m_transformSvgToPngEnabled = false;
|
||||
|
||||
qreal m_mathJaxScale = -1;
|
||||
|
||||
bool m_removeCodeToolBarEnabled = false;
|
||||
};
|
||||
|
||||
HtmlTemplateHelper() = delete;
|
||||
|
||||
// For MarkdownViewer.
|
||||
static const QString &getMarkdownViewerTemplate();
|
||||
static void updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config);
|
||||
static void updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, bool p_force = false);
|
||||
|
||||
static QString generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config,
|
||||
const QString &p_webStyleSheetFile,
|
||||
const QString &p_highlightStyleSheetFile,
|
||||
bool p_useTransparentBg = false,
|
||||
bool p_scrollable = true,
|
||||
int p_bodyWidth = -1,
|
||||
int p_bodyHeight = -1,
|
||||
bool p_transformSvgToPng = false,
|
||||
qreal p_mathJaxScale = -1);
|
||||
static QString generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, const MarkdownParas &p_paras);
|
||||
|
||||
static QString generateExportTemplate(const MarkdownEditorConfig &p_config,
|
||||
bool p_addOutlinePanel);
|
||||
static QString generateMarkdownExportTemplate(const MarkdownEditorConfig &p_config,
|
||||
bool p_addOutlinePanel);
|
||||
|
||||
// For common HTML content manipulation.
|
||||
static void fillTitle(QString &p_template, const QString &p_title);
|
||||
|
||||
static void fillStyleContent(QString &p_template, const QString &p_styles);
|
||||
@ -83,15 +112,39 @@ namespace vnotex
|
||||
|
||||
static void fillBodyClassList(QString &p_template, const QString &p_classList);
|
||||
|
||||
static void fillOutlinePanel(QString &p_template, WebResource &p_exportResource, bool p_addOutlinePanel);
|
||||
|
||||
// For PdfViewer.
|
||||
static const QString &getPdfViewerTemplate();
|
||||
static void updatePdfViewerTemplate(const PdfViewerConfig &p_config, bool p_force = false);
|
||||
|
||||
static const QString &getPdfViewerTemplatePath();
|
||||
|
||||
// For MindMapEditor.
|
||||
static const QString &getMindMapEditorTemplate();
|
||||
static void updateMindMapEditorTemplate(const MindMapEditorConfig &p_config, bool p_force = false);
|
||||
|
||||
private:
|
||||
struct Template
|
||||
{
|
||||
int m_revision = -1;
|
||||
QString m_template;
|
||||
QString m_templatePath;
|
||||
};
|
||||
|
||||
// Template for MarkdownViewer.
|
||||
static QString errorPage();
|
||||
|
||||
static void generatePdfViewerTemplate(const PdfViewerConfig &p_config, Template& p_template);
|
||||
|
||||
static void generateMindMapEditorTemplate(const MindMapEditorConfig &p_config,
|
||||
const QString &p_webStyleSheetFile,
|
||||
Template& p_template);
|
||||
|
||||
static Template s_markdownViewerTemplate;
|
||||
|
||||
static Template s_pdfViewerTemplate;
|
||||
|
||||
static Template s_mindMapEditorTemplate;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <QJsonArray>
|
||||
#include <QBitArray>
|
||||
#include <QDataStream>
|
||||
#include <QIODevice>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
@ -96,8 +97,8 @@ namespace vnotex
|
||||
auto arr = read(p_default, p_user, p_key).toArray();
|
||||
QStringList res;
|
||||
res.reserve(arr.size());
|
||||
for (const auto &ele : arr) {
|
||||
res.push_back(ele.toString());
|
||||
for (int i = 0; i < arr.size(); ++i) {
|
||||
res.push_back(arr[i].toString());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -108,8 +109,8 @@ namespace vnotex
|
||||
auto arr = p_obj.value(p_key).toArray();
|
||||
QStringList res;
|
||||
res.reserve(arr.size());
|
||||
for (const auto &ele : arr) {
|
||||
res.push_back(ele.toString());
|
||||
for (int i = 0; i < arr.size(); ++i) {
|
||||
res.push_back(arr[i].toString());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ namespace vnotex
|
||||
{
|
||||
Line() = default;
|
||||
|
||||
Line(int p_lineNumber, const QString &p_text, const QVector<Segment> &p_segments)
|
||||
Line(int p_lineNumber, const QString &p_text, const QList<Segment> &p_segments)
|
||||
: m_lineNumber(p_lineNumber),
|
||||
m_text(p_text),
|
||||
m_segments(p_segments)
|
||||
@ -51,10 +51,10 @@ namespace vnotex
|
||||
|
||||
QString m_text;
|
||||
|
||||
QVector<Segment> m_segments;
|
||||
QList<Segment> m_segments;
|
||||
};
|
||||
|
||||
void addLine(int p_lineNumber, const QString &p_text, const QVector<Segment> &p_segments)
|
||||
void addLine(int p_lineNumber, const QString &p_text, const QList<Segment> &p_segments)
|
||||
{
|
||||
m_lines.push_back(Line(p_lineNumber, p_text, p_segments));
|
||||
}
|
||||
|
@ -8,19 +8,27 @@ using namespace vnotex;
|
||||
|
||||
QFile Logger::s_file;
|
||||
|
||||
bool Logger::s_debugLog = false;
|
||||
bool Logger::s_verbose = false;
|
||||
|
||||
void Logger::init(bool p_debugLog)
|
||||
bool Logger::s_logToStderr = false;
|
||||
|
||||
void Logger::init(bool p_verbose, bool p_logToStderr)
|
||||
{
|
||||
s_debugLog = p_debugLog;
|
||||
s_verbose = p_verbose;
|
||||
s_logToStderr = p_logToStderr;
|
||||
|
||||
#if defined(QT_NO_DEBUG)
|
||||
s_file.setFileName(ConfigMgr::getInst().getLogFile());
|
||||
if (s_file.size() >= 5 * 1024 * 1024) {
|
||||
s_file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
} else {
|
||||
s_file.open(QIODevice::Append | QIODevice::Text);
|
||||
if (!s_logToStderr) {
|
||||
s_file.setFileName(ConfigMgr::getInst().getLogFile());
|
||||
if (s_file.size() >= 5 * 1024 * 1024) {
|
||||
s_file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
} else {
|
||||
s_file.open(QIODevice::Append | QIODevice::Text);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Always log to stderr in debug.
|
||||
s_logToStderr = true;
|
||||
#endif
|
||||
|
||||
qInstallMessageHandler(Logger::log);
|
||||
@ -44,7 +52,7 @@ static QString getFileName(const char *p_file)
|
||||
void Logger::log(QtMsgType p_type, const QMessageLogContext &p_context, const QString &p_msg)
|
||||
{
|
||||
#if defined(QT_NO_DEBUG)
|
||||
if (!s_debugLog && p_type == QtDebugMsg) {
|
||||
if (!s_verbose && p_type == QtDebugMsg) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@ -71,46 +79,48 @@ void Logger::log(QtMsgType p_type, const QMessageLogContext &p_context, const QS
|
||||
|
||||
case QtFatalMsg:
|
||||
header = QStringLiteral("Fatal:");
|
||||
break;
|
||||
}
|
||||
|
||||
QString fileName = getFileName(p_context.file);
|
||||
|
||||
#if defined(QT_NO_DEBUG)
|
||||
QTextStream stream(&s_file);
|
||||
stream << header << (QString("(%1:%2) ").arg(fileName).arg(p_context.line))
|
||||
<< localMsg << "\n";
|
||||
if (!s_logToStderr) {
|
||||
QTextStream stream(&s_file);
|
||||
stream << header << (QStringLiteral("(%1:%2) ").arg(fileName).arg(p_context.line))
|
||||
<< localMsg << "\n";
|
||||
|
||||
if (p_type == QtFatalMsg) {
|
||||
s_file.close();
|
||||
abort();
|
||||
if (p_type == QtFatalMsg) {
|
||||
s_file.close();
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
std::string fileStr = fileName.toStdString();
|
||||
const char *file = fileStr.c_str();
|
||||
|
||||
switch (p_type) {
|
||||
case QtDebugMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
}
|
||||
#else
|
||||
std::string fileStr = fileName.toStdString();
|
||||
const char *file = fileStr.c_str();
|
||||
|
||||
switch (p_type) {
|
||||
case QtDebugMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
abort();
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
#endif
|
||||
}
|
||||
|
@ -13,14 +13,16 @@ namespace vnotex
|
||||
public:
|
||||
Logger() = delete;
|
||||
|
||||
static void init(bool p_debugLog);
|
||||
static void init(bool p_verbose, bool p_logToStderr);
|
||||
|
||||
private:
|
||||
static void log(QtMsgType p_type, const QMessageLogContext &p_context, const QString &p_msg);
|
||||
|
||||
static QFile s_file;
|
||||
|
||||
static bool s_debugLog;
|
||||
static bool s_verbose;
|
||||
|
||||
static bool s_logToStderr;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "coreconfig.h"
|
||||
#include "editorconfig.h"
|
||||
#include "widgetconfig.h"
|
||||
#include "texteditorconfig.h"
|
||||
#include "markdowneditorconfig.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -117,6 +119,4 @@ QString MainConfig::getVersion(const QJsonObject &p_jobj)
|
||||
void MainConfig::doVersionSpecificOverride()
|
||||
{
|
||||
// In a new version, we may want to change one value by force.
|
||||
m_coreConfig->m_shortcuts[CoreConfig::Shortcut::LocationListDock] = "Ctrl+G, C";
|
||||
m_coreConfig->m_shortcuts[CoreConfig::Shortcut::NewWorkspace] = "";
|
||||
}
|
||||
|
@ -35,10 +35,14 @@ void MarkdownEditorConfig::init(const QJsonObject &p_app, const QJsonObject &p_u
|
||||
|
||||
m_plantUmlCommand = READSTR(QStringLiteral("plantuml_command"));
|
||||
|
||||
m_plantUmlWebService = READSTR(QStringLiteral("plantuml_web_service"));
|
||||
|
||||
m_webGraphviz = READBOOL(QStringLiteral("web_graphviz"));
|
||||
|
||||
m_graphvizExe = READSTR(QStringLiteral("graphviz_exe"));
|
||||
|
||||
m_mathJaxScript = READSTR(QStringLiteral("mathjax_script"));
|
||||
|
||||
m_prependDotInRelativeLink = READBOOL(QStringLiteral("prepend_dot_in_relative_link"));
|
||||
m_confirmBeforeClearObsoleteImages = READBOOL(QStringLiteral("confirm_before_clear_obsolete_images"));
|
||||
m_insertFileNameAsTitle = READBOOL(QStringLiteral("insert_file_name_as_title"));
|
||||
@ -48,6 +52,7 @@ void MarkdownEditorConfig::init(const QJsonObject &p_app, const QJsonObject &p_u
|
||||
m_sectionNumberStyle = stringToSectionNumberStyle(READSTR(QStringLiteral("section_number_style")));
|
||||
|
||||
m_constrainImageWidthEnabled = READBOOL(QStringLiteral("constrain_image_width"));
|
||||
m_imageAlignCenterEnabled = READBOOL(QStringLiteral("image_align_center"));
|
||||
m_constrainInplacePreviewWidthEnabled = READBOOL(QStringLiteral("constrain_inplace_preview_width"));
|
||||
m_zoomFactorInReadMode = READREAL(QStringLiteral("zoom_factor_in_read_mode"));
|
||||
m_fetchImagesInParseAndPaste = READBOOL(QStringLiteral("fetch_images_in_parse_and_paste"));
|
||||
@ -57,6 +62,7 @@ void MarkdownEditorConfig::init(const QJsonObject &p_app, const QJsonObject &p_u
|
||||
m_autoBreakEnabled = READBOOL(QStringLiteral("auto_break"));
|
||||
m_linkifyEnabled = READBOOL(QStringLiteral("linkify"));
|
||||
m_indentFirstLineEnabled = READBOOL(QStringLiteral("indent_first_line"));
|
||||
m_codeBlockLineNumberEnabled = READBOOL(QStringLiteral("code_block_line_number"));
|
||||
|
||||
m_smartTableEnabled = READBOOL(QStringLiteral("smart_table"));
|
||||
m_smartTableInterval = READINT(QStringLiteral("smart_table_interval"));
|
||||
@ -72,6 +78,10 @@ void MarkdownEditorConfig::init(const QJsonObject &p_app, const QJsonObject &p_u
|
||||
m_inplacePreviewSources |= stringToInplacePreviewSource(src);
|
||||
}
|
||||
}
|
||||
|
||||
m_editViewMode = stringToEditViewMode(READSTR(QStringLiteral("edit_view_mode")));
|
||||
|
||||
m_richPasteByDefaultEnabled = READBOOL(QStringLiteral("rich_paste_by_default"));
|
||||
}
|
||||
|
||||
QJsonObject MarkdownEditorConfig::toJson() const
|
||||
@ -82,8 +92,10 @@ QJsonObject MarkdownEditorConfig::toJson() const
|
||||
obj[QStringLiteral("web_plantuml")] = m_webPlantUml;
|
||||
obj[QStringLiteral("plantuml_jar")] = m_plantUmlJar;
|
||||
obj[QStringLiteral("plantuml_command")] = m_plantUmlCommand;
|
||||
obj[QStringLiteral("plantuml_web_service")] = m_plantUmlWebService;
|
||||
obj[QStringLiteral("web_graphviz")] = m_webGraphviz;
|
||||
obj[QStringLiteral("graphviz_exe")] = m_graphvizExe;
|
||||
obj[QStringLiteral("mathjax_script")] = m_mathJaxScript;
|
||||
obj[QStringLiteral("prepend_dot_in_relative_link")] = m_prependDotInRelativeLink;
|
||||
obj[QStringLiteral("confirm_before_clear_obsolete_images")] = m_confirmBeforeClearObsoleteImages;
|
||||
obj[QStringLiteral("insert_file_name_as_title")] = m_insertFileNameAsTitle;
|
||||
@ -93,6 +105,7 @@ QJsonObject MarkdownEditorConfig::toJson() const
|
||||
obj[QStringLiteral("section_number_style")] = sectionNumberStyleToString(m_sectionNumberStyle);
|
||||
|
||||
obj[QStringLiteral("constrain_image_width")] = m_constrainImageWidthEnabled;
|
||||
obj[QStringLiteral("image_align_center")] = m_imageAlignCenterEnabled;
|
||||
obj[QStringLiteral("constrain_inplace_preview_width")] = m_constrainInplacePreviewWidthEnabled;
|
||||
obj[QStringLiteral("zoom_factor_in_read_mode")] = m_zoomFactorInReadMode;
|
||||
obj[QStringLiteral("fetch_images_in_parse_and_paste")] = m_fetchImagesInParseAndPaste;
|
||||
@ -101,6 +114,7 @@ QJsonObject MarkdownEditorConfig::toJson() const
|
||||
obj[QStringLiteral("auto_break")] = m_autoBreakEnabled;
|
||||
obj[QStringLiteral("linkify")] = m_linkifyEnabled;
|
||||
obj[QStringLiteral("indent_first_line")] = m_indentFirstLineEnabled;
|
||||
obj[QStringLiteral("code_block_line_number")] = m_codeBlockLineNumberEnabled;
|
||||
obj[QStringLiteral("smart_table")] = m_smartTableEnabled;
|
||||
obj[QStringLiteral("smart_table_interval")] = m_smartTableInterval;
|
||||
obj[QStringLiteral("spell_check")] = m_spellCheckEnabled;
|
||||
@ -120,6 +134,10 @@ QJsonObject MarkdownEditorConfig::toJson() const
|
||||
obj[QStringLiteral("inplace_preview_sources")] = srcs.join(QLatin1Char(';'));
|
||||
}
|
||||
|
||||
obj[QStringLiteral("edit_view_mode")] = editViewModeToString(m_editViewMode);
|
||||
|
||||
obj[QStringLiteral("rich_paste_by_default")] = m_richPasteByDefaultEnabled;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -223,6 +241,16 @@ const QString &MarkdownEditorConfig::getPlantUmlCommand() const
|
||||
return m_plantUmlCommand;
|
||||
}
|
||||
|
||||
const QString &MarkdownEditorConfig::getPlantUmlWebService() const
|
||||
{
|
||||
return m_plantUmlWebService;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setPlantUmlWebService(const QString &p_service)
|
||||
{
|
||||
updateConfig(m_plantUmlWebService, p_service, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getWebGraphviz() const
|
||||
{
|
||||
return m_webGraphviz;
|
||||
@ -243,6 +271,16 @@ void MarkdownEditorConfig::setGraphvizExe(const QString &p_exe)
|
||||
updateConfig(m_graphvizExe, p_exe, this);
|
||||
}
|
||||
|
||||
const QString &MarkdownEditorConfig::getMathJaxScript() const
|
||||
{
|
||||
return m_mathJaxScript;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setMathJaxScript(const QString &p_script)
|
||||
{
|
||||
updateConfig(m_mathJaxScript, p_script, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getPrependDotInRelativeLink() const
|
||||
{
|
||||
return m_prependDotInRelativeLink;
|
||||
@ -255,9 +293,7 @@ bool MarkdownEditorConfig::getConfirmBeforeClearObsoleteImages() const
|
||||
|
||||
void MarkdownEditorConfig::setConfirmBeforeClearObsoleteImages(bool p_confirm)
|
||||
{
|
||||
updateConfig(m_confirmBeforeClearObsoleteImages,
|
||||
p_confirm,
|
||||
this);
|
||||
updateConfig(m_confirmBeforeClearObsoleteImages, p_confirm, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getInsertFileNameAsTitle() const
|
||||
@ -280,6 +316,16 @@ void MarkdownEditorConfig::setConstrainImageWidthEnabled(bool p_enabled)
|
||||
updateConfig(m_constrainImageWidthEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getImageAlignCenterEnabled() const
|
||||
{
|
||||
return m_imageAlignCenterEnabled;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setImageAlignCenterEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_imageAlignCenterEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getConstrainInplacePreviewWidthEnabled() const
|
||||
{
|
||||
return m_constrainInplacePreviewWidthEnabled;
|
||||
@ -355,6 +401,16 @@ void MarkdownEditorConfig::setIndentFirstLineEnabled(bool p_enabled)
|
||||
updateConfig(m_indentFirstLineEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getCodeBlockLineNumberEnabled() const
|
||||
{
|
||||
return m_codeBlockLineNumberEnabled;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setCodeBlockLineNumberEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_codeBlockLineNumberEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
QString MarkdownEditorConfig::sectionNumberModeToString(SectionNumberMode p_mode) const
|
||||
{
|
||||
switch (p_mode) {
|
||||
@ -507,3 +563,44 @@ void MarkdownEditorConfig::setInplacePreviewSources(InplacePreviewSources p_src)
|
||||
{
|
||||
updateConfig(m_inplacePreviewSources, p_src, this);
|
||||
}
|
||||
|
||||
QString MarkdownEditorConfig::editViewModeToString(EditViewMode p_mode) const
|
||||
{
|
||||
switch (p_mode) {
|
||||
case EditViewMode::EditPreview:
|
||||
return QStringLiteral("editpreview");
|
||||
|
||||
default:
|
||||
return QStringLiteral("editonly");
|
||||
}
|
||||
}
|
||||
|
||||
MarkdownEditorConfig::EditViewMode MarkdownEditorConfig::stringToEditViewMode(const QString &p_str) const
|
||||
{
|
||||
auto mode = p_str.toLower();
|
||||
if (mode == QStringLiteral("editpreview")) {
|
||||
return EditViewMode::EditPreview;
|
||||
} else {
|
||||
return EditViewMode::EditOnly;
|
||||
}
|
||||
}
|
||||
|
||||
MarkdownEditorConfig::EditViewMode MarkdownEditorConfig::getEditViewMode() const
|
||||
{
|
||||
return m_editViewMode;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setEditViewMode(EditViewMode p_mode)
|
||||
{
|
||||
updateConfig(m_editViewMode, p_mode, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getRichPasteByDefaultEnabled() const
|
||||
{
|
||||
return m_richPasteByDefaultEnabled;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setRichPasteByDefaultEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_richPasteByDefaultEnabled, p_enabled, this);
|
||||
}
|
||||
|
@ -39,6 +39,12 @@ namespace vnotex
|
||||
};
|
||||
Q_DECLARE_FLAGS(InplacePreviewSources, InplacePreviewSource);
|
||||
|
||||
enum EditViewMode
|
||||
{
|
||||
EditOnly,
|
||||
EditPreview
|
||||
};
|
||||
|
||||
MarkdownEditorConfig(ConfigMgr *p_mgr,
|
||||
IConfig *p_topConfig,
|
||||
const QSharedPointer<TextEditorConfig> &p_textEditorConfig);
|
||||
@ -64,12 +70,18 @@ namespace vnotex
|
||||
|
||||
const QString &getPlantUmlCommand() const;
|
||||
|
||||
const QString &getPlantUmlWebService() const;
|
||||
void setPlantUmlWebService(const QString &p_service);
|
||||
|
||||
bool getWebGraphviz() const;
|
||||
void setWebGraphviz(bool p_enabled);
|
||||
|
||||
const QString &getGraphvizExe() const;
|
||||
void setGraphvizExe(const QString &p_exe);
|
||||
|
||||
const QString &getMathJaxScript() const;
|
||||
void setMathJaxScript(const QString &p_script);
|
||||
|
||||
bool getPrependDotInRelativeLink() const;
|
||||
|
||||
bool getConfirmBeforeClearObsoleteImages() const;
|
||||
@ -90,6 +102,9 @@ namespace vnotex
|
||||
bool getConstrainImageWidthEnabled() const;
|
||||
void setConstrainImageWidthEnabled(bool p_enabled);
|
||||
|
||||
bool getImageAlignCenterEnabled() const;
|
||||
void setImageAlignCenterEnabled(bool p_enabled);
|
||||
|
||||
bool getConstrainInplacePreviewWidthEnabled() const;
|
||||
void setConstrainInplacePreviewWidthEnabled(bool p_enabled);
|
||||
|
||||
@ -113,6 +128,9 @@ namespace vnotex
|
||||
bool getIndentFirstLineEnabled() const;
|
||||
void setIndentFirstLineEnabled(bool p_enabled);
|
||||
|
||||
bool getCodeBlockLineNumberEnabled() const;
|
||||
void setCodeBlockLineNumberEnabled(bool p_enabled);
|
||||
|
||||
bool getSmartTableEnabled() const;
|
||||
void setSmartTableEnabled(bool p_enabled);
|
||||
|
||||
@ -127,7 +145,15 @@ namespace vnotex
|
||||
InplacePreviewSources getInplacePreviewSources() const;
|
||||
void setInplacePreviewSources(InplacePreviewSources p_src);
|
||||
|
||||
EditViewMode getEditViewMode() const;
|
||||
void setEditViewMode(EditViewMode p_mode);
|
||||
|
||||
bool getRichPasteByDefaultEnabled() const;
|
||||
void setRichPasteByDefaultEnabled(bool p_enabled);
|
||||
|
||||
private:
|
||||
friend class MainConfig;
|
||||
|
||||
QString sectionNumberModeToString(SectionNumberMode p_mode) const;
|
||||
SectionNumberMode stringToSectionNumberMode(const QString &p_str) const;
|
||||
|
||||
@ -143,6 +169,9 @@ namespace vnotex
|
||||
QString inplacePreviewSourceToString(InplacePreviewSource p_src) const;
|
||||
InplacePreviewSource stringToInplacePreviewSource(const QString &p_str) const;
|
||||
|
||||
QString editViewModeToString(EditViewMode p_mode) const;
|
||||
EditViewMode stringToEditViewMode(const QString &p_str) const;
|
||||
|
||||
QSharedPointer<TextEditorConfig> m_textEditorConfig;
|
||||
|
||||
WebResource m_viewerResource;
|
||||
@ -159,11 +188,17 @@ namespace vnotex
|
||||
// %1: the format to render in.
|
||||
QString m_plantUmlCommand;
|
||||
|
||||
// PlantUml Web service to override that in plantuml.js file.
|
||||
QString m_plantUmlWebService;
|
||||
|
||||
bool m_webGraphviz = true;
|
||||
|
||||
// Graphviz executable file.
|
||||
QString m_graphvizExe;
|
||||
|
||||
// MathJax script to override that in mathjax.js file.
|
||||
QString m_mathJaxScript;
|
||||
|
||||
// Whether prepend a dot in front of the relative link, like images.
|
||||
bool m_prependDotInRelativeLink = false;
|
||||
|
||||
@ -185,6 +220,8 @@ namespace vnotex
|
||||
// Whether enable image width constraint.
|
||||
bool m_constrainImageWidthEnabled = true;
|
||||
|
||||
bool m_imageAlignCenterEnabled = false;
|
||||
|
||||
// Whether enable in-place preview width constraint.
|
||||
bool m_constrainInplacePreviewWidthEnabled = false;
|
||||
|
||||
@ -194,7 +231,7 @@ namespace vnotex
|
||||
bool m_fetchImagesInParseAndPaste = true;
|
||||
|
||||
// Whether protect from Cross-Site Scripting.
|
||||
bool m_protectFromXss = false;
|
||||
bool m_protectFromXss = true;
|
||||
|
||||
// Whether allow HTML tag in Markdown source.
|
||||
bool m_htmlTagEnabled = true;
|
||||
@ -208,6 +245,9 @@ namespace vnotex
|
||||
// Whether indent the first line of a paragraph.
|
||||
bool m_indentFirstLineEnabled = false;
|
||||
|
||||
// Whether enable code block line number in read mode.
|
||||
bool m_codeBlockLineNumberEnabled = true;
|
||||
|
||||
bool m_smartTableEnabled = true;
|
||||
|
||||
// Interval time to do smart table format.
|
||||
@ -220,6 +260,11 @@ namespace vnotex
|
||||
QString m_editorOverriddenFontFamily;
|
||||
|
||||
InplacePreviewSources m_inplacePreviewSources = InplacePreviewSource::NoInplacePreview;
|
||||
|
||||
// View mode in edit mode.
|
||||
EditViewMode m_editViewMode = EditViewMode::EditOnly;
|
||||
|
||||
bool m_richPasteByDefaultEnabled = true;
|
||||
};
|
||||
}
|
||||
|
||||
|
61
src/core/mindmapeditorconfig.cpp
Normal file
61
src/core/mindmapeditorconfig.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include "mindmapeditorconfig.h"
|
||||
|
||||
#include "mainconfig.h"
|
||||
|
||||
#define READSTR(key) readString(appObj, userObj, (key))
|
||||
#define READBOOL(key) readBool(appObj, userObj, (key))
|
||||
#define READINT(key) readInt(appObj, userObj, (key))
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
MindMapEditorConfig::MindMapEditorConfig(ConfigMgr *p_mgr, IConfig *p_topConfig)
|
||||
: IConfig(p_mgr, p_topConfig)
|
||||
{
|
||||
m_sessionName = QStringLiteral("mindmap_editor");
|
||||
}
|
||||
|
||||
void MindMapEditorConfig::init(const QJsonObject &p_app,
|
||||
const QJsonObject &p_user)
|
||||
{
|
||||
const auto appObj = p_app.value(m_sessionName).toObject();
|
||||
const auto userObj = p_user.value(m_sessionName).toObject();
|
||||
|
||||
loadEditorResource(appObj, userObj);
|
||||
}
|
||||
|
||||
QJsonObject MindMapEditorConfig::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("editor_resource")] = saveEditorResource();
|
||||
return obj;
|
||||
}
|
||||
|
||||
void MindMapEditorConfig::loadEditorResource(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
{
|
||||
const QString name(QStringLiteral("editor_resource"));
|
||||
|
||||
if (MainConfig::isVersionChanged()) {
|
||||
bool needOverride = p_app[QStringLiteral("override_editor_resource")].toBool();
|
||||
if (needOverride) {
|
||||
qInfo() << "override \"editor_resource\" in user configuration due to version change";
|
||||
m_editorResource.init(p_app[name].toObject());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_user.contains(name)) {
|
||||
m_editorResource.init(p_user[name].toObject());
|
||||
} else {
|
||||
m_editorResource.init(p_app[name].toObject());
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject MindMapEditorConfig::saveEditorResource() const
|
||||
{
|
||||
return m_editorResource.toJson();
|
||||
}
|
||||
|
||||
const WebResource &MindMapEditorConfig::getEditorResource() const
|
||||
{
|
||||
return m_editorResource;
|
||||
}
|
31
src/core/mindmapeditorconfig.h
Normal file
31
src/core/mindmapeditorconfig.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef MINDMAPEDITORCONFIG_H
|
||||
#define MINDMAPEDITORCONFIG_H
|
||||
|
||||
#include "iconfig.h"
|
||||
|
||||
#include "webresource.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class MindMapEditorConfig : public IConfig
|
||||
{
|
||||
public:
|
||||
MindMapEditorConfig(ConfigMgr *p_mgr, IConfig *p_topConfig);
|
||||
|
||||
void init(const QJsonObject &p_app, const QJsonObject &p_user) Q_DECL_OVERRIDE;
|
||||
|
||||
QJsonObject toJson() const Q_DECL_OVERRIDE;
|
||||
|
||||
const WebResource &getEditorResource() const;
|
||||
|
||||
private:
|
||||
friend class MainConfig;
|
||||
|
||||
void loadEditorResource(const QJsonObject &p_app, const QJsonObject &p_user);
|
||||
QJsonObject saveEditorResource() const;
|
||||
|
||||
WebResource m_editorResource;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MINDMAPEDITORCONFIG_H
|
@ -1,39 +1,76 @@
|
||||
#include "bundlenotebook.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <notebookconfigmgr/bundlenotebookconfigmgr.h>
|
||||
#include <notebookconfigmgr/notebookconfig.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <core/historymgr.h>
|
||||
#include <core/exception.h>
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
|
||||
#include "notebookdatabaseaccess.h"
|
||||
#include "notebooktagmgr.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
BundleNotebook::BundleNotebook(const NotebookParameters &p_paras,
|
||||
const QSharedPointer<NotebookConfig> &p_notebookConfig,
|
||||
QObject *p_parent)
|
||||
: Notebook(p_paras, p_parent)
|
||||
: Notebook(p_paras, p_parent),
|
||||
m_configVersion(p_notebookConfig->m_version),
|
||||
m_history(p_notebookConfig->m_history),
|
||||
m_tagGraph(p_notebookConfig->m_tagGraph),
|
||||
m_extraConfigs(p_notebookConfig->m_extraConfigs)
|
||||
{
|
||||
m_nextNodeId = p_notebookConfig->m_nextNodeId;
|
||||
m_history = p_notebookConfig->m_history;
|
||||
setupDatabase();
|
||||
}
|
||||
|
||||
BundleNotebook::~BundleNotebook()
|
||||
{
|
||||
m_dbAccess->close();
|
||||
}
|
||||
|
||||
BundleNotebookConfigMgr *BundleNotebook::getBundleNotebookConfigMgr() const
|
||||
{
|
||||
return dynamic_cast<BundleNotebookConfigMgr *>(getConfigMgr().data());
|
||||
return static_cast<BundleNotebookConfigMgr *>(getConfigMgr().data());
|
||||
}
|
||||
|
||||
ID BundleNotebook::getNextNodeId() const
|
||||
void BundleNotebook::setupDatabase()
|
||||
{
|
||||
return m_nextNodeId;
|
||||
auto dbPath = getBackend()->getFullPath(BundleNotebookConfigMgr::getDatabasePath());
|
||||
m_dbAccess = new NotebookDatabaseAccess(this, dbPath, this);
|
||||
}
|
||||
|
||||
ID BundleNotebook::getAndUpdateNextNodeId()
|
||||
void BundleNotebook::initializeInternal()
|
||||
{
|
||||
auto id = m_nextNodeId++;
|
||||
getBundleNotebookConfigMgr()->writeNotebookConfig();
|
||||
return id;
|
||||
initDatabase();
|
||||
|
||||
if (m_configVersion != getConfigMgr()->getCodeVersion()) {
|
||||
updateNotebookConfig();
|
||||
}
|
||||
}
|
||||
|
||||
void BundleNotebook::initDatabase()
|
||||
{
|
||||
m_dbAccess->initialize(m_configVersion);
|
||||
|
||||
if (m_dbAccess->isFresh()) {
|
||||
// For previous version notebook without DB, just ignore the node Id from config.
|
||||
int cnt = 0;
|
||||
fillNodeTableFromConfig(getRootNode().data(), m_configVersion < 2, cnt);
|
||||
qDebug() << "fillNodeTableFromConfig nodes count" << cnt;
|
||||
|
||||
fillTagTableFromTagGraph();
|
||||
|
||||
cnt = 0;
|
||||
fillTagTableFromConfig(getRootNode().data(), cnt);
|
||||
}
|
||||
|
||||
if (m_tagMgr) {
|
||||
m_tagMgr->update();
|
||||
}
|
||||
}
|
||||
|
||||
void BundleNotebook::updateNotebookConfig()
|
||||
@ -56,21 +93,33 @@ void BundleNotebook::remove()
|
||||
|
||||
// Remove notebook root folder if it is empty.
|
||||
if (!FileUtils::removeDirIfEmpty(getRootFolderAbsolutePath())) {
|
||||
qInfo() << QString("root folder of notebook (%1) is not empty and needs manual clean up")
|
||||
qInfo() << QStringLiteral("root folder of notebook (%1) is not empty and needs manual clean up")
|
||||
.arg(getRootFolderAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
HistoryI *BundleNotebook::history()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
const QVector<HistoryItem> &BundleNotebook::getHistory() const
|
||||
{
|
||||
return m_history;
|
||||
}
|
||||
|
||||
void BundleNotebook::removeHistory(const QString &p_itemPath)
|
||||
{
|
||||
HistoryMgr::removeHistoryItem(m_history, p_itemPath);
|
||||
|
||||
updateNotebookConfig();
|
||||
}
|
||||
|
||||
void BundleNotebook::addHistory(const HistoryItem &p_item)
|
||||
{
|
||||
HistoryItem item(p_item);
|
||||
item.m_path = getBackend()->getRelativePath(item.m_path);
|
||||
HistoryMgr::insertHistoryItem(m_history, p_item);
|
||||
HistoryMgr::insertHistoryItem(m_history, item);
|
||||
|
||||
updateNotebookConfig();
|
||||
}
|
||||
@ -81,3 +130,139 @@ void BundleNotebook::clearHistory()
|
||||
|
||||
updateNotebookConfig();
|
||||
}
|
||||
|
||||
const QJsonObject &BundleNotebook::getExtraConfigs() const
|
||||
{
|
||||
return m_extraConfigs;
|
||||
}
|
||||
|
||||
void BundleNotebook::setExtraConfig(const QString &p_key, const QJsonObject &p_obj)
|
||||
{
|
||||
m_extraConfigs[p_key] = p_obj;
|
||||
|
||||
updateNotebookConfig();
|
||||
}
|
||||
|
||||
void BundleNotebook::fillNodeTableFromConfig(Node *p_node, bool p_ignoreId, int &p_totalCnt)
|
||||
{
|
||||
bool ret = m_dbAccess->addNode(p_node, p_ignoreId);
|
||||
if (!ret) {
|
||||
qWarning() << "failed to add node to DB" << p_node->getName() << p_ignoreId;
|
||||
return;
|
||||
}
|
||||
|
||||
if (++p_totalCnt % 10) {
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
const auto &children = p_node->getChildrenRef();
|
||||
for (const auto &child : children) {
|
||||
fillNodeTableFromConfig(child.data(), p_ignoreId, p_totalCnt);
|
||||
}
|
||||
}
|
||||
|
||||
NotebookDatabaseAccess *BundleNotebook::getDatabaseAccess() const
|
||||
{
|
||||
return m_dbAccess;
|
||||
}
|
||||
|
||||
bool BundleNotebook::rebuildDatabase()
|
||||
{
|
||||
Q_ASSERT(m_dbAccess);
|
||||
m_dbAccess->close();
|
||||
|
||||
auto backend = getBackend();
|
||||
const auto dbPath = BundleNotebookConfigMgr::getDatabasePath();
|
||||
if (backend->exists(dbPath)) {
|
||||
try {
|
||||
backend->removeFile(dbPath);
|
||||
} catch (Exception &p_e) {
|
||||
qWarning() << "failed to delete database file" << dbPath << p_e.what();
|
||||
if (!m_dbAccess->open()) {
|
||||
qWarning() << "failed to open notebook database (restart is needed)";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_dbAccess->deleteLater();
|
||||
|
||||
setupDatabase();
|
||||
initDatabase();
|
||||
|
||||
emit tagsUpdated();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const QString &BundleNotebook::getTagGraph() const
|
||||
{
|
||||
return m_tagGraph;
|
||||
}
|
||||
|
||||
void BundleNotebook::updateTagGraph(const QString &p_tagGraph)
|
||||
{
|
||||
if (m_tagGraph == p_tagGraph) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_tagGraph = p_tagGraph;
|
||||
updateNotebookConfig();
|
||||
}
|
||||
|
||||
void BundleNotebook::fillTagTableFromTagGraph()
|
||||
{
|
||||
auto tagGraph = NotebookTagMgr::stringToTagGraph(m_tagGraph);
|
||||
for (const auto &tagPair : tagGraph) {
|
||||
if (!m_dbAccess->addTag(tagPair.m_parent)) {
|
||||
qWarning() << "failed to add tag to DB" << tagPair.m_parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!m_dbAccess->addTag(tagPair.m_child, tagPair.m_parent)) {
|
||||
qWarning() << "failed to add tag to DB" << tagPair.m_child;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
void BundleNotebook::fillTagTableFromConfig(Node *p_node, int &p_totalCnt)
|
||||
{
|
||||
// @p_node must already exists in node table.
|
||||
bool ret = m_dbAccess->updateNodeTags(p_node);
|
||||
if (!ret) {
|
||||
qWarning() << "failed to add tags of node to DB" << p_node->getName() << p_node->getTags();
|
||||
return;
|
||||
}
|
||||
|
||||
if (++p_totalCnt % 10) {
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
const auto &children = p_node->getChildrenRef();
|
||||
for (const auto &child : children) {
|
||||
fillTagTableFromConfig(child.data(), p_totalCnt);
|
||||
}
|
||||
}
|
||||
|
||||
NotebookTagMgr *BundleNotebook::getTagMgr() const
|
||||
{
|
||||
if (!m_tagMgr) {
|
||||
auto th = const_cast<BundleNotebook *>(this);
|
||||
th->m_tagMgr = new NotebookTagMgr(th);
|
||||
}
|
||||
|
||||
return m_tagMgr;
|
||||
}
|
||||
|
||||
TagI *BundleNotebook::tag()
|
||||
{
|
||||
return getTagMgr();
|
||||
}
|
||||
|
||||
int BundleNotebook::getConfigVersion() const
|
||||
{
|
||||
return m_configVersion;
|
||||
}
|
||||
|
@ -3,13 +3,17 @@
|
||||
|
||||
#include "notebook.h"
|
||||
#include "global.h"
|
||||
#include "historyi.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class BundleNotebookConfigMgr;
|
||||
class NotebookConfig;
|
||||
class NotebookDatabaseAccess;
|
||||
class NotebookTagMgr;
|
||||
|
||||
class BundleNotebook : public Notebook
|
||||
class BundleNotebook : public Notebook,
|
||||
public HistoryI
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@ -17,9 +21,7 @@ namespace vnotex
|
||||
const QSharedPointer<NotebookConfig> &p_notebookConfig,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
ID getNextNodeId() const Q_DECL_OVERRIDE;
|
||||
|
||||
ID getAndUpdateNextNodeId() Q_DECL_OVERRIDE;
|
||||
~BundleNotebook();
|
||||
|
||||
void updateNotebookConfig() Q_DECL_OVERRIDE;
|
||||
|
||||
@ -27,16 +29,63 @@ namespace vnotex
|
||||
|
||||
void remove() Q_DECL_OVERRIDE;
|
||||
|
||||
const QString &getTagGraph() const;
|
||||
void updateTagGraph(const QString &p_tagGraph);
|
||||
|
||||
const QJsonObject &getExtraConfigs() const Q_DECL_OVERRIDE;
|
||||
void setExtraConfig(const QString &p_key, const QJsonObject &p_obj) Q_DECL_OVERRIDE;
|
||||
|
||||
bool rebuildDatabase() Q_DECL_OVERRIDE;
|
||||
|
||||
NotebookDatabaseAccess *getDatabaseAccess() const;
|
||||
|
||||
TagI *tag() Q_DECL_OVERRIDE;
|
||||
|
||||
int getConfigVersion() const;
|
||||
|
||||
// HistoryI.
|
||||
public:
|
||||
HistoryI *history() Q_DECL_OVERRIDE;
|
||||
|
||||
const QVector<HistoryItem> &getHistory() const Q_DECL_OVERRIDE;
|
||||
|
||||
void removeHistory(const QString &p_itemPath) Q_DECL_OVERRIDE;
|
||||
|
||||
void addHistory(const HistoryItem &p_item) Q_DECL_OVERRIDE;
|
||||
|
||||
void clearHistory() Q_DECL_OVERRIDE;
|
||||
|
||||
protected:
|
||||
void initializeInternal() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
BundleNotebookConfigMgr *getBundleNotebookConfigMgr() const;
|
||||
|
||||
ID m_nextNodeId = 1;
|
||||
void setupDatabase();
|
||||
|
||||
void fillNodeTableFromConfig(Node *p_node, bool p_ignoreId, int &p_totalCnt);
|
||||
|
||||
void initDatabase();
|
||||
|
||||
void fillTagTableFromTagGraph();
|
||||
|
||||
void fillTagTableFromConfig(Node *p_node, int &p_totalCnt);
|
||||
|
||||
NotebookTagMgr *getTagMgr() const;
|
||||
|
||||
const int m_configVersion;
|
||||
|
||||
QVector<HistoryItem> m_history;
|
||||
|
||||
QString m_tagGraph;
|
||||
|
||||
QJsonObject m_extraConfigs;
|
||||
|
||||
// Managed by QObject.
|
||||
NotebookDatabaseAccess *m_dbAccess = nullptr;
|
||||
|
||||
// Managed by QObject.
|
||||
NotebookTagMgr *m_tagMgr = nullptr;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
@ -40,7 +40,7 @@ static void checkRootFolderForNewNotebook(const NotebookParameters &p_paras)
|
||||
qCritical() << msg;
|
||||
throw Exception(Exception::Type::InvalidPath, msg);
|
||||
} else if (p_paras.m_ensureEmptyRootFolder && !PathUtils::isEmptyDir(p_paras.m_rootFolderPath)) {
|
||||
QString msg = QString("local root folder must be empty: %1 (%2)")
|
||||
QString msg = QStringLiteral("local root folder must be empty: %1 (%2)")
|
||||
.arg(p_paras.m_rootFolderPath, PathUtils::absolutePath(p_paras.m_rootFolderPath));
|
||||
qCritical() << msg;
|
||||
throw Exception(Exception::Type::InvalidPath, msg);
|
||||
@ -88,7 +88,7 @@ void BundleNotebookFactory::checkParameters(const NotebookParameters &p_paras) c
|
||||
auto configMgr = dynamic_cast<BundleNotebookConfigMgr *>(p_paras.m_notebookConfigMgr.data());
|
||||
if (!configMgr) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("Invalid notebook configuration manager"));
|
||||
QStringLiteral("Invalid notebook configuration manager"));
|
||||
}
|
||||
}
|
||||
|
||||
|
25
src/core/notebook/historyi.h
Normal file
25
src/core/notebook/historyi.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef HISTORYI_H
|
||||
#define HISTORYI_H
|
||||
|
||||
#include <QVector>
|
||||
|
||||
#include <core/historyitem.h>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// History interface for notebook.
|
||||
class HistoryI
|
||||
{
|
||||
public:
|
||||
virtual ~HistoryI() = default;
|
||||
|
||||
virtual const QVector<HistoryItem> &getHistory() const = 0;
|
||||
|
||||
virtual void addHistory(const HistoryItem &p_item) = 0;
|
||||
|
||||
virtual void removeHistory(const QString &p_itemPath) = 0;
|
||||
|
||||
virtual void clearHistory() = 0;
|
||||
};
|
||||
}
|
||||
#endif // HISTORYI_H
|
@ -7,30 +7,31 @@
|
||||
#include <utils/pathutils.h>
|
||||
#include <core/exception.h>
|
||||
#include "notebook.h"
|
||||
#include "nodeparameters.h"
|
||||
#include <QRandomGenerator>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
Node::Node(Flags p_flags,
|
||||
ID p_id,
|
||||
const QString &p_name,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QDateTime &p_modifiedTimeUtc,
|
||||
const QStringList &p_tags,
|
||||
const QString &p_attachmentFolder,
|
||||
const NodeParameters &p_paras,
|
||||
Notebook *p_notebook,
|
||||
Node *p_parent)
|
||||
: m_notebook(p_notebook),
|
||||
m_loaded(true),
|
||||
m_flags(p_flags),
|
||||
m_id(p_id),
|
||||
m_id(p_paras.m_id),
|
||||
m_signature(p_paras.m_signature),
|
||||
m_name(p_name),
|
||||
m_createdTimeUtc(p_createdTimeUtc),
|
||||
m_modifiedTimeUtc(p_modifiedTimeUtc),
|
||||
m_tags(p_tags),
|
||||
m_attachmentFolder(p_attachmentFolder),
|
||||
m_createdTimeUtc(p_paras.m_createdTimeUtc),
|
||||
m_modifiedTimeUtc(p_paras.m_modifiedTimeUtc),
|
||||
m_tags(p_paras.m_tags),
|
||||
m_attachmentFolder(p_paras.m_attachmentFolder),
|
||||
m_parent(p_parent)
|
||||
{
|
||||
Q_ASSERT(m_notebook);
|
||||
|
||||
checkSignature();
|
||||
}
|
||||
|
||||
Node::Node(Flags p_flags,
|
||||
@ -54,19 +55,22 @@ bool Node::isLoaded() const
|
||||
return m_loaded;
|
||||
}
|
||||
|
||||
void Node::loadCompleteInfo(ID p_id,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QDateTime &p_modifiedTimeUtc,
|
||||
const QStringList &p_tags,
|
||||
void Node::loadCompleteInfo(const NodeParameters &p_paras,
|
||||
const QVector<QSharedPointer<Node>> &p_children)
|
||||
{
|
||||
Q_ASSERT(!m_loaded);
|
||||
m_id = p_id;
|
||||
m_createdTimeUtc = p_createdTimeUtc;
|
||||
m_modifiedTimeUtc = p_modifiedTimeUtc;
|
||||
m_tags = p_tags;
|
||||
|
||||
m_id = p_paras.m_id;
|
||||
m_signature = p_paras.m_signature;
|
||||
m_createdTimeUtc = p_paras.m_createdTimeUtc;
|
||||
m_modifiedTimeUtc = p_paras.m_modifiedTimeUtc;
|
||||
Q_ASSERT(p_paras.m_tags.isEmpty());
|
||||
Q_ASSERT(p_paras.m_attachmentFolder.isEmpty());
|
||||
|
||||
m_children = p_children;
|
||||
m_loaded = true;
|
||||
|
||||
checkSignature();
|
||||
}
|
||||
|
||||
bool Node::isRoot() const
|
||||
@ -106,6 +110,24 @@ bool Node::containsChild(const QSharedPointer<Node> &p_node) const
|
||||
return m_children.indexOf(p_node) != -1;
|
||||
}
|
||||
|
||||
bool Node::isLegalNameForNewChild(const QString &p_name) const
|
||||
{
|
||||
if (p_name.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto mgr = getConfigMgr();
|
||||
if (mgr->isBuiltInFile(this, p_name) || mgr->isBuiltInFolder(this, p_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (containsChild(p_name, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Node::findChild(const QString &p_name, bool p_caseSensitive) const
|
||||
{
|
||||
auto targetName = p_caseSensitive ? p_name : p_name.toLower();
|
||||
@ -149,6 +171,22 @@ ID Node::getId() const
|
||||
return m_id;
|
||||
}
|
||||
|
||||
void Node::updateId(ID p_id)
|
||||
{
|
||||
if (m_id == p_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_id = p_id;
|
||||
save();
|
||||
emit m_notebook->nodeUpdated(this);
|
||||
}
|
||||
|
||||
ID Node::getSignature() const
|
||||
{
|
||||
return m_signature;
|
||||
}
|
||||
|
||||
const QDateTime &Node::getCreatedTimeUtc() const
|
||||
{
|
||||
return m_createdTimeUtc;
|
||||
@ -226,6 +264,17 @@ const QStringList &Node::getTags() const
|
||||
return m_tags;
|
||||
}
|
||||
|
||||
void Node::updateTags(const QStringList &p_tags)
|
||||
{
|
||||
if (p_tags == m_tags) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_tags = p_tags;
|
||||
save();
|
||||
emit m_notebook->nodeUpdated(this);
|
||||
}
|
||||
|
||||
bool Node::isReadOnly() const
|
||||
{
|
||||
return m_flags & Flag::ReadOnly;
|
||||
@ -326,7 +375,7 @@ bool Node::canRename(const QString &p_newName) const
|
||||
}
|
||||
}
|
||||
|
||||
if (m_parent->containsChild(p_newName, false)) {
|
||||
if (!m_parent->isLegalNameForNewChild(p_newName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -413,3 +462,34 @@ bool Node::checkExists()
|
||||
}
|
||||
return after;
|
||||
}
|
||||
|
||||
QList<QSharedPointer<File>> Node::collectFiles()
|
||||
{
|
||||
QList<QSharedPointer<File>> files;
|
||||
|
||||
load();
|
||||
|
||||
if (hasContent()) {
|
||||
files.append(getContentFile());
|
||||
}
|
||||
|
||||
if (isContainer()) {
|
||||
for (const auto &child : m_children) {
|
||||
files.append(child->collectFiles());
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
ID Node::generateSignature()
|
||||
{
|
||||
return static_cast<ID>(QDateTime::currentDateTime().toSecsSinceEpoch() + (static_cast<qulonglong>(QRandomGenerator::global()->generate()) << 32));
|
||||
}
|
||||
|
||||
void Node::checkSignature()
|
||||
{
|
||||
if (m_signature == InvalidId) {
|
||||
m_signature = generateSignature();
|
||||
}
|
||||
}
|
||||
|
@ -16,15 +16,7 @@ namespace vnotex
|
||||
class INotebookBackend;
|
||||
class File;
|
||||
class ExternalNode;
|
||||
|
||||
// Used when add/new a node.
|
||||
struct NodeParameters
|
||||
{
|
||||
QDateTime m_createdTimeUtc = QDateTime::currentDateTimeUtc();
|
||||
QDateTime m_modifiedTimeUtc = QDateTime::currentDateTimeUtc();
|
||||
QString m_attachmentFolder;
|
||||
QStringList m_tags;
|
||||
};
|
||||
class NodeParameters;
|
||||
|
||||
// Node of notebook.
|
||||
class Node : public QEnableSharedFromThis<Node>
|
||||
@ -44,7 +36,6 @@ namespace vnotex
|
||||
|
||||
enum Use {
|
||||
Normal,
|
||||
RecycleBin,
|
||||
Root
|
||||
};
|
||||
|
||||
@ -52,12 +43,8 @@ namespace vnotex
|
||||
|
||||
// Constructor with all information loaded.
|
||||
Node(Flags p_flags,
|
||||
ID p_id,
|
||||
const QString &p_name,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QDateTime &p_modifiedTimeUtc,
|
||||
const QStringList &p_tags,
|
||||
const QString &p_attachmentFolder,
|
||||
const NodeParameters &p_paras,
|
||||
Notebook *p_notebook,
|
||||
Node *p_parent);
|
||||
|
||||
@ -102,6 +89,9 @@ namespace vnotex
|
||||
void setUse(Node::Use p_use);
|
||||
|
||||
ID getId() const;
|
||||
void updateId(ID p_id);
|
||||
|
||||
ID getSignature() const;
|
||||
|
||||
const QDateTime &getCreatedTimeUtc() const;
|
||||
|
||||
@ -124,6 +114,8 @@ namespace vnotex
|
||||
// Case sensitive.
|
||||
bool containsContentChild(const QString &p_name) const;
|
||||
|
||||
bool isLegalNameForNewChild(const QString &p_name) const;
|
||||
|
||||
void addChild(const QSharedPointer<Node> &p_node);
|
||||
|
||||
void insertChild(int p_idx, const QSharedPointer<Node> &p_node);
|
||||
@ -137,10 +129,11 @@ namespace vnotex
|
||||
|
||||
Notebook *getNotebook() const;
|
||||
|
||||
void load();
|
||||
void save();
|
||||
virtual void load();
|
||||
virtual void save();
|
||||
|
||||
const QStringList &getTags() const;
|
||||
void updateTags(const QStringList &p_tags);
|
||||
|
||||
const QString &getAttachmentFolder() const;
|
||||
void setAttachmentFolder(const QString &p_attachmentFolder);
|
||||
@ -165,10 +158,7 @@ namespace vnotex
|
||||
// Get File if this node has content.
|
||||
virtual QSharedPointer<File> getContentFile() = 0;
|
||||
|
||||
void loadCompleteInfo(ID p_id,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QDateTime &p_modifiedTimeUtc,
|
||||
const QStringList &p_tags,
|
||||
void loadCompleteInfo(const NodeParameters &p_paras,
|
||||
const QVector<QSharedPointer<Node>> &p_children);
|
||||
|
||||
INotebookConfigMgr *getConfigMgr() const;
|
||||
@ -179,20 +169,31 @@ namespace vnotex
|
||||
|
||||
void sortChildren(const QVector<int> &p_beforeIdx, const QVector<int> &p_afterIdx);
|
||||
|
||||
// Get content files recursively.
|
||||
QList<QSharedPointer<File>> collectFiles();
|
||||
|
||||
static bool isAncestor(const Node *p_ancestor, const Node *p_child);
|
||||
|
||||
static ID generateSignature();
|
||||
|
||||
protected:
|
||||
Notebook *m_notebook = nullptr;
|
||||
|
||||
private:
|
||||
bool m_loaded = false;
|
||||
|
||||
private:
|
||||
void checkSignature();
|
||||
|
||||
Flags m_flags = Flag::None;
|
||||
|
||||
Use m_use = Use::Normal;
|
||||
|
||||
ID m_id = InvalidId;
|
||||
|
||||
// A long random number created when the node is created.
|
||||
// Use to avoid conflicts of m_id.
|
||||
ID m_signature = InvalidId;
|
||||
|
||||
QString m_name;
|
||||
|
||||
QDateTime m_createdTimeUtc;
|
||||
|
8
src/core/notebook/nodeparameters.cpp
Normal file
8
src/core/notebook/nodeparameters.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
#include "nodeparameters.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
NodeParameters::NodeParameters(ID p_id)
|
||||
: m_id(p_id)
|
||||
{
|
||||
}
|
34
src/core/notebook/nodeparameters.h
Normal file
34
src/core/notebook/nodeparameters.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef NODEPARAMETERS_H
|
||||
#define NODEPARAMETERS_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QStringList>
|
||||
|
||||
#include <core/global.h>
|
||||
|
||||
#include "node.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class NodeParameters
|
||||
{
|
||||
public:
|
||||
NodeParameters() = default;
|
||||
|
||||
NodeParameters(ID p_id);
|
||||
|
||||
ID m_id = Node::InvalidId;
|
||||
|
||||
ID m_signature = Node::InvalidId;
|
||||
|
||||
QDateTime m_createdTimeUtc = QDateTime::currentDateTimeUtc();
|
||||
|
||||
QDateTime m_modifiedTimeUtc = QDateTime::currentDateTimeUtc();
|
||||
|
||||
QStringList m_tags;
|
||||
|
||||
QString m_attachmentFolder;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NODEPARAMETERS_H
|
@ -7,7 +7,8 @@
|
||||
#include <notebookconfigmgr/inotebookconfigmgr.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include "exception.h"
|
||||
#include <core/exception.h>
|
||||
#include "nodeparameters.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -15,6 +16,8 @@ const QString Notebook::c_defaultAttachmentFolder = QStringLiteral("vx_attachmen
|
||||
|
||||
const QString Notebook::c_defaultImageFolder = QStringLiteral("vx_images");
|
||||
|
||||
const QString Notebook::c_defaultRecycleBinFolder = QStringLiteral("vx_recycle_bin");
|
||||
|
||||
static vnotex::ID generateNotebookID()
|
||||
{
|
||||
static vnotex::ID id = Notebook::InvalidId;
|
||||
@ -43,13 +46,33 @@ Notebook::Notebook(const NotebookParameters &p_paras,
|
||||
if (m_attachmentFolder.isEmpty()) {
|
||||
m_attachmentFolder = c_defaultAttachmentFolder;
|
||||
}
|
||||
if (m_recycleBinFolder.isEmpty()) {
|
||||
m_recycleBinFolder = c_defaultRecycleBinFolder;
|
||||
}
|
||||
|
||||
m_configMgr->setNotebook(this);
|
||||
}
|
||||
|
||||
Notebook::Notebook(const QString &p_name, QObject *p_parent)
|
||||
: QObject(p_parent),
|
||||
m_name(p_name)
|
||||
{
|
||||
}
|
||||
|
||||
Notebook::~Notebook()
|
||||
{
|
||||
}
|
||||
|
||||
void Notebook::initialize()
|
||||
{
|
||||
if (m_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
initializeInternal();
|
||||
}
|
||||
|
||||
vnotex::ID Notebook::getId() const
|
||||
{
|
||||
return m_id;
|
||||
@ -113,6 +136,16 @@ QString Notebook::getRootFolderAbsolutePath() const
|
||||
return PathUtils::absolutePath(m_rootFolderPath);
|
||||
}
|
||||
|
||||
QString Notebook::getConfigFolderAbsolutePath() const
|
||||
{
|
||||
const auto &folderPath = m_configMgr->getConfigFolderPath();
|
||||
if (folderPath.isEmpty()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return getBackend()->getFullPath(folderPath);
|
||||
}
|
||||
|
||||
const QIcon &Notebook::getIcon() const
|
||||
{
|
||||
return m_icon;
|
||||
@ -133,6 +166,28 @@ const QString &Notebook::getAttachmentFolder() const
|
||||
return m_attachmentFolder;
|
||||
}
|
||||
|
||||
const QString &Notebook::getRecycleBinFolder() const
|
||||
{
|
||||
return m_recycleBinFolder;
|
||||
}
|
||||
|
||||
QString Notebook::getRecycleBinFolderAbsolutePath() const
|
||||
{
|
||||
if (QDir::isAbsolutePath(m_recycleBinFolder)) {
|
||||
if (!QFileInfo::exists(m_recycleBinFolder)) {
|
||||
QDir dir(m_recycleBinFolder);
|
||||
dir.mkpath(m_recycleBinFolder);
|
||||
}
|
||||
return m_recycleBinFolder;
|
||||
} else {
|
||||
auto folderPath = getBackend()->getFullPath(m_recycleBinFolder);
|
||||
if (!getBackend()->exists(m_recycleBinFolder)) {
|
||||
getBackend()->makePath(m_recycleBinFolder);
|
||||
}
|
||||
return folderPath;
|
||||
}
|
||||
}
|
||||
|
||||
const QSharedPointer<INotebookBackend> &Notebook::getBackend() const
|
||||
{
|
||||
return m_backend;
|
||||
@ -158,23 +213,6 @@ const QSharedPointer<Node> &Notebook::getRootNode() const
|
||||
return m_root;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Notebook::getRecycleBinNode() const
|
||||
{
|
||||
auto root = getRootNode();
|
||||
const auto &children = root->getChildrenRef();
|
||||
auto it = std::find_if(children.begin(),
|
||||
children.end(),
|
||||
[this](const QSharedPointer<Node> &p_node) {
|
||||
return isRecycleBinNode(p_node.data());
|
||||
});
|
||||
|
||||
if (it != children.end()) {
|
||||
return *it;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Notebook::newNode(Node *p_parent,
|
||||
Node::Flags p_flags,
|
||||
const QString &p_name,
|
||||
@ -216,7 +254,7 @@ QSharedPointer<Node> Notebook::copyNodeAsChildOf(const QSharedPointer<Node> &p_s
|
||||
|
||||
if (Node::isAncestor(p_src.data(), p_dest)) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("source (%1) is the ancestor of destination (%2)")
|
||||
QStringLiteral("source (%1) is the ancestor of destination (%2)")
|
||||
.arg(p_src->fetchPath(), p_dest->fetchPath()));
|
||||
return nullptr;
|
||||
}
|
||||
@ -241,27 +279,6 @@ void Notebook::removeNode(Node *p_node, bool p_force, bool p_configOnly)
|
||||
removeNode(p_node->sharedFromThis(), p_force, p_configOnly);
|
||||
}
|
||||
|
||||
bool Notebook::isRecycleBinNode(const Node *p_node) const
|
||||
{
|
||||
return p_node && p_node->getUse() == Node::Use::RecycleBin;
|
||||
}
|
||||
|
||||
bool Notebook::isNodeInRecycleBin(const Node *p_node) const
|
||||
{
|
||||
if (p_node) {
|
||||
p_node = p_node->getParent();
|
||||
while (p_node) {
|
||||
if (isRecycleBinNode(p_node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
p_node = p_node->getParent();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Notebook::moveNodeToRecycleBin(Node *p_node)
|
||||
{
|
||||
moveNodeToRecycleBin(p_node->sharedFromThis());
|
||||
@ -270,59 +287,41 @@ void Notebook::moveNodeToRecycleBin(Node *p_node)
|
||||
void Notebook::moveNodeToRecycleBin(const QSharedPointer<Node> &p_node)
|
||||
{
|
||||
Q_ASSERT(p_node && !p_node->isRoot());
|
||||
auto destNode = getOrCreateRecycleBinDateNode();
|
||||
copyNodeAsChildOf(p_node, destNode.data(), true);
|
||||
m_configMgr->removeNodeToFolder(p_node, getOrCreateRecycleBinDateFolder());
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Notebook::getOrCreateRecycleBinDateNode()
|
||||
QString Notebook::getOrCreateRecycleBinDateFolder()
|
||||
{
|
||||
// Name after date.
|
||||
auto dateNodeName = QDate::currentDate().toString(QStringLiteral("yyyyMMdd"));
|
||||
|
||||
auto recycleBinNode = getRecycleBinNode();
|
||||
auto dateNode = recycleBinNode->findChild(dateNodeName,
|
||||
FileUtils::isPlatformNameCaseSensitive());
|
||||
if (!dateNode) {
|
||||
// Create a date node.
|
||||
dateNode = newNode(recycleBinNode.data(), Node::Flag::Container, dateNodeName);
|
||||
auto dateFolderName = QDate::currentDate().toString(QStringLiteral("yyyyMMdd"));
|
||||
auto folderPath = PathUtils::concatenateFilePath(getRecycleBinFolder(), dateFolderName);
|
||||
if (QDir::isAbsolutePath(folderPath)) {
|
||||
qDebug() << "using absolute recycle bin folder" << folderPath;
|
||||
QDir dir(folderPath);
|
||||
if (dir.exists()) {
|
||||
dir.mkpath(folderPath);
|
||||
}
|
||||
} else {
|
||||
if (!getBackend()->exists(folderPath)) {
|
||||
getBackend()->makePath(folderPath);
|
||||
}
|
||||
}
|
||||
|
||||
return dateNode;
|
||||
}
|
||||
|
||||
void Notebook::emptyNode(const Node *p_node, bool p_force)
|
||||
{
|
||||
// Copy the children.
|
||||
auto children = p_node->getChildren();
|
||||
for (const auto &child : children) {
|
||||
removeNode(child, p_force);
|
||||
}
|
||||
return folderPath;
|
||||
}
|
||||
|
||||
void Notebook::moveFileToRecycleBin(const QString &p_filePath)
|
||||
{
|
||||
auto node = getOrCreateRecycleBinDateNode();
|
||||
auto destFilePath = PathUtils::concatenateFilePath(node->fetchPath(),
|
||||
PathUtils::fileName(p_filePath));
|
||||
auto destFilePath = PathUtils::concatenateFilePath(getOrCreateRecycleBinDateFolder(), PathUtils::fileName(p_filePath));
|
||||
destFilePath = getBackend()->renameIfExistsCaseInsensitive(destFilePath);
|
||||
m_backend->copyFile(p_filePath, destFilePath);
|
||||
|
||||
getBackend()->removeFile(p_filePath);
|
||||
|
||||
emit nodeUpdated(node.data());
|
||||
m_backend->copyFile(p_filePath, destFilePath, true);
|
||||
}
|
||||
|
||||
void Notebook::moveDirToRecycleBin(const QString &p_dirPath)
|
||||
{
|
||||
auto node = getOrCreateRecycleBinDateNode();
|
||||
auto destDirPath = PathUtils::concatenateFilePath(node->fetchPath(),
|
||||
PathUtils::fileName(p_dirPath));
|
||||
auto destDirPath = PathUtils::concatenateFilePath(getOrCreateRecycleBinDateFolder(), PathUtils::fileName(p_dirPath));
|
||||
destDirPath = getBackend()->renameIfExistsCaseInsensitive(destDirPath);
|
||||
m_backend->copyDir(p_dirPath, destDirPath);
|
||||
|
||||
getBackend()->removeDir(p_dirPath);
|
||||
|
||||
emit nodeUpdated(node.data());
|
||||
m_backend->copyDir(p_dirPath, destDirPath, true);
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Notebook::addAsNode(Node *p_parent,
|
||||
@ -355,3 +354,55 @@ void Notebook::reloadNodes()
|
||||
m_root.clear();
|
||||
getRootNode();
|
||||
}
|
||||
|
||||
QJsonObject Notebook::getExtraConfig(const QString &p_key) const
|
||||
{
|
||||
const auto &configs = getExtraConfigs();
|
||||
return configs.value(p_key).toObject();
|
||||
}
|
||||
|
||||
QList<QSharedPointer<File>> Notebook::collectFiles()
|
||||
{
|
||||
QList<QSharedPointer<File>> files;
|
||||
|
||||
auto rootNode = getRootNode();
|
||||
|
||||
const auto &children = rootNode->getChildrenRef();
|
||||
for (const auto &child : children) {
|
||||
if (child->getUse() != Node::Use::Normal) {
|
||||
continue;
|
||||
}
|
||||
files.append(child->collectFiles());
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
QStringList Notebook::scanAndImportExternalFiles()
|
||||
{
|
||||
return m_configMgr->scanAndImportExternalFiles(getRootNode().data());
|
||||
}
|
||||
|
||||
bool Notebook::rebuildDatabase()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HistoryI *Notebook::history()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TagI *Notebook::tag()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Notebook::emptyRecycleBin()
|
||||
{
|
||||
QDir dir(getRecycleBinFolderAbsolutePath());
|
||||
auto children = dir.entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
|
||||
for (const auto &child : children) {
|
||||
FileUtils::removeDir(dir.filePath(child));
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user