Compare commits

...

85 Commits

Author SHA1 Message Date
Le Tan
856d2597df release v3.19.2 2025-05-15 20:23:24 +08:00
Le Tan
44a0d75996 theme: fix toolbar expansion button style 2025-05-14 15:16:55 +08:00
Le Tan
62f6b7f3c5 theme: support hot-reload via --watch-themes cmd option 2025-05-13 22:10:00 +08:00
Le Tan
6150d327da fix macos build 2025-05-13 19:07:52 +08:00
Le Tan
522ccfecc4
fix macos deployment (#2610) 2025-05-11 19:56:24 +08:00
Le
4788ae4ccf add icons for macOS store 2025-04-24 23:17:19 +08:00
Le Tan
c3191e8f88 add privacy policy 2025-04-23 20:21:16 +08:00
Le Tan
4952c88b2e fix readme 2025-04-23 19:35:35 +08:00
Le Tan
ebd4489adf update welcome doc 2025-04-23 19:14:01 +08:00
Le Tan
d9aee037ad
code sign and notarization on macOS(#2605) 2025-04-23 15:27:13 +08:00
Le Tan
87e87619fb release v3.19.1 2025-04-17 09:48:02 +08:00
Le Tan
8494a714cd
fix button popup (#2603) 2025-04-16 22:29:56 +08:00
goodl3000
3ec78f4c36 !1 fix popup windows
* fix popup windows
2025-04-16 13:33:06 +00:00
Le Tan
a27485c021 add gitee project 2025-04-15 21:04:08 +08:00
Le Tan
713b98f29e release v3.19.0 2025-04-10 20:46:58 +08:00
Le Tan
82b0ec751d update vtextedit for VSCode-style shortcuts 2025-04-10 10:25:55 +08:00
Le Tan
a7348c0aa0 update vtextedit 2025-04-08 22:23:49 +08:00
Le Tan
85d2ee950d export: allow local file remote access 2025-04-07 23:23:29 +08:00
Le Tan
b0b51570b0 init stylesheet before mainwindow is shown 2025-04-02 22:00:29 +08:00
Le Tan
a5a9b32ea3
upgrade to Qt 6.8.3 (#2598)
* upgrade to Qt 6.8.3

* fix cache action

* use 2022 for 6.8

* fix vs

* fix linjux

* fix
2025-04-02 13:27:30 +08:00
Le Tan
ac20b7f80c fix mainwindow flashing issue 2025-04-01 21:54:23 +08:00
Le Tan
b462337824 fix build error on Linux 2025-02-11 16:04:48 +08:00
faveoled
bbb88ded57
Metainfo update (#2584) 2025-02-10 08:06:22 +08:00
Integral
0f0995d4fe
ci: update actions/upload-artifact to v4 (#2568) 2024-12-03 10:34:05 +08:00
Integral
0881dd581a
refactor: replace non-empty QString constructors with QStringLiteral() (#2563) 2024-12-02 21:09:57 +08:00
Le Tan
c4512bb69a change minimum macOS supported version 2024-08-13 14:44:17 +08:00
Le Tan
5e4a6fa72a release v3.18.2 2024-08-06 21:56:46 +08:00
Le Tan
5da9268aa7 fix open alert 2024-08-06 21:23:48 +08:00
Le Tan
f1af78573a
fix xss (#2531) 2024-07-22 22:36:38 +08:00
Le Tan
a7600fa7f7
codesign --remove-signature will cause broken binary (#2530)
* codesign --remove-signature will cause broken binary

* fix
2024-07-19 19:44:24 +08:00
Le Tan
78de724757 update xss whitelist 2024-07-18 23:20:32 +08:00
Le Tan
05078a7857 update xss whitelist 2024-07-18 22:58:29 +08:00
Le Tan
84d396f6c4 release v3.18.1 2024-07-10 22:23:14 +08:00
Le Tan
b2fb5b1664 add mark for exemption 2024-07-08 21:52:02 +08:00
Le Tan
3477469b66 check link before open 2024-07-07 22:51:07 +08:00
Le Tan
bed95b1757 update vtextedit 2024-07-07 22:07:46 +08:00
Le Tan
f83761d95f fix artifact suffix 2024-07-03 14:46:45 +08:00
Le Tan
84cb60d882 release v3.18.0 2024-07-01 22:51:24 +08:00
Suman Kumar Chakraborty
313764b533
Create SECURITY.md (#2517) 2024-06-28 14:52:14 +08:00
Le Tan
44b5251045 fix todo list with XSS 2024-06-27 21:48:59 +08:00
Le Tan
743e63edbf [markdown-it] support markdown-it-mark for mark == 2024-06-20 23:11:55 +08:00
Le Tan
74e20dcb3e turn on XSS protection by default 2024-06-19 23:11:13 +08:00
Le Tan
9a971860e0 upgrade markdown-it 2024-06-19 22:39:17 +08:00
Le Tan
b1a1bf05d2 update flowchart.js 2024-06-19 22:26:33 +08:00
Le Tan
06c712cb64 upgrade Mermaid.js to v10 2024-06-19 16:46:21 +08:00
Le Tan
84891fb2f2 update QHotkey lib to support Wayland 2024-06-17 22:54:24 +08:00
Le Tan
889026bece fix main window blinking issue caused by OpenGL on Qt6 2024-06-11 22:41:29 +08:00
Le Tan
2ebb210c57 fix QMenu delete crash issue in WebViewer 2024-06-11 22:13:56 +08:00
Le Tan
bfb0e5bd29
fix build break on Qt 6.7 (#2511) 2024-06-11 21:22:08 +08:00
Le Tan
61528de505
remove code signature (#2506) 2024-05-29 23:10:39 +08:00
Le Tan
f62a82364c update readme 2024-05-24 16:14:20 +08:00
Le Tan
f461c706d1
fix Linux build (#2505) 2024-05-24 16:09:33 +08:00
Le Tan
898c82ec8f
try fix macOS CI (#2499) 2024-05-23 23:09:41 +08:00
Le Tan
f5b69b73cf
fix CI (#2497) 2024-05-11 22:58:25 +08:00
Le Tan
5a01b4bb35
Fix app dir path (#2496)
* fix app data location

* fix
2024-05-11 21:46:26 +08:00
Le Tan
918aa15e74
fix Linux build (#2494)
Fix Linux build

TODO: fix application dir path locations with AppImage. Translations and vnote_extra.rcc does not work now.
2024-05-10 16:22:05 +08:00
Le Tan
522b37425f
try fix Win CI (#2492)
fix CI and openssl
2024-05-03 22:51:25 +08:00
Le Tan
a04968913c refine translations 2024-04-30 22:07:00 +08:00
Le Tan
3b755287e7 support windows packaging 2024-04-30 15:13:21 +08:00
Le Tan
25b556ae14 migrate to cmake 2024-04-24 22:39:13 +08:00
faveoled
daa2fe1785
AppStream metadata (#2474) 2024-01-18 10:54:29 +08:00
zz of NSFOCUS Security Team
30712745bc
fix rcc path in QT5 (#2454)
see: https://github.com/mesonbuild/meson/pull/9792

Co-authored-by: henices <henices@163.com>
2023-11-22 22:54:55 +08:00
Le Tan
15d14359d7
try fix win ci (#2434)
* try fix win ci

* remove win32

* add more modules

* fix ci

* try fix linux-ci

* fix linux ci

* try fix rcc

* fix rcc path

* tree

* add back qt 5

* use jom
2023-10-08 23:09:31 +08:00
Le Tan
75f7250ed1 fix 2023-09-27 23:30:51 +08:00
Le Tan
853e9c08e0 add back Qt5 2023-09-27 23:24:24 +08:00
Le Tan
2f6afb2f97 upgrade to Qt6 2023-09-17 17:17:15 +08:00
hello
cbd3956cdc
remove Qt 5 (#2423)
Co-authored-by: plopop <plopop@github.com>
2023-09-15 00:09:19 +08:00
Le Tan
4ab2033a81 release v3.17.0 2023-08-27 23:03:19 +08:00
chendapao
e8fe0726ff
QuickAccess: add quick note (#2373)
* feature_quick_create_note

* adj name and complete quick create

* del unused

* del unused

* adj name

* adj to const

* adj name 2

* adj

* fix

---------

Co-authored-by: Le Tan <tamlokveer@gmail.com>
2023-08-24 22:51:17 +08:00
chendapao
f773bc0348
Merge pull request #2387 from dbkuaizi/hotifx-嵌套代码块样式问题修复
嵌套代码块样式问题修复
2023-05-08 10:36:21 +08:00
dbkuaizi
d208d4f9a7 嵌套代码块样式问题修复 2023-05-07 16:29:27 +08:00
Le Tan
a5f6d5a142 upgrade Mermaid and Flowchart.js 2023-05-04 22:09:02 +08:00
Le Tan
cfce10b0ca
fix ubuntu ci (#2383) 2023-05-01 21:15:14 +08:00
两双筷子
2ccfb5ca9f
增加标题层级显示功能 (#2381) 2023-04-30 21:47:08 +08:00
Le Tan
c7f471cb8f refine navigation mode 2023-04-13 22:01:28 +08:00
chendapao
c255cd54ed
feature/support_insert_multiple_mimedata (#2339)
* feature/support_insert_multiple_mimedata

* format

* reset

* adj

* adj isProcess

* adj func name

* adj logic and name
2023-02-03 10:53:29 +08:00
chendapao
075380749c
fix/pdf_name_ascii (#2341)
* fix/pdf_name_ascii

* adj

* Update src/widgets/pdfviewwindow.cpp

---------

Co-authored-by: Le Tan <tamlokveer@gmail.com>
2023-01-29 22:55:54 +08:00
两双筷子
f1a03554f0
优化分割线样式 (#2342) 2023-01-28 07:41:08 +08:00
Jachin
9353c9ef40
Fix mermaid display problem in the editing state. (#2334) 2023-01-16 21:54:46 +08:00
chendapao
d664c5a1c6
feature/adj_history_open_use_conf (#2330) 2023-01-11 18:32:21 +08:00
chendapao
d499b0d7be
feature/add_treewidget_enter_event_compat (#2323)
* feature/add_treewidget_enter_event_compat

* restore
2023-01-06 10:27:58 +08:00
chendapao
f764fec458
fix/selectdialog_QListWidget_to_ListWidget (#2320) 2023-01-04 14:40:36 +08:00
Le Tan
0cb502520f PdfViewer: set useWorkerFetch to false 2023-01-01 09:47:18 +08:00
Le Tan
b4e38409a9 release v3.16.0 2023-01-01 09:47:18 +08:00
Le Tan
03ffb12ff1 refine mindmap 2023-01-01 09:47:18 +08:00
245 changed files with 5981 additions and 18420 deletions

View File

@ -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.16.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,83 +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@v1 # not v2!
uses: actions/cache@v4
with:
path: ../Qt
key: ${{ runner.os }}-QtCache-5.15
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.15.2
version: 6.8.3
target: desktop
modules: qtwebengine
tools: tools_openssl_x64,1.1.1-4,qt.tools.openssl.gcc_64
cached: ${{ steps.cache-qt.outputs.cache-hit }}
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 --no-check-certificate -c https://versaweb.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
@ -123,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'
@ -144,7 +153,7 @@ 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
@ -153,7 +162,7 @@ jobs:
uses: ncipollo/release-action@v1.11.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
artifacts: ${{runner.workspace}}/build/vnote-linux-x64_v${{env.VNOTE_VER}}.AppImage
artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-linux-x64.AppImage
commit: master
tag: v${{env.VNOTE_VER}}
allowUpdates: true

View File

@ -8,27 +8,33 @@ 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.16.0
VNOTE_VER: 3.19.2
CMAKE_VER: 3.24.3
jobs:
build:
environment: Mac-code-sign
name: Build On MacOS
runs-on: macos-latest
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
config:
# - {
# name: "Build Qt 5.12",
# qt: 5.12.12
# }
- {
name: "Build Qt 5.15",
qt: 5.15.2
}
- 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.
@ -44,29 +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 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@v1 # not v2!
uses: actions/cache@v4
with:
path: ../Qt
key: ${{ runner.os }}-QtCache-${{ matrix.config.qt }}
path: ${{runner.workspace}}/Qt
key: ${{ runner.os }}-QtCache-6.8
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.config.qt }}
version: ${{matrix.config.qt}}
target: desktop
modules: qtwebengine
cached: ${{ steps.cache-qt.outputs.cache-hit }}
setup-python: 'false'
modules: 'qtwebengine qtwebchannel qtpositioning qtpdf qtimageformats qt5compat qtserialport'
cache: 'true'
- name: Create Build Dir
run: mkdir build
@ -75,77 +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
# 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
# Remove unused sql drivers
rm vnote.app/Contents/Plugins/sqldrivers/libqsqlodbc.dylib
rm vnote.app/Contents/Plugins/sqldrivers/libqsqlpsql.dylib
# 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"
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
python ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess $Qt5_Dir
popd
# 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 }}
popd
tree ./
working-directory: ${{runner.workspace}}/build
- name: Package Project
run: |
mkdir -p distrib/vnote
pushd distrib/vnote
mv ../../src/vnote.app ./
ln -s /Applications ./Applications
popd
sleep 60
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-qt${{matrix.config.qt}}.dmg
mv vnote-mac-x64_v${{env.VNOTE_VER}}.dmg vnote-mac-x64-qt${{matrix.config.qt}}_v${{env.VNOTE_VER}}.dmg
working-directory: ${{runner.workspace}}/build
- name: Archive Artifacts
uses: actions/upload-artifact@v2
- name: Archive DMG
uses: actions/upload-artifact@v4
with:
name: vnote-mac-x64-qt${{matrix.config.qt}}_v${{env.VNOTE_VER}}
path: ${{runner.workspace}}/build/vnote-mac-x64-qt${{matrix.config.qt}}_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'
@ -158,7 +219,7 @@ jobs:
uses: johnwbyrd/update-release@v1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: ${{runner.workspace}}/build/vnote-mac-x64-qt${{matrix.config.qt}}.dmg
files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
release: Continuous Build
tag: continuous-build
@ -167,7 +228,7 @@ jobs:
uses: ncipollo/release-action@v1.11.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
artifacts: ${{runner.workspace}}/build/vnote-mac-x64-qt${{matrix.config.qt}}_v${{env.VNOTE_VER}}.dmg
artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
commit: master
tag: v${{env.VNOTE_VER}}
allowUpdates: true

View File

@ -8,43 +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.16.0
VNOTE_VER: 3.19.2
jobs:
build:
name: ${{ matrix.config.name }}
runs-on: windows-2019
runs-on: windows-${{ matrix.config.vs_version }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
config:
# - {
# name: "Build On Win64 Qt 5.12",
# arch: win64_msvc2017_64,
# platform: x64,
# qt: 5.12.11
# }
# - {
# name: "Build On Win32 Qt 5.12",
# arch: win32_msvc2017,
# platform: x86,
# qt: 5.12.11
# }
- {
name: "Build On Win64 Qt 5.15",
arch: win64_msvc2019_64,
platform: x64,
- 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
}
- {
name: "Build On Win32 Qt 5.15",
arch: win32_msvc2019,
platform: x86,
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.
@ -61,94 +62,70 @@ jobs:
- name: Cache Qt
id: cache-qt
uses: actions/cache@v1 # not v2!
uses: actions/cache@v4
with:
path: ../Qt
key: ${{ runner.os }}-${{matrix.config.arch}}-QtCache-5.12-1
path: ${{runner.workspace}}/Qt
key: ${{runner.os}}-${{matrix.config.arch}}-QtCache-${{matrix.config.qt}}
- name: Install Qt Official Build
uses: jurplel/install-qt-action@v2
uses: jurplel/install-qt-action@v3
with:
version: ${{matrix.config.qt}}
target: desktop
arch: ${{matrix.config.arch}}
modules: qtwebengine
tools: tools_openssl_${{matrix.config.platform}},1.1.1-4,qt.tools.openssl.win_${{matrix.config.platform}}
cached: ${{ steps.cache-qt.outputs.cache-hit }}
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: |
qmake --version
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\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: |
qmake --version
mkdir "%DISTRIB_PATH%"
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\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.1j\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}}-qt${{matrix.config.qt}}.zip "%DISTRIB_PATH%"
copy vnote-win-${{matrix.config.platform}}-qt${{matrix.config.qt}}.zip vnote-win-${{matrix.config.platform}}-qt${{matrix.config.qt}}_v${{env.VNOTE_VER}}.zip
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: Package Installer
shell: cmd
run: |
copy "%GITHUB_WORKSPACE%\src\data\core\icons\vnote.ico" "%DISTRIB_PATH%\vnote.ico"
git clone https://github.com/vnotex/mkmsi.git mkmsi.git --depth=1
python .\mkmsi.git\mkmsi.py --auto-create qt --source-dir "%DISTRIB_PATH%" --wix-root "C:\Program Files (x86)\WiX Toolset v3.11" --merge-module "C:\Program Files (x86)\Common Files\Merge Modules\Microsoft_VC140_CRT_${{matrix.config.platform}}.msm" --add-desktop-shortcut --project-version ${{env.VNOTE_VER}} --license %GITHUB_WORKSPACE%\package\lgpl-3.0.rtf --executable vnote.exe --wix-banner %GITHUB_WORKSPACE%\package\wix_banner.png --wix-dialog %GITHUB_WORKSPACE%\package\wix_dialog.png VNote
move VNote.msi .\distrib\VNote-win-${{matrix.config.platform}}-qt${{matrix.config.qt}}_v${{env.VNOTE_VER}}.msi
del "%DISTRIB_PATH%\vnote.ico"
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}}-qt${{matrix.config.qt}}_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
uses: actions/upload-artifact@v2
if: ${{!startsWith(matrix.config.qt, '5.15')}}
uses: actions/upload-artifact@v4
with:
name: VNote-win-${{matrix.config.platform}}-qt${{matrix.config.qt}}_v${{env.VNOTE_VER}}.msi
path: ${{runner.workspace}}/build/distrib/VNote*.msi
env:
DISTRIB_PATH: ${{runner.workspace}}/build/distrib/vnote
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'
@ -162,24 +139,18 @@ jobs:
uses: johnwbyrd/update-release@v1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: ${{runner.workspace}}/build/vnote-win-${{matrix.config.platform}}-qt${{matrix.config.qt}}.zip
# glob not supported
files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}.zip
release: Continuous Build
tag: continuous-build
- name: Test
shell: cmd
run: |
dir .
dir .\distrib
working-directory: ${{runner.workspace}}/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 }}
# We need hard code here.
artifacts: "D:/a/vnote/build/vnote-win-${{matrix.config.platform}}-qt${{matrix.config.qt}}_v${{env.VNOTE_VER}}.zip"
# glob not supported
artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}
commit: master
tag: v${{env.VNOTE_VER}}
allowUpdates: true

4
.gitignore vendored
View File

@ -2,6 +2,7 @@
*.pro.user.*
.ccls
compile_commands.json
compile_commands.json.*
compile_flags.txt
.cache
.tasks
@ -11,3 +12,6 @@ GRTAGS
GTAGS
aqtinstall.log
tags
CMakeLists.txt.user
build
.DS_Store

26
CMakeLists.txt Normal file
View 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)

View File

@ -1,11 +1,13 @@
# VNote
![CI-Windows](https://github.com/vnotex/vnote/workflows/CI-Windows/badge.svg) ![CI-Linux](https://github.com/vnotex/vnote/workflows/CI-Linux/badge.svg) ![CI-MacOS](https://github.com/vnotex/vnote/workflows/CI-MacOS/badge.svg)
![CI-Windows](https://github.com/vnotex/vnote/actions/workflows/ci-win.yml/badge.svg?branch=master) ![CI-Linux](https://github.com/vnotex/vnote/actions/workflows/ci-linux.yml/badge.svg?branch=master) ![CI-MacOS](https://github.com/vnotex/vnote/actions/workflows/ci-macos.yml/badge.svg?branch=master)
[简体中文](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).
![VNote](pics/vnote.png)
@ -26,33 +28,14 @@ Continuous builds on `master` branch could be found at the [Continuous Build](ht
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/1Fou1flmBsQUQ8Qs9V_M6Aw) with the code `note`
* [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

View File

@ -1,11 +1,13 @@
# VNote
![CI-Windows](https://github.com/vnotex/vnote/workflows/CI-Windows/badge.svg) ![CI-Linux](https://github.com/vnotex/vnote/workflows/CI-Linux/badge.svg) ![CI-MacOS](https://github.com/vnotex/vnote/workflows/CI-MacOS/badge.svg)
![CI-Windows](https://github.com/vnotex/vnote/actions/workflows/ci-win.yml/badge.svg?branch=master) ![CI-Linux](https://github.com/vnotex/vnote/actions/workflows/ci-linux.yml/badge.svg?branch=master) ![CI-MacOS](https://github.com/vnotex/vnote/actions/workflows/ci-macos.yml/badge.svg?branch=master)
[English](README.md)
[Gitee托管项目](https://gitee.com/vnotex/vnote)
一个舒适的笔记平台!
更多信息,请访问[VNote主页](https://vnotex.github.io/vnote)或者[由Gitee托管的主页](https://tamlok.gitee.io/vnote)
更多信息,请访问[VNote主页](https://vnotex.github.io/vnote)。
![VNote](pics/vnote.png)
@ -26,33 +28,14 @@ VNote不是一个简单的Markdown编辑器。通过提供强大的笔记管理
最新的[稳定版本发布](https://github.com/vnotex/vnote/releases/latest)。其他下载选项:
* [天翼云盘](https://cloud.189.cn/t/Av67NvmEJVBv)
* [百度云盘](https://pan.baidu.com/s/1Fou1flmBsQUQ8Qs9V_M6Aw) 提取码`note`
* [百度云盘](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`
## 捐赠
有很多方式可以帮助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)
## 许可

15
SECURITY.md Normal file
View 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.

View File

@ -1,4 +1,34 @@
# 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`

2
libs/CMakeLists.txt Normal file
View File

@ -0,0 +1,2 @@
add_subdirectory(QHotkey)
add_subdirectory(vtextedit)

@ -1 +1 @@
Subproject commit 18ac011008d3ae55abc19233ba94fad1ea9801d8
Subproject commit 8abe0b2280533af57f423f5785acc4d9d4d73ab8

View File

@ -1,5 +0,0 @@
TEMPLATE = subdirs
SUBDIRS += \
vtextedit \
QHotkey

@ -1 +1 @@
Subproject commit 398ec0f8523d9c74a44cb746b5a1017741521e48
Subproject commit 50b1421793af3882ddc62ad4e6cf5537e1d7906f

View 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>

24
package/entitlements.xml Normal file
View 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>

2
package/qt.conf Normal file
View File

@ -0,0 +1,2 @@
[Platforms]
WindowsArguments = fontengine=freetype

26
privacy_policy.md Normal file
View 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.

View File

@ -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):
@ -20,6 +26,10 @@ for line in fileinput.input(['.github/workflows/ci-win.yml', '.github/workflows/
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='')

179
src/CMakeLists.txt Normal file
View 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)

View 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})

View 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
View 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)

View File

@ -2,6 +2,11 @@
#include <QFileOpenEvent>
#include <QDebug>
#include <QDir>
#include <QStyle>
#include <QFileSystemWatcher>
#include <QTimer>
#include <core/vnotex.h>
using namespace vnotex;
@ -10,6 +15,53 @@ Application::Application(int &p_argc, char **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.

View File

@ -1,8 +1,10 @@
#ifndef APPLICATION_H
#define APPLICATION_H
#include <QApplication>
class QFileSystemWatcher;
class QTimer;
namespace vnotex
{
class Application : public QApplication
@ -11,11 +13,21 @@ namespace vnotex
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;
};
}

View File

@ -22,6 +22,12 @@ 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.
{
@ -63,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;
}

View File

@ -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
View 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}
)

View File

@ -358,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()
@ -373,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);
}

View File

@ -1,31 +0,0 @@
SOURCES += \
$$PWD/buffer.cpp \
$$PWD/bufferprovider.cpp \
$$PWD/filebufferprovider.cpp \
$$PWD/markdownbuffer.cpp \
$$PWD/markdownbufferfactory.cpp \
$$PWD/filetypehelper.cpp \
$$PWD/mindmapbuffer.cpp \
$$PWD/mindmapbufferfactory.cpp \
$$PWD/nodebufferprovider.cpp \
$$PWD/pdfbuffer.cpp \
$$PWD/pdfbufferfactory.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/mindmapbuffer.h \
$$PWD/mindmapbufferfactory.h \
$$PWD/nodebufferprovider.h \
$$PWD/pdfbuffer.h \
$$PWD/pdfbufferfactory.h \
$$PWD/textbuffer.h \
$$PWD/textbufferfactory.h \
$$PWD/urlbasedbufferprovider.h

View File

@ -75,7 +75,7 @@ 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;
@ -133,7 +133,7 @@ void BufferMgr::open(const QString &p_filePath, const QSharedPointer<FileOpenPar
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));

View File

@ -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;
}

View File

@ -191,12 +191,11 @@ bool 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);
@ -571,6 +570,38 @@ 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."))) {

View File

@ -120,6 +120,8 @@ namespace vnotex
static QString getApplicationVersion();
static void initAppPrefixPath();
static void initForUnitTest();
static const QString c_orgName;

View File

@ -1,76 +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/global.cpp \
$$PWD/historyitem.cpp \
$$PWD/historymgr.cpp \
$$PWD/htmltemplatehelper.cpp \
$$PWD/logger.cpp \
$$PWD/mainconfig.cpp \
$$PWD/markdowneditorconfig.cpp \
$$PWD/mindmapeditorconfig.cpp \
$$PWD/pdfviewerconfig.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/mindmapeditorconfig.h \
$$PWD/noncopyable.h \
$$PWD/pdfviewerconfig.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

View File

@ -22,6 +22,7 @@ namespace vnotex
ExpandContentArea,
Settings,
NewNote,
NewQuickNote,
NewFolder,
CloseTab,
CloseAllTabs,

View File

@ -47,46 +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 QString("FileExistsOnCreate");
return QStringLiteral("FileExistsOnCreate");
case Type::DirExistsOnCreate:
return QString("DirExistsOnCreate");
return QStringLiteral("DirExistsOnCreate");
case Type::InvalidArgument:
return QString("InvalidArgument");
return QStringLiteral("InvalidArgument");
}
return QString::number(static_cast<int>(p_type));

View File

@ -25,27 +25,27 @@ HtmlTemplateHelper::Template HtmlTemplateHelper::s_mindMapEditorTemplate;
QString MarkdownWebGlobalOptions::toJavascriptObject() const
{
return QStringLiteral("window.vxOptions = {\n")
+ QString("webPlantUml: %1,\n").arg(Utils::boolToString(m_webPlantUml))
+ QString("plantUmlWebService: '%1',\n").arg(m_plantUmlWebService)
+ QString("webGraphviz: %1,\n").arg(Utils::boolToString(m_webGraphviz))
+ QString("mathJaxScript: '%1',\n").arg(m_mathJaxScript)
+ QString("constrainImageWidthEnabled: %1,\n").arg(Utils::boolToString(m_constrainImageWidthEnabled))
+ QString("imageAlignCenterEnabled: %1,\n").arg(Utils::boolToString(m_imageAlignCenterEnabled))
+ 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("codeBlockLineNumberEnabled: %1,\n").arg(Utils::boolToString(m_codeBlockLineNumberEnabled))
+ 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("removeCodeToolBarEnabled: %1,\n").arg(Utils::boolToString(m_removeCodeToolBarEnabled))
+ 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("}");
}
@ -83,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)
@ -92,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)
@ -300,7 +300,7 @@ 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)));
}
}

View File

@ -6,6 +6,7 @@
#include <QJsonArray>
#include <QBitArray>
#include <QDataStream>
#include <QIODevice>
namespace vnotex
{

View File

@ -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
@ -76,43 +84,43 @@ void Logger::log(QtMsgType p_type, const QMessageLogContext &p_context, const QS
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();
break;
}
fflush(stderr);
#endif
}

View File

@ -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;
};
}

View File

@ -231,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;

View File

@ -93,7 +93,7 @@ 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());
}
}

View File

@ -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"));
}
}

View File

@ -8,6 +8,7 @@
#include <core/exception.h>
#include "notebook.h"
#include "nodeparameters.h"
#include <QRandomGenerator>
using namespace vnotex;
@ -483,7 +484,7 @@ QList<QSharedPointer<File>> Node::collectFiles()
ID Node::generateSignature()
{
return static_cast<ID>(QDateTime::currentDateTime().toSecsSinceEpoch() + (static_cast<qulonglong>(qrand()) << 32));
return static_cast<ID>(QDateTime::currentDateTime().toSecsSinceEpoch() + (static_cast<qulonglong>(QRandomGenerator::global()->generate()) << 32));
}
void Node::checkSignature()

View File

@ -254,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;
}

View File

@ -1,30 +0,0 @@
SOURCES += \
$$PWD/externalnode.cpp \
$$PWD/nodeparameters.cpp \
$$PWD/notebook.cpp \
$$PWD/bundlenotebookfactory.cpp \
$$PWD/notebookdatabaseaccess.cpp \
$$PWD/notebookparameters.cpp \
$$PWD/bundlenotebook.cpp \
$$PWD/node.cpp \
$$PWD/notebooktagmgr.cpp \
$$PWD/tag.cpp \
$$PWD/vxnode.cpp \
$$PWD/vxnodefile.cpp
HEADERS += \
$$PWD/externalnode.h \
$$PWD/historyi.h \
$$PWD/nodeparameters.h \
$$PWD/notebook.h \
$$PWD/inotebookfactory.h \
$$PWD/bundlenotebookfactory.h \
$$PWD/notebookdatabaseaccess.h \
$$PWD/notebookparameters.h \
$$PWD/bundlenotebook.h \
$$PWD/node.h \
$$PWD/notebooktagmgr.h \
$$PWD/tag.h \
$$PWD/tagi.h \
$$PWD/vxnode.h \
$$PWD/vxnodefile.h

View File

@ -30,7 +30,7 @@ bool NotebookDatabaseAccess::open()
auto db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), m_connectionName);
db.setDatabaseName(m_databaseFile);
if (!db.open()) {
qWarning() << QString("failed to open notebook database (%1) (%2)").arg(m_databaseFile, db.lastError().text());
qWarning() << QStringLiteral("failed to open notebook database (%1) (%2)").arg(m_databaseFile, db.lastError().text());
return false;
}
@ -71,35 +71,35 @@ void NotebookDatabaseAccess::setupTables(QSqlDatabase &p_db, int p_configVersion
if (m_fresh) {
// Node.
bool ret = query.exec(QString("CREATE TABLE %1 (\n"
bool ret = query.exec(QStringLiteral("CREATE TABLE %1 (\n"
" id INTEGER PRIMARY KEY,\n"
" name TEXT NOT NULL,\n"
" signature INTEGER NOT NULL,\n"
" parent_id INTEGER NULL REFERENCES %1(id) ON DELETE CASCADE ON UPDATE CASCADE)\n").arg(c_nodeTableName));
if (!ret) {
qWarning() << QString("failed to create database table (%1) (%2)").arg(c_nodeTableName, query.lastError().text());
qWarning() << QStringLiteral("failed to create database table (%1) (%2)").arg(c_nodeTableName, query.lastError().text());
m_valid = false;
return;
}
// Tag.
ret = query.exec(QString("CREATE TABLE %1 (\n"
ret = query.exec(QStringLiteral("CREATE TABLE %1 (\n"
" name TEXT PRIMARY KEY,\n"
" parent_name TEXT NULL REFERENCES %1(name) ON DELETE CASCADE ON UPDATE CASCADE) WITHOUT ROWID\n").arg(c_tagTableName));
if (!ret) {
qWarning() << QString("failed to create database table (%1) (%2)").arg(c_tagTableName, query.lastError().text());
qWarning() << QStringLiteral("failed to create database table (%1) (%2)").arg(c_tagTableName, query.lastError().text());
m_valid = false;
return;
}
// Node_Tag.
ret = query.exec(QString("CREATE TABLE %1 (\n"
ret = query.exec(QStringLiteral("CREATE TABLE %1 (\n"
" node_id INTEGER REFERENCES %2(id) ON DELETE CASCADE ON UPDATE CASCADE,\n"
" tag_name TEXT REFERENCES %3(name) ON DELETE CASCADE ON UPDATE CASCADE)\n").arg(c_nodeTagTableName,
c_nodeTableName,
c_tagTableName));
if (!ret) {
qWarning() << QString("failed to create database table (%1) (%2)").arg(c_nodeTagTableName, query.lastError().text());
qWarning() << QStringLiteral("failed to create database table (%1) (%2)").arg(c_nodeTagTableName, query.lastError().text());
m_valid = false;
return;
}
@ -130,7 +130,7 @@ bool NotebookDatabaseAccess::addNode(Node *p_node, bool p_ignoreId)
auto db = getDatabase();
QSqlQuery query(db);
if (p_ignoreId) {
query.prepare(QString("INSERT INTO %1 (name, signature, parent_id)\n"
query.prepare(QStringLiteral("INSERT INTO %1 (name, signature, parent_id)\n"
" VALUES (:name, :signature, :parent_id)").arg(c_nodeTableName));
query.bindValue(":name", p_node->getName());
query.bindValue(":signature", p_node->getSignature());
@ -170,10 +170,10 @@ bool NotebookDatabaseAccess::addNode(Node *p_node, bool p_ignoreId)
}
if (useNewId) {
query.prepare(QString("INSERT INTO %1 (name, signature, parent_id)\n"
query.prepare(QStringLiteral("INSERT INTO %1 (name, signature, parent_id)\n"
" VALUES (:name, :signature, :parent_id)").arg(c_nodeTableName));
} else {
query.prepare(QString("INSERT INTO %1 (id, name, signature, parent_id)\n"
query.prepare(QStringLiteral("INSERT INTO %1 (id, name, signature, parent_id)\n"
" VALUES (:id, :name, :signature, :parent_id)").arg(c_nodeTableName));
query.bindValue(":id", p_node->getId());
}
@ -212,7 +212,7 @@ QSharedPointer<NotebookDatabaseAccess::NodeRecord> NotebookDatabaseAccess::query
{
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("SELECT id, name, signature, parent_id FROM %1 WHERE id = :id").arg(c_nodeTableName));
query.prepare(QStringLiteral("SELECT id, name, signature, parent_id FROM %1 WHERE id = :id").arg(c_nodeTableName));
query.bindValue(":id", p_id);
if (!query.exec()) {
qWarning() << "failed to query node" << query.executedQuery() << query.lastError().text();
@ -264,7 +264,7 @@ QStringList NotebookDatabaseAccess::queryNodeParentPath(ID p_id)
{
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("WITH RECURSIVE cte_parents(id, name, parent_id) AS (\n"
query.prepare(QStringLiteral("WITH RECURSIVE cte_parents(id, name, parent_id) AS (\n"
" SELECT node.id, node.name, node.parent_id\n"
" FROM %1 node\n"
" WHERE node.id = :id\n"
@ -315,7 +315,7 @@ bool NotebookDatabaseAccess::updateNode(const Node *p_node)
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("UPDATE %1\n"
query.prepare(QStringLiteral("UPDATE %1\n"
"SET name = :name,\n"
" signature = :signature,\n"
" parent_id = :parent_id\n"
@ -367,7 +367,7 @@ bool NotebookDatabaseAccess::removeNode(ID p_id)
{
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("DELETE FROM %1\n"
query.prepare(QStringLiteral("DELETE FROM %1\n"
"WHERE id = :id").arg(c_nodeTableName));
query.bindValue(":id", p_id);
if (!query.exec()) {
@ -455,7 +455,7 @@ bool NotebookDatabaseAccess::addTag(const QString &p_name, const QString &p_pare
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("INSERT INTO %1 (name, parent_name)\n"
query.prepare(QStringLiteral("INSERT INTO %1 (name, parent_name)\n"
" VALUES (:name, :parent_name)").arg(c_tagTableName));
query.bindValue(":name", p_name);
query.bindValue(":parent_name", p_parentName.isEmpty() ? QVariant() : p_parentName);
@ -473,7 +473,7 @@ QSharedPointer<NotebookDatabaseAccess::TagRecord> NotebookDatabaseAccess::queryT
{
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("SELECT name, parent_name FROM %1 WHERE name = :name").arg(c_tagTableName));
query.prepare(QStringLiteral("SELECT name, parent_name FROM %1 WHERE name = :name").arg(c_tagTableName));
query.bindValue(":name", p_name);
if (!query.exec()) {
qWarning() << "failed to query tag" << query.executedQuery() << query.lastError().text();
@ -494,7 +494,7 @@ bool NotebookDatabaseAccess::updateTagParent(const QString &p_name, const QStrin
{
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("UPDATE %1\n"
query.prepare(QStringLiteral("UPDATE %1\n"
"SET parent_name = :parent_name\n"
"WHERE name = :name").arg(c_tagTableName));
query.bindValue(":name", p_name);
@ -518,7 +518,7 @@ bool NotebookDatabaseAccess::renameTag(const QString &p_name, const QString &p_n
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("UPDATE %1\n"
query.prepare(QStringLiteral("UPDATE %1\n"
"SET name = :new_name\n"
"WHERE name = :name").arg(c_tagTableName));
query.bindValue(":name", p_name);
@ -537,7 +537,7 @@ bool NotebookDatabaseAccess::removeTag(const QString &p_name)
{
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("DELETE FROM %1\n"
query.prepare(QStringLiteral("DELETE FROM %1\n"
"WHERE name = :name").arg(c_tagTableName));
query.bindValue(":name", p_name);
if (!query.exec()) {
@ -560,7 +560,12 @@ bool NotebookDatabaseAccess::updateNodeTags(Node *p_node)
const auto &nodeTags = p_node->getTags();
{
const auto tags = QSet<QString>::fromList(queryNodeTags(p_node->getId()));
QStringList tagsList = queryNodeTags(p_node->getId());
QSet<QString> tags;
for (const auto &s : tagsList)
{
tags.insert(s);
}
if (tags.isEmpty() && nodeTags.isEmpty()) {
return true;
}
@ -598,7 +603,7 @@ QStringList NotebookDatabaseAccess::queryNodeTags(ID p_id)
{
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("SELECT tag_name FROM %1 WHERE node_id = :node_id").arg(c_nodeTagTableName));
query.prepare(QStringLiteral("SELECT tag_name FROM %1 WHERE node_id = :node_id").arg(c_nodeTagTableName));
query.bindValue(":node_id", p_id);
if (!query.exec()) {
qWarning() << "failed to query node's tags" << query.executedQuery() << query.lastError().text();
@ -616,7 +621,7 @@ bool NotebookDatabaseAccess::removeNodeTags(ID p_id)
{
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("DELETE FROM %1\n"
query.prepare(QStringLiteral("DELETE FROM %1\n"
"WHERE node_id = :node_id").arg(c_nodeTagTableName));
query.bindValue(":node_id", p_id);
if (!query.exec()) {
@ -636,7 +641,7 @@ bool NotebookDatabaseAccess::addNodeTags(ID p_id, const QStringList &p_tags)
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("INSERT INTO %1 (node_id, tag_name)\n"
query.prepare(QStringLiteral("INSERT INTO %1 (node_id, tag_name)\n"
" VALUES (?, ?)").arg(c_nodeTagTableName));
QVariantList ids;
@ -663,7 +668,7 @@ QList<ID> NotebookDatabaseAccess::queryTagNodes(const QString &p_tag)
QList<ID> nodes;
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("SELECT node_id FROM %1 WHERE tag_name = :tag_name").arg(c_nodeTagTableName));
query.prepare(QStringLiteral("SELECT node_id FROM %1 WHERE tag_name = :tag_name").arg(c_nodeTagTableName));
query.bindValue(":tag_name", p_tag);
if (!query.exec()) {
qWarning() << "failed to query nodes of tag" << query.executedQuery() << query.lastError().text();
@ -691,14 +696,14 @@ QList<ID> NotebookDatabaseAccess::queryTagNodesRecursive(const QString &p_tag)
}
}
return allIds.toList();
return allIds.values();
}
QStringList NotebookDatabaseAccess::queryTagAndChildren(const QString &p_tag)
{
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("WITH RECURSIVE cte_children(name, parent_name) AS (\n"
query.prepare(QStringLiteral("WITH RECURSIVE cte_children(name, parent_name) AS (\n"
" SELECT tag.name, tag.parent_name\n"
" FROM %1 tag\n"
" WHERE tag.name = :name\n"
@ -742,7 +747,7 @@ QStringList NotebookDatabaseAccess::getNodesOfTags(const QStringList &p_tags)
allIds.insert(id);
}
}
nodeIds = allIds.toList();
nodeIds = allIds.values();
}
for (const auto &id : nodeIds) {
@ -763,7 +768,7 @@ QList<NotebookDatabaseAccess::TagRecord> NotebookDatabaseAccess::getAllTags()
auto db = getDatabase();
QSqlQuery query(db);
query.prepare(QString("SELECT name, parent_name FROM %1 ORDER BY parent_name, name").arg(c_tagTableName));
query.prepare(QStringLiteral("SELECT name, parent_name FROM %1 ORDER BY parent_name, name").arg(c_tagTableName));
if (!query.exec()) {
qWarning() << "failed to query tags" << query.executedQuery() << query.lastError().text();
return ret;

View File

@ -11,7 +11,7 @@ void INotebookBackend::constrainPath(const QString &p_path) const
{
if (!PathUtils::pathContains(m_rootPath, p_path)) {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("path (%1) does not locate in root folder (%2)")
QStringLiteral("path (%1) does not locate in root folder (%2)")
.arg(p_path, m_rootPath));
}
}

View File

@ -52,7 +52,7 @@ void LocalNotebookBackend::makePath(const QString &p_dirPath)
QDir dir(getRootPath());
if (!dir.mkpath(p_dirPath)) {
Exception::throwOne(Exception::Type::FailToCreateDir,
QString("fail to create directory: %1").arg(p_dirPath));
QStringLiteral("fail to create directory: %1").arg(p_dirPath));
}
}

View File

@ -1,10 +0,0 @@
SOURCES += \
$$PWD/localnotebookbackend.cpp \
$$PWD/localnotebookbackendfactory.cpp \
$$PWD/inotebookbackend.cpp
HEADERS += \
$$PWD/inotebookbackend.h \
$$PWD/localnotebookbackend.h \
$$PWD/inotebookbackendfactory.h \
$$PWD/localnotebookbackendfactory.h

View File

@ -56,7 +56,7 @@ void NotebookConfig::fromJson(const QJsonObject &p_jobj)
|| !p_jobj.contains(QStringLiteral("version_controller"))
|| !p_jobj.contains(QStringLiteral("config_mgr"))) {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("failed to read notebook configuration from JSON (%1)").arg(QJsonObjectToString(p_jobj)));
QStringLiteral("failed to read notebook configuration from JSON (%1)").arg(QJsonObjectToString(p_jobj)));
return;
}

View File

@ -1,16 +0,0 @@
SOURCES += \
$$PWD/vxnodeconfig.cpp \
$$PWD/vxnotebookconfigmgr.cpp \
$$PWD/vxnotebookconfigmgrfactory.cpp \
$$PWD/inotebookconfigmgr.cpp \
$$PWD/notebookconfig.cpp \
$$PWD/bundlenotebookconfigmgr.cpp
HEADERS += \
$$PWD/inotebookconfigmgr.h \
$$PWD/vxnodeconfig.h \
$$PWD/vxnotebookconfigmgr.h \
$$PWD/inotebookconfigmgrfactory.h \
$$PWD/vxnotebookconfigmgrfactory.h \
$$PWD/notebookconfig.h \
$$PWD/bundlenotebookconfigmgr.h

View File

@ -126,12 +126,12 @@ QSharedPointer<NodeConfig> VXNotebookConfigMgr::readNodeConfig(const QString &p_
auto backend = getBackend();
if (!backend->exists(p_path)) {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("node path (%1) does not exist").arg(p_path));
QStringLiteral("node path (%1) does not exist").arg(p_path));
}
if (backend->isFile(p_path)) {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("node (%1) is a file node without config").arg(p_path));
QStringLiteral("node (%1) is a file node without config").arg(p_path));
} else {
auto configPath = PathUtils::concatenateFilePath(p_path, c_nodeConfigName);
auto data = backend->readFile(configPath);
@ -309,7 +309,7 @@ QSharedPointer<Node> VXNotebookConfigMgr::newFileNode(Node *p_parent,
if (getBackend()->childExistsCaseInsensitive(p_parent->fetchPath(), p_name)) {
// File already exists. Exception.
Exception::throwOne(Exception::Type::FileExistsOnCreate,
QString("file (%1) already exists when creating new node").arg(node->fetchPath()));
QStringLiteral("file (%1) already exists when creating new node").arg(node->fetchPath()));
return nullptr;
}
@ -345,7 +345,7 @@ QSharedPointer<Node> VXNotebookConfigMgr::newFolderNode(Node *p_parent,
if (getBackend()->childExistsCaseInsensitive(p_parent->fetchPath(), p_name)) {
// Dir already exists. Exception.
Exception::throwOne(Exception::Type::DirExistsOnCreate,
QString("dir (%1) already exists when creating new node").arg(node->fetchPath()));
QStringLiteral("dir (%1) already exists when creating new node").arg(node->fetchPath()));
return nullptr;
}
@ -461,7 +461,11 @@ void VXNotebookConfigMgr::addChildNode(Node *p_parent, const QSharedPointer<Node
QSharedPointer<Node> VXNotebookConfigMgr::loadNodeByPath(const QSharedPointer<Node> &p_root, const QString &p_relativePath)
{
auto p = PathUtils::cleanPath(p_relativePath);
auto paths = p.split('/', QString::SkipEmptyParts);
if (p == ".") {
return p_root;
}
auto paths = p.split('/', Qt::SkipEmptyParts);
auto node = p_root;
for (auto &pa : paths) {
// Find child @pa in @node.

View File

@ -108,7 +108,7 @@ QSharedPointer<INotebookBackend> NotebookMgr::createNotebookBackend(const QStrin
return factory->createNotebookBackend(p_rootFolderPath);
} else {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("failed to find notebook backend factory %1").arg(p_backendName));
QStringLiteral("failed to find notebook backend factory %1").arg(p_backendName));
}
return nullptr;
@ -121,7 +121,7 @@ QSharedPointer<IVersionController> NotebookMgr::createVersionController(const QS
return factory->createVersionController();
} else {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("failed to find version controller factory %1").arg(p_controllerName));
QStringLiteral("failed to find version controller factory %1").arg(p_controllerName));
}
return nullptr;
@ -135,7 +135,7 @@ QSharedPointer<INotebookConfigMgr> NotebookMgr::createNotebookConfigMgr(const QS
return factory->createNotebookConfigMgr(p_backend);
} else {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("failed to find notebook config manager factory %1").arg(p_mgrName));
QStringLiteral("failed to find notebook config manager factory %1").arg(p_mgrName));
}
return nullptr;
@ -171,7 +171,7 @@ QSharedPointer<Notebook> NotebookMgr::newNotebook(const QSharedPointer<NotebookP
auto factory = m_notebookServer->getItem(p_parameters->m_type);
if (!factory) {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("failed to find notebook factory %1").arg(p_parameters->m_type));
QStringLiteral("failed to find notebook factory %1").arg(p_parameters->m_type));
}
auto notebook = factory->newNotebook(*p_parameters);
@ -246,7 +246,7 @@ QSharedPointer<Notebook> NotebookMgr::readNotebookFromConfig(const SessionConfig
auto factory = m_notebookServer->getItem(p_item.m_type);
if (!factory) {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("failed to find notebook factory %1").arg(p_item.m_type));
QStringLiteral("failed to find notebook factory %1").arg(p_item.m_type));
}
auto backend = createNotebookBackend(p_item.m_backend, p_item.m_rootFolderPath);
@ -330,7 +330,7 @@ void NotebookMgr::closeNotebook(ID p_id)
emit notebooksUpdated();
setCurrentNotebookAfterUpdate();
qInfo() << QString("notebook %1 (%2) is closed").arg(notebookToClose->getName(),
qInfo() << QStringLiteral("notebook %1 (%2) is closed").arg(notebookToClose->getName(),
notebookToClose->getRootFolderPath());
}
@ -359,13 +359,13 @@ void NotebookMgr::removeNotebook(ID p_id)
try {
nbToRemove->remove();
} catch (Exception &p_e) {
qWarning() << QString("failed to remove notebook %1 (%2) (%3)").arg(nbToRemove->getName(),
qWarning() << QStringLiteral("failed to remove notebook %1 (%2) (%3)").arg(nbToRemove->getName(),
nbToRemove->getRootFolderPath(),
p_e.what());
throw;
}
qInfo() << QString("notebook %1 (%2) is removed").arg(nbToRemove->getName(),
qInfo() << QStringLiteral("notebook %1 (%2) is removed").arg(nbToRemove->getName(),
nbToRemove->getRootFolderPath());
}

View File

@ -38,6 +38,34 @@ QJsonObject SessionConfig::NotebookItem::toJson() const
return jobj;
}
bool SessionConfig::QuickNoteScheme::operator==(const QuickNoteScheme &p_other) const
{
return m_name == p_other.m_name &&
m_folderPath == p_other.m_folderPath &&
m_noteName == p_other.m_noteName &&
m_template == p_other.m_template;
}
void SessionConfig::QuickNoteScheme::fromJson(const QJsonObject &p_jobj)
{
m_name = p_jobj[QStringLiteral("name")].toString();
m_folderPath = p_jobj[QStringLiteral("folder_path")].toString();
m_noteName = p_jobj[QStringLiteral("note_name")].toString();
m_template = p_jobj[QStringLiteral("template")].toString();
}
QJsonObject SessionConfig::QuickNoteScheme::toJson() const
{
QJsonObject jobj;
jobj[QStringLiteral("name")] = m_name;
jobj[QStringLiteral("folder_path")] = m_folderPath;
jobj[QStringLiteral("note_name")] = m_noteName;
jobj[QStringLiteral("template")] = m_template;
return jobj;
}
void SessionConfig::ExternalProgram::fromJson(const QJsonObject &p_jobj)
{
m_name = p_jobj[QStringLiteral("name")].toString();
@ -59,7 +87,7 @@ QJsonObject SessionConfig::ExternalProgram::toJson() const
QString SessionConfig::ExternalProgram::fetchCommand(const QString &p_file) const
{
auto command(m_command);
command.replace(QStringLiteral("%1"), QString("\"%1\"").arg(p_file));
command.replace(QStringLiteral("%1"), QStringLiteral("\"%1\"").arg(p_file));
return command;
}
@ -97,6 +125,8 @@ void SessionConfig::init()
loadHistory(sessionJobj);
loadQuickNoteSchemes(sessionJobj);
if (MainConfig::isVersionChanged()) {
doVersionSpecificOverride();
}
@ -235,6 +265,7 @@ QJsonObject SessionConfig::toJson() const
writeByteArray(obj, QStringLiteral("notebook_explorer_session"), m_notebookExplorerSession);
obj[QStringLiteral("external_programs")] = saveExternalPrograms();
obj[QStringLiteral("history")] = saveHistory();
obj[QStringLiteral("quick_note_schemes")] = saveQuickNoteSchemes();
return obj;
}
@ -458,6 +489,24 @@ QJsonArray SessionConfig::saveExternalPrograms() const
return arr;
}
void SessionConfig::loadQuickNoteSchemes(const QJsonObject &p_session)
{
const auto arr = p_session.value(QStringLiteral("quick_note_schemes")).toArray();
m_quickNoteSchemes.resize(arr.size());
for (int i = 0; i < arr.size(); ++i) {
m_quickNoteSchemes[i].fromJson(arr[i].toObject());
}
}
QJsonArray SessionConfig::saveQuickNoteSchemes() const
{
QJsonArray arr;
for (const auto &scheme : m_quickNoteSchemes) {
arr.append(scheme.toJson());
}
return arr;
}
const QVector<SessionConfig::ExternalProgram> &SessionConfig::getExternalPrograms() const
{
return m_externalPrograms;
@ -541,3 +590,13 @@ QJsonObject SessionConfig::saveExportOption() const
return obj;
}
const QVector<SessionConfig::QuickNoteScheme> &SessionConfig::getQuickNoteSchemes() const
{
return m_quickNoteSchemes;
}
void SessionConfig::setQuickNoteSchemes(const QVector<QuickNoteScheme>& p_schemes)
{
updateConfig(m_quickNoteSchemes, p_schemes, this);
}

View File

@ -55,6 +55,25 @@ namespace vnotex
QByteArray m_locationListState;
};
struct QuickNoteScheme
{
bool operator==(const QuickNoteScheme &p_other) const;
void fromJson(const QJsonObject &p_jobj);
QJsonObject toJson() const;
QString m_name;
// Where to create the quick note.
QString m_folderPath;
// Name of the quick note. Snippet is supported.
QString m_noteName;
QString m_template;
};
enum OpenGL
{
None,
@ -149,6 +168,9 @@ namespace vnotex
void removeHistory(const QString &p_itemPath);
void clearHistory();
const QVector<QuickNoteScheme> &getQuickNoteSchemes() const;
void setQuickNoteSchemes(const QVector<QuickNoteScheme>& p_schemes);
private:
void loadCore(const QJsonObject &p_session);
@ -166,6 +188,10 @@ namespace vnotex
QJsonArray saveExternalPrograms() const;
void loadQuickNoteSchemes(const QJsonObject &p_session);
QJsonArray saveQuickNoteSchemes() const;
void doVersionSpecificOverride();
void loadHistory(const QJsonObject &p_session);
@ -215,7 +241,9 @@ namespace vnotex
QVector<HistoryItem> m_history;
// Default folder path to open for external media like images and files.
QString m_externalMediaDefaultPath;;
QString m_externalMediaDefaultPath;
QVector<QuickNoteScheme> m_quickNoteSchemes;
};
} // ns vnotex

View File

@ -2,6 +2,8 @@
#include <QDir>
#include <utils/fileutils.h>
#include "configmgr.h"
using namespace vnotex;
@ -20,5 +22,17 @@ QStringList TemplateMgr::getTemplates() const
QString TemplateMgr::getTemplateFilePath(const QString &p_name) const
{
if (p_name.isEmpty()) {
return QString();
}
return QDir(getTemplateFolder()).filePath(p_name);
}
QString TemplateMgr::getTemplateContent(const QString &p_name) const
{
const auto filePath = getTemplateFilePath(p_name);
if (filePath.isEmpty()) {
return QString();
}
return FileUtils::readTextFile(filePath);
}

View File

@ -24,6 +24,8 @@ namespace vnotex
QString getTemplateFilePath(const QString &p_name) const;
QString getTemplateContent(const QString &p_name) const;
private:
TemplateMgr() = default;
};

View File

@ -24,6 +24,11 @@ Theme::Theme(const QString &p_themeFolderPath,
{
}
QString vnotex::Theme::getThemeFolder() const
{
return m_themeFolderPath;
}
bool Theme::isValidThemeFolder(const QString &p_folder)
{
QDir dir(p_folder);
@ -50,12 +55,12 @@ QString Theme::getDisplayName(const QString &p_folder, const QString &p_locale)
if (!p_locale.isEmpty()) {
// Check full locale.
auto fullLocale = QString("%1_%2").arg(prefix, p_locale);
auto fullLocale = QStringLiteral("%1_%2").arg(prefix, p_locale);
if (metaObj.contains(fullLocale)) {
return metaObj.value(fullLocale).toString();
}
auto shortLocale = QString("%1_%2").arg(prefix, p_locale.split('_')[0]);
auto shortLocale = QStringLiteral("%1_%2").arg(prefix, p_locale.split('_')[0]);
if (metaObj.contains(shortLocale)) {
return metaObj.value(shortLocale).toString();
}
@ -258,7 +263,6 @@ QString Theme::fetchQtStyleSheet() const
translateStyleByPalette(m_palette, style);
translateUrlToAbsolute(m_themeFolderPath, style);
translateFontFamilyList(style);
translateScaledSize(WidgetUtils::calculateScaleFactor(), style);
return style;
}
@ -297,7 +301,7 @@ void Theme::translateUrlToAbsolute(const QString &p_basePath, QString &p_style)
const int urlCapturedIdx = 2;
QDir dir(p_basePath);
const int literalSize = QString("url(").size();
const int literalSize = QStringLiteral("url(").size();
int pos = 0;
QRegularExpressionMatch match;
while (pos < p_style.size()) {
@ -346,7 +350,7 @@ void Theme::translateFontFamilyList(QString &p_style)
family = "\"" + family + "\"";
}
auto newStr = QString("%1font-family: %2;").arg(match.captured(prefixCapturedIdx), family);
auto newStr = QStringLiteral("%1font-family: %2;").arg(match.captured(prefixCapturedIdx), family);
p_style.replace(idx, match.capturedLength(), newStr);
pos = idx + newStr.size();
} else {
@ -355,40 +359,6 @@ void Theme::translateFontFamilyList(QString &p_style)
}
}
void Theme::translateScaledSize(qreal p_factor, QString &p_style)
{
QRegularExpression scaleRe("(\\s|:)\\$([+-]?)(\\d+)(?=\\D)");
const int prefixCapturedIdx = 1;
const int signCapturedIdx = 2;
const int numCapturedIdx = 3;
qDebug() << "translateScaledSize of Qt style sheet" << p_factor;
int pos = 0;
QRegularExpressionMatch match;
while (pos < p_style.size()) {
int idx = p_style.indexOf(scaleRe, pos, &match);
if (idx == -1) {
break;
}
auto numStr = match.captured(numCapturedIdx);
bool ok = false;
int val = numStr.toInt(&ok);
if (!ok) {
pos = idx + match.capturedLength();
continue;
}
val = val * p_factor + 0.5;
auto newStr = QString("%1%2%3").arg(match.captured(prefixCapturedIdx),
match.captured(signCapturedIdx),
QString::number(val));
p_style.replace(idx, match.capturedLength(), newStr);
pos = idx + newStr.size();
}
}
QString Theme::paletteColor(const QString &p_name) const
{
auto val = findValueByKeyPath(m_palette, p_name).toString();

View File

@ -47,6 +47,8 @@ namespace vnotex
QString name() const;
QString getThemeFolder() const;
static bool isValidThemeFolder(const QString &p_folder);
static Theme *fromFolder(const QString &p_folder);
@ -113,8 +115,6 @@ namespace vnotex
// Thus we need to choose one available font from the list.
static void translateFontFamilyList(QString &p_style);
static void translateScaledSize(qreal p_factor, QString &p_style);
static QJsonObject readJsonFile(const QString &p_filePath);
static QJsonObject readPaletteFile(const QString &p_folder);

View File

@ -24,8 +24,6 @@ ThemeMgr::ThemeMgr(const QString &p_currentThemeName, QObject *p_parent)
loadAvailableThemes();
loadCurrentTheme(p_currentThemeName);
IconUtils::setDefaultIconForeground(paletteColor("base#icon#fg"), paletteColor("base#icon#disabled#fg"));
}
QString ThemeMgr::getIconFile(const QString &p_icon) const
@ -56,7 +54,7 @@ void ThemeMgr::loadAvailableThemes()
if (m_themes.isEmpty()) {
Exception::throwOne(Exception::Type::EssentialFileMissing,
QString("no available themes found in paths: %1").arg(s_searchPaths.join(QLatin1Char(';'))));
QStringLiteral("no available themes found in paths: %1").arg(s_searchPaths.join(QLatin1Char(';'))));
}
}
@ -91,6 +89,7 @@ const Theme &ThemeMgr::getCurrentTheme() const
void ThemeMgr::loadCurrentTheme(const QString &p_themeName)
{
m_currentTheme.reset();
auto themeFolder = findThemeFolder(p_themeName);
if (themeFolder.isNull()) {
qWarning() << "failed to locate theme" << p_themeName;
@ -104,6 +103,8 @@ void ThemeMgr::loadCurrentTheme(const QString &p_themeName)
qWarning() << "fall back to default theme" << defaultTheme;
m_currentTheme.reset(loadTheme(findThemeFolder(defaultTheme)));
}
IconUtils::setDefaultIconForeground(paletteColor("base#icon#fg"), paletteColor("base#icon#disabled#fg"));
}
Theme *ThemeMgr::loadTheme(const QString &p_themeFolder)
@ -211,6 +212,14 @@ QPixmap ThemeMgr::getThemePreview(const QString &p_name) const
void ThemeMgr::refresh()
{
loadAvailableThemes();
refreshCurrentTheme();
}
void vnotex::ThemeMgr::refreshCurrentTheme()
{
if (m_currentTheme) {
loadCurrentTheme(m_currentTheme->name());
}
}
void ThemeMgr::addWebStylesSearchPath(const QString &p_path)

View File

@ -60,10 +60,11 @@ namespace vnotex
const ThemeInfo *findTheme(const QString &p_name) const;
// Refresh the themes list.
// Won't affect current theme since we do not support changing theme real time for now.
// Refresh the themes list and reload current theme.
void refresh();
void refreshCurrentTheme();
// Return all web stylesheets available, including those from themes and web styles search paths.
// <DisplayName, FilePath>.
QVector<QPair<QString, QString>> getWebStyles() const;

View File

@ -1,11 +0,0 @@
SOURCES += \
$$PWD/dummyversioncontroller.cpp \
$$PWD/versioncontrollerserver.cpp \
$$PWD/dummyversioncontrollerfactory.cpp
HEADERS += \
$$PWD/iversioncontroller.h \
$$PWD/dummyversioncontroller.h \
$$PWD/versioncontrollerserver.h \
$$PWD/iversioncontrollerfactory.h \
$$PWD/dummyversioncontrollerfactory.h

View File

@ -79,6 +79,9 @@ namespace vnotex
// The handler should determine in which folder this note belongs to.
void newNoteRequested();
// Requested to new a quick note (maybe in current folder).
void newQuickNoteRequested();
// Requested to new a folder in current notebook.
void newFolderRequested();

View File

@ -16,28 +16,32 @@
<string>public.plain-text</string>
<string>net.daringfireball.markdown</string>
</array>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
</array>
<key>CFBundleName</key>
<string>VNote</string>
<key>CFBundleExecutable</key>
<string>vnote</string>
<string>VNote</string>
<key>CFBundleShortVersionString</key>
<string>3.16.0</string>
<string>3.19</string>
<key>CFBundleVersion</key>
<string>3.16.0.1</string>
<string>3.19.2</string>
<key>NSHumanReadableCopyright</key>
<string>Created by VNoteX</string>
<string>Distributed under LGPL-3.0 license. Copyright (c) 2025 app.vnote.fun</string>
<key>CFBundleIconFile</key>
<string>vnote.icns</string>
<key>CFBundleIdentifier</key>
<string>com.vnotex.vnote</string>
<string>fun.vnote.vnote</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string>
<key>LSMinimumSystemVersion</key>
<string>10.12</string>
<string>10.15</string>
<key>NOTE</key>
<string>Visit [GitHub VNote](https://github.com/vnotex/vnote) for details.</string>
<string>A pleasant note-taking platform</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>

View File

@ -95,15 +95,5 @@
<file>logo/vnote.png</file>
<file>logo/256x256/vnote.png</file>
<file>logo/vnote_mono.png</file>
<file>translations/qdialogbuttonbox_zh_CN.qm</file>
<file>translations/qwebengine_zh_CN.qm</file>
<file>translations/qt_zh_CN.qm</file>
<file>translations/vnote_zh_CN.qm</file>
<file>translations/qtbase_zh_CN.qm</file>
<file>translations/vtextedit_zh_CN.qm</file>
<file>translations/qt_ja.qm</file>
<file>translations/vnote_ja.qm</file>
<file>translations/qtbase_ja.qm</file>
<file>translations/vtextedit_ja.qm</file>
</qresource>
</RCC>

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>fun.vnote.app.VNote</id>
<name>VNote</name>
<summary>A pleasant note-taking platform</summary>
<developer_name>Le Tan</developer_name>
<metadata_license>CC0-1.0</metadata_license>
<project_license>LGPL-3.0-only</project_license>
<description>
<p>Simple notes management</p>
<ul>
<li>All plain text, no database</li>
<li>Self-contained notebooks, infinite levels of folders, plain notes</li>
<li>Tags and attachments</li>
<li>Explore and edit external files</li>
</ul>
<p>Own your data</p>
<ul>
<li>All files on your local disk</li>
<li>One notebook is exactly one directory</li>
<li>Work from anywhere seamlessly via third-party service of your choice</li>
</ul>
<p>Concentration</p>
<ul>
<li>In-place preview or side-by-side live preview</li>
<li>Focus on your note in both read and edit mode</li>
</ul>
<p>Pleasant Markdown experience</p>
<ul>
<li>Minimized gap between read and write of Markdown</li>
<li>Syntax highlights and in-place preview</li>
<li>Effective images management</li>
<li>Interactive outline</li>
<li>UML diagrams, flowcharts, and math formulas</li>
</ul>
<p>Highly customizable</p>
<ul>
<li>Themes and styles</li>
<li>Shortcuts and behaviors</li>
</ul>
<p>By programmers, for programmers</p>
<ul>
<li>Vi mode and Vi-like navigation</li>
<li>Line number in editor</li>
<li>Multiple tabs</li>
<li>Window splits</li>
<li>Fuzzy search and jump</li>
<li>Versatile shortcuts</li>
</ul>
</description>
<launchable type="desktop-id">fun.vnote.app.VNote.desktop</launchable>
<url type="donation">https://github.com/vnotex/vnote#donate</url>
<url type="faq">https://app.vnote.fun/en_us/#!docs/Users/Frequently%20Asked%20Questions.md</url>
<url type="help">https://join.slack.com/t/vnote/shared_invite/enQtNDg2MzY0NDg3NzI4LTQ1Yzk1YjA5MjAyYTU0MjJkMTUxNmRiYWQ2YjlkOWU0ZGZlMTFlZTAxNzg0ZGUyNzI0ZGY2NDg4MmU1M2FkMDg</url>
<url type="homepage">https://app.vnote.fun/</url>
<url type="vcs-browser">https://github.com/vnotex/vnote</url>
<screenshots>
<screenshot type="default">
<image>https://app.vnote.fun/en_us/vx_images/90420129296.png</image>
</screenshot>
<screenshot>
<image>https://app.vnote.fun/en_us/vx_images/4085821110870.png</image>
</screenshot>
</screenshots>
<content_rating type="oars-1.1" />
<releases>
<release version="3.18.2" date="2024-08-06" />
<release version="3.18.1" date="2024-07-10" />
<release version="3.18.0" date="2024-07-01" />
<release version="3.17.0" date="2023-09-27" />
</releases>
</component>

Binary file not shown.

View File

@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "vnote.ico"

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,79 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN">
<context>
<name>QPlatformTheme</name>
<message>
<source>OK</source>
<translation></translation>
</message>
<message>
<source>Save</source>
<translation></translation>
</message>
<message>
<source>Save All</source>
<translation></translation>
</message>
<message>
<source>Open</source>
<translation></translation>
</message>
<message>
<source>&amp;Yes</source>
<translation>(&amp;Y)</translation>
</message>
<message>
<source>Yes to &amp;All</source>
<translation>(&amp;A)</translation>
</message>
<message>
<source>&amp;No</source>
<translation>(&amp;N)</translation>
</message>
<message>
<source>N&amp;o to All</source>
<translation>(&amp;O)</translation>
</message>
<message>
<source>Abort</source>
<translation></translation>
</message>
<message>
<source>Retry</source>
<translation></translation>
</message>
<message>
<source>Ignore</source>
<translation></translation>
</message>
<message>
<source>Close</source>
<translation></translation>
</message>
<message>
<source>Cancel</source>
<translation></translation>
</message>
<message>
<source>Discard</source>
<translation></translation>
</message>
<message>
<source>Help</source>
<translation></translation>
</message>
<message>
<source>Apply</source>
<translation></translation>
</message>
<message>
<source>Reset</source>
<translation></translation>
</message>
<message>
<source>Restore Defaults</source>
<translation></translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,193 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN">
<!--QWebEngineView uses different sources for its standard context menu.-->
<!--https://code.woboq.org/qt5/qtwebengine/src/core/render_view_context_menu_qt.cpp.html-->
<!--https://code.woboq.org/qt5/qtwebengine/src/webenginewidgets/api/qwebenginepage.cpp.html-->
<context>
<name>RenderViewContextMenuQt</name>
<message>
<source>Back</source>
<translation>退</translation>
</message>
<message>
<source>Forward</source>
<translation></translation>
</message>
<message>
<source>Reload</source>
<translation></translation>
</message>
<message>
<source>Cut</source>
<translation></translation>
</message>
<message>
<source>Copy</source>
<translation></translation>
</message>
<message>
<source>Paste</source>
<translation></translation>
</message>
<message>
<source>Undo</source>
<translation></translation>
</message>
<message>
<source>Redo</source>
<translation></translation>
</message>
<message>
<source>Select all</source>
<translation></translation>
</message>
<message>
<source>Paste and match style</source>
<translation></translation>
</message>
<message>
<source>Open link in new window</source>
<translation></translation>
</message>
<message>
<source>Open link in new tab</source>
<translation></translation>
</message>
<message>
<source>Copy link address</source>
<translation></translation>
</message>
<message>
<source>Save link</source>
<translation></translation>
</message>
<message>
<source>Copy image</source>
<translation></translation>
</message>
<message>
<source>Copy image address</source>
<translation></translation>
</message>
<message>
<source>Save image</source>
<translation></translation>
</message>
<message>
<source>Copy media address</source>
<translation></translation>
</message>
<message>
<source>Show controls</source>
<translation></translation>
</message>
<message>
<source>Loop</source>
<translation></translation>
</message>
<message>
<source>Save media</source>
<translation></translation>
</message>
<message>
<source>Inspect</source>
<translation></translation>
</message>
<message>
<source>Exit full screen</source>
<translation>退</translation>
</message>
<message>
<source>Save page</source>
<translation></translation>
</message>
<message>
<source>View page source</source>
<translation></translation>
</message>
</context>
<context>
<name>QWebEnginePage</name>
<message>
<source>Stop</source>
<translation></translation>
</message>
<message>
<source>Reload and Bypass Cache</source>
<translation></translation>
</message>
<message>
<source>Open link in this window</source>
<translation></translation>
</message>
<message>
<source>Open link in new background tab</source>
<translation></translation>
</message>
<message>
<source>Toggle Play/Pause</source>
<translation>/</translation>
</message>
<message>
<source>Toggle Mute</source>
<translation></translation>
</message>
<message>
<source>Close Page</source>
<translation></translation>
</message>
<message>
<source>Unselect</source>
<translation></translation>
</message>
<message>
<source>&amp;Bold</source>
<translation>(&amp;B)</translation>
</message>
<message>
<source>&amp;Italic</source>
<translation>(&amp;I)</translation>
</message>
<message>
<source>&amp;Underline</source>
<translation>线(&amp;U)</translation>
</message>
<message>
<source>&amp;Strikethrough</source>
<translation>线(&amp;S)</translation>
</message>
<message>
<source>Align &amp;Left</source>
<translation>(&amp;L)</translation>
</message>
<message>
<source>Align &amp;Center</source>
<translation>(&amp;C)</translation>
</message>
<message>
<source>Align &amp;Right</source>
<translation>(&amp;R)</translation>
</message>
<message>
<source>Align &amp;Justified</source>
<translation>(&amp;J)</translation>
</message>
<message>
<source>&amp;Indent</source>
<translation>(&amp;I)</translation>
</message>
<message>
<source>&amp;Outdent</source>
<translation>(&amp;O)</translation>
</message>
<message>
<source>Insert &amp;Ordered List</source>
<translation>(&amp;O)</translation>
</message>
<message>
<source>Insert &amp;Unordered List</source>
<translation>(&amp;U)</translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -10,4 +10,4 @@ Icon=vnote
Terminal=false
Exec=vnote %F
MimeType=text/markdown;
Categories=Qt;Utility;TextEditor;Office;
Categories=Utility

View File

@ -3,7 +3,7 @@
"metadata" : {
"//comment": "When releasing new version, please go through the following configs to check if override is needed.",
"//Comment": "markdown_editor#override_viewer_resource",
"version" : "3.16.0"
"version" : "3.19.2"
},
"core" : {
"theme" : "pure",
@ -14,6 +14,7 @@
"ExpandContentArea" : "Ctrl+G, E",
"Settings" : "Ctrl+Alt+P",
"NewNote" : "Ctrl+Alt+N",
"NewQuickNote" : "Ctrl+Alt+Q",
"NewFolder" : "Ctrl+Alt+S",
"CloseTab" : "Ctrl+G, X",
"CloseAllTabs": "",
@ -324,6 +325,7 @@
"web/js/markdown-it/markdownItAnchor.umd.js",
"web/js/markdown-it/markdownItTocDoneRight.umd.js",
"web/js/markdown-it/markdown-it-implicit-figure.js",
"web/js/markdown-it/markdown-it-mark.min.js",
"web/js/markdownit.js"
],
"styles" : [
@ -461,7 +463,7 @@
"//comment" : "Whether fetch images to local in Parse To Markdown And Paste",
"fetch_images_in_parse_and_paste" : true,
"//comment" : "Whether protect from Cross-Site Scripting attack",
"protect_from_xss" : false,
"protect_from_xss" : true,
"//comment" : "Whether allow HTML tags in source",
"html_tag" : true,
"//comment" : "Whether auto break a line with '\\n'",

View File

@ -213,6 +213,11 @@ This is the 1^st^ superscript.
This is the H~2~O subscript.
```
### Mark
```md
Let's mark the ==word==.
```
### Alert
```md
::: alert-info

View File

@ -59,6 +59,18 @@ Expand the selection to the beginning or end of current paragraph.
Expand the selection to the beginning or end of current line.
- `Ctrl+Shift+Home`, `Ctrl+Shift+End`
Expand the selection to the beginning or end of current note.
- `Ctrl+Shift+G`
Go to line.
- `Ctrl+C/X`
Copy/Cut line if there is no selection.
- `Ctrl+L`
Select line.
- `Alt+Up/Down`
Move line up/down.
- `Shift+Alt+Up/Down`
Copy line up/down.
- `Ctrl+Shift+K`
Delete line.
## Markdown Editor
### Read Mode

View File

@ -1,7 +1,7 @@
# Welcome to 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).
## FAQs
* Hover the mouse over buttons to get detailed information.
@ -11,4 +11,4 @@ For more information, please visit [**VNote's Home Page**](https://vnotex.github
* Feedbacks are appreciated! Please [post an issue](https://github.com/vnotex/vnote/issues) on GitHub if there is any.
### Windows Users
* if VNote hangs frequently or behaves unexpectedly in interface, please check the **OpenGL** option. [Details here](https://github.com/vnotex/vnote/issues/853).
* If VNote hangs frequently or behaves unexpectedly in interface, please check the **OpenGL** option. Check details [here](https://github.com/vnotex/vnote/issues/853).

View File

@ -213,6 +213,11 @@ This is the 1^st^ superscript.
This is the H~2~O subscript.
```
### 标记
```md
Let's mark the ==word==.
```
### 警告
```md
::: alert-info

View File

@ -59,6 +59,18 @@ VNote的很多部件均支持`Ctrl+J`和`Ctrl+K`导航。
扩展选定到行首和行尾。
- `Ctrl+Shift+Home`, `Ctrl+Shift+End`
扩展选定到笔记开始或结尾处。
- `Ctrl+Shift+G`
跳转到指定行。
- `Ctrl+C/X`
在没有选中文本时复制/剪切整行。
- `Ctrl+L`
选择当前行。
- `Alt+Up/Down`
向上/向下移动行。
- `Shift+Alt+Up/Down`
向上/向下复制行。
- `Ctrl+Shift+K`
删除行。
## Markdown 编辑器
### 阅读模式

View File

@ -1,7 +1,7 @@
# 欢迎使用 VNote
一个舒适的笔记平台。
更多信息,请访问[VNote主页](https://vnotex.github.io/vnote)或者[由Gitee托管的主页](https://tamlok.gitee.io/vnote)
更多信息,请访问[VNote主页](https://vnotex.github.io/vnote)。
## 常见问题
* 将鼠标悬停在按钮上可以获取详细信息。

View File

@ -49,8 +49,10 @@
<file>web/js/markdown-it/markdown-it-sup.min.js</file>
<file>web/js/markdown-it/markdown-it-task-lists.js</file>
<file>web/js/markdown-it/markdown-it-texmath.js</file>
<file>web/js/markdown-it/markdown-it-mark.min.js</file>
<file>web/js/markdown-it/markdown-it-inject-linenumbers.js</file>
<file>web/js/markdown-it/markdown-it-xss.min.js</file>
<file>web/js/markdown-it/xss.min.js</file>
<file>web/js/markdown-it/markdown-it-xss.js</file>
<file>web/js/markdown-it/markdown-it-implicit-figure.js</file>
<file>web/js/markdown-it/markdown-it.min.js</file>
<file>web/js/markdownit.js</file>
@ -347,6 +349,7 @@
<file>themes/moonlight/checkbox_unchecked.svg</file>
<file>themes/moonlight/checkbox_unchecked_disabled.svg</file>
<file>themes/moonlight/close.svg</file>
<file>themes/moonlight/expand_toolbar.svg</file>
<file>themes/moonlight/close_grey.svg</file>
<file>themes/moonlight/cover.png</file>
<file>themes/moonlight/down.svg</file>
@ -379,6 +382,7 @@
<file>themes/pure/checkbox_unchecked.svg</file>
<file>themes/pure/checkbox_unchecked_disabled.svg</file>
<file>themes/pure/close.svg</file>
<file>themes/pure/expand_toolbar.svg</file>
<file>themes/pure/close_grey.svg</file>
<file>themes/pure/cover.png</file>
<file>themes/pure/down.svg</file>
@ -411,6 +415,7 @@
<file>themes/solarized-dark/checkbox_unchecked.svg</file>
<file>themes/solarized-dark/checkbox_unchecked_disabled.svg</file>
<file>themes/solarized-dark/close.svg</file>
<file>themes/solarized-dark/expand_toolbar.svg</file>
<file>themes/solarized-dark/close_grey.svg</file>
<file>themes/solarized-dark/cover.png</file>
<file>themes/solarized-dark/down.svg</file>
@ -443,6 +448,7 @@
<file>themes/solarized-light/checkbox_unchecked.svg</file>
<file>themes/solarized-light/checkbox_unchecked_disabled.svg</file>
<file>themes/solarized-light/close.svg</file>
<file>themes/solarized-light/expand_toolbar.svg</file>
<file>themes/solarized-light/close_grey.svg</file>
<file>themes/solarized-light/cover.png</file>
<file>themes/solarized-light/down.svg</file>
@ -802,6 +808,7 @@
<file>themes/vscode-dark/checkbox_unchecked.svg</file>
<file>themes/vscode-dark/checkbox_unchecked_disabled.svg</file>
<file>themes/vscode-dark/close.svg</file>
<file>themes/vscode-dark/expand_toolbar.svg</file>
<file>themes/vscode-dark/close_grey.svg</file>
<file>themes/vscode-dark/cover.png</file>
<file>themes/vscode-dark/down.svg</file>
@ -835,6 +842,7 @@
<file>themes/vue-light/checkbox_unchecked.svg</file>
<file>themes/vue-light/checkbox_unchecked_disabled.svg</file>
<file>themes/vue-light/close.svg</file>
<file>themes/vue-light/expand_toolbar.svg</file>
<file>themes/vue-light/close_grey.svg</file>
<file>themes/vue-light/cover.png</file>
<file>themes/vue-light/down.svg</file>
@ -867,6 +875,7 @@
<file>themes/vue-dark/checkbox_unchecked.svg</file>
<file>themes/vue-dark/checkbox_unchecked_disabled.svg</file>
<file>themes/vue-dark/close.svg</file>
<file>themes/vue-dark/expand_toolbar.svg</file>
<file>themes/vue-dark/close_grey.svg</file>
<file>themes/vue-dark/cover.png</file>
<file>themes/vue-dark/down.svg</file>
@ -903,6 +912,7 @@
<file>themes/vx-idea/checkbox_unchecked.svg</file>
<file>themes/vx-idea/checkbox_unchecked_disabled.svg</file>
<file>themes/vx-idea/close.svg</file>
<file>themes/vx-idea/expand_toolbar.svg</file>
<file>themes/vx-idea/close_grey.svg</file>
<file>themes/vx-idea/cover.png</file>
<file>themes/vx-idea/down.svg</file>

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747190112225" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2510" width="256" height="256" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M798.165333 524.501333a42.666667 42.666667 0 0 0-60.330666 0L512 750.336l-225.834667-225.834667a42.666667 42.666667 0 0 0-60.330666 60.330667l256 256a42.666667 42.666667 0 0 0 60.330666 0l256-256a42.666667 42.666667 0 0 0 0-60.330667z" p-id="2511" fill="#9EA5B4"></path><path d="M798.165333 225.834667a42.666667 42.666667 0 0 0-60.330666 0L512 451.669333 286.165333 225.834667a42.666667 42.666667 0 0 0-60.330666 60.330666l256 256a42.666667 42.666667 0 0 0 60.330666 0l256-256a42.666667 42.666667 0 0 0 0-60.330666z" p-id="2512" fill="#9EA5B4"></path></svg>

After

Width:  |  Height:  |  Size: 891 B

View File

@ -5,7 +5,6 @@
* same specificity and the rule that appears last takes precedence.
* VNote specific syntax:
* - @widgets#toolbox#title#border: reference to a color defined in palette.ini;
* - $2px: 2 will be scaled by multiplying current display scaled factor.
*/
QWidget {
@ -148,13 +147,16 @@ QToolBar::separator {
}
QToolBarExtension#qt_toolbar_ext_button {
background-color: @widgets#qtoolbar#extension#bg;
margin: 30px;
qproperty-icon: url(expand_toolbar.svg);
background-color: @widgets#qtoolbutton#bg;
}
QToolBarExtension#qt_toolbar_ext_button:hover {
background-color: @widgets#qtoolbar#extension#hover#bg;
margin: 30px;
background-color: @widgets#qtoolbutton#hover#bg;
}
QToolBarExtension#qt_toolbar_ext_button:pressed {
background-color: @widgets#qtoolbutton#pressed#bg;
}
/* QToolButton */
@ -586,7 +588,7 @@ QTabBar::close-button:hover {
}
QTabBar::scroller {
width: $20px;
width: 20px;
}
QTabBar QToolButton {

View File

@ -5,7 +5,6 @@
* same specificity and the rule that appears last takes precedence.
* VNote specific syntax:
* - @widgets#toolbox#title#border: reference to a color defined in palette.ini;
* - $2px: 2 will be scaled by multiplying current display scaled factor.
*/
QWidget[HitSettingWidget="true"] {

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747190112225" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2510" width="256" height="256" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M798.165333 524.501333a42.666667 42.666667 0 0 0-60.330666 0L512 750.336l-225.834667-225.834667a42.666667 42.666667 0 0 0-60.330666 60.330667l256 256a42.666667 42.666667 0 0 0 60.330666 0l256-256a42.666667 42.666667 0 0 0 0-60.330667z" p-id="2511" fill="#222222"></path><path d="M798.165333 225.834667a42.666667 42.666667 0 0 0-60.330666 0L512 451.669333 286.165333 225.834667a42.666667 42.666667 0 0 0-60.330666 60.330666l256 256a42.666667 42.666667 0 0 0 60.330666 0l256-256a42.666667 42.666667 0 0 0 0-60.330666z" p-id="2512" fill="#222222"></path></svg>

After

Width:  |  Height:  |  Size: 891 B

View File

@ -5,7 +5,6 @@
* same specificity and the rule that appears last takes precedence.
* VNote specific syntax:
* - @widgets#toolbox#title#border: reference to a color defined in palette.ini;
* - $2px: 2 will be scaled by multiplying current display scaled factor.
*/
QWidget {
@ -167,13 +166,16 @@ QToolBar::separator {
}
QToolBarExtension#qt_toolbar_ext_button {
background-color: @widgets#qtoolbar#extension#bg;
margin: 30px;
qproperty-icon: url(expand_toolbar.svg);
background-color: @widgets#qtoolbutton#bg;
}
QToolBarExtension#qt_toolbar_ext_button:hover {
background-color: @widgets#qtoolbar#extension#hover#bg;
margin: 30px;
background-color: @widgets#qtoolbutton#hover#bg;
}
QToolBarExtension#qt_toolbar_ext_button:pressed {
background-color: @widgets#qtoolbutton#pressed#bg;
}
/* QToolButton */
@ -624,7 +626,7 @@ QTabBar::close-button:hover {
}
QTabBar::scroller {
width: $20px;
width: 20px;
}
QTabBar QToolButton {

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747190112225" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2510" width="256" height="256" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M798.165333 524.501333a42.666667 42.666667 0 0 0-60.330666 0L512 750.336l-225.834667-225.834667a42.666667 42.666667 0 0 0-60.330666 60.330667l256 256a42.666667 42.666667 0 0 0 60.330666 0l256-256a42.666667 42.666667 0 0 0 0-60.330667z" p-id="2511" fill="#9EA5B4"></path><path d="M798.165333 225.834667a42.666667 42.666667 0 0 0-60.330666 0L512 451.669333 286.165333 225.834667a42.666667 42.666667 0 0 0-60.330666 60.330666l256 256a42.666667 42.666667 0 0 0 60.330666 0l256-256a42.666667 42.666667 0 0 0 0-60.330666z" p-id="2512" fill="#9EA5B4"></path></svg>

After

Width:  |  Height:  |  Size: 891 B

View File

@ -5,7 +5,6 @@
* same specificity and the rule that appears last takes precedence.
* VNote specific syntax:
* - @widgets#toolbox#title#border: reference to a color defined in palette.ini;
* - $2px: 2 will be scaled by multiplying current display scaled factor.
*/
QWidget {
@ -144,13 +143,16 @@ QToolBar::separator {
}
QToolBarExtension#qt_toolbar_ext_button {
background-color: @widgets#qtoolbar#extension#bg;
margin: 30px;
qproperty-icon: url(expand_toolbar.svg);
background-color: @widgets#qtoolbutton#bg;
}
QToolBarExtension#qt_toolbar_ext_button:hover {
background-color: @widgets#qtoolbar#extension#hover#bg;
margin: 30px;
background-color: @widgets#qtoolbutton#hover#bg;
}
QToolBarExtension#qt_toolbar_ext_button:pressed {
background-color: @widgets#qtoolbutton#pressed#bg;
}
/* QToolButton */
@ -582,7 +584,7 @@ QTabBar::close-button:hover {
}
QTabBar::scroller {
width: $20px;
width: 20px;
}
QTabBar QToolButton {

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747190112225" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2510" width="256" height="256" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M798.165333 524.501333a42.666667 42.666667 0 0 0-60.330666 0L512 750.336l-225.834667-225.834667a42.666667 42.666667 0 0 0-60.330666 60.330667l256 256a42.666667 42.666667 0 0 0 60.330666 0l256-256a42.666667 42.666667 0 0 0 0-60.330667z" p-id="2511" fill="#9EA5B4"></path><path d="M798.165333 225.834667a42.666667 42.666667 0 0 0-60.330666 0L512 451.669333 286.165333 225.834667a42.666667 42.666667 0 0 0-60.330666 60.330666l256 256a42.666667 42.666667 0 0 0 60.330666 0l256-256a42.666667 42.666667 0 0 0 0-60.330666z" p-id="2512" fill="#9EA5B4"></path></svg>

After

Width:  |  Height:  |  Size: 891 B

Some files were not shown because too many files have changed in this diff Show More