say goodbye to VNote 2.0
236
.github/workflows/test-cmake-build.yml
vendored
@ -1,236 +0,0 @@
|
||||
name: Build and Run tests
|
||||
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
CMAKE_VER: 3.16.2
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
name: Build and package on Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 3600
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Init submodules
|
||||
shell: bash
|
||||
run: |
|
||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||
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.2.0
|
||||
with:
|
||||
plugins: qt appimage
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get update
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y libfcitx-qt5-dev
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: 5.9.9
|
||||
target: desktop
|
||||
modules: qtwebchannel qtwebengine qtsvg qtlocation qttools qttranslations
|
||||
|
||||
- name: Create Build Directory
|
||||
shell: bash
|
||||
run: mkdir build
|
||||
working-directory: ${{runner.workspace}}
|
||||
|
||||
# install-qt-action exports Qt5_Dir environment variable
|
||||
- name: Configure the Project
|
||||
shell: bash
|
||||
run: cmake -DQt5_DIR=${Qt5_Dir}/lib/cmake/Qt5/ ${GITHUB_WORKSPACE}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Build the Project
|
||||
run: cmake --build . --target bundle
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Collect artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p artifacts
|
||||
mv *.bz2 *.xz *.deb *.rpm *.AppImage artifacts || /bin/true
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: linux packages
|
||||
path: ${{runner.workspace}}/build/artifacts
|
||||
|
||||
build_macos:
|
||||
name: Build and package on MacOS X
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 3600
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Init submodules
|
||||
shell: bash
|
||||
run: |
|
||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||
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}-Darwin-x86_64.tar.gz
|
||||
tar xzf cmake-${CMAKE_VER}-Darwin-x86_64.tar.gz
|
||||
sudo rm -f /usr/local/bin/cmake /usr/local/bin/cpack
|
||||
sudo ln -s ${{runner.workspace}}/cmake-${CMAKE_VER}-Darwin-x86_64/CMake.app/Contents/bin/cmake /usr/local/bin/cmake
|
||||
sudo ln -s ${{runner.workspace}}/cmake-${CMAKE_VER}-Darwin-x86_64/CMake.app/Contents/bin/cpack /usr/local/bin/cpack
|
||||
working-directory: ${{runner.workspace}}
|
||||
|
||||
- name: Install macdeployqtFix
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: aurelien-rainone/macdeployqtfix
|
||||
path: macdeployqtfix
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: 5.9.9
|
||||
target: desktop
|
||||
modules: qtwebchannel qtwebengine qtsvg qtlocation qttools qttranslations
|
||||
|
||||
- name: Create Build Directory
|
||||
run: mkdir build
|
||||
working-directory: ${{runner.workspace}}
|
||||
|
||||
# install-qt-action exports Qt5_Dir environment variable
|
||||
- name: Configure the Project
|
||||
run: cmake -DQt5_DIR=${Qt5_Dir}/lib/cmake/Qt5/ -DMACDEPLOYQTFIX_EXECUTABLE=${GITHUB_WORKSPACE}/macdeployqtfix/macdeployqtfix.py ${GITHUB_WORKSPACE}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Build the Project
|
||||
run: cmake --build . --target bundle
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Collect artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p artifacts
|
||||
mv ./_CPack_Packages/Darwin/External/vnote-2.8.2-Darwin/Applications/VNote.dmg artifacts || (exit 0)
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: mac osx packages
|
||||
path: ${{runner.workspace}}/build/artifacts
|
||||
|
||||
build_win64:
|
||||
name: Build and package on Windows(64bit)
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 3600
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Init submodules
|
||||
shell: bash
|
||||
run: |
|
||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||
git submodule sync --recursive
|
||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: 5.9.9
|
||||
target: desktop
|
||||
arch: win64_msvc2017_64
|
||||
modules: qtwebchannel qtwebengine qtsvg qtlocation qttools qttranslations
|
||||
|
||||
- name: Create Build Directory
|
||||
shell: bash
|
||||
run: mkdir build
|
||||
working-directory: ${{runner.workspace}}
|
||||
|
||||
# install-qt-action exports Qt5_Dir environment variable
|
||||
- name: Configure the Project
|
||||
shell: bash
|
||||
run: cmake -G "Visual Studio 16 2019" -A x64 -DQt5_DIR=${Qt5_Dir}/lib/cmake/Qt5/ ${GITHUB_WORKSPACE}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Build the Project(Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: cmake --build . --target bundle --config ${env:BUILD_TYPE}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Collect artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p artifacts
|
||||
mv *.zip *.exe *.nupkg artifacts || (exit 0)
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: windows packages
|
||||
path: "${{runner.workspace}}\\build\\artifacts"
|
||||
|
||||
build_win32:
|
||||
name: Build and package on Windows(32bit)
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 3600
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Init submodules
|
||||
shell: bash
|
||||
run: |
|
||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||
git submodule sync --recursive
|
||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
||||
|
||||
# Qt 5.9 only support msvc 2015 for win32 but gh-a run on msvc2019
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: 5.9.9
|
||||
target: desktop
|
||||
arch: win32_msvc2015
|
||||
modules: qtwebchannel qtwebengine qtsvg qtlocation qttools qttranslations
|
||||
|
||||
- name: Create Build Directory
|
||||
shell: bash
|
||||
run: mkdir build
|
||||
working-directory: ${{runner.workspace}}
|
||||
|
||||
# install-qt-action exports Qt5_Dir environment variable
|
||||
- name: Configure the Project
|
||||
shell: bash
|
||||
run: cmake -G "Visual Studio 16 2019" -A Win32 -DQt5_DIR=${Qt5_Dir}/lib/cmake/Qt5/ ${GITHUB_WORKSPACE}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Build the Project(Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: cmake --build . --target bundle --config ${env:BUILD_TYPE}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- name: Collect artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p artifacts
|
||||
mv *.zip *.exe *.nupkg artifacts || (exit 0)
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: win32 packages
|
||||
path: "${{runner.workspace}}\\build\\artifacts"
|
3
.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
VNote.pro.user*
|
||||
CMakeLists.txt.user
|
||||
.DS_Store
|
6
.gitmodules
vendored
@ -1,6 +0,0 @@
|
||||
[submodule "src/utils/marked"]
|
||||
path = src/utils/marked
|
||||
url = https://github.com/chjj/marked.git
|
||||
[submodule "hoedown"]
|
||||
path = hoedown
|
||||
url = https://github.com/tamlok/hoedown.git
|
@ -1,31 +0,0 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "vnote",
|
||||
"repo": "vnote",
|
||||
"subject": "tamlok",
|
||||
"desc": "Continuous deployment by Travis-CI",
|
||||
"website_url": "https://github.com/tamlok/vnote",
|
||||
"issue_tracker_url": "https://github.com/tamlok/vnote/issues",
|
||||
"vcs_url": "https://github.com/tamlok/vnote.git",
|
||||
"github_use_tag_release_notes": false,
|
||||
"github_release_notes_file": "",
|
||||
"licenses": ["MIT"],
|
||||
"labels": ["Markdown", "Note-Taking", "Office", "Utils", "Efficiency"],
|
||||
"public_download_numbers": true,
|
||||
"public_stats": true
|
||||
},
|
||||
|
||||
"version": {
|
||||
"name": "2.10",
|
||||
"desc": "VNote Releases",
|
||||
"released": "2020-09-05",
|
||||
"vcs_tag": "2.10",
|
||||
"gpgSign": false
|
||||
},
|
||||
|
||||
"files":
|
||||
[{"includePattern": "build/(VNote-.*\\.AppImage)", "uploadPattern": "$1",
|
||||
"matrixParams": {"override": 1 }}],
|
||||
|
||||
"publish": true
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
{
|
||||
"package": {
|
||||
"name": "vnote",
|
||||
"repo": "vnote",
|
||||
"subject": "tamlok",
|
||||
"desc": "Continuous deployment by Travis-CI",
|
||||
"website_url": "https://github.com/tamlok/vnote",
|
||||
"issue_tracker_url": "https://github.com/tamlok/vnote/issues",
|
||||
"vcs_url": "https://github.com/tamlok/vnote.git",
|
||||
"github_use_tag_release_notes": false,
|
||||
"github_release_notes_file": "",
|
||||
"licenses": ["MIT"],
|
||||
"labels": ["Markdown", "Note-Taking", "Office", "Utils", "Efficiency"],
|
||||
"public_download_numbers": true,
|
||||
"public_stats": true
|
||||
},
|
||||
|
||||
"version": {
|
||||
"name": "2.10",
|
||||
"desc": "VNote Releases",
|
||||
"released": "2020-09-05",
|
||||
"vcs_tag": "2.10",
|
||||
"gpgSign": false
|
||||
},
|
||||
|
||||
"files":
|
||||
[{"includePattern": "build/distrib/(VNote-.*\\.dmg)", "uploadPattern": "$1",
|
||||
"matrixParams": {"override": 1 }}],
|
||||
|
||||
"publish": true
|
||||
}
|
61
.travis.yml
@ -1,61 +0,0 @@
|
||||
dist: trusty
|
||||
sudo: required
|
||||
git:
|
||||
depth: 1
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
compiler: clang
|
||||
osx_image: xcode9.4
|
||||
- os: linux
|
||||
compiler: g++
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
before_install:
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; fi
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then sudo apt-get -qq update ; fi
|
||||
- export version="2.10"
|
||||
|
||||
install:
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install p7zip-full ; fi
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install chrpath ; fi
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install openssl libssl-dev ; fi
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install libgl1-mesa-dev ; fi
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install tree ; fi
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then sudo apt-get install -qq gcc-6; sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 100 ; fi
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then sudo apt-get install -qq g++-6; sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-6 100 ; fi
|
||||
|
||||
before_script:
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then chmod +x .travis_linux.sh ; fi
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then chmod +x .travis_macos.sh ; fi
|
||||
|
||||
script:
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then CXX="g++-6" CC="gcc-6" ./.travis_linux.sh ; fi
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then ./.travis_macos.sh ; fi
|
||||
|
||||
deploy:
|
||||
- provider: bintray
|
||||
skip_cleanup: true
|
||||
file: "./.linux_bintray.json"
|
||||
user: tamlok
|
||||
key:
|
||||
secure: vADlrYSusARnHxK3NTT3r1r6I/lrxyEuFKLOO73KXl6rvDywrrDDi6YsFekL14/o3clZKLOC8LbvA+4BKPVSoGTO4Dpx/i51fXphf+mMFB3QXSGnbqHm4+2ZnhCl62vfVlFeOPBLDO0KUBI8DCP0I+QogOUickXv3zYx69z6AudSxgjY7nPoN5jGSPgyId4r3o8Shrj1Fk1WtN6qciKyPne57v+jmvRlJTkoEfYkvwTRNMBlmaKy8Vj/CSoIvB8TH8bv3rhHLrN4o1QCwePdaX9G7xlImSpXqgRgzQVU3SRFN2bYTj5DZc1aFyA7iNOhmwgCLAN6OrqXyyJCbyywNhylfgad2rQANcfXiZw9ywooKEYafLDRuiK2G6Pk8m+R6Ded9JBoX9MSz/ZDxxfk4NmLO991wS7IK5aFTQfHZz5W8Tmh1rCphFwlDI/Rxs7ExbXu7INA8IhoLpOSmLIA5lfdC4fD8rv8mjy5gfpMkWlP8XATfi2eMH9uGj89aJa6xp7Bc2mfW3M71YHtYP/pyE/7Bk5vVonP8Cq9rK6OruMIUYtNmXoJnpte20ecSXj9yw7oTR27dhSx3XuAKOXiNpIau0CPxtXA5cSm5X0OS1Lt8RWrms0jp9Ch2dgxlNMpr0soF9DmwekhfHnVoOLyFBWBMDwvkx5TWij9FNAAJb4=
|
||||
dry-run: false
|
||||
on:
|
||||
condition: $TRAVIS_OS_NAME = linux
|
||||
branch: master
|
||||
- provider: bintray
|
||||
skip_cleanup: true
|
||||
file: "./.macos_bintray.json"
|
||||
user: tamlok
|
||||
key:
|
||||
secure: vADlrYSusARnHxK3NTT3r1r6I/lrxyEuFKLOO73KXl6rvDywrrDDi6YsFekL14/o3clZKLOC8LbvA+4BKPVSoGTO4Dpx/i51fXphf+mMFB3QXSGnbqHm4+2ZnhCl62vfVlFeOPBLDO0KUBI8DCP0I+QogOUickXv3zYx69z6AudSxgjY7nPoN5jGSPgyId4r3o8Shrj1Fk1WtN6qciKyPne57v+jmvRlJTkoEfYkvwTRNMBlmaKy8Vj/CSoIvB8TH8bv3rhHLrN4o1QCwePdaX9G7xlImSpXqgRgzQVU3SRFN2bYTj5DZc1aFyA7iNOhmwgCLAN6OrqXyyJCbyywNhylfgad2rQANcfXiZw9ywooKEYafLDRuiK2G6Pk8m+R6Ded9JBoX9MSz/ZDxxfk4NmLO991wS7IK5aFTQfHZz5W8Tmh1rCphFwlDI/Rxs7ExbXu7INA8IhoLpOSmLIA5lfdC4fD8rv8mjy5gfpMkWlP8XATfi2eMH9uGj89aJa6xp7Bc2mfW3M71YHtYP/pyE/7Bk5vVonP8Cq9rK6OruMIUYtNmXoJnpte20ecSXj9yw7oTR27dhSx3XuAKOXiNpIau0CPxtXA5cSm5X0OS1Lt8RWrms0jp9Ch2dgxlNMpr0soF9DmwekhfHnVoOLyFBWBMDwvkx5TWij9FNAAJb4=
|
||||
dry-run: false
|
||||
on:
|
||||
condition: $TRAVIS_OS_NAME = osx
|
||||
branch: master
|
102
.travis_linux.sh
@ -1,102 +0,0 @@
|
||||
#!/bin/bash
|
||||
project_dir=$(pwd)
|
||||
|
||||
# Complain when not in Travis environment
|
||||
if [ -z ${TRAVIS_COMMIT+x} ]; then
|
||||
echo "This script is intended to be used only in Travis CI environment."
|
||||
echo "To build VNote from source, please see the [documentation](https://tamlok.github.io/vnote/en_us/#!docs/Developers/Build%20VNote.md)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install qt5.9
|
||||
sudo add-apt-repository ppa:george-edison55/cmake-3.x -y
|
||||
sudo add-apt-repository ppa:beineri/opt-qt597-trusty -y
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get -y install qt59base qt59webengine qt59webchannel qt59svg qt59location qt59tools qt59translations
|
||||
source /opt/qt*/bin/qt*-env.sh
|
||||
|
||||
# Compile qt5ct
|
||||
wget -c https://excellmedia.dl.sourceforge.net/project/qt5ct/qt5ct-0.37.tar.bz2
|
||||
tar xf qt5ct-0.*.tar.bz2
|
||||
cd qt5ct-0.*/
|
||||
QT_SELECT=5 qmake
|
||||
make -j$(nproc) && sudo make install
|
||||
|
||||
# Compile newer version fcitx-qt5
|
||||
sudo apt-get -y install fcitx-libs-dev libgl1-mesa-dev bison
|
||||
sudo apt-get -y install cmake
|
||||
|
||||
wget http://xkbcommon.org/download/libxkbcommon-0.5.0.tar.xz
|
||||
tar xf libxkbcommon-0.5.0.tar.xz
|
||||
cd libxkbcommon-0.5.0
|
||||
./configure -prefix=/usr -libdir=/usr/lib/x86_64-linux-gnu -disable-x11
|
||||
make -j$(nproc) && sudo make install
|
||||
|
||||
git clone git://anongit.kde.org/extra-cmake-modules
|
||||
cd extra-cmake-modules
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make -j$(nproc) && sudo make install
|
||||
|
||||
git clone https://github.com/fcitx/fcitx-qt5
|
||||
cd fcitx-qt5
|
||||
git checkout 1.0.5
|
||||
cmake .
|
||||
make -j$(nproc) && sudo make install
|
||||
|
||||
# Copy fcitx-qt5 files to qt
|
||||
sudo cp /usr/local/lib/libFcitxQt5DBusAddons.so* /opt/qt*/lib/
|
||||
sudo cp /usr/local/lib/libFcitxQt5WidgetsAddons.so* /opt/qt*/lib/
|
||||
|
||||
tree /opt/qt59/lib/
|
||||
|
||||
cd ${project_dir}
|
||||
mkdir build
|
||||
cd build
|
||||
qmake -v
|
||||
qmake CONFIG+=release -spec linux-g++-64 ../VNote.pro
|
||||
make -j$(nproc)
|
||||
|
||||
#
|
||||
# Pack AppImage using linuxdeployqt
|
||||
#
|
||||
mkdir dist
|
||||
INSTALL_ROOT=${project_dir}/build/dist make install ; tree dist/
|
||||
|
||||
# Copy SVG module
|
||||
mkdir -p ./dist/usr/plugins/iconengines
|
||||
mkdir -p ./dist/usr/plugins/imageformats
|
||||
mkdir -p ./dist/usr/plugins/platforminputcontexts
|
||||
cp /opt/qt59/plugins/iconengines/* ./dist/usr/plugins/iconengines/
|
||||
cp /opt/qt59/plugins/imageformats/* ./dist/usr/plugins/imageformats/
|
||||
cp /opt/qt59/plugins/platforminputcontexts/* ./dist/usr/plugins/platforminputcontexts/
|
||||
|
||||
# Copy other project files
|
||||
cp "${project_dir}/README.md" "dist/README.md"
|
||||
cp "${project_dir}/LICENSE" "dist/LICENSE"
|
||||
echo ${version} > ./dist/version
|
||||
echo "${TRAVIS_COMMIT}" >> ./dist/version
|
||||
|
||||
# Get linuxdeployqt tool
|
||||
git clone https://github.com/tamlok/vnote-utils.git vnote-utils.git
|
||||
cp vnote-utils.git/linuxdeployqt-continuous-x86_64.AppImage ./linuxdeployqt-continuous-x86_64.AppImage
|
||||
# wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
|
||||
chmod a+x linuxdeployqt*.AppImage
|
||||
unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
|
||||
./linuxdeployqt*.AppImage ./dist/usr/share/applications/*.desktop -bundle-non-qt-libs -exclude-libs=libnss3,libnssutil3 -extra-plugins=platformthemes/libqt5ct.so,styles/libqt5ct-style.so
|
||||
|
||||
# Copy translations
|
||||
cp /opt/qt59/translations/*_zh_CN.qm ./dist/usr/translations/
|
||||
|
||||
# Package it for the second time.
|
||||
./linuxdeployqt*.AppImage ./dist/usr/share/applications/*.desktop -appimage -exclude-libs=libnss3,libnssutil3 -extra-plugins=platformthemes/libqt5ct.so,styles/libqt5ct-style.so
|
||||
|
||||
tree dist/
|
||||
|
||||
ls -l *.AppImage
|
||||
|
||||
mv VNote-*.AppImage VNote-${version}-x86_64.AppImage
|
||||
|
||||
cd ..
|
||||
|
||||
exit 0
|
@ -1,74 +0,0 @@
|
||||
#!/bin/bash
|
||||
project_dir=$(pwd)
|
||||
|
||||
# Complain when not in Travis environment
|
||||
if [ -z ${TRAVIS_COMMIT+x} ]; then
|
||||
echo "This script is intended to be used only in Travis CI environment."
|
||||
echo "To build VNote from source, please see the [documentation](https://tamlok.github.io/vnote/en_us/#!docs/Developers/Build%20VNote.md)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
brew update > /dev/null
|
||||
|
||||
brew install gnu-getopt
|
||||
export PATH="/usr/local/opt/gnu-getopt/bin:$PATH"
|
||||
|
||||
# Download Qt from Qt Installer
|
||||
cd ${project_dir}
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
export VERBOSE=1
|
||||
export QT_CI_PACKAGES="qt.qt5.598.clang_64,qt.qt5.598.qtwebengine"
|
||||
export QT_CI_LOGIN="tamlok@qq.com"
|
||||
export QT_CI_PASSWORD="TravisCI@VNote"
|
||||
|
||||
git clone https://github.com/tamlok/qtci.git
|
||||
source qtci/path.env
|
||||
|
||||
install-qt 5.9.8
|
||||
source qt-5.9.8.env
|
||||
|
||||
echo $PATH
|
||||
|
||||
QTDIR="${project_dir}/build/Qt/5.9.8/clang_64"
|
||||
LDFLAGS=-L$QTDIR/lib
|
||||
CPPFLAGS=-I$QTDIR/include
|
||||
|
||||
# Build your app
|
||||
cd ${project_dir}/build
|
||||
qmake -v
|
||||
qmake CONFIG-=debug CONFIG+=release ../VNote.pro
|
||||
make -j2
|
||||
|
||||
git clone https://github.com/aurelien-rainone/macdeployqtfix.git
|
||||
|
||||
# Package DMG from build/src/VNote.app directory
|
||||
cd src/
|
||||
|
||||
sed -i -e 's/com.yourcompany.VNote/com.tamlok.VNote/g' VNote.app/Contents/Info.plist
|
||||
$QTDIR/bin/macdeployqt VNote.app
|
||||
python ../macdeployqtfix/macdeployqtfix.py VNote.app/Contents/MacOS/VNote $QTDIR
|
||||
|
||||
# Fix Helpers/QtWebEngineProcess.app
|
||||
cd VNote.app/Contents/Frameworks/QtWebEngineCore.framework/Versions/5/Helpers
|
||||
$QTDIR/bin/macdeployqt QtWebEngineProcess.app
|
||||
python ${project_dir}/build/macdeployqtfix/macdeployqtfix.py QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess $QTDIR
|
||||
|
||||
cd ${project_dir}/build
|
||||
mkdir -p distrib/VNote
|
||||
cd distrib/VNote
|
||||
mv ../../src/VNote.app ./
|
||||
cp "${project_dir}/LICENSE" "LICENSE"
|
||||
cp "${project_dir}/README.md" "README.md"
|
||||
echo "${version}" > version
|
||||
echo "${TRAVIS_COMMIT}" >> version
|
||||
|
||||
ln -s /Applications ./Applications
|
||||
|
||||
cd ..
|
||||
hdiutil create -srcfolder ./VNote -format UDBZ ./VNote.dmg
|
||||
mv VNote.dmg VNote-${version}-x64.dmg
|
||||
cd ..
|
||||
|
||||
exit 0
|
@ -1,47 +0,0 @@
|
||||
# Minimum requirement for CMake version.
|
||||
# 3.13 or later for Linux and Mac OSX.
|
||||
# it causes error for build with cmake 3.12 or earlier.
|
||||
# 3.12 or later for Windows
|
||||
# because Qt5.9 only support MSVC2017,
|
||||
# and MSVC2017 is integrated with cmake 3.12.
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
cmake_minimum_required (VERSION 3.12)
|
||||
project(VNote VERSION 2.8.2
|
||||
DESCRIPTION "VNote is a markdown note taking application"
|
||||
HOMEPAGE_URL "https://tamlok.github.io/vnote"
|
||||
LANGUAGES C CXX)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
## Qt5 configurations
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
# find Qt5 installation
|
||||
find_package(Qt5 REQUIRED COMPONENTS Core Gui Network PrintSupport WebChannel WebEngine
|
||||
WebEngineWidgets Positioning Svg Widgets LinguistTools
|
||||
HINTS
|
||||
"/opt/qt59/lib/cmake/Qt5/"
|
||||
"c:/Qt/Qt5.9.8/5.9.8/msvc2017_64/lib/cmake/Qt5/"
|
||||
"c:/Qt/Qt5.9.9/5.9.9/msvc2017_64/lib/cmake/Qt5/"
|
||||
"/usr/local/Cellar/qt/5.9.8/clang_64/lib/cmake/Qt5/"
|
||||
"/usr/local/Cellar/qt/5.9.9/clang_64/lib/cmake/Qt5/")
|
||||
|
||||
## hoedown library
|
||||
add_library(hoedown STATIC
|
||||
hoedown/src/autolink.c hoedown/src/document.c hoedown/src/html.c hoedown/src/html_smartypants.c
|
||||
hoedown/src/version.c hoedown/src/buffer.c hoedown/src/escape.c hoedown/src/html_blocks.c
|
||||
hoedown/src/stack.c )
|
||||
target_link_libraries(hoedown PRIVATE Qt5::Core Qt5::Gui)
|
||||
|
||||
## peg-highlight library
|
||||
add_library(peg-highlight STATIC peg-highlight/pmh_parser.c peg-highlight/pmh_styleparser.c)
|
||||
target_link_libraries(peg-highlight PRIVATE Qt5::Core Qt5::Gui)
|
||||
|
||||
## project sources
|
||||
add_subdirectory(src)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/Packaging.cmake)
|
||||
|
||||
# vim: ts=2 sw=2 sts=2 et
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"inheritEnvironments": [
|
||||
"msvc_x64_x64"
|
||||
],
|
||||
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
|
||||
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
}
|
||||
]
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
OS Version (操作系统): Windows, Linux, macOS
|
||||
|
||||
VNote Version (Help - About VNote - Version: )(VNote版本): 2.2, d78e892d5cc etc..
|
||||
|
||||
Symptoms (表现):
|
||||
|
||||
|
||||
How to Repro (如何复现):
|
||||
|
21
LICENSE
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Le Tan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
197
Packaging.cmake
@ -1,197 +0,0 @@
|
||||
find_package(Qt5Core REQUIRED)
|
||||
get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION)
|
||||
get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY)
|
||||
|
||||
set(CPACK_PACKAGE_VENDOR "Le Tan")
|
||||
set(CPACK_PACKAGE_NAME "vnote")
|
||||
set(CPACK_PACKAGE_CONTACT "Le Tan <tamlokveer@gmail.com>")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_DESCRIPTION}")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
|
||||
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 "vnote")
|
||||
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
|
||||
# set human names to executables
|
||||
set(CPACK_PACKAGE_EXECUTABLES "VNote" "VNote")
|
||||
set(CPACK_CREATE_DESKTOP_LINKS "VNote")
|
||||
set(CPACK_STRIP_FILES TRUE)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# include CPack, so we get target for packages
|
||||
set(CPACK_OUTPUT_CONFIG_FILE "${CMAKE_BINARY_DIR}/BundleConfig.cmake")
|
||||
|
||||
add_custom_target(bundle
|
||||
COMMAND ${CMAKE_CPACK_COMMAND} "--config" "${CMAKE_BINARY_DIR}/BundleConfig.cmake"
|
||||
COMMENT "Running CPACK. Please wait..."
|
||||
DEPENDS VNote)
|
||||
set(CPACK_GENERATOR)
|
||||
|
||||
# Qt IFW packaging framework
|
||||
set(CPACK_IFW_ROOT $ENV{HOME}/Qt/QtIFW-3.0.6/ CACHE PATH "Qt Installer Framework installation base path")
|
||||
find_program(BINARYCREATOR_EXECUTABLE binarycreator HINTS "${_qt_bin_dir}" ${CPACK_IFW_ROOT}/bin)
|
||||
if(BINARYCREATOR_EXECUTABLE)
|
||||
list(APPEND CPACK_GENERATOR IFW)
|
||||
message(STATUS " + Qt Installer Framework YES ")
|
||||
else()
|
||||
message(STATUS " + Qt Installer Framework NO ")
|
||||
endif()
|
||||
|
||||
if (WIN32 AND NOT UNIX)
|
||||
#--------------------------------------------------------------------------
|
||||
# Windows specific
|
||||
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")
|
||||
message(STATUS " + ZIP YES ")
|
||||
|
||||
set(PACKAGE_ICON "${CMAKE_SOURCE_DIR}/src/resources/icons/vnote.ico")
|
||||
|
||||
# NSIS windows installer
|
||||
find_program(NSIS_PATH nsis PATH_SUFFIXES nsis)
|
||||
if(NSIS_PATH)
|
||||
list(APPEND CPACK_GENERATOR NSIS)
|
||||
message(STATUS " + NSIS YES ")
|
||||
set(CPACK_NSIS_DISPLAY_NAME ${CPACK_PACKAGE_NAME})
|
||||
set(CPACK_NSIS_MUI_ICON "${PACKAGE_ICON}")
|
||||
set(CPACK_NSIS_MUI_HEADERIMAGE_BITMAP "${PACKAGE_ICON}")
|
||||
set(CPACK_NSIS_CONTACT "${CPACK_PACKAGE_CONTACT}")
|
||||
set(CPACK_NSIS_MODIFY_PATH ON)
|
||||
else()
|
||||
message(STATUS " + NSIS NO ")
|
||||
endif()
|
||||
|
||||
# NuGet package
|
||||
find_program(NUGET_EXECUTABLE nuget)
|
||||
if(NUGET_EXECUTABLE)
|
||||
list(APPEND CPACK_GENERATOR NuGET)
|
||||
message(STATUS " + NuGET YES ")
|
||||
set(CPACK_NUGET_PACKAGE_NAME "VNote")
|
||||
else()
|
||||
message(STATUS " + NuGET NO ")
|
||||
endif()
|
||||
|
||||
# Bundle Library Files
|
||||
if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG")
|
||||
set(WINDEPLOYQT_ARGS --debug)
|
||||
else()
|
||||
set(WINDEPLOYQT_ARGS --release)
|
||||
endif()
|
||||
file(MAKE_DIRECTORY "${CPACK_PACKAGE_DIRECTORY}/_qtstaff")
|
||||
add_custom_command(TARGET bundle PRE_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E
|
||||
env PATH="${_qt_bin_dir}" "${WINDEPLOYQT_EXECUTABLE}"
|
||||
${WINDEPLOYQT_ARGS}
|
||||
--verbose 0
|
||||
--no-compiler-runtime
|
||||
--no-angle
|
||||
--no-opengl-sw
|
||||
--dir "${CPACK_PACKAGE_DIRECTORY}/_qtstaff"
|
||||
$<TARGET_FILE:VNote>
|
||||
COMMENT "Deploying Qt..."
|
||||
)
|
||||
install(DIRECTORY "${CPACK_PACKAGE_DIRECTORY}/_qtstaff/" DESTINATION bin)
|
||||
set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE)
|
||||
include(InstallRequiredSystemLibraries)
|
||||
|
||||
elseif (APPLE)
|
||||
#--------------------------------------------------------------------------
|
||||
# Apple specific
|
||||
find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${_qt_bin_dir}" DOC "Path to the macdeployqt utility")
|
||||
find_program(MACDEPLOYQTFIX_EXECUTABLE macdeployqtfix.py HINTS "${_qt_bin_dir}" DOC "Path to the macdeployqtfix utility")
|
||||
find_package(Python2 REQUIRED COMPONENTS Interpreter)
|
||||
message(STATUS "Package generation - Mac OS X")
|
||||
set(CPACK_PACKAGE_ICON ${CMAKE_SOURCE_DIR}/resources/Icon.icns)
|
||||
list(APPEND CPACK_GENERATOR External)
|
||||
set(CPACK_EXTERNAL_ENABLE_STAGING ON)
|
||||
set(CPACK_SET_DESTDIR ON)
|
||||
message(STATUS " + macdeployqt -dmg YES ")
|
||||
set(EXTERNAL_WORK "_CPack_Packages/Darwin/External/${CPACK_PACKAGE_NAME}-${PROJECT_VERSION}-Darwin")
|
||||
set(APP_BUNDLE "${CPACK_PACKAGE_DIRECTORY}/${EXTERNAL_WORK}/${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app")
|
||||
string(FIND "${Qt5_DIR}" "/cmake/Qt5" _STR_LOC)
|
||||
string(SUBSTRING "${Qt5_DIR}" 0 ${_STR_LOC} _QT_LIB_ROOT)
|
||||
file(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/CPackExternal.cmake"
|
||||
CONTENT "execute_process(COMMAND \"${MACDEPLOYQT_EXECUTABLE}\" \"${APP_BUNDLE}\" -always-overwrite -verbose=2 -libpath=${_QT_LIB_ROOT})
|
||||
execute_process(COMMAND ${Python2_EXECUTABLE} \"${MACDEPLOYQTFIX_EXECUTABLE}\" \"${APP_BUNDLE}/Contents/MacOS/VNote\" ${_QT_LIB_ROOT} -v)
|
||||
execute_process(COMMAND \"${MACDEPLOYQT_EXECUTABLE}\" \"${APP_BUNDLE}/Contents/Frameworks/QtWebEngineCore.framework/Versions/5/Helpers/QtWebEngineProcess.app\" -verbose=2 -libpath=${_QT_LIB_ROOT})
|
||||
execute_process(COMMAND ${Python2_EXECUTABLE} \"${MACDEPLOYQTFIX_EXECUTABLE}\" \"${APP_BUNDLE}/Contents/Frameworks/QtWebEngineCore.framework/Versions/5/Helpers/QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess\" ${_QT_LIB_ROOT} -v)
|
||||
execute_process(COMMAND \"${MACDEPLOYQT_EXECUTABLE}\" \"${APP_BUNDLE}\" -dmg -verbose=2 -always-overwrite -libpath=${_QT_LIB_ROOT})"
|
||||
)
|
||||
set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_BINARY_DIR}/CPackExternal.cmake")
|
||||
include(InstallRequiredSystemLibraries)
|
||||
|
||||
else ()
|
||||
#-----------------------------------------------------------------------------
|
||||
# Linux specific
|
||||
list(APPEND CPACK_GENERATOR TBZ2 TXZ)
|
||||
message(STATUS "Package generation - UNIX")
|
||||
message(STATUS " + TBZ2 YES ")
|
||||
message(STATUS " + TXZ YES ")
|
||||
|
||||
find_program(RPMBUILD_PATH rpmbuild)
|
||||
if(RPMBUILD_PATH)
|
||||
message(STATUS " + RPM YES ")
|
||||
set(CPACK_GENERATOR "${CPACK_GENERATOR};RPM")
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
||||
else()
|
||||
message(STATUS " + RPM NO ")
|
||||
endif()
|
||||
|
||||
find_program(DEBUILD_PATH debuild)
|
||||
if(DEBUILD_PATH)
|
||||
list(APPEND CPACK_GENERATOR DEB)
|
||||
message(STATUS " + DEB YES ")
|
||||
# use default, that is an output of `dpkg --print-architecture`
|
||||
#set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64")
|
||||
set(CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION TRUE)
|
||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://tamlok.github.io/vnote")
|
||||
if(Qt5_DIR STREQUAL "/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/cmake/Qt5" )
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
else()
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5core5a, libqt5gui5, libqt5positioning5, libqt5webenginewidgets5")
|
||||
endif()
|
||||
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "graphviz")
|
||||
set(CPACK_DEBIAN_PACKAGE_SUGGESTS "libjs-mathjax")
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
|
||||
endif()
|
||||
|
||||
find_program(LINUXDEPLOY_EXECUTABLE linuxdeploy linuxdeploy-x86_64.AppImage HINTS "${_qt_bin_dir}")
|
||||
if(LINUXDEPLOY_EXECUTABLE)
|
||||
message(STATUS " + AppImage YES ")
|
||||
find_path(NSS3_PLUGIN_PATH NAMES libsoftokn3.so PATHS /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} /usr/lib /usr/local/lib
|
||||
PATH_SUFFIXES nss NO_DEFAULT_PATH)
|
||||
set(CPACK_GENERATOR "External;${CPACK_GENERATOR}")
|
||||
# run make DESTDIR=<CPACK_INSTALL_PREFIX> install before run package script
|
||||
set(CPACK_EXTERNAL_ENABLE_STAGING ON)
|
||||
set(CPACK_SET_DESTDIR ON)
|
||||
set(_EXTERNAL_PACKDIR ${CPACK_PACKAGE_DIRECTORY}/_CPack_Packages/Linux/External/${CPACK_PACKAGE_NAME}-${PROJECT_VERSION}-Linux)
|
||||
string(FIND "${Qt5_DIR}" "/cmake/Qt5" _STR_LOC)
|
||||
string(SUBSTRING "${Qt5_DIR}" 0 ${_STR_LOC} _QT_LIB_ROOT)
|
||||
set(_LINUXDEPLOY_ENV "QMAKE=${_qmake_executable}" "LD_LIBRARY_PATH=${_QT_LIB_ROOT}:$ENV{LD_LIBRARY_PATH}")
|
||||
file(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/CPackExternal.cmake"
|
||||
CONTENT "execute_process(COMMAND ${CMAKE_COMMAND} -E env ${_LINUXDEPLOY_ENV} \"${LINUXDEPLOY_EXECUTABLE}\" --plugin qt --output appimage
|
||||
-v1
|
||||
--appdir ${_EXTERNAL_PACKDIR} -e ${_EXTERNAL_PACKDIR}${CMAKE_INSTALL_PREFIX}/bin/VNote
|
||||
--desktop-file ${_EXTERNAL_PACKDIR}${CMAKE_INSTALL_PREFIX}/share/applications/vnote.desktop
|
||||
-i ${CMAKE_SOURCE_DIR}/src/resources/icons/16x16/vnote.png -i ${CMAKE_SOURCE_DIR}/src/resources/icons/32x32/vnote.png
|
||||
-i ${CMAKE_SOURCE_DIR}/src/resources/icons/48x48/vnote.png -i ${CMAKE_SOURCE_DIR}/src/resources/icons/64x64/vnote.png
|
||||
-i ${CMAKE_SOURCE_DIR}/src/resources/icons/128x128/vnote.png -i ${CMAKE_SOURCE_DIR}/src/resources/icons/256x256/vnote.png
|
||||
-i ${CMAKE_SOURCE_DIR}/src/resources/icons/vnote.svg
|
||||
-l ${NSS3_PLUGIN_PATH}/libfreebl3.so -l ${NSS3_PLUGIN_PATH}/libfreeblpriv3.so -l ${NSS3_PLUGIN_PATH}/libnssckbi.so
|
||||
-l ${NSS3_PLUGIN_PATH}/libnssdbm3.so -l ${NSS3_PLUGIN_PATH}/libsoftokn3.so
|
||||
WORKING_DIRECTORY ${CPACK_PACKAGE_DIRECTORY})"
|
||||
)
|
||||
set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_BINARY_DIR}/CPackExternal.cmake")
|
||||
else()
|
||||
message(STATUS " + AppImage NO ")
|
||||
endif()
|
||||
|
||||
# set package icon
|
||||
set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/src/resources/icons/vnote.png")
|
||||
endif()
|
||||
|
||||
include(CPack)
|
227
README.md
@ -1,227 +0,0 @@
|
||||
# VNote
|
||||
[中文 Chinese](./README_zh.md) | [日本語 Japanese](./README_ja.md)
|
||||
|
||||
**VNote is a note-taking application that knows programmers and Markdown better.**
|
||||
|
||||
For more information, please visit [**VNote's Homepage**](https://tamlok.github.io/vnote).
|
||||
|
||||
Check this [showcase screencast](https://www.youtube.com/watch?v=EFCmCFZKxJ4) to know what you could do with VNote!
|
||||
|
||||

|
||||
|
||||
# Downloads
|
||||
Users from China can download the latest release of VNote from [Tianyi Netdisk](https://cloud.189.cn/t/Av67NvmEJVBv).
|
||||
|
||||
## Windows
|
||||
### Official Zip
|
||||

|
||||
|
||||
- [Github releases](https://github.com/tamlok/vnote/releases)
|
||||
- Latest builds on master: [  ](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
||||
|
||||
**NOT** supported in XP since QtWebEngineProcess used by VNote could not work in XP.
|
||||
|
||||
### Scoop
|
||||
VNote can be installed from `extras` bucket of Scoop.
|
||||
|
||||
```shell
|
||||
scoop bucket add extras
|
||||
scoop install vnote
|
||||
scoop update vnote
|
||||
```
|
||||
|
||||
## Linux
|
||||
### AppImage
|
||||
[](https://travis-ci.org/tamlok/vnote)
|
||||
|
||||
There is an AppImage format standalone executable of VNote for major Linux distributions. **Any help for packaging and distribution on Linux is appreciated!**
|
||||
|
||||
- [Github releases](https://github.com/tamlok/vnote/releases)
|
||||
- Latest builds on master: [  ](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
||||
|
||||
### openSUSE
|
||||
Currently `vnote` on openSUSE Tumbleweed can be installed from `home:opensuse_zh` project on OBS. You can execute the following command directly:
|
||||
|
||||
```shell
|
||||
sudo zypper ar https://download.opensuse.org/repositories/home:/opensuse_zh/openSUSE_Tumbleweed/ home:opensuse_zh
|
||||
sudo zypper ref
|
||||
sudo zypper in vnote
|
||||
```
|
||||
|
||||
For other architectures, please search for `vnote` at [software.opensuse.org](https://software.opensuse.org).
|
||||
|
||||
We don't support Leap 42 and below due to the Qt version. Please use AppImage or build it yourself.
|
||||
|
||||
### Arch Linux
|
||||
VNote on Arch Linux can be installed from the AUR as [vnote](https://aur.archlinux.org/packages/vnote-bin/):
|
||||
|
||||
```shell
|
||||
git clone https://aur.archlinux.org/vnote-bin.git
|
||||
cd vnote-bin
|
||||
makepkg -sic
|
||||
```
|
||||
|
||||
There is also a development version that tracks the latest master [vnote-git](https://aur.archlinux.org/packages/vnote-git/).
|
||||
|
||||
### NixOS
|
||||
Thank @kuznero for packaging VNote in NixOS. It should be available in `unstable` and `unstable-small` channels.
|
||||
|
||||
## MacOS
|
||||
[](https://travis-ci.org/tamlok/vnote)
|
||||
|
||||
- [Github releases](https://github.com/tamlok/vnote/releases)
|
||||
- Latest builds on master: [  ](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
||||
|
||||
You can also install VNote using homebrew, through the cask tap:
|
||||
|
||||
```shell
|
||||
brew cask install vnote
|
||||
```
|
||||
|
||||
# Description
|
||||
**VNote** is a Qt-based, free and open source note-taking application, focusing on Markdown. VNote is designed to provide a comfortable editing experience, especially for programmers.
|
||||
|
||||
VNote is **NOT** just a simple editor for Markdown. By providing notes management, VNote makes taking notes in Markdown simpler and more pleasant.
|
||||
|
||||
Utilizing Qt, VNote could run on **Linux**, **Windows**, and **macOS**.
|
||||
|
||||

|
||||
|
||||
# Supports
|
||||
- [Github issues](https://github.com/tamlok/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: VNote笔记 (vnote_md);
|
||||

|
||||
|
||||
# Highlights
|
||||
- Powerful **full-text search**;
|
||||
- **Universal Entry** to reach anything just by typing;
|
||||
- Inserting images directly from clipboard;
|
||||
- Syntax highlights of fenced code blocks in both **edit** and **read** mode;
|
||||
- Powerful **In-Place Preview** for images, diagrams, and formulas;
|
||||
- Side-by-side live preview for diagrams;
|
||||
- Outline in both edit and read mode;
|
||||
- Custom styles in both edit and read mode;
|
||||
- **Vim** mode and a set of powerful shortcuts;
|
||||
- Infinite levels of folders;
|
||||
- Multiple tabs and splitting windows;
|
||||
- [Mermaid](http://knsv.github.io/mermaid/), [Flowchart.js](http://flowchart.js.org/), [MathJax](https://www.mathjax.org/), [PlantUML](http://plantuml.com/), and [Graphviz](http://www.graphviz.org/);
|
||||
- Supports HiDPI;
|
||||
- Attachments of notes;
|
||||
- Themes and dark mode;
|
||||
- Rich and extensible export, such as HTML, PDF, PDF (All In One), and images;
|
||||
- GitHub and WeChat image hosting;
|
||||
|
||||
# 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 PullRequest to make VNote perfect.
|
||||
- Last, really appreciate your donate to VNote if you think VNote really helps you and you want to help VNote.
|
||||
|
||||
**PayPal**: [PayPal.Me/vnotemd](https://www.paypal.me/vnotemd)
|
||||
|
||||
**Alipay**: `tamlokveer@gmail.com`
|
||||
|
||||
<img src="screenshots/alipay.png" width="256px" height="256px" />
|
||||
|
||||
**WeChat**
|
||||
|
||||
<img src="screenshots/wechat_pay.png" width="256px" height="256px" />
|
||||
|
||||
Thank [users who donated to VNote](https://github.com/tamlok/vnote/wiki/Donate-List)!
|
||||
|
||||
# Why VNote
|
||||
## Markdown Editor & Notes Management
|
||||
VNote tries to be a powerful Markdown editor with notes management, or a note-taking application with pleasant Markdown support. If you are a fan of Markdown and enjoy writing Markdown notes for study, work, and life, VNote is the right tool for you.
|
||||
|
||||
## Pleasant Markdown Experience
|
||||
### Insights About Markdown
|
||||
Markdown, as a simple mark language, unlike rich text, was born with a **gap between edit and read**. There are about three ways to handle this gap:
|
||||
|
||||
1. As one extreme, some editors just treat Markdown as **plain text**. Users may lose themselves in the messy black characters. It is hard to keep track of the information of the note.
|
||||
2. Most Markdown editors use two panels to **edit and preview Markdown notes simultaneously**. It makes things easier since users could see a pleasant typesetting and layout while editing the text. However, two panels may occupy the whole screen and users keep moving eyes left and right which will be a big distraction.
|
||||
3. As another extreme, some editors convert the Markdown elements in place immediately after the typing, which makes editing Markdown just like editing rich text document in Word.
|
||||
|
||||
Since most editors choose the second way to handle the gap, people always think of preview when it comes to Markdown. It may be a misunderstanding about Markdown. Designed as a simple mark language, Markdown is intended to help to keep track of the information of the text when editing and provide a beautiful typesetting when reading after being converted to HTML.
|
||||
|
||||
### Tradeoff: VNote's Way
|
||||
VNote tries to minimize the gap and provide the best-effort *WYSIWYG* for Markdown via tuned **syntax highlights** and some other features. By helping to keep track of the content, there is no need to preview or alter the text immediately after being typed.
|
||||
|
||||
# Features
|
||||
## Notebook-Based Notes Management
|
||||
VNote uses **notebooks** to hold your notes. Like OneNote, a notebook can be hosted on any location on your system. A notebook is designed to represent one account. For example, you could have one notebook hosted on local file system and another notebook hosted on an OwnCloud server. This is really useful when notes require different levels of security.
|
||||
|
||||
A notebook corresponds to a self-contained folder (called the *Root Folder* of the notebook) in the file system. You could copy the folder to another location (or on another computer) and import it into VNote.
|
||||
|
||||
A notebook could have infinite levels of folders. VNote supports copying or moving folders or notes within or between notebooks.
|
||||
|
||||
## Simple Notes Management
|
||||
All your notes are managed by some plaintext configuration files and stored as plaintext files. You could access your notes without VNote. You could use external file synchronization services to synchronize your notes and import them on another machine.
|
||||
|
||||
VNote supports both Markdown (suffix `md`) and rich text notes.
|
||||
|
||||
## Syntax Highlight
|
||||
VNote supports accurate syntax highlight for Markdown. Tuning the style of the highlight, VNote enables you to keep track of your document easily.
|
||||
|
||||
VNote also supports **syntax highlight for fenced code blocks** in edit mode in Markdown, which is **superior** than almost all current Markdown editors.
|
||||
|
||||

|
||||
|
||||
## In-Place Preview
|
||||
VNote supports powerful **in-place preview** for images, diagrams, and formulas in edit mode.
|
||||
|
||||

|
||||
|
||||
## Pleasant Image Experience
|
||||
Just paste your image into the Markdown note, VNote will manage all other stuffs. VNote stores images in a specified folder in the same folder with the note. VNote will pop up a window to preview the image when you insert an image. Furthermore, VNote will delete the useless image files automatically after you remove the image links.
|
||||
|
||||
## Universal Entry & Full-Text Search
|
||||
VNote has powerful built-in full-text search with **Regular Expression** and **Fuzzy Search** support. The search could be executed within all notebooks, current notebook, or current folder, targeted at name or content.
|
||||
|
||||
Like the `CtrlP` in Vim, VNote supports **Universal Entry** to reach anything by typing in a simple input.
|
||||
|
||||

|
||||
|
||||
## Interactive Outline Viewer In Read & Edit Mode
|
||||
VNote provides a user-friendly outline viewer for both edit and view mode. The outline viewer is a responsive item tree instead of a segment of HTML.
|
||||
|
||||
## Powerful Shortcuts
|
||||
VNote supports many pleasant and powerful shortcuts which facilitate your editing, including **Vim Mode**, **Captain Mode**, and **Navigation Mode** which enable you to work without the mouse.
|
||||
|
||||
Please refer to the [shortcuts help](src/resources/docs/shortcuts_en.md) in the help menu for more details.
|
||||
|
||||
## Highly Configurable
|
||||
In VNote, almost everything is configurable, such as background color, font, and Markdown style. VNote uses a plaintext file to record all your configuration, so you could just copy that file to initialize a new VNote on another computer.
|
||||
|
||||
# Dependencies
|
||||
- [Qt 5.9](http://qt-project.org) (L-GPL v3)
|
||||
- [PEG Markdown Highlight](http://hasseg.org/peg-markdown-highlight/) (MIT License)
|
||||
- [Hoedown 3.0.7](https://github.com/hoedown/hoedown/) (ISC License)
|
||||
- [Marked 0.5.1](https://github.com/markedjs/marked) (MIT License)
|
||||
- [Highlight.js](https://github.com/isagalaev/highlight.js/) (BSD License)
|
||||
- [Ionicons 2.0.1](https://github.com/driftyco/ionicons/) (MIT License)
|
||||
- [markdown-it 8.3.1](https://github.com/markdown-it/markdown-it) (MIT License)
|
||||
- [markdown-it-headinganchor 1.3.0](https://github.com/adam-p/markdown-it-headinganchor) (MIT License)
|
||||
- [markdown-it-task-lists 1.4.0](https://github.com/revin/markdown-it-task-lists) (ISC License)
|
||||
- [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) (MIT License)
|
||||
- [markdown-it-sub](https://github.com/markdown-it/markdown-it-sub) (MIT License)
|
||||
- [markdown-it-sup](https://github.com/markdown-it/markdown-it-sup) (MIT License)
|
||||
- [markdown-it-front-matter](https://github.com/craigdmckenna/markdown-it-front-matter) (MIT License)
|
||||
- [markdown-it-imsize](https://github.com/tatsy/markdown-it-imsize) (Unknown) (Thanks @Kinka for help)
|
||||
- [markdown-it-emoji](https://github.com/markdown-it/markdown-it-emoji) (MIT License)
|
||||
- [markdown-it-texmath](https://github.com/goessner/markdown-it-texmath) (MIT License)
|
||||
- [markdown-it-container 2.0.0](https://github.com/markdown-it/markdown-it-container) (MIT License)
|
||||
- [mermaid 7.0.0](https://github.com/knsv/mermaid) (MIT License)
|
||||
- [MathJax](https://www.mathjax.org/) (Apache-2.0)
|
||||
- [showdown](https://github.com/showdownjs/showdown) (MIT License)
|
||||
- [flowchart.js](https://github.com/adrai/flowchart.js) (MIT License)
|
||||
- [PlantUML](http://plantuml.com/) (MIT License)
|
||||
- [dom-to-image](https://github.com/tsayen/dom-to-image) (MIT License)
|
||||
- [turndown](https://github.com/domchristie/turndown) (MIT License)
|
||||
|
||||
# License
|
||||
VNote is licensed under the [MIT license](http://opensource.org/licenses/MIT).
|
220
README_ja.md
@ -1,220 +0,0 @@
|
||||
# VNote
|
||||
[中文 Chinese](./README_zh.md) | [英語 English](./README.md)
|
||||
|
||||
**VNote は、プログラマとMarkdownをよく理解するノート作成アプリケーションです。**
|
||||
|
||||
詳細情報は、[**VNoteのホームページ**](https://tamlok.github.io/vnote)に訪問してください。
|
||||
|
||||

|
||||
|
||||
# ダウンロード
|
||||
## Windows
|
||||
### Zipアーカイブ
|
||||

|
||||
|
||||
- [Githubリリース](https://github.com/tamlok/vnote/releases)
|
||||
- 開発中の最新ビルド: [](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
||||
|
||||
Windows XPはサポート**されません。**
|
||||
|
||||
### Scoop
|
||||
VNote can be installed from `extras` bucket of Scoop.
|
||||
|
||||
```shell
|
||||
scoop bucket add extras
|
||||
scoop install vnote
|
||||
scoop update vnote
|
||||
```
|
||||
|
||||
## Linux
|
||||
### AppImage
|
||||
[](https://travis-ci.org/tamlok/vnote)
|
||||
|
||||
主要Linuxディストリビューションむけには、AppImageのVNoteスタンドアロン実行ファイルがあります。**Linuxのパッケージングと配布の支援を歓迎します!**
|
||||
|
||||
- [Githubリリース](https://github.com/tamlok/vnote/releases)
|
||||
- 開発中の最新ビルド: [](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
||||
|
||||
### openSUSE
|
||||
現在、OpenSUSE Tumbleweedの`vnote`は、OBS上の`home:openuse_zh`プロジェクトからインストールすることができます。次のコマンドを直接実行できます。
|
||||
|
||||
```shell
|
||||
sudo zypper ar https://download.opensuse.org/repositories/home:/opensuse_zh/openSUSE_Tumbleweed/ home:opensuse_zh
|
||||
sudo zypper ref
|
||||
sudo zypper in vnote
|
||||
```
|
||||
|
||||
その他のアーキテクチャについては、[software.opensuse.org](https://software.opensuse.org)で`vnote`を検索してください。
|
||||
|
||||
Qtバージョンの関係で、Leap 42およびそれ以下のバージョンをサポートしていません。AppImageを使ったり、自分でビルドしてください。
|
||||
|
||||
### Arch Linux
|
||||
Arch Linux上のVNoteは、AURから[vnote](https://aur.archlinux.org/packages/vnote-bin/)のインストールができます。
|
||||
|
||||
```shell
|
||||
git clone https://aur.archlinux.org/vnote-bin.git
|
||||
cd vnote-bin
|
||||
makepkg -sic
|
||||
```
|
||||
|
||||
また、最新のマスター[vnote git](https://aur.archlinux.org/packages/vnote-git/)を追跡する開発バージョンもあります。
|
||||
|
||||
### NixOS
|
||||
Thank @kuznero for packaging VNote in NixOS. It should be available in `unstable` and `unstable-small` channels.
|
||||
|
||||
## MacOS
|
||||
[](https://travis-ci.org/tamlok/vnote)
|
||||
|
||||
- [Githubリリース](https://github.com/tamlok/vnote/releases)
|
||||
- 開発中の最新ビルド: [](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
||||
|
||||
cask tapを通じ、homebrewをつかってVNoteをインストールすることもできます。
|
||||
|
||||
```shell
|
||||
brew cask install vnote
|
||||
```
|
||||
|
||||
# 説明
|
||||
**VNote**は、Qtベースの無料のオープンソースのノート作成アプリケーションで、Markdownに焦点を当てています。VNoteは、特にプログラマーにとって快適な編集エクスペリエンスを提供するように設計されています。
|
||||
|
||||
VNoteは、Markdownの単純なエディタ**ではありません**。ノート管理を提供することで、VNoteはマークダウンのノート作成をより簡単に、より楽しくすることができます。
|
||||
|
||||
Qtを使用し、VNoteは、**Linux**, **Windows**そして、**macOS**で利用できます。
|
||||
|
||||

|
||||
|
||||
# サポート
|
||||
- [Github issues](https://github.com/tamlok/vnote/issues);
|
||||
- Email: `tamlokveer at gmail.com`;
|
||||
- [Slack](https://join.slack.com/t/vnote/shared_invite/enQtNDg2MzY0NDg3NzI4LTVhMzBlOTY0YzVhMmQyMTFmZDdhY2M3MDQxYTBjOTA2Y2IxOGRiZjg2NzdhMjkzYmUyY2VkMWJlZTNhMTQyODU);
|
||||
- [Telegram](https://t.me/vnotex);
|
||||
|
||||
# ハイライト
|
||||
- 強力な**全文検索**;
|
||||
- **ユニバーサルエントリ**は、入力するだけで何かに到達することができます。
|
||||
- 直接クリップボードから画像を挿入;
|
||||
- **編集**と**読み込み**の両方で、フェンシングされたコードブロックの構文のハイライト表示;
|
||||
- 編集モードでの画像、図、数式の強力な**その場プレビュー**;
|
||||
- 図の横並びのライブプレビュー;
|
||||
- 編集モードと読み込みモードの両方でアウトラインを作成;
|
||||
- 編集モードと読み込みモードの両方でカスタムスタイル;
|
||||
- **Vim**モードと協力なショートカットキー;
|
||||
- 無限レベルのフォルダ;
|
||||
- 複数のタブとウィンドウ分割;
|
||||
- [Mermaid](http://knsv.github.io/mermaid/), [Flowchart.js](http://flowchart.js.org/), [MathJax](https://www.mathjax.org/), [PlantUML](http://plantuml.com/), and [Graphviz](http://www.graphviz.org/);
|
||||
- HiDPIのサポート;
|
||||
- ノートの添付ファイル;
|
||||
- テーマとダーク・モード;
|
||||
- HTML、PDF、PDF(All In One)、イメージなど、拡張性と拡張性に優れたエクスポート
|
||||
|
||||
# 寄付
|
||||
VNoteの開発に、さまざまな方法で支援できます。
|
||||
|
||||
- VNoteの開発に注目し、改善のためにフィードバックを送信する
|
||||
- VNoteを友達に広める。人気がひろまることは、開発者にとって大きなエネルギーになります。
|
||||
- VNoteの開発に参加し、VNoteを完全にするためにPullRequestを送信します。
|
||||
- VNoteが本当に役に立ち、VNoteを助けたいと思ったら、VNoteへの寄付を本当に感謝します。
|
||||
|
||||
**PayPal**: [PayPal.Me/vnotemd](https://www.paypal.me/vnotemd)
|
||||
|
||||
**Alipay**: `tamlokveer@gmail.com`
|
||||
|
||||
<img src="screenshots/alipay.png" width="256px" height="256px" />
|
||||
|
||||
**WeChat**
|
||||
|
||||
<img src="screenshots/wechat_pay.png" width="256px" height="256px" />
|
||||
|
||||
[VNoteヘ寄付されたユーザ](https://github.com/tamlok/vnote/wiki/Donate-List)に感謝いたします!
|
||||
|
||||
# VNoteのわけ
|
||||
## Markdown編集とノート管理
|
||||
VNoteは、ノート管理機能のついた協力なMarkdownエディタ、あるいは快適なMarkdownサポートつきのノート作成アプリケーションを目指しています。もしMarkdownのファンで、勉強や仕事、人生のためにMarkdownのノートを書くのなら、VNoteはあなたにとって正しいツールです。
|
||||
|
||||
## 快適なマークダウン体験
|
||||
### Markdownについての洞察
|
||||
Markdownは、リッチテキストとは異なり、編集と読み取りの間にある**ギャップ**を持って生まれた、シンプルなマークアップ言語です。このギャップを扱うには、3つの方法があります。
|
||||
|
||||
1. 一つの極端な例として、エディタの中には、Markdownを**プレーンテキスト**として扱うだけのものもある。ユーザーは、ごちゃごちゃした黒い文字で自分を失うことがあります。メモの情報を追跡するのは難しくなる。
|
||||
2. ほとんどのMarkdownエディターは、**編集とプレビューを同時に行うために、2つのパネルを使用します**。ユーザーはテキストの編集中に、楽しいタイプの設定とレイアウトを見ることができるので、より簡単になります。しかし、2つのパネルが画面全体を占め、ユーザは目を左右に動かすことになり、大きな混乱を招くことになりかねません。
|
||||
3. もう一つの極端な点として、編集の直後にMarkdown要素を変換するエディタもあり、Wordのリッチテキスト文書を編集するのと同じように、Markdownを編集することができます。
|
||||
|
||||
ほとんどのエディタは、ギャップを処理するための2番目の方法を選んでいるので、Markdownをつかうときは、常にプレビューを考えていることになります。これは、Markdownの誤解かもしれません。シンプルなマーク言語として設計されたMarkdownは、編集時にテキストの情報を記録し、HTMLに変換された後に美しいタイプの設定を提供することを目的としています。
|
||||
|
||||
### トレードオフ:VNoteの方法
|
||||
VNoteは、チューニングされた**構文ハイライト**とその他の機能によって、ギャップを最小限に抑えて、Markdownのためにベストエフォート*WYSIWYG*を提供します。コンテンツを追跡することを助けることにより、テキストを入力した直後にテキストをプレビューしたり変更する必要がなくなります。
|
||||
|
||||
# 機能
|
||||
## ノートブック・ベースのノート管理
|
||||
VNoteは、**ノートブック**を使用してノートを管理することができます。OneNoteと同様に、ノートブックはシステム上の任意の場所にホストすることができます。ノートブックは1つのアカウントを表すように設計されています。たとえば、ローカル・ファイルシステムで管理されているノートブックと、OwnCloudサーバ上に保存されている別のノートブックをもつことができます。これは、メモが異なるレベルのセキュリティを必要とする場合に、実際に役立ちます。
|
||||
|
||||
ノートブックは、ファイルシステム内の自己完結型フォルダ(ノートブックの*Rootフォルダ*と呼ばれます)に対応しています。フォルダを別の場所(または別のコンピュータ)にコピーして、VNoteにインポートすることができます。
|
||||
|
||||
ノートブックは、無限レベルのフォルダを持つことができます。VNoteでは、ノートブック内またはノートブック間でフォルダやノートをコピーまたは移動することができます。
|
||||
|
||||
## シンプルなノート管理
|
||||
すべてのメモは、平文の設定ファイルによって管理され、平文ファイルとして格納されます。ノートは、VNOTEなしでアクセスできます。外部のファイル同期サービスを使用して、ノートを同期させたり、別のマシンにインポートすることができます。
|
||||
|
||||
VNoteは、Markdown(拡張子`md`)とリッチテキストノートの両方をサポートしています。
|
||||
|
||||
## 構文ハイライト
|
||||
VNoteでは、Markdownの正確な構文強調表示をサポートしています。ハイライトのスタイルを調整することで、VNoteを使用してドキュメントを簡単に追跡することができます。
|
||||
|
||||
また、VNoteは、**Fenceされたコードブロックに対する**構文ハイライトをサポートしています。これは、現在のあらゆるMarkdownエディタよりも**優れています**。
|
||||
|
||||

|
||||
|
||||
## インプレイスプレビュー
|
||||
VNoteは、編集モードでのイメージ、図、数式の強力な**その場プレビュー**をサポートしています。
|
||||
|
||||

|
||||
|
||||
## 快適なイメージ体験
|
||||
画像をMarkdownに貼り付けするだけで、VNoteは他のすべてのものを管理することになります。VNoteは、同じフォルダ内の指定されたフォルダに、ノートとともに画像を格納します。VNoteは、画像を挿入するときに画像プレビューするウィンドウをポップアップ表示させます。また、VNoteは、画像リンクを削除した後に、自動的に画像ファイルを削除します。
|
||||
|
||||
## ユニバーサルエントリと全文検索
|
||||
VNote には、**正規表現**と**ファジー検索**サポートのある、強力な全文検索が組み込まれています。検索は、すべてのノートブック、現在のノートブック、または現在のフォルダ、名前またはコンテンツを対象に実行することができます。
|
||||
|
||||
VNoteは、Vimでの`Ctrl+P`のように、**ユニバーサルエントリ**機能をサポートし、シンプルな入力であらゆる機能へ到達できます。
|
||||
|
||||

|
||||
|
||||
## 読み込み&編集モードのインタラクティブなアウトライン表示
|
||||
VNoteでは、編集モードと閲覧モードの両方でユーザフレンドリなアウトラインビューアが提供されています。アウトライン・ビューアは、HTMLのセグメントではなく、応答するアイテム・ツリーです。
|
||||
|
||||
## 強力なショートカット
|
||||
VNoteは、<x/>Vim Mode**、**キャプテンモード**、<x/>、**ナビゲーションモード**など、編集を容易にする強力なショートカットをサポートしています。これにより、マウスを使用せずに作業を行うことができます。
|
||||
|
||||
詳細については、ヘルプメニューの[ショートカットヘルプ](src/resources/docs/shortcuts_en.md)を参照してください。
|
||||
|
||||
## 柔軟に調整可能
|
||||
VNoteでは、背景色、フォント、マークダウンなど、ほとんどすべてのものが調整可能です。VNoteは平文ファイルを使ってすべての構成を記録しますので、そのファイルをコピーすれば、別のコンピュータで新しいVNoteを初期化することができます。
|
||||
|
||||
# 依存関係
|
||||
- [Qt 5.9](http://qt-project.org) (L-GPL v3)
|
||||
- [PEG Markdown Highlight](http://hasseg.org/peg-markdown-highlight/) (MIT License)
|
||||
- [Hoedown 3.0.7](https://github.com/hoedown/hoedown/) (ISC License)
|
||||
- [Marked 0.5.1](https://github.com/markedjs/marked) (MIT License)
|
||||
- [Highlight.js](https://github.com/isagalaev/highlight.js/) (BSD License)
|
||||
- [Ionicons 2.0.1](https://github.com/driftyco/ionicons/) (MIT License)
|
||||
- [markdown-it 8.3.1](https://github.com/markdown-it/markdown-it) (MIT License)
|
||||
- [markdown-it-headinganchor 1.3.0](https://github.com/adam-p/markdown-it-headinganchor) (MIT License)
|
||||
- [markdown-it-task-lists 1.4.0](https://github.com/revin/markdown-it-task-lists) (ISC License)
|
||||
- [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) (MIT License)
|
||||
- [markdown-it-sub](https://github.com/markdown-it/markdown-it-sub) (MIT License)
|
||||
- [markdown-it-sup](https://github.com/markdown-it/markdown-it-sup) (MIT License)
|
||||
- [markdown-it-front-matter](https://github.com/craigdmckenna/markdown-it-front-matter) (MIT License)
|
||||
- [markdown-it-imsize](https://github.com/tatsy/markdown-it-imsize) (Unknown) (Thanks @Kinka for help)
|
||||
- [markdown-it-emoji](https://github.com/markdown-it/markdown-it-emoji) (MIT License)
|
||||
- [markdown-it-texmath](https://github.com/goessner/markdown-it-texmath) (MIT License)
|
||||
- [markdown-it-container 2.0.0](https://github.com/markdown-it/markdown-it-container) (MIT License)
|
||||
- [mermaid 7.0.0](https://github.com/knsv/mermaid) (MIT License)
|
||||
- [MathJax](https://www.mathjax.org/) (Apache-2.0)
|
||||
- [showdown](https://github.com/showdownjs/showdown) (Unknown)
|
||||
- [flowchart.js](https://github.com/adrai/flowchart.js) (MIT License)
|
||||
- [PlantUML](http://plantuml.com/) (MIT License)
|
||||
- [dom-to-image](https://github.com/tsayen/dom-to-image) (MIT License)
|
||||
- [turndown](https://github.com/domchristie/turndown) (MIT License)
|
||||
|
||||
# ライセンス
|
||||
VNoteは、[MITライセンス](http://opensource.org/licenses/MIT)の下でライセンスされています。
|
228
README_zh.md
@ -1,228 +0,0 @@
|
||||
# VNote
|
||||
[英文 English](./README.md) | [日本語 Japanese](./README_ja.md)
|
||||
|
||||
**VNote 是一个更懂程序员和 Markdown 的笔记软件!**
|
||||
|
||||
更多信息,请访问 [**VNote 主页**](https://tamlok.github.io/vnote) 。
|
||||
|
||||
观看[展示录屏](https://www.bilibili.com/video/av77455284) !
|
||||
|
||||

|
||||
|
||||
# 下载
|
||||
国内的用户可以尝试在[天翼云盘](https://cloud.189.cn/t/Av67NvmEJVBv)下载 VNote 的最新发行版本。
|
||||
|
||||
## Windows
|
||||
### 官方压缩包
|
||||

|
||||
|
||||
- [Github releases](https://github.com/tamlok/vnote/releases)
|
||||
- master 分支的最新构建:[  ](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
||||
|
||||
VNote 不支持 **XP**,因为 QtWebEngineProcess 无法在 XP 上运行。
|
||||
|
||||
### Scoop
|
||||
VNote 也可以通过 Scoop 的 `extras` 仓库进行安装。
|
||||
|
||||
```shell
|
||||
scoop bucket add extras
|
||||
scoop install vnote
|
||||
scoop update vnote
|
||||
```
|
||||
|
||||
## Linux
|
||||
### AppImage
|
||||
[](https://travis-ci.org/tamlok/vnote)
|
||||
|
||||
VNote 当前为主要 Linux 发行版提供了一个 AppImage 格式的独立可执行文件。希望了解 Linux 系统下打包发布的开发人员能提供这方面进一步的帮助!
|
||||
|
||||
- [Github releases](https://github.com/tamlok/vnote/releases)
|
||||
- master 分支的最新构建:[  ](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
||||
|
||||
### openSUSE
|
||||
目前 openSUSE Tumbleweed 可以通过 OBS 上的 `home:opensuse_zh` 源直接安装。您可以直接执行以下命令:
|
||||
|
||||
```shell
|
||||
sudo zypper ar https://download.opensuse.org/repositories/home:/opensuse_zh/openSUSE_Tumbleweed/ home:opensuse_zh
|
||||
sudo zypper ref
|
||||
sudo zypper in vnote
|
||||
```
|
||||
|
||||
其他架构请直接在 [software.opensuse.org](https://software.opensuse.org) 搜索 `vnote`。
|
||||
|
||||
由于 Leap 42 及以下版本的 Qt 版本过低,我们无法在 OBS 上进行打包。请使用 AppImage 或自行构建。
|
||||
|
||||
### Arch Linux
|
||||
Arch Linux 可以通过 AUR 中的 [vnote-bin](https://aur.archlinux.org/packages/vnote-bin/) 进行安装:
|
||||
|
||||
```shell
|
||||
git clone https://aur.archlinux.org/vnote-bin.git
|
||||
cd vnote-bin
|
||||
makepkg -sic
|
||||
```
|
||||
|
||||
AUR 也提供一个和最新 master 分支同步的开发版本 [vnote-git](https://aur.archlinux.org/packages/vnote-git/) 。
|
||||
|
||||
## MacOS
|
||||
[](https://travis-ci.org/tamlok/vnote)
|
||||
|
||||
- [Github releases](https://github.com/tamlok/vnote/releases)
|
||||
- master 分支的最新构建:[  ](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
||||
|
||||
也可以通过 homebrew cask 进行安装:
|
||||
|
||||
```shell
|
||||
brew cask install vnote
|
||||
```
|
||||
|
||||
### NixOS
|
||||
感谢 @kuznero 在 NixOS 上打包 VNote。您可以在 `unstable` 和 `unstable-small` 频道中找到 VNote。
|
||||
|
||||
# 简介
|
||||
|
||||
**VNote** 是一个基于 Qt 框架的、免费的开源笔记软件。VNote 专注于 Markdown 的编辑与阅读,以提供舒适的编辑体验为设计目标。
|
||||
|
||||
VNote 不是一个简单的 Markdown 编辑器。通过提供笔记管理功能,VNote 使得编写 Markdown 笔记更简单和舒适!
|
||||
|
||||
得益于 Qt 框架,VNote 能够在主流操作系统上运行,包括 **Linux**, **Windows** 以及 **macOS** 。
|
||||
|
||||

|
||||
|
||||
# 支持
|
||||
- [Github issues](https://github.com/tamlok/vnote/issues);
|
||||
- 邮箱: `tamlokveer at gmail.com` ;
|
||||
- [Slack](https://join.slack.com/t/vnote/shared_invite/enQtNDg2MzY0NDg3NzI4LTVhMzBlOTY0YzVhMmQyMTFmZDdhY2M3MDQxYTBjOTA2Y2IxOGRiZjg2NzdhMjkzYmUyY2VkMWJlZTNhMTQyODU);
|
||||
- [Telegram](https://t.me/vnotex) ;
|
||||
- 微信公众号: VNote笔记(vnote_md);
|
||||

|
||||
|
||||
# 亮点
|
||||
- 强大的**全文搜索**;
|
||||
- **通用入口**,通过按键操控一切;
|
||||
- 直接从剪切板插入图片;
|
||||
- 编辑和阅读模式下代码块的语法高亮;
|
||||
- 强大的**原地预览**,包括图片、图表和公式;
|
||||
- 针对图表的两边实时预览;
|
||||
- 编辑和阅读模式下的大纲;
|
||||
- 自定义编辑和阅读模式的样式;
|
||||
- **Vim** 模式以及一系列强大的快捷键;
|
||||
- 无限层级的文件夹;
|
||||
- 多个标签页和窗口分割;
|
||||
- [Mermaid](http://knsv.github.io/mermaid/)、 [Flowchart.js](http://flowchart.js.org/)、 [MathJax](https://www.mathjax.org/)、 [PlantUML](http://plantuml.com/) 和 [Graphviz](http://www.graphviz.org/);
|
||||
- 支持高分辨率;
|
||||
- 笔记附件;
|
||||
- 主题以及深色模式;
|
||||
- 丰富、可扩展的导出,包括 HTML、PDF、PDF(多合一)和图片;
|
||||
- GitHub和微信图床;
|
||||
|
||||
# 捐赠
|
||||
您可以通过很多途径帮助 VNote 的开发。
|
||||
|
||||
- 持续关注 VNote 并反馈。
|
||||
- 向您的朋友们推广和传播 VNote。普及度是驱动开发者的一股强大力量。
|
||||
- 参与到 VNote 的开发中,发送 Pull Request 来帮助 VNote 变得更完美。
|
||||
- 最后,如果您觉得 VNote 真的帮助到您并且想回馈 VNote,非常感谢您的捐赠。
|
||||
|
||||
**PayPal**: [PayPal.Me/vnotemd](https://www.paypal.me/vnotemd)
|
||||
|
||||
**支付宝**: `tamlokveer@gmail.com`
|
||||
|
||||
<img src="screenshots/alipay.png" width="256px" height="256px" />
|
||||
|
||||
**微信**
|
||||
|
||||
<img src="screenshots/wechat_pay.png" width="256px" height="256px" />
|
||||
|
||||
非常感谢[这些用户](https://github.com/tamlok/vnote/wiki/Donate-List)对 VNote 的捐赠!
|
||||
|
||||
# 开发 VNote 的动机
|
||||
## Markdown 编辑器与笔记管理
|
||||
VNote 设计为带有笔记管理功能的 Markdown 编辑器,或者有良好 Markdown 支持的笔记软件。如果您喜欢 Markdown 并经常在学习、工作和生活中使用 Markdown 记录笔记,那么 VNote 就是一个适合您的工具。
|
||||
|
||||
## 舒适的 Markdown 体验
|
||||
### Markdown 的本质
|
||||
Markdown 作为一个简单标记语言,不像富文本,它的编辑和阅读有着与生俱来的隔阂。一般目前大概有三类方法来处理这个隔阂:
|
||||
|
||||
1. 作为一个极端,一些编辑器只是将 Markdown 作为无格式的纯文本处理。用户很容易在密密麻麻的黑漆漆的一片文字中找不着方向。
|
||||
2. 大部分编辑器使用两个面板来同时编辑和预览 Markdown 笔记。从而,用户可以在编辑的同时看到优美的排版和布局。但是,两个面板基本会占据了整个屏幕,而用户的目光焦点左右频繁移动,往往也会使得用户无法专注编辑。
|
||||
3. 作为另一个极端,一些编辑器在用户输入文本后立即将 Markdown 的标记转换为 HTML 元素,使得编写 Markdown 如同在 Word 文档里面编写富文本一样。
|
||||
|
||||
由于几乎所有的编辑器都选择第二种方法来处理隔阂,一提到 Markdown 人们往往会想起预览。这可能是对 Markdown 的一个最大的误解了。设计为一个简单的标记语言,Markdown 的设计初衷就是为了在编辑的时候方便帮助跟踪文本的信息,而又能在阅读的时候被转换为 HTML 为发布提供美观的排版输出。所以,Markdown 本身就应该在编辑的时候能够方便地跟踪和掌控文本的信息和脉络,而不需要通过预览这种接近饮鸩止渴的方法来方便编辑。
|
||||
|
||||
### 折中:VNote 的方案
|
||||
VNote 尝试通过精心调配的**语法高亮**和其他一些特性,来最大程度地减小 Markdown 的这种割裂感,尽可能地提供一个*所见即所得*的编辑体验。用户在编辑的时候就能有效第把握内容脉络,也就没有必要进行预览或者强制更改文本为 HTML 元素了。
|
||||
|
||||
# 功能
|
||||
## 基于笔记本的管理
|
||||
VNote 使用 **笔记本** 来管理笔记。类似于 OneNote,一个笔记本可以保存在系统上的任意位置。一个笔记本对应于一个账户的概念。例如,您可以在本地文件系统上有一个笔记本,另外在某台 OwnCloud 服务器上保存另一个笔记本。当不同的笔记有不同的保密要求时,独立的笔记本就非常适用了。
|
||||
|
||||
一个笔记本对应于文件系统上的一个独立完整的文件夹(称为笔记本的 **根目录** )。您可以将该文件夹拷贝到其他位置(或者另一台计算机上),然后将其导入到 VNote 中。
|
||||
|
||||
VNote 支持一个笔记本中包含无限层级的文件夹。VNote 支持在笔记本内或笔记本间拷贝或剪切文件夹和笔记。
|
||||
|
||||
## 直观的笔记管理
|
||||
所有笔记被保存为纯文本而且通过纯文本的配置文件进行管理。即使没有 VNote,您也能方便访问您的数据。这样,您也可以使用第三方的文件同步服务来同步您的笔记,并在另一台计算机上导入到 VNote 中。
|
||||
|
||||
VNote 支持 Markdown 和富文本笔记,其中 Markdown 笔记必须以 `md` 为后缀名。
|
||||
|
||||
## 语法高亮
|
||||
VNote 支持精确的 Markdown 语法高亮。通过精心调试的高亮样式,VNote 使得您能够轻松跟踪和阅读您的文档。
|
||||
|
||||
VNote 还支持 Markdown 编辑模式中代码块的语法高亮。目前的 Markdown 编辑器中绝大部分都尚不支持该特性。
|
||||
|
||||

|
||||
|
||||
## 原地预览
|
||||
VNote 支持在编辑时原地预览图片、图表和公式。
|
||||
|
||||

|
||||
|
||||
## 良好的图片体验
|
||||
编辑时,支持像其他富文本编辑器一样直接粘贴插入图片,VNote 会帮您管理所插入的图片。VNote 将这些图片保存在和笔记同一目录下的一个指定目录中。插入图片时,VNote 会弹出一个窗口预览即将要插入的图片。另外,当您移除笔记中的图片链接时,VNote 会自动删除对应的图片文件。
|
||||
|
||||
## 通用入口和全文搜索
|
||||
VNote 内置强大的全文搜索,支持**正则表达式**和**模糊搜索**。搜索可以指定范围,包括全部笔记本、当前笔记本和当前文件夹,也可以指定目标,如名字或内容。
|
||||
|
||||
类似于 Vim 中的 `Ctrl+P`,VNote 支持**通用入口**,可以在一个输入框中进行搜索或跳转。
|
||||
|
||||

|
||||
|
||||
## 编辑和阅读模式中的交互式大纲视图
|
||||
VNote 为编辑和预览模式都提供了一个用户友好的大纲视图。该大纲视图是一个项目树,而不是简单地插入一段 HTML。
|
||||
|
||||
## 强大的快捷键
|
||||
VNote 提供很多快捷键,从而提供一个愉悦的编辑体验。其中包括 **Vim模式**、**舰长模式** 和 **导航模式**,它们能让您完全摆脱鼠标进行操作。
|
||||
|
||||
更多细节请参考帮助菜单中的[快捷键帮助](src/resources/docs/shortcuts_zh.md)。
|
||||
|
||||
## 高度可定制
|
||||
在 VNote 中,几乎一切都是可以定制的,例如背景颜色、字体以及 Markdown 样式等。VNote 使用一个纯文本文件来记录您的所有配置,因此通过拷贝该文件就能够很快地在另一台电脑上初始化一个新的 VNote。
|
||||
|
||||
# 依赖
|
||||
- [Qt 5.9](http://qt-project.org) (L-GPL v3)
|
||||
- [PEG Markdown Highlight](http://hasseg.org/peg-markdown-highlight/) (MIT License)
|
||||
- [Hoedown 3.0.7](https://github.com/hoedown/hoedown/) (ISC License)
|
||||
- [Marked 0.5.1](https://github.com/markedjs/marked) (MIT License)
|
||||
- [Highlight.js](https://github.com/isagalaev/highlight.js/) (BSD License)
|
||||
- [Ionicons 2.0.1](https://github.com/driftyco/ionicons/) (MIT License)
|
||||
- [markdown-it 8.3.1](https://github.com/markdown-it/markdown-it) (MIT License)
|
||||
- [markdown-it-headinganchor 1.3.0](https://github.com/adam-p/markdown-it-headinganchor) (MIT License)
|
||||
- [markdown-it-task-lists 1.4.0](https://github.com/revin/markdown-it-task-lists) (ISC License)
|
||||
- [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) (MIT License)
|
||||
- [markdown-it-sub](https://github.com/markdown-it/markdown-it-sub) (MIT License)
|
||||
- [markdown-it-sup](https://github.com/markdown-it/markdown-it-sup) (MIT License)
|
||||
- [markdown-it-front-matter](https://github.com/craigdmckenna/markdown-it-front-matter) (MIT License)
|
||||
- [markdown-it-imsize](https://github.com/tatsy/markdown-it-imsize) (Unknown) (Thanks @Kinka for help)
|
||||
- [markdown-it-emoji](https://github.com/markdown-it/markdown-it-emoji) (MIT License)
|
||||
- [markdown-it-texmath](https://github.com/goessner/markdown-it-texmath) (MIT License)
|
||||
- [markdown-it-container 2.0.0](https://github.com/markdown-it/markdown-it-container) (MIT License)
|
||||
- [mermaid 7.0.0](https://github.com/knsv/mermaid) (MIT License)
|
||||
- [MathJax](https://www.mathjax.org/) (Apache-2.0)
|
||||
- [showdown](https://github.com/showdownjs/showdown) (Unknown)
|
||||
- [flowchart.js](https://github.com/adrai/flowchart.js) (MIT License)
|
||||
- [PlantUML](http://plantuml.com/) (MIT License)
|
||||
- [dom-to-image](https://github.com/tsayen/dom-to-image) (MIT License)
|
||||
- [turndown](https://github.com/domchristie/turndown) (MIT License)
|
||||
|
||||
# 代码许可
|
||||
VNote 使用 [MIT 许可](http://opensource.org/licenses/MIT)。
|
15
VNote.pro
@ -1,15 +0,0 @@
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2016-10-01T11:03:59
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
TEMPLATE = subdirs
|
||||
|
||||
CONFIG += c++11
|
||||
|
||||
SUBDIRS = hoedown \
|
||||
peg-highlight \
|
||||
src
|
||||
|
||||
src.depends = hoedown peg-highlight
|
78
appveyor.yml
@ -1,78 +0,0 @@
|
||||
image: Visual Studio 2015
|
||||
|
||||
version: 2.10.{build}
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
environment:
|
||||
COMPILER: msvc
|
||||
VSVER: 14
|
||||
|
||||
matrix:
|
||||
- QT: C:\Qt\5.9\msvc2015_64
|
||||
PLATFORM: amd64
|
||||
- QT: C:\Qt\5.9\msvc2015
|
||||
PLATFORM: x86
|
||||
|
||||
clone_depth: 1
|
||||
|
||||
# scripts that run after cloning repository
|
||||
install:
|
||||
- set PATH=%QT%\bin\;C:\Qt\Tools\QtCreator\bin\;C:\Qt\QtIFW2.0.1\bin\;%PATH%
|
||||
- git submodule update --init --recursive
|
||||
|
||||
# scripts that run before build
|
||||
before_build:
|
||||
- call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %PLATFORM%
|
||||
# After calling vcvarsall.bat, %PLATFORM% will be X64 or x86
|
||||
- qmake --version
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if "%PLATFORM%" EQU "X64" (qmake -r -spec win32-msvc CONFIG+=x86_64 CONFIG-=debug CONFIG+=release ../VNote.pro)
|
||||
- if "%PLATFORM%" EQU "x86" (qmake -r -spec win32-msvc CONFIG+=Win32 CONFIG-=debug CONFIG+=release ../VNote.pro)
|
||||
|
||||
# custom build scripts
|
||||
build_script:
|
||||
- nmake
|
||||
|
||||
# scripts that run after build
|
||||
after_build:
|
||||
- set vnote_version=2.10
|
||||
# Clone OpenSSL DLLs
|
||||
- git clone https://github.com/tamlok/openssl-utils.git openssl-utils.git
|
||||
- mkdir distrib\VNote
|
||||
- windeployqt.exe --dir .\distrib\VNote %APPVEYOR_BUILD_FOLDER%\build\src\release\VNote.exe
|
||||
- copy "%APPVEYOR_BUILD_FOLDER%\build\src\release\VNote.exe" "distrib\VNote\VNote.exe"
|
||||
- copy "%APPVEYOR_BUILD_FOLDER%\README.md" "distrib\VNote\README.md"
|
||||
- copy "%APPVEYOR_BUILD_FOLDER%\LICENSE" "distrib\VNote\LICENSE.txt"
|
||||
- echo %vnote_version% > "distrib\VNote\version.txt"
|
||||
- echo %APPVEYOR_REPO_COMMIT% >> "distrib\VNote\version.txt"
|
||||
- copy "distrib\VNote\VNote.exe" "distrib\VNote_win_%PLATFORM%.exe"
|
||||
# Delete translations\qt_*.qm
|
||||
- del /F /Q "distrib\VNote\translations\qt_*.qm"
|
||||
# Copy OpenSSL DLLs
|
||||
- if "%PLATFORM%" EQU "X64" (xcopy "openssl-utils.git\win64\*.dll" "distrib\VNote")
|
||||
- if "%PLATFORM%" EQU "x86" (xcopy "openssl-utils.git\win32\*.dll" "distrib\VNote")
|
||||
- cd distrib
|
||||
- 7z a VNote_win_%PLATFORM%_portable_%vnote_version%.zip VNote
|
||||
|
||||
artifacts:
|
||||
- path: build\distrib\VNote_win_%PLATFORM%_portable_%vnote_version%.zip
|
||||
name: portable
|
||||
- path: build\distrib\VNote_win_%PLATFORM%.exe
|
||||
name: exe_only
|
||||
|
||||
deploy:
|
||||
- provider: BinTray
|
||||
username: tamlok
|
||||
api_key:
|
||||
secure: YJqzsVDlC2NYH2RgzbUMtjZWbSXOdWUiiENOIqImo31hOfeiB0MiVGmBLmKPAHEg
|
||||
subject: tamlok
|
||||
repo: vnote
|
||||
package: vnote
|
||||
version: 2.10
|
||||
publish: true
|
||||
override: true
|
||||
artifact: portable
|
404
changes.md
@ -1,404 +0,0 @@
|
||||
# Changes History
|
||||
## v2.10
|
||||
- Add support for Git push/pull; (@musmus9405)
|
||||
- Bug fixes; (@tootal, @hlyang1992)
|
||||
|
||||
## v2.9.1
|
||||
- Fix start issue on macOS (no need to add `-m` option);
|
||||
|
||||
## v2.9
|
||||
- Upgrade to MathJax v3;
|
||||
- Support `e` command in Vim mode; (@gydeng)
|
||||
- Fix start issue on macOS (no need to add `-m` option);
|
||||
|
||||
## v2.8.2
|
||||
- Upgrade Mermaid.js;
|
||||
|
||||
## v2.8.1
|
||||
- Fix PlantUML online resources;
|
||||
- MacOS: support staying in tray;
|
||||
- Add a configuration in Settings to control code block copy button;
|
||||
|
||||
## v2.8
|
||||
- Add supports for image hosting, including service GitHub, Gitee, Wechat, and Tencent Cloud; (@fengwenhua)
|
||||
- Support sorting notebooks in context menu;
|
||||
- Add theme v_next; (@hzhu212);
|
||||
|
||||
## v2.7.2
|
||||
- Use 5.9.8 on macOS;
|
||||
- Call Java (PlantUML) directly on Win;
|
||||
|
||||
## v2.7.1
|
||||
- Fix PlantUML command;
|
||||
- Use 5.12.0 on macOS;
|
||||
- Add config [web]/enable_code_block_copy_button for copy button in code block in read mode;
|
||||
|
||||
## v2.7
|
||||
- Editor
|
||||
- Fix smart table bugs;
|
||||
- Fix improper leading line space on macOS;
|
||||
- Fix bug which will open two tabs for the same file;
|
||||
- Add copy button to copy code block in read mode;
|
||||
- PluatUML: use shell to launch Java;
|
||||
- Use qt 5.12.1 on macOS for better performance;
|
||||
- Add theme `v_simple` provided by user LuoshuiTianyi;
|
||||
- Support adding sibling folder in context menu;
|
||||
|
||||
## v2.6
|
||||
- Version-specific user track;
|
||||
|
||||
## v2.5
|
||||
- Do not open unknown type file when creating a new note;
|
||||
- Support Right-To-Left text in edit mode (with Vim mode disabled);
|
||||
- Remove credict information in export template;
|
||||
|
||||
## v2.4
|
||||
- Bundle qt5ct in Linux for native look support;
|
||||
- Use `Return` or `Enter` to confirm selection in macOS (besides `Cmd+O`);
|
||||
- Support sync note list to current note automatically via config `global/sync_note_list_to_current_tab`;
|
||||
- Add `Ctrl+E N` to view and edit current note's information;
|
||||
- Fix Markdown highlighter to handle fenced code block without a leading empty line;
|
||||
- Prompt for restart after changing theme;
|
||||
|
||||
## v2.3
|
||||
- Add Japanese translations (thanks to @miurahr);
|
||||
- Editor
|
||||
- Bug fix of smart table;
|
||||
- Allow to disable smart table;
|
||||
- Update to Qt 5.9.7 in CI in Linux;
|
||||
- Add user track logics for users counting;
|
||||
- Add CMake build system;
|
||||
- Support multiple packaging genration;
|
||||
- QtIFW GUI installer
|
||||
- Debian package
|
||||
- RPM package
|
||||
- AppImage
|
||||
- Tar.gz package
|
||||
- ZIP package
|
||||
- Windows Nullsoft installer
|
||||
- Windows NuGet package
|
||||
- Mac OS X dmg package
|
||||
- Support staic code checkers;
|
||||
- clang-tidy
|
||||
- crazy
|
||||
- include-what-you-see
|
||||
- Support modern IDEs;
|
||||
- Microsoft Visual Studio 2017
|
||||
- JetBrains CLion
|
||||
- QtCreator
|
||||
- XCode
|
||||
- VSCode
|
||||
|
||||
## v2.2
|
||||
- Editor
|
||||
- Support smart GFM table;
|
||||
- support auto scrolling cursor into center;
|
||||
- Support specifying font via settings to override style config;
|
||||
- Guess image title from the file name;
|
||||
- Support ~~~ as fenced code block marker;
|
||||
- MathJax
|
||||
- Support equation number;
|
||||
- Support \begin and \end directly;
|
||||
- Support $..$ in \text{} within $$...$$;
|
||||
- NotebookPanel: support spliting file list out;
|
||||
- Add openGL option in settings on Windows;
|
||||
- Markdown-it
|
||||
- Support alert text via markdown-it-container plugin;
|
||||
- Support WaveDrom for digital timing diagram;
|
||||
|
||||
## v2.1
|
||||
- UniversalEntry/Searcher: `Ctrl+B` to expand/collapse all items;
|
||||
- Captain mode: support specifying keyboard layout mappings, such as Russian or French;
|
||||
- Editor
|
||||
- Support downloading images to local when Parse&Paste;
|
||||
- Support scaling image when inserting image;
|
||||
- Support Parse&Paste as table from Microsoft Excel;
|
||||
- Support attach a file as attachment and insert as link by Drag&Drop;
|
||||
- Support `Ctrl+Sfhit+V` to paste as plain text;
|
||||
- Fix Unicode issue;
|
||||
- Attachment
|
||||
- Support copying file path;
|
||||
- Support Drag&Drop to copy/move notes;
|
||||
- Support MathJax euqation number in read mode;
|
||||
- Fix blurry toolbar icon on macOS;
|
||||
- Fix blurry renderring in read mode on Windows;
|
||||
|
||||
## v2.0
|
||||
- Vim: support specifying leader key via "[editor]/vim_leader_key";
|
||||
- LivePreview
|
||||
- Smart live preview for PlantUML (Live Preview Tunnel);
|
||||
- Expand live preview area via `Ctrl+E U`;
|
||||
- In-Place Preview: support in-place preview for online PlantUML;
|
||||
- Captain: fix captain mode shortcuts in different keyboard layout (Windows only);
|
||||
- Quick Access: specify a note for quick access via `Ctrl+Alt+I`;
|
||||
- Magic Word: support `%att%` as the relative path of the attachment folder;
|
||||
- Search: highlight matches of full-text search results in page in edit mode;
|
||||
- Editor
|
||||
- Allow inserting link or content when dropping files in;
|
||||
- Support highlighting tabs;
|
||||
- Support parsing HTML tables without head;
|
||||
- Export: support outline panel in exported HTML files;
|
||||
|
||||
## v1.22
|
||||
- Editor
|
||||
- Support parsting HTML as converted Markdown text;
|
||||
- Improve performance of in-place preview;
|
||||
- Be aware of links, images, and in-place preview in edit mode (visit or copy);
|
||||
- Support exporting and copying PlantUML and Graphviz diagram in edit mode;
|
||||
- Support middle click to close a tab;
|
||||
- Support searching files via multiple tags, such as "vnote && markdown";
|
||||
|
||||
## v1.21
|
||||
- Improve performance of editor (now could process 10K lines);
|
||||
- Support `<mark>vnote</mark>` highlight in edit mode;
|
||||
- Support **completion** in edit mode by `Ctrl+N/P`;
|
||||
- Support building a notebook from external directory recursively;
|
||||
- Update Flowchart.js to 1.11.3;
|
||||
|
||||
## v1.20
|
||||
- More efficient and accurate Markdown parser and highlighter;
|
||||
- MathJax: get rid of escaping;
|
||||
- Draw background for **hrule** in edit mode;
|
||||
- Support specifying the expanded level of outline;
|
||||
- Support drag&drop a directory to Explorer to open it;
|
||||
- `Ctrl+;` to insert inline code and `Ctrl+J/K` to scroll page in edit mode;
|
||||
- Support YAML front matter in edit mode;
|
||||
- Support searching Explorer root directory;
|
||||
|
||||
## v1.19
|
||||
- Support tags for notes;
|
||||
- UniversalEntry: support searching tags;
|
||||
- WebView: click an image or diagram to view it in read mode;
|
||||
- Add test buttons for PlantUML and Graphviz in settings dialog;
|
||||
- CaptainMode: Y to focus to the edit area;
|
||||
|
||||
## v1.18
|
||||
- Explorer: fix the crash when no root entry is set;
|
||||
- New dark theme **v_detorte** from a Vim *detorte* theme;
|
||||
- Support maximizing split or distributing splits;
|
||||
- Refine styles and HiDPI support;
|
||||
|
||||
## v1.17
|
||||
- Add **History** to browse history;
|
||||
- Add **Explorer** to browse external files;
|
||||
- Support view order in note list;
|
||||
- Support relative path for notebook;
|
||||
- UniversalEntry
|
||||
- Fix input method issue on macOS;
|
||||
- Add `j` for listing and searching History;
|
||||
- Support customized zoom delta of editor;
|
||||
- Add cache for in-place preview;
|
||||
- Better support for hiDPI;
|
||||
- Lazy initialization;
|
||||
- Support stay-on-top;
|
||||
|
||||
## v1.16
|
||||
- Markdown-it: supports specifying image size, emoji, and YAML metadata;
|
||||
- Bug fixes;
|
||||
|
||||
## v1.15
|
||||
- Support **PlantUML** and **Graphviz**;
|
||||
- **In-Place Preview** for MathJax, PlantUML, Graphviz, and Flowchart.js;
|
||||
- **Live Preview** for diagrams via `Ctrl+E I`;
|
||||
- Restore cursor position when recovering pages at startup;
|
||||
- UniversalEntry
|
||||
- Ctrl+I to expand/collapse current item;
|
||||
- Ctrl+L to go to current item's parent item;
|
||||
- Markdown-it: aware of YAML format metadata in notes;
|
||||
- Show hovered link in status line in read mode;
|
||||
- Export: support embedding images as data URI into HTML pages;
|
||||
|
||||
## v1.14
|
||||
- Support **Universal Entry** by `Ctrl+G`;
|
||||
- Single click a note in note list to open it in a new tab by default;
|
||||
- Translate `Ctrl` in default shortcuts to `Meta` on macOS;
|
||||
- Do not copy files when import if they locate in current folder;
|
||||
|
||||
## v1.13
|
||||
- Replace **v_white** theme with **v_native**, which behaves more like native applications;
|
||||
- Support **full-text search**;
|
||||
- Support `&&` and `||` logics (space-separated keywords are treated as AND);
|
||||
- Enhanced export;
|
||||
- Support MHTML format;
|
||||
- Support All-In-One PDF via tool *wkhtmltopdf*;
|
||||
- Support *pandoc* and random tool for custom export;
|
||||
- Support **Word Count** information in both read and edit mode;
|
||||
- Support SavePage action in read mode;
|
||||
- Support *back reference* in replace text via `\1`, `\2`, and so on;
|
||||
- Support sorting in Cart;
|
||||
- Support sorting notes and folders via name or modification date;
|
||||
- Support both `flow` and `flowchart` as the language of *flowchart.js* diagram;
|
||||
- Add PasteAsBlockQuote menu action to paste text as block quote from clipboard;
|
||||
- Add options for Markdown-it to support subscript and superscript;
|
||||
- Better support for 4K display;
|
||||
|
||||
## v1.12
|
||||
- Combine `EditNote` and `SaveExitNote` as `EditReadNote` (`Ctrl+T`);
|
||||
- Support exporting notes as Markdown, HTML, and PDF;
|
||||
- Support simple search in directory tree, file list, and outline;
|
||||
- Support copying selected text as HTML in edit mode;
|
||||
- Support copying text to Evernote, OneNote, Word, WeChat Public Account editor and so on;
|
||||
- Support auto-save;
|
||||
- Support fullscreen mode and hiding menu bar;
|
||||
- Support `Ctrl+H/W/U` to delete text in most line edits;
|
||||
- Support zooming in/out in edit mode;
|
||||
- Support MathJax in fenced code block with language `mathjax` specified;
|
||||
- More shortcuts;
|
||||
- Add **Cart** to collect notes for further processing;
|
||||
- Output built-in themes on start of VNote;
|
||||
- `Esc` to exit edit mode when Vim mode is disabled;
|
||||
- Support Vim command line for search in read mode;
|
||||
- Support printing;
|
||||
- Single click in file list to open file in current tab, double clicks to open in a new tab;
|
||||
|
||||
## v1.11.1
|
||||
- Refine copy function in read mode. Better support for copying and pasting into OneNote or WeChat editor;
|
||||
- Do not highlight code blocks without language specified by default;
|
||||
- Refine themes and styles;
|
||||
- Support foreground for selected/searched word in MDHL style;
|
||||
- Support shortcuts for external programs;
|
||||
- Support resetting VNote;
|
||||
- Cover more scenarios for Chinese translations;
|
||||
|
||||
## v1.11
|
||||
- Support themes;
|
||||
- Three built-in mordern themes;
|
||||
- One dark mode theme;
|
||||
- Vim mode
|
||||
- Support block cursor in Normal/Visual mode;
|
||||
- `=` to auto-indent selected lines as the first line;
|
||||
- Support custom external editors to open notes;
|
||||
- Enable `Ctrl+C`/`Ctrl+V` in Vim mode to copy/paste;
|
||||
- Support Flash Page to record ideas quickly;
|
||||
- Support previewing inline images;
|
||||
|
||||
## v1.10
|
||||
- Migrate to Qt 5.9.1;
|
||||
- Support Compact mode in main window;
|
||||
- Update icons;
|
||||
- Support custom startup pages;
|
||||
- Remove obsolete title marker when inserting new one;
|
||||
- Support Magic Words;
|
||||
- Vim mode
|
||||
- Share registers among all tabs;
|
||||
- Support `Ctrl+O` in Insert mode;
|
||||
- Add "Code Block", "Insert Link", and "Insert Image" tool bar buttons;
|
||||
- Support `Ctrl+Shift+T` to recover last closed tabs;
|
||||
- Support view read-only files in edit mode;
|
||||
- Refactor editor for speed;
|
||||
- Support templates when creating notes;
|
||||
- Support snippets;
|
||||
- Support file change check;
|
||||
- Support backup file (save changes automatically);
|
||||
|
||||
## v1.9
|
||||
- Support attachments of notes.
|
||||
- Add recycle bin to notebook to hold deleted files.
|
||||
- Refine Vim mode:
|
||||
- Support J and gJ to join line;
|
||||
- Support S, {, and };
|
||||
- <leader>w to save note;
|
||||
- Fix Y and D actions in Visual mode.
|
||||
- Support AppImage package for Linux.
|
||||
- More responsive and efficient syntax highlight and image preview.
|
||||
- More pleasant line distance.
|
||||
- More natural interaction of folder and note management.
|
||||
- Support inserting note name as title.
|
||||
- Support custom default mode to open a note.
|
||||
- Support auto heading sequence.
|
||||
- Support color column in fenced code block in edit mode.
|
||||
- Support line number in code block in both read and edit mode.
|
||||
- Support created time and modified time of notes, folders, and notebooks.
|
||||
- Support custom Markdown-it options, such as auto line break.
|
||||
- Confirm when cleaning up unused images.
|
||||
- Support custom Mathjax location.
|
||||
- Support custom style for code block highlights in read mode.
|
||||
- Double click on a tab to close it.
|
||||
|
||||
## v1.8
|
||||
- Support editing external files. VNote could open files from command line.
|
||||
- Support drag-and-drop to open external files.
|
||||
- Refine tab context menu.
|
||||
- Support system tray icon.
|
||||
- Refine Vim mode.
|
||||
- Make all tool buttons always visible and smaller.
|
||||
- Support custom file type suffix.
|
||||
- Make the name of notebook/folder/note case-insensitive.
|
||||
- Support links to internal notes in notes.
|
||||
|
||||
## v1.7
|
||||
- ATTENTION: please add font-size option to the "editor" section of your custom MDHL style.
|
||||
- Refine Vim mode (more functions, please refer to the shortcuts help).
|
||||
- Support Find in Vim mode.
|
||||
- Refine tab context menu.
|
||||
- Support Flowchart.js for flowchart.
|
||||
- Add toolbar for common text edit functions.
|
||||
- Support line number (both absolute and relative) in edit mode.
|
||||
- Support custom shortcuts.
|
||||
- Support [[, ]], [], ][, [{, ]} to navigate through titles in both edit and read mode.
|
||||
- Many minor bug fixes.
|
||||
|
||||
## v1.6
|
||||
- Support simple but powerful **Vim mode**.
|
||||
- Change the shortcut of ExitAndRead from `Ctrl+R` to `Ctrl+T`.
|
||||
- Add a edit status indicator in the status bar.
|
||||
- Dragging mouse with Ctrl and left button pressed to scroll in read and edit mode.
|
||||
- Refine highlighting cursor line.
|
||||
- Support subscript, superscript and footnote in markdown-it renderer.
|
||||
- Refactor outline logics to not show extra [EMPTY] headers.
|
||||
- Handle HTML comments correctly.
|
||||
- Provide a default root folder when adding notebooks.
|
||||
- Support check for updates.
|
||||
- Redraw app icons.
|
||||
- Many minor bug fixes.
|
||||
|
||||
## v1.5
|
||||
- Support logging in release mode.
|
||||
- Fix Chinese font matching in mdhl.
|
||||
- Fix VimagePreviewer to support optional title.
|
||||
- Refactor local image folder logics.
|
||||
- Support custom local image folder for both notebook scope and global scope.
|
||||
- Support constraining the width of images in read mode.
|
||||
- Fix and refine default style.
|
||||
- Centering the images and display the alt text as caption in read mode.
|
||||
- Support exporting a single note as PDF file.
|
||||
- Add "Open File Location" menu item in folder tree and note list.
|
||||
- Support highlighting trailing space.
|
||||
|
||||
## v1.4
|
||||
- Use `_vnote.json` as the config file.
|
||||
- More user friendly messages.
|
||||
- Delete notebook by deleting root directories one by one.
|
||||
- Refactor image preview logics to support previewing all images in edit mode.
|
||||
- Support constraining the width of previewed images to the edit window.
|
||||
- bugfix.
|
||||
|
||||
## v1.3
|
||||
- Support code block syntax highlight in edit mode.
|
||||
- A more pleasant AutoIndent and AutoList.
|
||||
- `Ctrl+<Num>` instead of `Ctrl+Alt+<Num>` to insert title.
|
||||
- Support custom Markdown CSS styles and editor styles.
|
||||
|
||||
## v1.2
|
||||
- Support **MathJax**.
|
||||
- Fix a crash on macOS.
|
||||
- Change default font family.
|
||||
- Refine tab order.
|
||||
- Better support for HiDPI.
|
||||
- Support zoom in/out page when reading.
|
||||
- Introduce **Captain Mode** and **Navigation Mode**.
|
||||
- A more user friendly popup opened notes list.
|
||||
- Support jumping to specified tab efficiently by num keys.
|
||||
- Add shortcuts documentation.
|
||||
- AutoList and AutoIndent.
|
||||
|
||||
## v1.1
|
||||
- Refine messages and dialogs. Add Chinese translations.
|
||||
- A new application icon.
|
||||
- Support install target for Linux.
|
||||
- Continuous build and deployment for Linux, macOS, and Windows.
|
||||
- Support both X64 and x86 version of Windows.
|
||||
- Add `.md` suffix automatically when creating a note.
|
||||
- A more user friendly insert dialog.
|
||||
- Support **Mermaid** diagram.
|
||||
- Add **markdown-it** as the default renderer. Support task list.
|
@ -1,35 +0,0 @@
|
||||
@echo off
|
||||
rem Update .ccls project file for ccls LPS and compile_flags.txt for clangd
|
||||
|
||||
if "%~1"=="" (
|
||||
echo missing argument: the location of Qt's include directory
|
||||
EXIT /B 0
|
||||
)
|
||||
|
||||
set qt_inc=%~1
|
||||
set qt_inc=%qt_inc:\=\\%
|
||||
|
||||
(
|
||||
echo clang
|
||||
echo -fcxx-exceptions
|
||||
echo -std=c++11
|
||||
echo -Isrc
|
||||
echo -Isrc\\dialog
|
||||
echo -Isrc\\utils
|
||||
echo -Isrc\\widgets
|
||||
echo -Ihoedown
|
||||
echo -Ipeg-highlight
|
||||
echo -I%qt_inc%
|
||||
echo -I%qt_inc%\\QtCore
|
||||
echo -I%qt_inc%\\QtWebEngineWidgets
|
||||
echo -I%qt_inc%\\QtSvg
|
||||
echo -I%qt_inc%\\QtPrintSupport
|
||||
echo -I%qt_inc%\\QtWidgets
|
||||
echo -I%qt_inc%\\QtWebEngineCore
|
||||
echo -I%qt_inc%\\QtGui
|
||||
echo -I%qt_inc%\\QtWebChannel
|
||||
echo -I%qt_inc%\\QtNetwork
|
||||
echo -I%qt_inc%\\QtTest
|
||||
) > ".ccls"
|
||||
|
||||
copy /Y .ccls compile_flags.txt
|
6
debian/changelog
vendored
@ -1,6 +0,0 @@
|
||||
vnote (1.8-0ubuntu1) zesty; urgency=medium
|
||||
|
||||
* Initial release
|
||||
* Adjusted the Makefile to fix $(DESTDIR) problems.
|
||||
|
||||
-- Le Tan <tamlokveer@gmail.com> Sun, 20 Aug 2017 09:32:22 +0800
|
1
debian/compat
vendored
@ -1 +0,0 @@
|
||||
9
|
17
debian/control
vendored
@ -1,17 +0,0 @@
|
||||
Source: vnote
|
||||
Section: editors
|
||||
Priority: optional
|
||||
Maintainer: Le Tan <tamlokveer@gmail.com>
|
||||
Build-Depends: debhelper (>=9), qt5-default (>= 5.7.1), libqt5svg5-dev, qtwebengine5-dev, libqt5webchannel5-dev, qtpositioning5-dev
|
||||
Standards-Version: 3.9.6
|
||||
Homepage: https://tamlok.github.io/vnote
|
||||
Vcs-Browser: https://github.com/tamlok/vnote.git
|
||||
|
||||
Package: vnote
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libQt5WebEngineWidgets (>= 5.7.1), libQt5WebEngineCore, libQt5PrintSupport, libQt5Widgets, libQt5Gui, libQt5WebChannel, libQt5Network, libQt5Core
|
||||
Suggests: libssl
|
||||
Description: A Vim-inspired note-taking application for Markdown.
|
||||
VNote is a cross-platform, free note-taking application, designed especially
|
||||
for Markdown. VNote provides both simple note management and pleasant Markdown
|
||||
edit experience.
|
7
debian/copyright
vendored
@ -1,7 +0,0 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: vnote
|
||||
Source: https://tamlok.github.io/vnote
|
||||
|
||||
Files: *
|
||||
Copyright: 2017 Le Tan <tamlokveer@gmail.com>
|
||||
License: MIT License
|
28
debian/rules
vendored
@ -1,28 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
# See debhelper(7) (uncomment to enable)
|
||||
# output every command that modifies files on the build system.
|
||||
#export DH_VERBOSE = 1
|
||||
|
||||
|
||||
# see FEATURE AREAS in dpkg-buildflags(1)
|
||||
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
# see ENVIRONMENT in dpkg-buildflags(1)
|
||||
# package maintainers to append CFLAGS
|
||||
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
|
||||
# package maintainers to append LDFLAGS
|
||||
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
||||
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
|
||||
# dh_make generated override targets
|
||||
# This is example for Cmake (See https://bugs.debian.org/641051 )
|
||||
#override_dh_auto_configure:
|
||||
# dh_auto_configure -- # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)
|
||||
override_dh_shlibdeps:
|
||||
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
|
||||
|
||||
export QT_SELECT=qt5
|
1
debian/source/format
vendored
@ -1 +0,0 @@
|
||||
3.0 (quilt)
|
1
hoedown
@ -1 +0,0 @@
|
||||
Subproject commit e63d2160a7d9895defc253d933b58a83ac54f81a
|
@ -1,18 +0,0 @@
|
||||
# PEG-Markdown-Highlight
|
||||
# Github: https://github.com/ali-rantakari/peg-markdown-highlight
|
||||
|
||||
QT -= core gui
|
||||
|
||||
TARGET = peg-highlight
|
||||
|
||||
TEMPLATE = lib
|
||||
|
||||
CONFIG += warn_off
|
||||
CONFIG += staticlib
|
||||
|
||||
SOURCES += pmh_parser.c \
|
||||
pmh_styleparser.c
|
||||
|
||||
HEADERS += pmh_parser.h \
|
||||
pmh_styleparser.h \
|
||||
pmh_definitions.h
|
@ -1,138 +0,0 @@
|
||||
/* PEG Markdown Highlight
|
||||
* Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
|
||||
* Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
|
||||
*
|
||||
* pmh_definitions.h
|
||||
*/
|
||||
|
||||
#ifndef pmh_MARKDOWN_DEFINITIONS
|
||||
#define pmh_MARKDOWN_DEFINITIONS
|
||||
|
||||
/** \file
|
||||
* \brief Global definitions for the parser.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief Element types.
|
||||
*
|
||||
* The first (documented) ones are language element types.
|
||||
*
|
||||
* The last (non-documented) ones are utility types used
|
||||
* by the parser itself.
|
||||
*
|
||||
* \sa pmh_element
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
pmh_LINK, /**< Explicit link */
|
||||
pmh_AUTO_LINK_URL, /**< Implicit URL link */
|
||||
pmh_AUTO_LINK_EMAIL, /**< Implicit email link */
|
||||
pmh_IMAGE, /**< Image definition */
|
||||
pmh_CODE, /**< Code (inline) */
|
||||
pmh_HTML, /**< HTML */
|
||||
pmh_HTML_ENTITY, /**< HTML special entity definition */
|
||||
pmh_EMPH, /**< Emphasized text */
|
||||
pmh_STRONG, /**< Strong text */
|
||||
pmh_LIST_BULLET, /**< Bullet for an unordered list item */
|
||||
pmh_LIST_ENUMERATOR, /**< Enumerator for an ordered list item */
|
||||
pmh_COMMENT, /**< (HTML) Comment */
|
||||
|
||||
// Code assumes that pmh_H1-6 are in order.
|
||||
pmh_H1, /**< Header, level 1 */
|
||||
pmh_H2, /**< Header, level 2 */
|
||||
pmh_H3, /**< Header, level 3 */
|
||||
pmh_H4, /**< Header, level 4 */
|
||||
pmh_H5, /**< Header, level 5 */
|
||||
pmh_H6, /**< Header, level 6 */
|
||||
|
||||
pmh_BLOCKQUOTE, /**< Blockquote */
|
||||
pmh_VERBATIM, /**< Verbatim (e.g. block of code) */
|
||||
pmh_HTMLBLOCK, /**< Block of HTML */
|
||||
pmh_HRULE, /**< Horizontal rule */
|
||||
pmh_REFERENCE, /**< Reference */
|
||||
pmh_FENCEDCODEBLOCK, /**< Fenced code block */
|
||||
pmh_NOTE, /**< Note */
|
||||
pmh_STRIKE, /**< Strike-through */
|
||||
pmh_FRONTMATTER, /**< Front matter */
|
||||
pmh_DISPLAYFORMULA, /**< Math display formula */
|
||||
pmh_INLINEEQUATION, /**< Math inline equation */
|
||||
pmh_MARK, /**< HTML <mark> tag content */
|
||||
pmh_TABLE, /**< GFM table */
|
||||
pmh_TABLEHEADER, /**< GFM table header */
|
||||
pmh_TABLEBORDER, /**< GFM table border | */
|
||||
|
||||
// Utility types used by the parser itself:
|
||||
|
||||
// List of pmh_RAW element lists, each to be processed separately from
|
||||
// others (for each element in linked lists of this type, `children` points
|
||||
// to a linked list of pmh_RAW elements):
|
||||
pmh_RAW_LIST, /**< Internal to parser. Please ignore. */
|
||||
|
||||
// Span marker for positions in original input to be post-processed
|
||||
// in a second parsing step:
|
||||
pmh_RAW, /**< Internal to parser. Please ignore. */
|
||||
|
||||
// Additional text to be parsed along with spans in the original input
|
||||
// (these may be added to linked lists of pmh_RAW elements):
|
||||
pmh_EXTRA_TEXT, /**< Internal to parser. Please ignore. */
|
||||
|
||||
// Separates linked lists of pmh_RAW elements into parts to be processed
|
||||
// separate from each other:
|
||||
pmh_SEPARATOR, /**< Internal to parser. Please ignore. */
|
||||
|
||||
// Placeholder element used while parsing:
|
||||
pmh_NO_TYPE, /**< Internal to parser. Please ignore. */
|
||||
|
||||
// Linked list of *all* elements created while parsing:
|
||||
pmh_ALL /**< Internal to parser. Please ignore. */
|
||||
} pmh_element_type;
|
||||
|
||||
/**
|
||||
* \brief Number of types in pmh_element_type.
|
||||
* \sa pmh_element_type
|
||||
*/
|
||||
#define pmh_NUM_TYPES 39
|
||||
|
||||
/**
|
||||
* \brief Number of *language element* types in pmh_element_type.
|
||||
* \sa pmh_element_type
|
||||
*/
|
||||
#define pmh_NUM_LANG_TYPES (pmh_NUM_TYPES - 6)
|
||||
|
||||
|
||||
/**
|
||||
* \brief A Language element occurrence.
|
||||
*/
|
||||
struct pmh_Element
|
||||
{
|
||||
pmh_element_type type; /**< \brief Type of element */
|
||||
unsigned long pos; /**< \brief Unicode code point offset marking the
|
||||
beginning of this element in the
|
||||
input. */
|
||||
unsigned long end; /**< \brief Unicode code point offset marking the
|
||||
end of this element in the input. */
|
||||
struct pmh_Element *next; /**< \brief Next element in list */
|
||||
char *label; /**< \brief Label (for links and references) */
|
||||
char *address; /**< \brief Address (for links and references) */
|
||||
};
|
||||
typedef struct pmh_Element pmh_element;
|
||||
|
||||
/**
|
||||
* \brief Bitfield enumeration of supported Markdown extensions.
|
||||
*/
|
||||
enum pmh_extensions
|
||||
{
|
||||
pmh_EXT_NONE = 0, /**< No extensions */
|
||||
pmh_EXT_NOTES = (1 << 0), /**< Footnote syntax:
|
||||
http://pandoc.org/README.html#footnotes */
|
||||
pmh_EXT_STRIKE = (1 << 1), /**< Strike-through syntax:
|
||||
http://pandoc.org/README.html#strikeout */
|
||||
pmh_EXT_FRONTMATTER = (1 << 2), /**< YAML meta data */
|
||||
pmh_EXT_MATH = (1 << 3), /**< Math */
|
||||
pmh_EXT_MARK = (1 << 4), /**< HTML <mark> tag content */
|
||||
pmh_EXT_MATH_RAW = (1 << 5), /**< Math in format \begin and \end */
|
||||
pmh_EXT_TABLE = (1 << 6), /** GFM Table */
|
||||
};
|
||||
|
||||
#endif
|
@ -1,91 +0,0 @@
|
||||
/* PEG Markdown Highlight
|
||||
* Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
|
||||
* Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
|
||||
*
|
||||
* pmh_parser.h
|
||||
*/
|
||||
|
||||
#ifdef Q_CC_GNU
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
|
||||
/** \file
|
||||
* \brief Parser public interface.
|
||||
*/
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "pmh_definitions.h"
|
||||
|
||||
|
||||
/**
|
||||
* \brief Parse Markdown text, return elements
|
||||
*
|
||||
* Parses the given Markdown text and returns the results as an
|
||||
* array of linked lists of elements, indexed by type.
|
||||
*
|
||||
* \param[in] text The Markdown text to parse for highlighting.
|
||||
* \param[in] extensions The extensions to use in parsing (a bitfield
|
||||
* of pmh_extensions values).
|
||||
* \param[out] out_result A pmh_element array, indexed by type, containing
|
||||
* the results of the parsing (linked lists of elements).
|
||||
* You must pass this to pmh_free_elements() when it's
|
||||
* not needed anymore.
|
||||
*
|
||||
* \sa pmh_element_type
|
||||
*/
|
||||
void pmh_markdown_to_elements(char *text, int extensions,
|
||||
pmh_element **out_result[]);
|
||||
|
||||
/**
|
||||
* \brief Sort elements in list by start offset.
|
||||
*
|
||||
* Sorts the linked lists of elements in the list returned by
|
||||
* pmh_markdown_to_elements() by their start offsets (pos).
|
||||
*
|
||||
* \param[in] element_lists Array of linked lists of elements (output
|
||||
* from pmh_markdown_to_elements()).
|
||||
*
|
||||
* \sa pmh_markdown_to_elements
|
||||
* \sa pmh_element::pos
|
||||
*/
|
||||
void pmh_sort_elements_by_pos(pmh_element *element_lists[]);
|
||||
|
||||
/**
|
||||
* \brief Free pmh_element array
|
||||
*
|
||||
* Frees an pmh_element array returned by pmh_markdown_to_elements().
|
||||
*
|
||||
* \param[in] elems The pmh_element array resulting from calling
|
||||
* pmh_markdown_to_elements().
|
||||
*
|
||||
* \sa pmh_markdown_to_elements
|
||||
*/
|
||||
void pmh_free_elements(pmh_element **elems);
|
||||
|
||||
/**
|
||||
* \brief Get element type name
|
||||
*
|
||||
* \param[in] type The type value to get the name for.
|
||||
*
|
||||
* \return The name of the given type as a null-terminated string.
|
||||
*
|
||||
* \sa pmh_element_type
|
||||
*/
|
||||
char *pmh_element_name_from_type(pmh_element_type type);
|
||||
|
||||
/**
|
||||
* \brief Get element type from a name
|
||||
*
|
||||
* \param[in] name The name of the type.
|
||||
*
|
||||
* \return The element type corresponding to the given name.
|
||||
*
|
||||
* \sa pmh_element_type
|
||||
*/
|
||||
pmh_element_type pmh_element_type_from_name(char *name);
|
||||
|
@ -1,937 +0,0 @@
|
||||
/* PEG Markdown Highlight
|
||||
* Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
|
||||
* Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
|
||||
*
|
||||
* styleparser.c
|
||||
*
|
||||
* Parser for custom syntax highlighting stylesheets.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "pmh_styleparser.h"
|
||||
#include "pmh_parser.h"
|
||||
|
||||
|
||||
#if pmh_DEBUG_OUTPUT
|
||||
#define pmhsp_PRINTF(x, ...) fprintf(stderr, x, ##__VA_ARGS__)
|
||||
#else
|
||||
#define pmhsp_PRINTF(x, ...)
|
||||
#endif
|
||||
|
||||
|
||||
// vasprintf is not in the C standard nor in POSIX so we provide our own
|
||||
static int our_vasprintf(char **strptr, const char *fmt, va_list argptr)
|
||||
{
|
||||
int ret;
|
||||
va_list argptr2;
|
||||
*strptr = NULL;
|
||||
|
||||
va_copy(argptr2, argptr);
|
||||
ret = vsnprintf(NULL, 0, fmt, argptr2);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
|
||||
*strptr = (char *)malloc(ret+1);
|
||||
if (*strptr == NULL)
|
||||
return -1;
|
||||
|
||||
va_copy(argptr2, argptr);
|
||||
ret = vsnprintf(*strptr, ret+1, fmt, argptr2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Parsing context data
|
||||
typedef struct
|
||||
{
|
||||
char *input;
|
||||
void (*error_callback)(char*,int,void*);
|
||||
void *error_callback_context;
|
||||
int styles_pos;
|
||||
pmh_style_collection *styles;
|
||||
} style_parser_data;
|
||||
|
||||
typedef struct raw_attribute
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
int line_number;
|
||||
struct raw_attribute *next;
|
||||
} raw_attribute;
|
||||
|
||||
static raw_attribute *new_raw_attribute(char *name, char *value,
|
||||
int line_number)
|
||||
{
|
||||
raw_attribute *v = (raw_attribute *)malloc(sizeof(raw_attribute));
|
||||
v->name = name;
|
||||
v->value = value;
|
||||
v->line_number = line_number;
|
||||
v->next = NULL;
|
||||
return v;
|
||||
}
|
||||
|
||||
static void free_raw_attributes(raw_attribute *list)
|
||||
{
|
||||
raw_attribute *cur = list;
|
||||
while (cur != NULL)
|
||||
{
|
||||
raw_attribute *pattr = NULL;
|
||||
if (cur->name != NULL) free(cur->name);
|
||||
if (cur->value != NULL) free(cur->value);
|
||||
pattr = cur;
|
||||
cur = cur->next;
|
||||
free(pattr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void report_error(style_parser_data *p_data,
|
||||
int line_number, char *str, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
if (p_data->error_callback == NULL)
|
||||
return;
|
||||
|
||||
va_start(argptr, str);
|
||||
char *errmsg;
|
||||
our_vasprintf(&errmsg, str, argptr);
|
||||
va_end(argptr);
|
||||
p_data->error_callback(errmsg, line_number,
|
||||
p_data->error_callback_context);
|
||||
free(errmsg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static char *trim_str(char *str)
|
||||
{
|
||||
while (isspace(*str))
|
||||
str++;
|
||||
if (*str == '\0')
|
||||
return str;
|
||||
char *end = str + strlen(str) - 1;
|
||||
while (end > str && isspace(*end))
|
||||
end--;
|
||||
*(end+1) = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *trim_str_dup(char *str)
|
||||
{
|
||||
size_t start = 0;
|
||||
while (isspace(*(str + start)))
|
||||
start++;
|
||||
size_t end = strlen(str) - 1;
|
||||
while (start < end && isspace(*(str + end)))
|
||||
end--;
|
||||
|
||||
size_t len = end - start + 1;
|
||||
char *ret = (char *)malloc(sizeof(char)*len + 1);
|
||||
*ret = '\0';
|
||||
strncat(ret, (str + start), len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *strcpy_lower(char *str)
|
||||
{
|
||||
char *low = strdup(str);
|
||||
int i;
|
||||
int len = strlen(str);
|
||||
for (i = 0; i < len; i++)
|
||||
*(low+i) = tolower(*(low+i));
|
||||
return low;
|
||||
}
|
||||
|
||||
static char *standardize_str(char *str)
|
||||
{
|
||||
return strcpy_lower(trim_str(str));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static pmh_attr_argb_color *new_argb_color(int r, int g, int b, int a)
|
||||
{
|
||||
pmh_attr_argb_color *c = (pmh_attr_argb_color *)
|
||||
malloc(sizeof(pmh_attr_argb_color));
|
||||
c->red = r; c->green = g; c->blue = b; c->alpha = a;
|
||||
return c;
|
||||
}
|
||||
static pmh_attr_argb_color *new_argb_from_hex(long long hex, bool has_alpha)
|
||||
{
|
||||
// 0xaarrggbb
|
||||
int a = has_alpha ? ((hex >> 24) & 0xFF) : 255;
|
||||
int r = ((hex >> 16) & 0xFF);
|
||||
int g = ((hex >> 8) & 0xFF);
|
||||
int b = (hex & 0xFF);
|
||||
return new_argb_color(r,g,b,a);
|
||||
}
|
||||
static pmh_attr_argb_color *new_argb_from_hex_str(style_parser_data *p_data,
|
||||
int attr_line_number,
|
||||
char *str)
|
||||
{
|
||||
// "aarrggbb"
|
||||
int len = strlen(str);
|
||||
if (len != 6 && len != 8) {
|
||||
report_error(p_data, attr_line_number,
|
||||
"Value '%s' is not a valid color value: it should be a "
|
||||
"hexadecimal number, 6 or 8 characters long.",
|
||||
str);
|
||||
return NULL;
|
||||
}
|
||||
char *endptr = NULL;
|
||||
long long num = strtoll(str, &endptr, 16);
|
||||
if (*endptr != '\0') {
|
||||
report_error(p_data, attr_line_number,
|
||||
"Value '%s' is not a valid color value: the character "
|
||||
"'%c' is invalid. The color value should be a hexadecimal "
|
||||
"number, 6 or 8 characters long.",
|
||||
str, *endptr);
|
||||
return NULL;
|
||||
}
|
||||
return new_argb_from_hex(num, (len == 8));
|
||||
}
|
||||
|
||||
static pmh_attr_value *new_attr_value()
|
||||
{
|
||||
return (pmh_attr_value *)malloc(sizeof(pmh_attr_value));
|
||||
}
|
||||
|
||||
static pmh_attr_font_styles *new_font_styles()
|
||||
{
|
||||
pmh_attr_font_styles *ret = (pmh_attr_font_styles *)
|
||||
malloc(sizeof(pmh_attr_font_styles));
|
||||
ret->italic = false;
|
||||
ret->bold = false;
|
||||
ret->underlined = false;
|
||||
ret->strikeout = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static pmh_attr_font_size *new_font_size()
|
||||
{
|
||||
pmh_attr_font_size *ret = (pmh_attr_font_size *)
|
||||
malloc(sizeof(pmh_attr_font_size));
|
||||
ret->is_relative = false;
|
||||
ret->size_pt = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static pmh_style_attribute *new_attr(char *name, pmh_attr_type type)
|
||||
{
|
||||
pmh_style_attribute *attr = (pmh_style_attribute *)malloc(sizeof(pmh_style_attribute));
|
||||
attr->name = strdup(name);
|
||||
attr->type = type;
|
||||
attr->next = NULL;
|
||||
return attr;
|
||||
}
|
||||
|
||||
static void free_style_attributes(pmh_style_attribute *list)
|
||||
{
|
||||
pmh_style_attribute *cur = list;
|
||||
while (cur != NULL)
|
||||
{
|
||||
if (cur->name != NULL)
|
||||
free(cur->name);
|
||||
if (cur->value != NULL)
|
||||
{
|
||||
if (cur->type == pmh_attr_type_foreground_color
|
||||
|| cur->type == pmh_attr_type_background_color
|
||||
|| cur->type == pmh_attr_type_caret_color
|
||||
|| cur->type == pmh_attr_type_strike_color)
|
||||
free(cur->value->argb_color);
|
||||
else if (cur->type == pmh_attr_type_font_family)
|
||||
free(cur->value->font_family);
|
||||
else if (cur->type == pmh_attr_type_font_style)
|
||||
free(cur->value->font_styles);
|
||||
else if (cur->type == pmh_attr_type_font_size_pt)
|
||||
free(cur->value->font_size);
|
||||
else if (cur->type == pmh_attr_type_other)
|
||||
free(cur->value->string);
|
||||
free(cur->value);
|
||||
}
|
||||
pmh_style_attribute *pattr = cur;
|
||||
cur = cur->next;
|
||||
free(pattr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define IF_ATTR_NAME(x) if (strcmp(x, name) == 0)
|
||||
pmh_attr_type pmh_attr_type_from_name(char *name)
|
||||
{
|
||||
IF_ATTR_NAME("color") return pmh_attr_type_foreground_color;
|
||||
else IF_ATTR_NAME("foreground") return pmh_attr_type_foreground_color;
|
||||
else IF_ATTR_NAME("foreground-color") return pmh_attr_type_foreground_color;
|
||||
else IF_ATTR_NAME("background") return pmh_attr_type_background_color;
|
||||
else IF_ATTR_NAME("background-color") return pmh_attr_type_background_color;
|
||||
else IF_ATTR_NAME("caret") return pmh_attr_type_caret_color;
|
||||
else IF_ATTR_NAME("caret-color") return pmh_attr_type_caret_color;
|
||||
else IF_ATTR_NAME("strike") return pmh_attr_type_strike_color;
|
||||
else IF_ATTR_NAME("strike-color") return pmh_attr_type_strike_color;
|
||||
else IF_ATTR_NAME("font-size") return pmh_attr_type_font_size_pt;
|
||||
else IF_ATTR_NAME("font-family") return pmh_attr_type_font_family;
|
||||
else IF_ATTR_NAME("font-style") return pmh_attr_type_font_style;
|
||||
return pmh_attr_type_other;
|
||||
}
|
||||
|
||||
char *pmh_attr_name_from_type(pmh_attr_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case pmh_attr_type_foreground_color:
|
||||
return "foreground-color"; break;
|
||||
case pmh_attr_type_background_color:
|
||||
return "background-color"; break;
|
||||
case pmh_attr_type_caret_color:
|
||||
return "caret-color"; break;
|
||||
case pmh_attr_type_strike_color:
|
||||
return "strike-color"; break;
|
||||
case pmh_attr_type_font_size_pt:
|
||||
return "font-size"; break;
|
||||
case pmh_attr_type_font_family:
|
||||
return "font-family"; break;
|
||||
case pmh_attr_type_font_style:
|
||||
return "font-style"; break;
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct multi_value
|
||||
{
|
||||
char *value;
|
||||
size_t length;
|
||||
int line_number;
|
||||
struct multi_value *next;
|
||||
} multi_value;
|
||||
|
||||
static multi_value *split_multi_value(char *input, char separator)
|
||||
{
|
||||
multi_value *head = NULL;
|
||||
multi_value *tail = NULL;
|
||||
|
||||
char *c = input;
|
||||
while (*c != '\0')
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; (*(c+i) != '\0' && *(c+i) != separator); i++);
|
||||
|
||||
multi_value *mv = (multi_value *)malloc(sizeof(multi_value));
|
||||
mv->value = (char *)malloc(sizeof(char)*i + 1);
|
||||
mv->length = i;
|
||||
mv->line_number = 0;
|
||||
mv->next = NULL;
|
||||
*mv->value = '\0';
|
||||
strncat(mv->value, c, i);
|
||||
|
||||
if (head == NULL) {
|
||||
head = mv;
|
||||
tail = mv;
|
||||
} else {
|
||||
tail->next = mv;
|
||||
tail = mv;
|
||||
}
|
||||
|
||||
if (*(c+i) == separator)
|
||||
i++;
|
||||
c += i;
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
static void free_multi_value(multi_value *val)
|
||||
{
|
||||
multi_value *cur = val;
|
||||
while (cur != NULL)
|
||||
{
|
||||
multi_value *pvalue = cur;
|
||||
multi_value *next_cur = cur->next;
|
||||
free(pvalue->value);
|
||||
free(pvalue);
|
||||
cur = next_cur;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#define EQUALS(a,b) (strcmp(a, b) == 0)
|
||||
|
||||
static pmh_style_attribute *interpret_attributes(style_parser_data *p_data,
|
||||
pmh_element_type lang_element_type,
|
||||
raw_attribute *raw_attributes)
|
||||
{
|
||||
pmh_style_attribute *attrs = NULL;
|
||||
|
||||
raw_attribute *cur = raw_attributes;
|
||||
while (cur != NULL)
|
||||
{
|
||||
pmh_attr_type atype = pmh_attr_type_from_name(cur->name);
|
||||
pmh_style_attribute *attr = new_attr(cur->name, atype);
|
||||
attr->lang_element_type = lang_element_type;
|
||||
attr->value = new_attr_value();
|
||||
|
||||
if (atype == pmh_attr_type_foreground_color
|
||||
|| atype == pmh_attr_type_background_color
|
||||
|| atype == pmh_attr_type_caret_color
|
||||
|| atype == pmh_attr_type_strike_color)
|
||||
{
|
||||
char *hexstr = trim_str(cur->value);
|
||||
// new_argb_from_hex_str() reports conversion errors
|
||||
attr->value->argb_color =
|
||||
new_argb_from_hex_str(p_data, cur->line_number, hexstr);
|
||||
if (attr->value->argb_color == NULL) {
|
||||
free_style_attributes(attr);
|
||||
attr = NULL;
|
||||
}
|
||||
}
|
||||
else if (atype == pmh_attr_type_font_size_pt)
|
||||
{
|
||||
pmh_attr_font_size *fs = new_font_size();
|
||||
attr->value->font_size = fs;
|
||||
|
||||
char *trimmed_value = trim_str_dup(cur->value);
|
||||
|
||||
fs->is_relative = (*trimmed_value == '+' || *trimmed_value == '-');
|
||||
char *endptr = NULL;
|
||||
fs->size_pt = (int)strtol(cur->value, &endptr, 10);
|
||||
if (endptr == cur->value) {
|
||||
report_error(p_data, cur->line_number,
|
||||
"Value '%s' is invalid for attribute '%s'",
|
||||
cur->value, cur->name);
|
||||
free_style_attributes(attr);
|
||||
attr = NULL;
|
||||
}
|
||||
|
||||
free(trimmed_value);
|
||||
}
|
||||
else if (atype == pmh_attr_type_font_family)
|
||||
{
|
||||
attr->value->font_family = trim_str_dup(cur->value);
|
||||
}
|
||||
else if (atype == pmh_attr_type_font_style)
|
||||
{
|
||||
attr->value->font_styles = new_font_styles();
|
||||
multi_value *values = split_multi_value(cur->value, ',');
|
||||
multi_value *value_cur = values;
|
||||
while (value_cur != NULL)
|
||||
{
|
||||
char *standardized_value = standardize_str(value_cur->value);
|
||||
|
||||
if (EQUALS(standardized_value, "italic"))
|
||||
attr->value->font_styles->italic = true;
|
||||
else if (EQUALS(standardized_value, "bold"))
|
||||
attr->value->font_styles->bold = true;
|
||||
else if (EQUALS(standardized_value, "underlined"))
|
||||
attr->value->font_styles->underlined = true;
|
||||
else if (EQUALS(standardized_value, "strikeout"))
|
||||
attr->value->font_styles->strikeout = true;
|
||||
else {
|
||||
report_error(p_data, cur->line_number,
|
||||
"Value '%s' is invalid for attribute '%s'",
|
||||
standardized_value, cur->name);
|
||||
}
|
||||
|
||||
free(standardized_value);
|
||||
value_cur = value_cur->next;
|
||||
}
|
||||
free_multi_value(values);
|
||||
}
|
||||
else if (atype == pmh_attr_type_other)
|
||||
{
|
||||
attr->value->string = trim_str_dup(cur->value);
|
||||
}
|
||||
|
||||
if (attr != NULL) {
|
||||
// add to linked list
|
||||
attr->next = attrs;
|
||||
attrs = attr;
|
||||
}
|
||||
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
return attrs;
|
||||
}
|
||||
|
||||
|
||||
static void interpret_and_add_style(style_parser_data *p_data,
|
||||
char *style_rule_name,
|
||||
int style_rule_line_number,
|
||||
raw_attribute *raw_attributes)
|
||||
{
|
||||
bool isEditorType = false;
|
||||
bool isCurrentLineType = false;
|
||||
bool isSelectionType = false;
|
||||
pmh_element_type type = pmh_element_type_from_name(style_rule_name);
|
||||
if (type == pmh_NO_TYPE)
|
||||
{
|
||||
if (EQUALS(style_rule_name, "editor"))
|
||||
isEditorType = true, type = pmh_NO_TYPE;
|
||||
else if (EQUALS(style_rule_name, "editor-current-line"))
|
||||
isCurrentLineType = true, type = pmh_NO_TYPE;
|
||||
else if (EQUALS(style_rule_name, "editor-selection"))
|
||||
isSelectionType = true, type = pmh_NO_TYPE;
|
||||
else {
|
||||
report_error(p_data, style_rule_line_number,
|
||||
"Style rule '%s' is not a language element type name or "
|
||||
"one of the following: 'editor', 'editor-current-line', "
|
||||
"'editor-selection'",
|
||||
style_rule_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
pmh_style_attribute *attrs = interpret_attributes(p_data, type, raw_attributes);
|
||||
if (isEditorType)
|
||||
p_data->styles->editor_styles = attrs;
|
||||
else if (isCurrentLineType)
|
||||
p_data->styles->editor_current_line_styles = attrs;
|
||||
else if (isSelectionType)
|
||||
p_data->styles->editor_selection_styles = attrs;
|
||||
else
|
||||
p_data->styles->element_styles[(p_data->styles_pos)++] = attrs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static bool char_is_whitespace(char c)
|
||||
{
|
||||
return (c == ' ' || c == '\t');
|
||||
}
|
||||
|
||||
static bool char_begins_linecomment(char c)
|
||||
{
|
||||
return (c == '#');
|
||||
}
|
||||
|
||||
static bool line_is_comment(multi_value *line)
|
||||
{
|
||||
char *c;
|
||||
for (c = line->value; *c != '\0'; c++)
|
||||
{
|
||||
if (!char_is_whitespace(*c))
|
||||
return char_begins_linecomment(*c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool line_is_empty(multi_value *line)
|
||||
{
|
||||
char *c;
|
||||
for (c = line->value; *c != '\0'; c++)
|
||||
{
|
||||
if (!char_is_whitespace(*c))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct block
|
||||
{
|
||||
multi_value *lines;
|
||||
struct block *next;
|
||||
} block;
|
||||
|
||||
static block *new_block()
|
||||
{
|
||||
block *ret = (block *)malloc(sizeof(block));
|
||||
ret->next = NULL;
|
||||
ret->lines = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void free_blocks(block *val)
|
||||
{
|
||||
block *cur = val;
|
||||
while (cur != NULL)
|
||||
{
|
||||
block *pblock = cur;
|
||||
block *next = pblock->next;
|
||||
free_multi_value(pblock->lines);
|
||||
free(pblock);
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
static block *get_blocks(char *input)
|
||||
{
|
||||
block *head = NULL;
|
||||
block *tail = NULL;
|
||||
block *current_block = NULL;
|
||||
|
||||
multi_value *discarded_lines = NULL;
|
||||
|
||||
int line_number_counter = 1;
|
||||
|
||||
multi_value *lines = split_multi_value(input, '\n');
|
||||
multi_value *previous_line = NULL;
|
||||
multi_value *line_cur = lines;
|
||||
while (line_cur != NULL)
|
||||
{
|
||||
bool discard_line = false;
|
||||
|
||||
line_cur->line_number = line_number_counter++;
|
||||
|
||||
if (line_is_empty(line_cur))
|
||||
{
|
||||
discard_line = true;
|
||||
|
||||
if (current_block != NULL)
|
||||
{
|
||||
// terminate block
|
||||
if (tail != current_block)
|
||||
tail->next = current_block;
|
||||
tail = current_block;
|
||||
current_block = NULL;
|
||||
previous_line->next = NULL;
|
||||
}
|
||||
}
|
||||
else if (line_is_comment(line_cur))
|
||||
{
|
||||
// Do not discard (i.e. free()) comment lines within blocks:
|
||||
if (current_block == NULL)
|
||||
discard_line = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (current_block == NULL)
|
||||
{
|
||||
// start block
|
||||
current_block = new_block();
|
||||
current_block->lines = line_cur;
|
||||
if (previous_line != NULL)
|
||||
previous_line->next = NULL;
|
||||
}
|
||||
if (head == NULL) {
|
||||
head = current_block;
|
||||
tail = current_block;
|
||||
}
|
||||
}
|
||||
|
||||
multi_value *next_cur = line_cur->next;
|
||||
previous_line = (discard_line) ? NULL : line_cur;
|
||||
|
||||
if (discard_line) {
|
||||
line_cur->next = discarded_lines;
|
||||
discarded_lines = line_cur;
|
||||
}
|
||||
|
||||
line_cur = next_cur;
|
||||
}
|
||||
|
||||
if (current_block != NULL && tail != current_block)
|
||||
tail->next = current_block;
|
||||
|
||||
free_multi_value(discarded_lines);
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
|
||||
#define ASSIGNMENT_OP_UITEXT "':' or '='"
|
||||
#define IS_ASSIGNMENT_OP(c) ((c) == ':' || (c) == '=')
|
||||
#define IS_STYLE_RULE_NAME_CHAR(c) \
|
||||
( (c) != '\0' && !isspace(c) \
|
||||
&& !char_begins_linecomment(c) && !IS_ASSIGNMENT_OP(c) )
|
||||
#define IS_ATTRIBUTE_NAME_CHAR(c) \
|
||||
( (c) != '\0' && !char_begins_linecomment(c) && !IS_ASSIGNMENT_OP(c) )
|
||||
#define IS_ATTRIBUTE_VALUE_CHAR(c) \
|
||||
( (c) != '\0' && !char_begins_linecomment(c) )
|
||||
|
||||
static char *get_style_rule_name(multi_value *line)
|
||||
{
|
||||
char *str = line->value;
|
||||
|
||||
// Scan past leading whitespace:
|
||||
size_t start_index;
|
||||
for (start_index = 0;
|
||||
(*(str+start_index) != '\0' && isspace(*(str+start_index)));
|
||||
start_index++);
|
||||
|
||||
// Scan until style rule name characters end:
|
||||
size_t value_end_index;
|
||||
for (value_end_index = start_index;
|
||||
IS_STYLE_RULE_NAME_CHAR(*(str + value_end_index));
|
||||
value_end_index++);
|
||||
|
||||
// Copy style rule name:
|
||||
size_t value_len = value_end_index - start_index;
|
||||
char *value = (char *)malloc(sizeof(char)*value_len + 1);
|
||||
*value = '\0';
|
||||
strncat(value, (str + start_index), value_len);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool parse_attribute_line(style_parser_data *p_data, multi_value *line,
|
||||
char **out_attr_name, char **out_attr_value)
|
||||
{
|
||||
char *str = line->value;
|
||||
|
||||
// Scan past leading whitespace:
|
||||
size_t name_start_index;
|
||||
for (name_start_index = 0;
|
||||
( *(str+name_start_index) != '\0' &&
|
||||
isspace(*(str+name_start_index)) );
|
||||
name_start_index++);
|
||||
|
||||
// Scan until attribute name characters end:
|
||||
size_t name_end_index;
|
||||
for (name_end_index = name_start_index;
|
||||
IS_ATTRIBUTE_NAME_CHAR(*(str + name_end_index));
|
||||
name_end_index++);
|
||||
// Scan backwards to trim trailing whitespace off:
|
||||
while (name_start_index < name_end_index
|
||||
&& isspace(*(str + name_end_index - 1)))
|
||||
name_end_index--;
|
||||
|
||||
// Scan until just after the first assignment operator:
|
||||
size_t assignment_end_index;
|
||||
for (assignment_end_index = name_end_index;
|
||||
( *(str + assignment_end_index) != '\0' &&
|
||||
!IS_ASSIGNMENT_OP(*(str + assignment_end_index)) );
|
||||
assignment_end_index++);
|
||||
|
||||
// Scan over the found assignment operator, or report error:
|
||||
if (IS_ASSIGNMENT_OP(*(str + assignment_end_index)))
|
||||
assignment_end_index++;
|
||||
else
|
||||
{
|
||||
report_error(p_data, line->line_number,
|
||||
"Invalid attribute definition: str does not contain "
|
||||
"an assignment operator (%s): '%s'",
|
||||
ASSIGNMENT_OP_UITEXT, str);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t value_start_index = assignment_end_index;
|
||||
// Scan until attribute value characters end:
|
||||
size_t value_end_index;
|
||||
for (value_end_index = value_start_index;
|
||||
IS_ATTRIBUTE_VALUE_CHAR(*(str + value_end_index));
|
||||
value_end_index++);
|
||||
|
||||
// Copy attribute name:
|
||||
size_t name_len = name_end_index - name_start_index;
|
||||
char *attr_name = (char *)malloc(sizeof(char)*name_len + 1);
|
||||
*attr_name = '\0';
|
||||
strncat(attr_name, (str + name_start_index), name_len);
|
||||
*out_attr_name = attr_name;
|
||||
|
||||
// Copy attribute value:
|
||||
size_t attr_value_len = value_end_index - assignment_end_index;
|
||||
char *attr_value_str = (char *)malloc(sizeof(char)*attr_value_len + 1);
|
||||
*attr_value_str = '\0';
|
||||
strncat(attr_value_str, (str + assignment_end_index), attr_value_len);
|
||||
*out_attr_value = attr_value_str;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#define HAS_UTF8_BOM(x) ( ((*x & 0xFF) == 0xEF)\
|
||||
&& ((*(x+1) & 0xFF) == 0xBB)\
|
||||
&& ((*(x+2) & 0xFF) == 0xBF) )
|
||||
|
||||
// - Removes UTF-8 BOM
|
||||
// - Standardizes line endings to \n
|
||||
static char *strcpy_preformat_style(char *str)
|
||||
{
|
||||
char *new_str = (char *)malloc(sizeof(char) * strlen(str) + 1);
|
||||
|
||||
char *c = str;
|
||||
int i = 0;
|
||||
|
||||
if (HAS_UTF8_BOM(c))
|
||||
c += 3;
|
||||
|
||||
while (*c != '\0')
|
||||
{
|
||||
if (*c == '\r' && *(c+1) == '\n')
|
||||
{
|
||||
*(new_str+i) = '\n';
|
||||
i++;
|
||||
c += 2;
|
||||
}
|
||||
else if (*c == '\r')
|
||||
{
|
||||
*(new_str+i) = '\n';
|
||||
i++;
|
||||
c++;
|
||||
}
|
||||
else
|
||||
{
|
||||
*(new_str+i) = *c;
|
||||
i++;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
*(new_str+i) = '\0';
|
||||
|
||||
return new_str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void _sty_parse(style_parser_data *p_data)
|
||||
{
|
||||
// We don't have to worry about leaking the original p_data->input;
|
||||
// the user of the library is responsible for that:
|
||||
p_data->input = strcpy_preformat_style(p_data->input);
|
||||
|
||||
block *blocks = get_blocks(p_data->input);
|
||||
|
||||
block *block_cur = blocks;
|
||||
while (block_cur != NULL)
|
||||
{
|
||||
raw_attribute *attributes_head = NULL;
|
||||
raw_attribute *attributes_tail = NULL;
|
||||
|
||||
pmhsp_PRINTF("Block:\n");
|
||||
multi_value *header_line = block_cur->lines;
|
||||
if (header_line == NULL) {
|
||||
block_cur = block_cur->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
pmhsp_PRINTF(" Head line (len %ld): '%s'\n",
|
||||
header_line->length, header_line->value);
|
||||
char *style_rule_name = get_style_rule_name(header_line);
|
||||
pmhsp_PRINTF(" Style rule name: '%s'\n", style_rule_name);
|
||||
|
||||
multi_value *attr_line_cur = header_line->next;
|
||||
if (attr_line_cur == NULL)
|
||||
report_error(p_data, header_line->line_number,
|
||||
"No style attributes defined for style rule '%s'",
|
||||
style_rule_name);
|
||||
|
||||
while (attr_line_cur != NULL)
|
||||
{
|
||||
if (line_is_comment(attr_line_cur))
|
||||
{
|
||||
attr_line_cur = attr_line_cur->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
pmhsp_PRINTF(" Attr line (len %ld): '%s'\n",
|
||||
attr_line_cur->length, attr_line_cur->value);
|
||||
char *attr_name_str;
|
||||
char *attr_value_str;
|
||||
bool success = parse_attribute_line(p_data,
|
||||
attr_line_cur,
|
||||
&attr_name_str,
|
||||
&attr_value_str);
|
||||
if (success)
|
||||
{
|
||||
pmhsp_PRINTF(" Attr: '%s' Value: '%s'\n",
|
||||
attr_name_str, attr_value_str);
|
||||
raw_attribute *attribute =
|
||||
new_raw_attribute(attr_name_str, attr_value_str,
|
||||
attr_line_cur->line_number);
|
||||
if (attributes_head == NULL) {
|
||||
attributes_head = attribute;
|
||||
attributes_tail = attribute;
|
||||
} else {
|
||||
attributes_tail->next = attribute;
|
||||
attributes_tail = attribute;
|
||||
}
|
||||
}
|
||||
|
||||
attr_line_cur = attr_line_cur->next;
|
||||
}
|
||||
|
||||
if (attributes_head != NULL)
|
||||
{
|
||||
interpret_and_add_style(p_data, style_rule_name,
|
||||
header_line->line_number, attributes_head);
|
||||
free_raw_attributes(attributes_head);
|
||||
}
|
||||
|
||||
free(style_rule_name);
|
||||
|
||||
block_cur = block_cur->next;
|
||||
}
|
||||
|
||||
free_blocks(blocks);
|
||||
free(p_data->input);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static pmh_style_collection *new_style_collection()
|
||||
{
|
||||
pmh_style_collection *sc = (pmh_style_collection *)
|
||||
malloc(sizeof(pmh_style_collection));
|
||||
|
||||
sc->element_styles = (pmh_style_attribute**)
|
||||
malloc(sizeof(pmh_style_attribute*)
|
||||
* pmh_NUM_LANG_TYPES);
|
||||
int i;
|
||||
for (i = 0; i < pmh_NUM_LANG_TYPES; i++)
|
||||
sc->element_styles[i] = NULL;
|
||||
|
||||
sc->editor_styles = NULL;
|
||||
sc->editor_current_line_styles = NULL;
|
||||
sc->editor_selection_styles = NULL;
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
void pmh_free_style_collection(pmh_style_collection *coll)
|
||||
{
|
||||
free_style_attributes(coll->editor_styles);
|
||||
free_style_attributes(coll->editor_current_line_styles);
|
||||
free_style_attributes(coll->editor_selection_styles);
|
||||
int i;
|
||||
for (i = 0; i < pmh_NUM_LANG_TYPES; i++)
|
||||
free_style_attributes(coll->element_styles[i]);
|
||||
free(coll->element_styles);
|
||||
free(coll);
|
||||
}
|
||||
|
||||
static style_parser_data *new_style_parser_data(char *input)
|
||||
{
|
||||
style_parser_data *p_data = (style_parser_data*)
|
||||
malloc(sizeof(style_parser_data));
|
||||
p_data->input = input;
|
||||
p_data->styles_pos = 0;
|
||||
p_data->styles = new_style_collection();
|
||||
return p_data;
|
||||
}
|
||||
|
||||
pmh_style_collection *pmh_parse_styles(char *input,
|
||||
void(*error_callback)(char*,int,void*),
|
||||
void *error_callback_context)
|
||||
{
|
||||
style_parser_data *p_data = new_style_parser_data(input);
|
||||
p_data->error_callback = error_callback;
|
||||
p_data->error_callback_context = error_callback_context;
|
||||
|
||||
_sty_parse(p_data);
|
||||
|
||||
pmh_style_collection *ret = p_data->styles;
|
||||
free(p_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1,149 +0,0 @@
|
||||
/* PEG Markdown Highlight
|
||||
* Copyright 2011-2016 Ali Rantakari -- http://hasseg.org
|
||||
* Licensed under the GPL2+ and MIT licenses (see LICENSE for more info).
|
||||
*
|
||||
* pmh_styleparser.h
|
||||
*
|
||||
* Public interface of a parser for custom syntax highlighting stylesheets.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \brief Style parser public interface.
|
||||
*/
|
||||
|
||||
#include "pmh_definitions.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* \brief Color (ARGB) attribute value.
|
||||
*
|
||||
* All values are 0-255.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int red; /**< Red color component (0-255) */
|
||||
int green; /**< Green color component (0-255) */
|
||||
int blue; /**< Blue color component (0-255) */
|
||||
int alpha; /**< Alpha (opacity) color component (0-255) */
|
||||
} pmh_attr_argb_color;
|
||||
|
||||
/** \brief Font style attribute value. */
|
||||
typedef struct
|
||||
{
|
||||
bool italic;
|
||||
bool bold;
|
||||
bool underlined;
|
||||
bool strikeout;
|
||||
} pmh_attr_font_styles;
|
||||
|
||||
/** \brief Font size attribute value. */
|
||||
typedef struct
|
||||
{
|
||||
int size_pt; /**< The font point size */
|
||||
bool is_relative; /**< Whether the size is relative (i.e. size_pt points
|
||||
larger than the default font) */
|
||||
} pmh_attr_font_size;
|
||||
|
||||
/** \brief Style attribute types. */
|
||||
typedef enum
|
||||
{
|
||||
pmh_attr_type_foreground_color, /**< Foreground color */
|
||||
pmh_attr_type_background_color, /**< Background color */
|
||||
pmh_attr_type_caret_color, /**< Caret (insertion point) color */
|
||||
pmh_attr_type_font_size_pt, /**< Font size (in points) */
|
||||
pmh_attr_type_font_family, /**< Font family */
|
||||
pmh_attr_type_font_style, /**< Font style */
|
||||
pmh_attr_type_strike_color, /**< Strike-through color */
|
||||
pmh_attr_type_other /**< Arbitrary custom attribute */
|
||||
} pmh_attr_type;
|
||||
|
||||
/**
|
||||
* \brief Style attribute value.
|
||||
*
|
||||
* Determine which member to access in this union based on the
|
||||
* 'type' value of the pmh_style_attribute.
|
||||
*
|
||||
* \sa pmh_style_attribute
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
pmh_attr_argb_color *argb_color; /**< ARGB color */
|
||||
pmh_attr_font_styles *font_styles; /**< Font styles */
|
||||
pmh_attr_font_size *font_size; /**< Font size */
|
||||
char *font_family; /**< Font family */
|
||||
char *string; /**< Arbitrary custom string value
|
||||
(use this if the attribute's type
|
||||
is pmh_attr_type_other) */
|
||||
} pmh_attr_value;
|
||||
|
||||
/** \brief Style attribute. */
|
||||
typedef struct pmh_style_attribute
|
||||
{
|
||||
pmh_element_type lang_element_type; /**< The Markdown language element this
|
||||
style applies to */
|
||||
pmh_attr_type type; /**< The type of the attribute */
|
||||
char *name; /**< The name of the attribute (if type
|
||||
is pmh_attr_type_other, you can
|
||||
use this value to determine what
|
||||
the attribute is) */
|
||||
pmh_attr_value *value; /**< The value of the attribute */
|
||||
struct pmh_style_attribute *next; /**< Next attribute in linked list */
|
||||
} pmh_style_attribute;
|
||||
|
||||
/** \brief Collection of styles. */
|
||||
typedef struct
|
||||
{
|
||||
/** Styles that apply to the editor in general */
|
||||
pmh_style_attribute *editor_styles;
|
||||
|
||||
/** Styles that apply to the line in the editor where the caret (insertion
|
||||
point) resides */
|
||||
pmh_style_attribute *editor_current_line_styles;
|
||||
|
||||
/** Styles that apply to the range of selected text in the editor */
|
||||
pmh_style_attribute *editor_selection_styles;
|
||||
|
||||
/** Styles that apply to specific Markdown language elements */
|
||||
pmh_style_attribute **element_styles;
|
||||
} pmh_style_collection;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Parse stylesheet string, return style collection
|
||||
*
|
||||
* \param[in] input The stylesheet string to parse.
|
||||
* \param[in] error_callback Callback function to be called when errors
|
||||
* occur during parsing. The first argument
|
||||
* to the callback function is the error
|
||||
* message and the second one the line number
|
||||
* in the original input where the error
|
||||
* occurred. The last argument will always
|
||||
* get the value you pass in for the
|
||||
* error_callback_context argument to this
|
||||
* function.
|
||||
* Pass in NULL to suppress error reporting.
|
||||
* \param[in] error_callback_context Arbitrary context pointer for the error
|
||||
* callback function; will be passed in as
|
||||
* the last argument to error_callback.
|
||||
*
|
||||
* \return A pmh_style_collection. You must pass this value to
|
||||
* pmh_free_style_collection() when it's not needed anymore.
|
||||
*/
|
||||
pmh_style_collection *pmh_parse_styles(char *input,
|
||||
void(*error_callback)(char*,int,void*),
|
||||
void *error_callback_context);
|
||||
|
||||
/**
|
||||
* \brief Free a pmh_style_collection.
|
||||
*
|
||||
* Frees a pmh_style_collection value returned by pmh_parse_styles().
|
||||
*
|
||||
* \param[in] collection The collection to free.
|
||||
*/
|
||||
void pmh_free_style_collection(pmh_style_collection *collection);
|
||||
|
||||
|
||||
char *pmh_attr_name_from_type(pmh_attr_type type);
|
||||
|
||||
pmh_attr_type pmh_attr_type_from_name(char *name);
|
||||
|
@ -1,8 +0,0 @@
|
||||
NONEXISTENT_TYPE
|
||||
x: 3
|
||||
|
||||
STRONG
|
||||
font-style: funkadelic, bold, snazzy
|
||||
foreground: 13bx12
|
||||
background: 5
|
||||
|
@ -1,87 +0,0 @@
|
||||
# Styles using 'Solarized' color scheme
|
||||
# by Ethan Schoonover: http://ethanschoonover.com/solarized
|
||||
#
|
||||
# (dark background version)
|
||||
|
||||
editor
|
||||
foreground: 93a1a1 # base1
|
||||
background: 002b36 # base03
|
||||
caret: ffffff
|
||||
font-size: 13
|
||||
|
||||
H1
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
font-size: +6
|
||||
|
||||
H2
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
font-size: +5
|
||||
|
||||
H3
|
||||
foreground: 6c71c4 # violet
|
||||
font-size: +4
|
||||
|
||||
H4
|
||||
foreground: 268bd2 # blue
|
||||
font-size: +3
|
||||
|
||||
H5
|
||||
foreground: 268bd2 # blue
|
||||
font-size: +2
|
||||
|
||||
H6
|
||||
foreground: 268bd2 # blue
|
||||
font-size: +1
|
||||
|
||||
HRULE
|
||||
foreground: 586e75 # base01
|
||||
|
||||
LIST_BULLET
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LIST_ENUMERATOR
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LINK
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_URL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_EMAIL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
IMAGE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
REFERENCE
|
||||
foreground: 80b58900 # yellow, reduced alpha
|
||||
font-size: -2
|
||||
|
||||
CODE
|
||||
foreground: 859900 # green
|
||||
|
||||
EMPH
|
||||
foreground: cb4b16 # orange
|
||||
font-style: italic
|
||||
|
||||
STRONG
|
||||
foreground: dc322f # red
|
||||
font-style: bold
|
||||
|
||||
HTML_ENTITY
|
||||
foreground: 6c71c4 # violet
|
||||
|
||||
COMMENT
|
||||
foreground: 93a1a1 # base1
|
||||
|
||||
VERBATIM
|
||||
foreground: 859900 # green
|
||||
|
||||
BLOCKQUOTE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
STRIKE
|
||||
strike-color: 93a1a1 # base1
|
@ -1 +0,0 @@
|
||||
# linecomment
editor # comment
foreground : 13ff13
background : 000000 # comment
# linecomment
STRONG:
EMPH=
# comment
foreground: 00ff00
comment asd
background: AB0000ff
STRONG :
dog: 1 # something
cat: 4
font-style: underlined, Italic , BoLD #hi, hello
font-size: 14pt
font-family: Courier New, Times
# linecomment
BOO
x: 3
editor-selection:
foreground: abcdef
background: abcdef
editor-current-line:
background: ffffff
|
@ -1,22 +0,0 @@
|
||||
editor :
|
||||
foreground : 13ff13
|
||||
background : 000000
|
||||
caret: ffffff
|
||||
|
||||
EMPH
|
||||
font-style: italic
|
||||
|
||||
STRONG
|
||||
font-style: bold
|
||||
|
||||
LINK
|
||||
font-style: underlined
|
||||
|
||||
editor-selection:
|
||||
foreground: ff0000
|
||||
background: eeeeee
|
||||
font-style: underlined
|
||||
|
||||
editor-current-line:
|
||||
background: ffffff
|
||||
|
@ -1,79 +0,0 @@
|
||||
# Styles using 'Solarized' color scheme
|
||||
# by Ethan Schoonover: http://ethanschoonover.com/solarized
|
||||
#
|
||||
# (dark background version)
|
||||
|
||||
editor
|
||||
foreground: 93a1a1 # base1
|
||||
background: 002b36 # base03
|
||||
caret: ffffff
|
||||
|
||||
H1
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
|
||||
H2
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
|
||||
H3
|
||||
foreground: 6c71c4 # violet
|
||||
|
||||
H4
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
H5
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
H6
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
HRULE
|
||||
foreground: 586e75 # base01
|
||||
|
||||
LIST_BULLET
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LIST_ENUMERATOR
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LINK
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_URL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_EMAIL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
IMAGE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
REFERENCE
|
||||
foreground: 80b58900 # yellow, reduced alpha
|
||||
|
||||
CODE
|
||||
foreground: 859900 # green
|
||||
|
||||
EMPH
|
||||
foreground: cb4b16 # orange
|
||||
font-style: italic
|
||||
|
||||
STRONG
|
||||
foreground: dc322f # red
|
||||
font-style: bold
|
||||
|
||||
HTML_ENTITY
|
||||
foreground: 6c71c4 # violet
|
||||
|
||||
COMMENT
|
||||
foreground: 93a1a1 # base1
|
||||
|
||||
VERBATIM
|
||||
foreground: 859900 # green
|
||||
|
||||
BLOCKQUOTE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
STRIKE
|
||||
strike-color: 93a1a1 # base1
|
@ -1,80 +0,0 @@
|
||||
# Styles using 'Solarized' color scheme
|
||||
# by Ethan Schoonover: http://ethanschoonover.com/solarized
|
||||
#
|
||||
# (light background version)
|
||||
|
||||
editor
|
||||
foreground: 586e75 # base01
|
||||
background: fdf6e3 # base3
|
||||
caret: 000000
|
||||
|
||||
H1
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
|
||||
H2
|
||||
foreground: 6c71c4 # violet
|
||||
font-style: bold
|
||||
|
||||
H3
|
||||
foreground: 6c71c4 # violet
|
||||
|
||||
H4
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
H5
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
H6
|
||||
foreground: 268bd2 # blue
|
||||
|
||||
HRULE
|
||||
foreground: 586e75 # base01
|
||||
|
||||
LIST_BULLET
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LIST_ENUMERATOR
|
||||
foreground: b58900 # yellow
|
||||
|
||||
LINK
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_URL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
AUTO_LINK_EMAIL
|
||||
foreground: 2aa198 # cyan
|
||||
|
||||
IMAGE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
REFERENCE
|
||||
foreground: 80b58900 # yellow, reduced alpha
|
||||
|
||||
CODE
|
||||
foreground: 859900 # green
|
||||
|
||||
EMPH
|
||||
foreground: cb4b16 # orange
|
||||
font-style: italic
|
||||
|
||||
STRONG
|
||||
foreground: dc322f # red
|
||||
font-style: bold
|
||||
|
||||
HTML_ENTITY
|
||||
foreground: 6c71c4 # violet
|
||||
|
||||
COMMENT
|
||||
foreground: 93a1a1 # base1
|
||||
|
||||
VERBATIM
|
||||
foreground: 859900 # green
|
||||
|
||||
BLOCKQUOTE
|
||||
foreground: d33682 # magenta
|
||||
|
||||
STRIKE
|
||||
strike-color: 586e75 # base01
|
||||
|
@ -1,33 +0,0 @@
|
||||
# linecomment
|
||||
|
||||
editor # comment
|
||||
foreground : 13ff13
|
||||
background : 000000 # comment
|
||||
|
||||
# linecomment
|
||||
STRONG:
|
||||
|
||||
EMPH=
|
||||
# comment
|
||||
foreground: 00ff00
|
||||
comment asd
|
||||
background: AB0000ff
|
||||
|
||||
STRONG :
|
||||
dog: 1 # something
|
||||
cat: 4
|
||||
font-style: underlined, Italic , BoLD #hi, hello
|
||||
font-size: 14pt
|
||||
font-family: Courier New, Times
|
||||
|
||||
# linecomment
|
||||
BOO
|
||||
x: 3
|
||||
|
||||
editor-selection:
|
||||
foreground: abcdef
|
||||
background: abcdef
|
||||
|
||||
editor-current-line:
|
||||
background: ffffff
|
||||
|
@ -1,33 +0,0 @@
|
||||
# linecomment
|
||||
|
||||
editor # comment
|
||||
foreground : 13ff13
|
||||
background : 000000 # comment
|
||||
|
||||
# linecomment
|
||||
STRONG:
|
||||
|
||||
EMPH=
|
||||
# comment
|
||||
foreground: 00ff00
|
||||
comment asd
|
||||
background: AB0000ff
|
||||
|
||||
STRONG :
|
||||
dog: 1 # something
|
||||
cat: 4
|
||||
font-style: underlined, Italic , BoLD #hi, hello
|
||||
font-size: 14pt
|
||||
font-family: Courier New, Times
|
||||
|
||||
# linecomment
|
||||
BOO
|
||||
x: 3
|
||||
|
||||
editor-selection:
|
||||
foreground: abcdef
|
||||
background: abcdef
|
||||
|
||||
editor-current-line:
|
||||
background: ffffff
|
||||
|
@ -1,199 +0,0 @@
|
||||
|
||||
The Syntax of PEG Markdown Highlight Stylesheets
|
||||
================================================
|
||||
|
||||
[PEG Markdown Highlight][pmh] includes a parser for stylesheets that define how different Markdown language elements are to be highlighted. This document describes the syntax of these stylesheets.
|
||||
|
||||
[pmh]: http://hasseg.org/peg-markdown-highlight/
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
Here is a quick, simple example of a stylesheet:
|
||||
|
||||
<style>
|
||||
.codetable { border-collapse: collapse; }
|
||||
.codetable .left { text-align: right; padding-right: 10px; }
|
||||
.codetable .right { text-align: left; padding-left: 10px; }
|
||||
.codetable .content { font-family: monospace; background: #eee; padding: 0 5px; }
|
||||
.codetable .comment { color: #174EB3; }
|
||||
.codetable .rule { color: #491B8F; }
|
||||
.codetable .attrname { color: #48B317; }
|
||||
.codetable .attrvalue { color: #A65C1F; }
|
||||
</style>
|
||||
|
||||
<table class="codetable">
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"># The first comment lines</td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"># describe the stylesheet.</td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"> </td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><span class="rule">Style rule →</span></td>
|
||||
<td class="content"><span class="rule">editor:</span></td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"> foreground: ff0000 <span class="comment"># red text</span></td>
|
||||
<td class="right"><span class="comment">← Comment</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"><span class="attrname">Attribute name →</span></td>
|
||||
<td class="content"> <span class="attrname">font-family</span>: <span class="attrvalue">Consolas</span></td>
|
||||
<td class="right"><span class="attrvalue">← Attribute value</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"> </td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content">EMPH:</td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"> font-size: 14</td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="left"></td>
|
||||
<td class="content"> font-style: bold, underlined</td>
|
||||
<td class="right"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
Style Rules
|
||||
-----------
|
||||
|
||||
A stylesheet is composed of one or more *rules*. Rules are separated from each other by **empty lines** like so:
|
||||
|
||||
H2:
|
||||
foreground: ff0000
|
||||
|
||||
H3:
|
||||
foreground: 00ff00
|
||||
|
||||
Each begins with the ***name* of the rule**, which is always on its own line, and may be one of the following:
|
||||
|
||||
- **`editor`**: Styles that apply to the whole document/editor
|
||||
- **`editor-current-line`**: Styles that apply to the current line in the editor (i.e. the line where the caret is)
|
||||
- **`editor-selection`**: Styles that apply to the selected range in the editor when the user makes a selection in the text
|
||||
- A Markdown element type (like `EMPH`, `REFERENCE` or `H1`): Styles that apply to occurrences of that particular element. The supported element types are:
|
||||
- **`LINK`:** Explicit link (like `[click here][ref]`)
|
||||
- **`AUTO_LINK_URL`:** Implicit URL link (like `<http://google.com>`)
|
||||
- **`AUTO_LINK_EMAIL`:** Implicit email link (like `<first.last@google.com>`)
|
||||
- **`IMAGE`:** Image definition
|
||||
- **`REFERENCE`:** Reference (like `[id]: http://www.google.com`)
|
||||
- **`CODE`:** Inline code
|
||||
- **`EMPH`:** Emphasized text
|
||||
- **`STRONG`:** Strong text
|
||||
- **`LIST_BULLET`:** Bullet for an unordered list item
|
||||
- **`LIST_ENUMERATOR`:** Enumerator for an ordered list item
|
||||
- **`H1`:** Header, level 1
|
||||
- **`H2`:** Header, level 2
|
||||
- **`H3`:** Header, level 3
|
||||
- **`H4`:** Header, level 4
|
||||
- **`H5`:** Header, level 5
|
||||
- **`H6`:** Header, level 6
|
||||
- **`BLOCKQUOTE`:** Blockquote marker
|
||||
- **`VERBATIM`:** Block of code
|
||||
- **`HRULE`:** Horizontal rule
|
||||
- **`HTML`:** HTML tag
|
||||
- **`HTML_ENTITY`:** HTML special entity definition (like `…`)
|
||||
- **`HTMLBLOCK`:** Block of HTML
|
||||
- **`COMMENT`:** (HTML) Comment
|
||||
- **`NOTE`:** Note
|
||||
- **`STRIKE`:** Strike-through
|
||||
|
||||
The name may be optionally followed by an assignment operator (either `:` or `=`):
|
||||
|
||||
H1:
|
||||
foreground: ff00ff
|
||||
|
||||
H2 =
|
||||
foreground: ff0000
|
||||
|
||||
H3
|
||||
foreground: 00ff00
|
||||
|
||||
The **order of style rules is significant**; it defines the order in which different language elements should be highlighted. *(Of course applications that use PEG Markdown Highlight and the style parser may disregard this and highlight elements in whatever order they desire.)*
|
||||
|
||||
After the name of the rule, there can be one or more *attributes*.
|
||||
|
||||
|
||||
Style Attributes
|
||||
----------------
|
||||
|
||||
Attribute assignments are each on their own line, and they consist of the *name* of the attribute as well as the *value* assigned to it. An assignment operator (either `:` or `=`) separates the name from the value:
|
||||
|
||||
attribute-name: value
|
||||
attribute-name= value
|
||||
|
||||
Attribute assignment lines **may be indented**.
|
||||
|
||||
### Attribute Names and Types
|
||||
|
||||
The following is a list of the names of predefined attributes, and the values they may be assigned:
|
||||
|
||||
- `foreground-color` *(aliases: `foreground` and `color`)*
|
||||
- See the *Color Attribute Values* subsection for information about valid values for this attribute.
|
||||
- `background-color` *(alias: `background`)*
|
||||
- See the *Color Attribute Values* subsection for information about valid values for this attribute.
|
||||
- `caret-color` *(alias: `caret`)*
|
||||
- See the *Color Attribute Values* subsection for information about valid values for this attribute.
|
||||
- `strike-color` *(alias: `strike`)*
|
||||
- See the *Color Attribute Values* subsection for information about valid values for this attribute.
|
||||
- `font-size`
|
||||
- An integer value for the font size, *in points* (i.e. not in pixels). The number may have a textual suffix such as `pt`.
|
||||
- If the value begins with `+` or `-`, it is considered *relative* to some base font size (as defined by the host application). For example, the value `3` defines the font size as 3 (absolute) while `+3` defines it as +3 (relative), i.e. 3 point sizes larger than the base font size.
|
||||
- `font-family`
|
||||
- A comma-separated list of one or more arbitrary font family names. *(It is up to the application that uses the PEG Markdown Highlight library to resolve this string to actual fonts on the system.)*
|
||||
- `font-style`
|
||||
- A comma-separated list of one or more of the following:
|
||||
- `italic`
|
||||
- `bold`
|
||||
- `underlined`
|
||||
|
||||
Applications may also include support for any **custom attribute names and values** they desire — attributes other than the ones listed above will be included in the style parser results, with their values stored as strings.
|
||||
|
||||
|
||||
## Color Attribute Values
|
||||
|
||||
Colors can be specified either in **RGB** (red, green, blue) or **ARGB** (alpha, red, green, blue) formats. In both, each component is a two-character hexadecimal value (from `00` to `FF`):
|
||||
|
||||
foreground: ff00ee # red = ff, green = 00, blue = ee (and implicitly, alpha = ff)
|
||||
background: 99ff00ee # alpha = 99, red = ff, green = 00, blue = ee
|
||||
|
||||
|
||||
Comments
|
||||
--------
|
||||
|
||||
Each line in a stylesheet may have a comment. The `#` character begins a line comment that continues until the end of the line:
|
||||
|
||||
# this line has only this comment
|
||||
H1: # this line has a style rule name and then a comment
|
||||
foreground: ff0000 # this line has an attribute and then a comment
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 198 KiB |
Before Width: | Height: | Size: 570 KiB |
Before Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 351 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 44 KiB |
@ -1,57 +0,0 @@
|
||||
name: vnote
|
||||
version: "2.3"
|
||||
summary: Markdown note-taking app
|
||||
description: |
|
||||
VNote is a note-taking application that
|
||||
knows programmers and Markdown better.
|
||||
It's designed to provide both powerful
|
||||
note management and pleasant Markdown
|
||||
experience.
|
||||
|
||||
confinement: devmode
|
||||
|
||||
apps:
|
||||
vnote:
|
||||
command: desktop-launch VNote
|
||||
|
||||
parts:
|
||||
vnote:
|
||||
source: https://github.com/tamlok/vnote.git
|
||||
plugin: qmake
|
||||
qt-version: qt5
|
||||
options:
|
||||
- CONFIG+=release
|
||||
- CONFIG+=c++11
|
||||
- CONFIG+=c99
|
||||
- QMAKE_CXX=g++-6
|
||||
- QMAKE_CC=gcc-6
|
||||
- QMAKE_CFLAGS+=-std=c99
|
||||
- ./VNote.pro
|
||||
build-packages:
|
||||
- build-essential
|
||||
- g++-6
|
||||
- gcc-6
|
||||
- qtbase5-dev
|
||||
- libqt5svg5-dev
|
||||
- libqt5webchannel5-dev
|
||||
- qt5-default
|
||||
- qtpositioning5-dev
|
||||
- qtwebengine5-dev
|
||||
- libxcb1-dev
|
||||
- libxcb-xkb-dev
|
||||
- libssl-dev
|
||||
- libgl1-mesa-dev
|
||||
stage-packages:
|
||||
- libqt5gui5
|
||||
- libqt5core5a
|
||||
- libqt5network5
|
||||
- libqt5printsupport5
|
||||
- libqt5webchannel5
|
||||
- libqt5webenginecore5
|
||||
- libqt5webenginewidgets5
|
||||
- libqt5widgets5
|
||||
- libqt5svg5
|
||||
- libxcb1
|
||||
- libxcb-xkb1
|
||||
- openssl
|
||||
after: [desktop-qt5]
|
@ -1,126 +0,0 @@
|
||||
add_executable(VNote MACOSX_BUNDLE main.cpp)
|
||||
|
||||
file(GLOB SRC_FILES *.cpp)
|
||||
file(GLOB DIALOG_SRCS dialog/*.cpp)
|
||||
file(GLOB UTILS_SRCS utils/*.cpp)
|
||||
file(GLOB WIDGETS_SRCS widgets/*.cpp)
|
||||
file(GLOB QRC_FILES *.qrc)
|
||||
file(GLOB TRANSLATIONS translations/*.qm)
|
||||
|
||||
target_sources(VNote PRIVATE ${SRC_FILES})
|
||||
target_sources(VNote PRIVATE ${DIALOG_SRCS})
|
||||
target_sources(VNote PRIVATE ${UTILS_SRCS})
|
||||
target_sources(VNote PRIVATE ${WIDGETS_SRCS})
|
||||
target_sources(VNote PRIVATE ${QRC_FILES})
|
||||
if(WIN32)
|
||||
target_sources(VNote PRIVATE resources/icon.rc)
|
||||
endif(WIN32)
|
||||
|
||||
include_directories(dialog utils widgets)
|
||||
|
||||
# Remove the console of gui program
|
||||
if(WIN32)
|
||||
if(MSVC)
|
||||
set_target_properties(VNote PROPERTIES
|
||||
WIN32_EXECUTABLE YES
|
||||
LINK_FLAGS "/ENTRY:mainCRTStartup"
|
||||
)
|
||||
elseif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mwindows") # Not tested
|
||||
else()
|
||||
message(SEND_ERROR "You are using an unsupported Windows compiler! (Not MSVC or GCC)")
|
||||
endif(MSVC)
|
||||
elseif(APPLE)
|
||||
set_target_properties(VNote PROPERTIES
|
||||
MACOSX_BUNDLE YES
|
||||
)
|
||||
elseif(UNIX)
|
||||
# Nothing special required
|
||||
else()
|
||||
message(SEND_ERROR "You are on an unsupported platform! (Not Win32, Mac OS X or Unix)")
|
||||
endif(WIN32)
|
||||
|
||||
# Qt5 libraries
|
||||
target_link_libraries(VNote PRIVATE Qt5::Core Qt5::WebEngine Qt5::WebEngineWidgets
|
||||
Qt5::Network Qt5::PrintSupport Qt5::WebChannel Qt5::Widgets
|
||||
Qt5::PrintSupport Qt5::Svg)
|
||||
set_property(TARGET VNote PROPERTY AUTORCC_OPTIONS "--compress;9")
|
||||
|
||||
# Thirdparty libraries
|
||||
target_include_directories(VNote PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/peg-highlight ${CMAKE_SOURCE_DIR}/hoedown)
|
||||
target_link_libraries(VNote PRIVATE peg-highlight hoedown)
|
||||
|
||||
# Compile options
|
||||
if(GCC_VERSION VERSION_GREATER_EQUAL 8.0)
|
||||
target_compile_options(VNote PRIVATE "-Wno-class-memaccess")
|
||||
endif()
|
||||
|
||||
if (WIN32 AND NOT UNIX)
|
||||
# MSVC and not mingw32
|
||||
install(TARGETS VNote RUNTIME DESTINATION bin)
|
||||
install(FILES ${TRANSLATIONS} DESTINATION translations )
|
||||
elseif (APPLE)
|
||||
set_target_properties(VNote PROPERTIES MACOSX_PACKAGE_LOCATION "${PROJECT_NAME}.app/Contents")
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set(CMAKE_INSTALL_PREFIX "/Applications" CACHE PATH "Reset installation path to MacOS default." FORCE)
|
||||
endif()
|
||||
install(TARGETS VNote BUNDLE DESTINATION . COMPONENT Runtime
|
||||
RUNTIME DESTINATION bin COMPONENT Runtime)
|
||||
install(FILES ${TRANSLATIONS} DESTINATION translations COMPONENT Runtime)
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "VNote")
|
||||
set(MACOSX_BUNDLE_BUNDLE_GUI_IDENTIFIER "com.tamlok.VNote")
|
||||
set(MACOSX_BUNDLE_ICON_FILE ${CMAKE_SOURCE_DIR}/src/resources/icons/vnote.icns)
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "VNOTE ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING ${MACOSX_BUNDLE_BUNDLE_VERSION})
|
||||
# Set short version independent with project version to be able to increment independendently.
|
||||
math(EXPR SHORT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR} * 100 + ${PROJECT_VERSION_MINOR}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${SHORT_VERSION_MAJOR}.${PROJECT_VERSION_PATCH}.0")
|
||||
set(MACOSX_BUNDLE_EXECUTABLE_NAME "VNote")
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Distributed under MIT license. Copyright 2016-2019 Le Tan")
|
||||
set(MACOSX_BUNDLE_INFO_STRING "VNote is a note-taking application that knows programmers and Markdown better. Distributed under MIT license. Copyright 2017 Le Tan")
|
||||
|
||||
set_source_files_properties(${MACOSX_BUNDLE_ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||
else()
|
||||
# Linux, mingw32
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Reset installation path to MacOS default." FORCE)
|
||||
endif()
|
||||
install(TARGETS VNote RUNTIME DESTINATION bin)
|
||||
install(FILES ${TRANSLATIONS} DESTINATION translations )
|
||||
|
||||
set(desktop.path applications)
|
||||
set(desktop.files vnote.desktop)
|
||||
|
||||
set(icon16.path icons/hicolor/16x16/apps)
|
||||
set(icon16.files resources/icons/16x16/vnote.png)
|
||||
|
||||
set(icon32.path icons/hicolor/32x32/apps)
|
||||
set(icon32.files resources/icons/32x32/vnote.png)
|
||||
|
||||
set(icon48.path icons/hicolor/48x48/apps)
|
||||
set(icon48.files resources/icons/48x48/vnote.png)
|
||||
|
||||
set(icon64.path icons/hicolor/64x64/apps)
|
||||
set(icon64.files resources/icons/64x64/vnote.png)
|
||||
|
||||
set(icon128.path icons/hicolor/128x128/apps)
|
||||
set(icon128.files resources/icons/128x128/vnote.png)
|
||||
|
||||
set(icon256.path icons/hicolor/256x256/apps)
|
||||
set(icon256.files resources/icons/256x256/vnote.png)
|
||||
|
||||
set(iconsvg.path icons/hicolor/scalable/apps)
|
||||
set(iconsvg.files resources/icons/vnote.svg)
|
||||
|
||||
foreach(items IN ITEMS desktop icon16 icon32 icon48 icon64 icon128 icon256 iconsvg)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${${items}.files}
|
||||
DESTINATION share/${${items}.path}
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
|
||||
endforeach()
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE
|
||||
DESTINATION share/doc/vnote/
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
|
||||
RENAME copyright)
|
||||
endif()
|
||||
|
@ -1,248 +0,0 @@
|
||||
#include "vconfirmdeletiondialog.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "utils/vutils.h"
|
||||
#include "vconfigmanager.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
ConfirmItemWidget::ConfirmItemWidget(bool p_checked,
|
||||
const QString &p_file,
|
||||
const QString &p_tip,
|
||||
int p_index,
|
||||
QWidget *p_parent)
|
||||
: QWidget(p_parent), m_index(p_index)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
m_checkBox->setChecked(p_checked);
|
||||
m_fileLabel->setText(p_file);
|
||||
if (!p_tip.isEmpty()) {
|
||||
m_fileLabel->setToolTip(p_tip);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfirmItemWidget::setupUI()
|
||||
{
|
||||
m_checkBox = new QCheckBox;
|
||||
connect(m_checkBox, &QCheckBox::stateChanged,
|
||||
this, &ConfirmItemWidget::checkStateChanged);
|
||||
|
||||
m_fileLabel = new QLabel;
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
mainLayout->addWidget(m_checkBox);
|
||||
mainLayout->addWidget(m_fileLabel);
|
||||
mainLayout->addStretch();
|
||||
mainLayout->setContentsMargins(3, 0, 0, 0);
|
||||
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
bool ConfirmItemWidget::isChecked() const
|
||||
{
|
||||
return m_checkBox->isChecked();
|
||||
}
|
||||
|
||||
int ConfirmItemWidget::getIndex() const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
VConfirmDeletionDialog::VConfirmDeletionDialog(const QString &p_title,
|
||||
const QString &p_text,
|
||||
const QString &p_info,
|
||||
const QVector<ConfirmItemInfo> &p_items,
|
||||
bool p_enableAskAgain,
|
||||
bool p_askAgainEnabled,
|
||||
bool p_enablePreview,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent),
|
||||
m_enableAskAgain(p_enableAskAgain),
|
||||
m_askAgainEnabled(p_askAgainEnabled),
|
||||
m_enablePreview(p_enablePreview),
|
||||
m_items(p_items)
|
||||
{
|
||||
setupUI(p_title, p_text, p_info);
|
||||
|
||||
initItems();
|
||||
|
||||
updateCountLabel();
|
||||
}
|
||||
|
||||
void VConfirmDeletionDialog::setupUI(const QString &p_title,
|
||||
const QString &p_text,
|
||||
const QString &p_info)
|
||||
{
|
||||
QLabel *textLabel = NULL;
|
||||
if (!p_text.isEmpty()) {
|
||||
textLabel = new QLabel(p_text);
|
||||
textLabel->setWordWrap(true);
|
||||
}
|
||||
|
||||
QLabel *infoLabel = NULL;
|
||||
if (!p_info.isEmpty()) {
|
||||
infoLabel = new QLabel(p_info);
|
||||
infoLabel->setWordWrap(true);
|
||||
}
|
||||
|
||||
m_countLabel = new QLabel("Items");
|
||||
QHBoxLayout *labelLayout = new QHBoxLayout;
|
||||
labelLayout->addWidget(m_countLabel);
|
||||
labelLayout->addStretch();
|
||||
labelLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_listWidget = new QListWidget();
|
||||
m_listWidget->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
connect(m_listWidget, &QListWidget::currentRowChanged,
|
||||
this, &VConfirmDeletionDialog::currentFileChanged);
|
||||
connect(m_listWidget, &QListWidget::itemActivated,
|
||||
this, [this](QListWidgetItem *p_item) {
|
||||
// Open it using resource manager.
|
||||
if (!p_item) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConfirmItemWidget *widget = getItemWidget(p_item);
|
||||
Q_ASSERT(widget);
|
||||
QString filePath = m_items[widget->getIndex()].m_path;
|
||||
|
||||
if (!filePath.isEmpty()) {
|
||||
QUrl url = QUrl::fromLocalFile(filePath);
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
});
|
||||
|
||||
m_previewer = new QLabel();
|
||||
|
||||
m_askAgainCB = new QCheckBox(tr("Do not ask for confirmation again"));
|
||||
m_askAgainCB->setChecked(!m_askAgainEnabled);
|
||||
m_askAgainCB->setVisible(m_enableAskAgain);
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
m_btnBox->button(QDialogButtonBox::Ok)->setProperty("DangerBtn", true);
|
||||
|
||||
QHBoxLayout *midLayout = new QHBoxLayout;
|
||||
midLayout->addWidget(m_listWidget);
|
||||
if (m_enablePreview) {
|
||||
midLayout->addStretch();
|
||||
midLayout->addWidget(m_previewer);
|
||||
midLayout->addStretch();
|
||||
} else {
|
||||
midLayout->addWidget(m_previewer);
|
||||
m_previewer->setVisible(false);
|
||||
}
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
if (textLabel) {
|
||||
mainLayout->addWidget(textLabel);
|
||||
}
|
||||
|
||||
if (infoLabel) {
|
||||
mainLayout->addWidget(infoLabel);
|
||||
}
|
||||
|
||||
mainLayout->addWidget(m_askAgainCB);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
mainLayout->addLayout(labelLayout);
|
||||
mainLayout->addLayout(midLayout);
|
||||
|
||||
setLayout(mainLayout);
|
||||
setWindowTitle(p_title);
|
||||
}
|
||||
|
||||
QVector<ConfirmItemInfo> VConfirmDeletionDialog::getConfirmedItems() const
|
||||
{
|
||||
QVector<ConfirmItemInfo> confirmedItems;
|
||||
|
||||
for (int i = 0; i < m_listWidget->count(); ++i) {
|
||||
ConfirmItemWidget *widget = getItemWidget(m_listWidget->item(i));
|
||||
if (widget->isChecked()) {
|
||||
confirmedItems.push_back(m_items[widget->getIndex()]);
|
||||
}
|
||||
}
|
||||
|
||||
return confirmedItems;
|
||||
}
|
||||
|
||||
void VConfirmDeletionDialog::initItems()
|
||||
{
|
||||
m_listWidget->clear();
|
||||
|
||||
for (int i = 0; i < m_items.size(); ++i) {
|
||||
ConfirmItemWidget *itemWidget = new ConfirmItemWidget(true,
|
||||
m_items[i].m_name,
|
||||
m_items[i].m_tip,
|
||||
i,
|
||||
this);
|
||||
connect(itemWidget, &ConfirmItemWidget::checkStateChanged,
|
||||
this, &VConfirmDeletionDialog::updateCountLabel);
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem();
|
||||
QSize size = itemWidget->sizeHint();
|
||||
size.setHeight(size.height() * 2);
|
||||
item->setSizeHint(size);
|
||||
|
||||
m_listWidget->addItem(item);
|
||||
m_listWidget->setItemWidget(item, itemWidget);
|
||||
}
|
||||
|
||||
m_listWidget->setMinimumSize(m_listWidget->sizeHint());
|
||||
m_listWidget->setCurrentRow(-1);
|
||||
m_listWidget->setCurrentRow(0);
|
||||
}
|
||||
|
||||
bool VConfirmDeletionDialog::getAskAgainEnabled() const
|
||||
{
|
||||
return !m_askAgainCB->isChecked();
|
||||
}
|
||||
|
||||
void VConfirmDeletionDialog::currentFileChanged(int p_row)
|
||||
{
|
||||
bool succeed = false;
|
||||
if (p_row > -1 && m_enablePreview) {
|
||||
ConfirmItemWidget *widget = getItemWidget(m_listWidget->item(p_row));
|
||||
if (widget) {
|
||||
int idx = widget->getIndex();
|
||||
Q_ASSERT(idx < m_items.size());
|
||||
QPixmap image(m_items[idx].m_path);
|
||||
if (!image.isNull()) {
|
||||
int width = 512 * VUtils::calculateScaleFactor();
|
||||
QSize previewSize(width, width);
|
||||
if (image.width() > width || image.height() > width) {
|
||||
m_previewer->setPixmap(image.scaled(previewSize, Qt::KeepAspectRatio));
|
||||
} else {
|
||||
m_previewer->setPixmap(image);
|
||||
}
|
||||
|
||||
succeed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_previewer->setVisible(succeed);
|
||||
}
|
||||
|
||||
ConfirmItemWidget *VConfirmDeletionDialog::getItemWidget(QListWidgetItem *p_item) const
|
||||
{
|
||||
QWidget *wid = m_listWidget->itemWidget(p_item);
|
||||
return dynamic_cast<ConfirmItemWidget *>(wid);
|
||||
}
|
||||
|
||||
void VConfirmDeletionDialog::updateCountLabel()
|
||||
{
|
||||
int total = m_listWidget->count();
|
||||
int checked = 0;
|
||||
|
||||
for (int i = 0; i < total; ++i) {
|
||||
ConfirmItemWidget *widget = getItemWidget(m_listWidget->item(i));
|
||||
if (widget->isChecked()) {
|
||||
++checked;
|
||||
}
|
||||
}
|
||||
|
||||
m_countLabel->setText(tr("%1/%2 Items").arg(checked).arg(total));
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
#ifndef VCONFIRMDELETIONDIALOG_H
|
||||
#define VCONFIRMDELETIONDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVector>
|
||||
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class QDialogButtonBox;
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class ConfirmItemWidget;
|
||||
class QCheckBox;
|
||||
|
||||
// Information about a deletion item needed to confirm.
|
||||
struct ConfirmItemInfo
|
||||
{
|
||||
ConfirmItemInfo()
|
||||
: m_data(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
ConfirmItemInfo(const QString &p_name,
|
||||
const QString &p_tip,
|
||||
const QString &p_path,
|
||||
void *p_data)
|
||||
: m_name(p_name), m_tip(p_tip), m_path(p_path), m_data(p_data)
|
||||
{
|
||||
}
|
||||
|
||||
QString m_name;
|
||||
QString m_tip;
|
||||
QString m_path;
|
||||
void *m_data;
|
||||
};
|
||||
|
||||
class ConfirmItemWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ConfirmItemWidget(bool p_checked,
|
||||
const QString &p_file,
|
||||
const QString &p_tip,
|
||||
int p_index,
|
||||
QWidget *p_parent = NULL);
|
||||
|
||||
bool isChecked() const;
|
||||
|
||||
int getIndex() const;
|
||||
|
||||
signals:
|
||||
// Emit when item's check state changed.
|
||||
void checkStateChanged(int p_state);
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
QCheckBox *m_checkBox;
|
||||
QLabel *m_fileLabel;
|
||||
|
||||
int m_index;
|
||||
};
|
||||
|
||||
class VConfirmDeletionDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VConfirmDeletionDialog(const QString &p_title,
|
||||
const QString &p_text,
|
||||
const QString &p_info,
|
||||
const QVector<ConfirmItemInfo> &p_items,
|
||||
bool p_enableAskAgain,
|
||||
bool p_askAgainEnabled,
|
||||
bool p_enablePreview,
|
||||
QWidget *p_parent = 0);
|
||||
|
||||
QVector<ConfirmItemInfo> getConfirmedItems() const;
|
||||
|
||||
bool getAskAgainEnabled() const;
|
||||
|
||||
private slots:
|
||||
void currentFileChanged(int p_row);
|
||||
|
||||
void updateCountLabel();
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title,
|
||||
const QString &p_text,
|
||||
const QString &p_info);
|
||||
|
||||
void initItems();
|
||||
|
||||
ConfirmItemWidget *getItemWidget(QListWidgetItem *p_item) const;
|
||||
|
||||
QLabel *m_countLabel;
|
||||
QListWidget *m_listWidget;
|
||||
QLabel *m_previewer;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
QCheckBox *m_askAgainCB;
|
||||
|
||||
bool m_enableAskAgain;
|
||||
// Init value if m_enableAskAgain is true.
|
||||
bool m_askAgainEnabled;
|
||||
|
||||
bool m_enablePreview;
|
||||
|
||||
QVector<ConfirmItemInfo> m_items;
|
||||
};
|
||||
|
||||
#endif // VCONFIRMDELETIONDIALOG_H
|
@ -1,82 +0,0 @@
|
||||
#include "vcopytextashtmldialog.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QWebEngineView>
|
||||
#include <QClipboard>
|
||||
#include <QMimeData>
|
||||
#include <QApplication>
|
||||
|
||||
#include "utils/vutils.h"
|
||||
#include "utils/vclipboardutils.h"
|
||||
#include "utils/vwebutils.h"
|
||||
#include "vconfigmanager.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
extern VWebUtils *g_webUtils;
|
||||
|
||||
VCopyTextAsHtmlDialog::VCopyTextAsHtmlDialog(const QString &p_text,
|
||||
const QString &p_copyTarget,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent), m_text(p_text), m_copyTarget(p_copyTarget)
|
||||
{
|
||||
setupUI();
|
||||
}
|
||||
|
||||
void VCopyTextAsHtmlDialog::setupUI()
|
||||
{
|
||||
QLabel *textLabel = new QLabel(tr("Text:"));
|
||||
m_textEdit = new QPlainTextEdit(m_text);
|
||||
m_textEdit->setReadOnly(true);
|
||||
m_textEdit->setProperty("LineEdit", true);
|
||||
|
||||
m_htmlLabel = new QLabel(tr("HTML:"));
|
||||
m_htmlViewer = VUtils::getWebEngineView(g_config->getBaseBackground());
|
||||
m_htmlViewer->setContextMenuPolicy(Qt::NoContextMenu);
|
||||
m_htmlViewer->setMinimumSize(600, 400);
|
||||
|
||||
m_infoLabel = new QLabel(tr("Converting text to HTML ..."));
|
||||
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
m_btnBox->button(QDialogButtonBox::Ok)->setProperty("SpecialBtn", true);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addWidget(textLabel);
|
||||
mainLayout->addWidget(m_textEdit);
|
||||
mainLayout->addWidget(m_htmlLabel);
|
||||
mainLayout->addWidget(m_htmlViewer);
|
||||
mainLayout->addWidget(m_infoLabel);
|
||||
mainLayout->addStretch();
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
|
||||
setLayout(mainLayout);
|
||||
setWindowTitle(tr("Copy Text As HTML (%1)").arg(m_copyTarget));
|
||||
|
||||
setHtmlVisible(false);
|
||||
}
|
||||
|
||||
void VCopyTextAsHtmlDialog::setHtmlVisible(bool p_visible)
|
||||
{
|
||||
m_htmlLabel->setVisible(p_visible);
|
||||
m_htmlViewer->setVisible(p_visible);
|
||||
}
|
||||
|
||||
void VCopyTextAsHtmlDialog::setConvertedHtml(const QUrl &p_baseUrl,
|
||||
const QString &p_html)
|
||||
{
|
||||
QString html = p_html;
|
||||
m_htmlViewer->setHtml("<html><body>" + html + "</body></html>", p_baseUrl);
|
||||
setHtmlVisible(true);
|
||||
|
||||
g_webUtils->alterHtmlAsTarget(p_baseUrl, html, m_copyTarget);
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
QMimeData *data = new QMimeData();
|
||||
data->setText(m_text);
|
||||
data->setHtml(html);
|
||||
VClipboardUtils::setMimeDataToClipboard(clipboard, data, QClipboard::Clipboard);
|
||||
|
||||
QTimer::singleShot(3000, this, &VCopyTextAsHtmlDialog::accept);
|
||||
m_infoLabel->setText(tr("HTML has been copied. Will be closed in 3 seconds."));
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#ifndef VCOPYTEXTASHTMLDIALOG_H
|
||||
#define VCOPYTEXTASHTMLDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QUrl>
|
||||
|
||||
|
||||
class QPlainTextEdit;
|
||||
class QWebEngineView;
|
||||
class QDialogButtonBox;
|
||||
class VWaitingWidget;
|
||||
class QLabel;
|
||||
|
||||
class VCopyTextAsHtmlDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VCopyTextAsHtmlDialog(const QString &p_text,
|
||||
const QString &p_copyTarget,
|
||||
QWidget *p_parent = nullptr);
|
||||
|
||||
void setConvertedHtml(const QUrl &p_baseUrl, const QString &p_html);
|
||||
|
||||
const QString &getText() const;
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void setHtmlVisible(bool p_visible);
|
||||
|
||||
QPlainTextEdit *m_textEdit;
|
||||
|
||||
QLabel *m_htmlLabel;
|
||||
|
||||
QWebEngineView *m_htmlViewer;
|
||||
|
||||
QLabel *m_infoLabel;
|
||||
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
QString m_text;
|
||||
|
||||
QString m_copyTarget;
|
||||
};
|
||||
|
||||
inline const QString &VCopyTextAsHtmlDialog::getText() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
#endif // VCOPYTEXTASHTMLDIALOG_H
|
@ -1,125 +0,0 @@
|
||||
#include <QtWidgets>
|
||||
#include "vdeletenotebookdialog.h"
|
||||
#include "vconfigmanager.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VDeleteNotebookDialog::VDeleteNotebookDialog(const QString &p_title,
|
||||
const VNotebook *p_notebook,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent), m_notebook(p_notebook)
|
||||
{
|
||||
setupUI(p_title, m_notebook->getName());
|
||||
}
|
||||
|
||||
void VDeleteNotebookDialog::setupUI(const QString &p_title, const QString &p_name)
|
||||
{
|
||||
QLabel *infoLabel = new QLabel(tr("Are you sure to delete notebook <span style=\"%1\">%2</span>?")
|
||||
.arg(g_config->c_dataTextStyle).arg(p_name));
|
||||
m_warningLabel = new QLabel();
|
||||
m_warningLabel->setWordWrap(true);
|
||||
|
||||
m_deleteCheck = new QCheckBox(tr("Delete files from disk"), this);
|
||||
m_deleteCheck->setChecked(false);
|
||||
m_deleteCheck->setToolTip(tr("When checked, VNote will delete all files (including Recycle Bin) of this notebook from disk"));
|
||||
connect(m_deleteCheck, &QCheckBox::stateChanged,
|
||||
this, &VDeleteNotebookDialog::deleteCheckChanged);
|
||||
|
||||
deleteCheckChanged(false);
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("DangerBtn", true);
|
||||
|
||||
// Standard Warning icon.
|
||||
QLabel *iconLabel = new QLabel();
|
||||
QPixmap pixmap = standardIcon(QMessageBox::Warning);
|
||||
if (pixmap.isNull()) {
|
||||
iconLabel->hide();
|
||||
} else {
|
||||
iconLabel->setPixmap(pixmap);
|
||||
}
|
||||
|
||||
QVBoxLayout *iconLayout = new QVBoxLayout();
|
||||
iconLayout->addStretch();
|
||||
iconLayout->addWidget(iconLabel);
|
||||
iconLayout->addStretch();
|
||||
|
||||
QVBoxLayout *infoLayout = new QVBoxLayout();
|
||||
infoLayout->addWidget(infoLabel);
|
||||
infoLayout->addWidget(m_deleteCheck);
|
||||
infoLayout->addWidget(m_warningLabel);
|
||||
|
||||
QHBoxLayout *topLayout = new QHBoxLayout();
|
||||
topLayout->addLayout(iconLayout);
|
||||
topLayout->addLayout(infoLayout);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
|
||||
setLayout(mainLayout);
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setWindowTitle(p_title);
|
||||
}
|
||||
|
||||
bool VDeleteNotebookDialog::getDeleteFiles() const
|
||||
{
|
||||
return m_deleteCheck->isChecked();
|
||||
}
|
||||
|
||||
QPixmap VDeleteNotebookDialog::standardIcon(QMessageBox::Icon p_icon)
|
||||
{
|
||||
QStyle *style = this->style();
|
||||
int iconSize = style->pixelMetric(QStyle::PM_MessageBoxIconSize, 0, this);
|
||||
QIcon tmpIcon;
|
||||
switch (p_icon) {
|
||||
case QMessageBox::Information:
|
||||
tmpIcon = style->standardIcon(QStyle::SP_MessageBoxInformation, 0, this);
|
||||
break;
|
||||
case QMessageBox::Warning:
|
||||
tmpIcon = style->standardIcon(QStyle::SP_MessageBoxWarning, 0, this);
|
||||
break;
|
||||
case QMessageBox::Critical:
|
||||
tmpIcon = style->standardIcon(QStyle::SP_MessageBoxCritical, 0, this);
|
||||
break;
|
||||
case QMessageBox::Question:
|
||||
tmpIcon = style->standardIcon(QStyle::SP_MessageBoxQuestion, 0, this);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!tmpIcon.isNull()) {
|
||||
QWindow *window = this->windowHandle();
|
||||
if (!window) {
|
||||
if (const QWidget *nativeParent = this->nativeParentWidget()) {
|
||||
window = nativeParent->windowHandle();
|
||||
}
|
||||
}
|
||||
return tmpIcon.pixmap(window, QSize(iconSize, iconSize));
|
||||
}
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
void VDeleteNotebookDialog::deleteCheckChanged(int p_state)
|
||||
{
|
||||
if (!p_state) {
|
||||
m_warningLabel->setText(tr("VNote won't delete files in directory <span style=\"%1\">%2</span>.")
|
||||
.arg(g_config->c_dataTextStyle).arg(m_notebook->getPath()));
|
||||
} else {
|
||||
m_warningLabel->setText(tr("<span style=\"%1\">WARNING</span>: "
|
||||
"VNote may delete <b>ANY</b> files in directory <span style=\"%2\">%3</span> "
|
||||
"and directory <span style=\"%2\">%4</span>!<br>"
|
||||
"VNote will try to delete all the root folders within this notebook one by one.<br>"
|
||||
"It may be UNRECOVERABLE!")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(m_notebook->getPath())
|
||||
.arg(m_notebook->getRecycleBinFolderPath()));
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#ifndef VDELETENOTEBOOKDIALOG_H
|
||||
#define VDELETENOTEBOOKDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
class QLabel;
|
||||
class QString;
|
||||
class QCheckBox;
|
||||
class QDialogButtonBox;
|
||||
class VNotebook;
|
||||
|
||||
class VDeleteNotebookDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VDeleteNotebookDialog(const QString &p_title,
|
||||
const VNotebook *p_notebook,
|
||||
QWidget *p_parent = 0);
|
||||
|
||||
// Whether delete files from disk.
|
||||
bool getDeleteFiles() const;
|
||||
|
||||
private slots:
|
||||
void deleteCheckChanged(int p_state);
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title, const QString &p_name);
|
||||
QPixmap standardIcon(QMessageBox::Icon p_icon);
|
||||
|
||||
const VNotebook *m_notebook;
|
||||
QLabel *m_warningLabel;
|
||||
QCheckBox *m_deleteCheck;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
};
|
||||
|
||||
#endif // VDELETENOTEBOOKDIALOG_H
|
@ -1,116 +0,0 @@
|
||||
#include <QtWidgets>
|
||||
#include "vdirinfodialog.h"
|
||||
#include "vdirectory.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "vmetawordlineedit.h"
|
||||
#include "utils/vutils.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VDirInfoDialog::VDirInfoDialog(const QString &title,
|
||||
const QString &info,
|
||||
const VDirectory *directory,
|
||||
VDirectory *parentDirectory,
|
||||
QWidget *parent)
|
||||
: QDialog(parent), title(title), info(info),
|
||||
m_directory(directory), m_parentDirectory(parentDirectory)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
connect(m_nameEdit, &VMetaWordLineEdit::textChanged, this, &VDirInfoDialog::handleInputChanged);
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VDirInfoDialog::setupUI()
|
||||
{
|
||||
QLabel *infoLabel = NULL;
|
||||
if (!info.isEmpty()) {
|
||||
infoLabel = new QLabel(info);
|
||||
}
|
||||
|
||||
m_nameEdit = new VMetaWordLineEdit(m_directory->getName());
|
||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
|
||||
m_nameEdit);
|
||||
m_nameEdit->setValidator(validator);
|
||||
m_nameEdit->selectAll();
|
||||
|
||||
// Created time.
|
||||
QString createdTimeStr = VUtils::displayDateTime(m_directory->getCreatedTimeUtc().toLocalTime());
|
||||
QLabel *createdTimeLabel = new QLabel(createdTimeStr);
|
||||
|
||||
QFormLayout *topLayout = new QFormLayout();
|
||||
topLayout->addRow(tr("Folder &name:"), m_nameEdit);
|
||||
topLayout->addRow(tr("Created time:"), createdTimeLabel);
|
||||
|
||||
m_warnLabel = new QLabel();
|
||||
m_warnLabel->setWordWrap(true);
|
||||
m_warnLabel->hide();
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("SpecialBtn", true);
|
||||
m_nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
if (infoLabel) {
|
||||
mainLayout->addWidget(infoLabel);
|
||||
}
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_warnLabel);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle(title);
|
||||
}
|
||||
|
||||
void VDirInfoDialog::handleInputChanged()
|
||||
{
|
||||
bool showWarnLabel = false;
|
||||
QString name = m_nameEdit->getEvaluatedText();
|
||||
bool nameOk = !name.isEmpty();
|
||||
if (nameOk && name != m_directory->getName()) {
|
||||
// Check if the name conflicts with existing directory name.
|
||||
// Case-insensitive when creating note.
|
||||
const VDirectory *directory = m_parentDirectory->findSubDirectory(name, false);
|
||||
QString warnText;
|
||||
if (directory && directory != m_directory) {
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> already exists. "
|
||||
"Please choose another name.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
} else if (!VUtils::checkFileNameLegal(name)) {
|
||||
// Check if evaluated name contains illegal characters.
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> contains illegal characters "
|
||||
"(after magic word evaluation).")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
}
|
||||
|
||||
if (!nameOk) {
|
||||
showWarnLabel = true;
|
||||
m_warnLabel->setText(warnText);
|
||||
}
|
||||
}
|
||||
|
||||
m_warnLabel->setVisible(showWarnLabel);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(nameOk);
|
||||
}
|
||||
|
||||
QString VDirInfoDialog::getNameInput() const
|
||||
{
|
||||
return m_nameEdit->getEvaluatedText();
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#ifndef VDIRINFODIALOG_H
|
||||
#define VDIRINFODIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QLabel;
|
||||
class VMetaWordLineEdit;
|
||||
class QDialogButtonBox;
|
||||
class QString;
|
||||
class VDirectory;
|
||||
|
||||
class VDirInfoDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VDirInfoDialog(const QString &title,
|
||||
const QString &info,
|
||||
const VDirectory *directory,
|
||||
VDirectory *parentDirectory,
|
||||
QWidget *parent = 0);
|
||||
|
||||
QString getNameInput() const;
|
||||
|
||||
private slots:
|
||||
void handleInputChanged();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
VMetaWordLineEdit *m_nameEdit;
|
||||
QLabel *m_warnLabel;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
QString title;
|
||||
QString info;
|
||||
|
||||
const VDirectory *m_directory;
|
||||
VDirectory *m_parentDirectory;
|
||||
};
|
||||
#endif // VDIRINFODIALOG_H
|
@ -1,310 +0,0 @@
|
||||
#include "veditsnippetdialog.h"
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "utils/vutils.h"
|
||||
#include "vmetawordlineedit.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "utils/vmetawordmanager.h"
|
||||
#include "vlineedit.h"
|
||||
|
||||
extern VMetaWordManager *g_mwMgr;
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VEditSnippetDialog::VEditSnippetDialog(const QString &p_title,
|
||||
const QString &p_info,
|
||||
const QVector<VSnippet> &p_snippets,
|
||||
const VSnippet &p_snippet,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent),
|
||||
m_snippets(p_snippets),
|
||||
m_snippet(p_snippet)
|
||||
{
|
||||
setupUI(p_title, p_info);
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VEditSnippetDialog::setupUI(const QString &p_title, const QString &p_info)
|
||||
{
|
||||
QLabel *infoLabel = NULL;
|
||||
if (!p_info.isEmpty()) {
|
||||
infoLabel = new QLabel(p_info);
|
||||
infoLabel->setWordWrap(true);
|
||||
}
|
||||
|
||||
// Name.
|
||||
m_nameEdit = new VMetaWordLineEdit(m_snippet.getName());
|
||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
|
||||
m_nameEdit);
|
||||
m_nameEdit->setValidator(validator);
|
||||
|
||||
// Type.
|
||||
m_typeCB = VUtils::getComboBox();
|
||||
for (int i = 0; i < VSnippet::Type::Invalid; ++i) {
|
||||
m_typeCB->addItem(VSnippet::typeStr(static_cast<VSnippet::Type>(i)), i);
|
||||
}
|
||||
|
||||
int typeIdx = m_typeCB->findData((int)m_snippet.getType());
|
||||
Q_ASSERT(typeIdx > -1);
|
||||
m_typeCB->setCurrentIndex(typeIdx);
|
||||
|
||||
// Shortcut.
|
||||
m_shortcutCB = VUtils::getComboBox();
|
||||
m_shortcutCB->addItem(tr("None"), QChar());
|
||||
auto shortcuts = getAvailableShortcuts();
|
||||
for (auto it : shortcuts) {
|
||||
m_shortcutCB->addItem(it, it);
|
||||
}
|
||||
|
||||
QChar sh = m_snippet.getShortcut();
|
||||
if (sh.isNull()) {
|
||||
m_shortcutCB->setCurrentIndex(0);
|
||||
} else {
|
||||
int shortcutIdx = m_shortcutCB->findData(sh);
|
||||
m_shortcutCB->setCurrentIndex(shortcutIdx < 0 ? 0 : shortcutIdx);
|
||||
}
|
||||
|
||||
// Cursor mark.
|
||||
m_cursorMarkEdit = new VLineEdit(m_snippet.getCursorMark());
|
||||
m_cursorMarkEdit->setToolTip(tr("String in the content to mark the cursor position"));
|
||||
|
||||
// Selection mark.
|
||||
m_selectionMarkEdit = new VLineEdit(m_snippet.getSelectionMark());
|
||||
m_selectionMarkEdit->setToolTip(tr("String in the content to be replaced with selected text"));
|
||||
|
||||
// Auto Indent.
|
||||
m_autoIndentCB = new QCheckBox(tr("Auto indent"), this);
|
||||
m_autoIndentCB->setToolTip(tr("Auto indent the content according to the first line"));
|
||||
m_autoIndentCB->setChecked(m_snippet.getAutoIndent());
|
||||
|
||||
// Content.
|
||||
m_contentEdit = new QTextEdit();
|
||||
m_contentEdit->setProperty("LineEdit", true);
|
||||
setContentEditByType();
|
||||
|
||||
QFormLayout *topLayout = new QFormLayout();
|
||||
topLayout->addRow(tr("Snippet &name:"), m_nameEdit);
|
||||
topLayout->addRow(tr("Snippet &type:"), m_typeCB);
|
||||
topLayout->addRow(tr("Shortc&ut:"), m_shortcutCB);
|
||||
topLayout->addRow(tr("Cursor &mark:"), m_cursorMarkEdit);
|
||||
topLayout->addRow(tr("&Selection mark:"), m_selectionMarkEdit);
|
||||
topLayout->addWidget(m_autoIndentCB);
|
||||
topLayout->addRow(tr("&Content:"), m_contentEdit);
|
||||
|
||||
m_warnLabel = new QLabel();
|
||||
m_warnLabel->setWordWrap(true);
|
||||
m_warnLabel->hide();
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("SpecialBtn", true);
|
||||
m_nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
if (infoLabel) {
|
||||
mainLayout->addWidget(infoLabel);
|
||||
}
|
||||
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_warnLabel);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle(p_title);
|
||||
|
||||
connect(m_nameEdit, &VMetaWordLineEdit::textChanged,
|
||||
this, &VEditSnippetDialog::handleInputChanged);
|
||||
|
||||
connect(m_typeCB, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &VEditSnippetDialog::handleInputChanged);
|
||||
|
||||
connect(m_cursorMarkEdit, &VLineEdit::textChanged,
|
||||
this, &VEditSnippetDialog::handleInputChanged);
|
||||
|
||||
connect(m_selectionMarkEdit, &VLineEdit::textChanged,
|
||||
this, &VEditSnippetDialog::handleInputChanged);
|
||||
|
||||
connect(m_contentEdit, &QTextEdit::textChanged,
|
||||
this, &VEditSnippetDialog::handleInputChanged);
|
||||
}
|
||||
|
||||
void VEditSnippetDialog::handleInputChanged()
|
||||
{
|
||||
bool showWarnLabel = false;
|
||||
QString name = m_nameEdit->getEvaluatedText();
|
||||
bool nameOk = !name.isEmpty();
|
||||
if (nameOk && name != m_snippet.getName()) {
|
||||
// Check if the name conflicts with existing snippet name.
|
||||
// Case-insensitive.
|
||||
QString lowerName = name.toLower();
|
||||
bool conflicted = false;
|
||||
for (auto const & item : m_snippets) {
|
||||
if (item.getName() == name) {
|
||||
conflicted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QString warnText;
|
||||
if (conflicted) {
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> already exists. "
|
||||
"Please choose another name.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
} else if (!VUtils::checkFileNameLegal(name)) {
|
||||
// Check if evaluated name contains illegal characters.
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> contains illegal characters "
|
||||
"(after magic word evaluation).")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
}
|
||||
|
||||
if (!nameOk) {
|
||||
showWarnLabel = true;
|
||||
m_warnLabel->setText(warnText);
|
||||
}
|
||||
}
|
||||
|
||||
QString cursorMark = m_cursorMarkEdit->text();
|
||||
bool cursorMarkOk = true;
|
||||
if (nameOk && !cursorMark.isEmpty()) {
|
||||
// Check if the mark appears more than once in the content.
|
||||
QString selectionMark = m_selectionMarkEdit->text();
|
||||
QString content = getContentEditByType();
|
||||
content = g_mwMgr->evaluate(content);
|
||||
QString warnText;
|
||||
if (content.count(cursorMark) > 1) {
|
||||
cursorMarkOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Cursor mark <span style=\"%2\">%3</span> occurs more than once "
|
||||
"in the content (after magic word evaluation). "
|
||||
"Please choose another mark.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(cursorMark);
|
||||
} else if ((cursorMark == selectionMark
|
||||
|| cursorMark.contains(selectionMark)
|
||||
|| selectionMark.contains(cursorMark))
|
||||
&& !selectionMark.isEmpty()) {
|
||||
cursorMarkOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Cursor mark <span style=\"%2\">%3</span> conflicts with selection mark. "
|
||||
"Please choose another mark.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(cursorMark);
|
||||
}
|
||||
|
||||
if (!cursorMarkOk) {
|
||||
showWarnLabel = true;
|
||||
m_warnLabel->setText(warnText);
|
||||
}
|
||||
}
|
||||
|
||||
m_warnLabel->setVisible(showWarnLabel);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(nameOk && cursorMarkOk);
|
||||
}
|
||||
|
||||
void VEditSnippetDialog::setContentEditByType()
|
||||
{
|
||||
switch (m_snippet.getType()) {
|
||||
case VSnippet::Type::PlainText:
|
||||
m_contentEdit->setPlainText(m_snippet.getContent());
|
||||
break;
|
||||
|
||||
case VSnippet::Type::Html:
|
||||
m_contentEdit->setHtml(m_snippet.getContent());
|
||||
break;
|
||||
|
||||
default:
|
||||
m_contentEdit->setPlainText(m_snippet.getContent());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QString VEditSnippetDialog::getContentEditByType() const
|
||||
{
|
||||
if (m_typeCB->currentIndex() == -1) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
switch (static_cast<VSnippet::Type>(m_typeCB->currentData().toInt())) {
|
||||
case VSnippet::Type::PlainText:
|
||||
return m_contentEdit->toPlainText();
|
||||
|
||||
case VSnippet::Type::Html:
|
||||
return m_contentEdit->toHtml();
|
||||
|
||||
default:
|
||||
return m_contentEdit->toPlainText();
|
||||
}
|
||||
}
|
||||
|
||||
QString VEditSnippetDialog::getNameInput() const
|
||||
{
|
||||
return m_nameEdit->getEvaluatedText();
|
||||
}
|
||||
|
||||
VSnippet::Type VEditSnippetDialog::getTypeInput() const
|
||||
{
|
||||
return static_cast<VSnippet::Type>(m_typeCB->currentData().toInt());
|
||||
}
|
||||
|
||||
QString VEditSnippetDialog::getCursorMarkInput() const
|
||||
{
|
||||
return m_cursorMarkEdit->text();
|
||||
}
|
||||
|
||||
QString VEditSnippetDialog::getSelectionMarkInput() const
|
||||
{
|
||||
return m_selectionMarkEdit->text();
|
||||
}
|
||||
|
||||
QString VEditSnippetDialog::getContentInput() const
|
||||
{
|
||||
return getContentEditByType();
|
||||
}
|
||||
|
||||
QChar VEditSnippetDialog::getShortcutInput() const
|
||||
{
|
||||
return m_shortcutCB->currentData().toChar();
|
||||
}
|
||||
|
||||
bool VEditSnippetDialog::getAutoIndentInput() const
|
||||
{
|
||||
return m_autoIndentCB->isChecked();
|
||||
}
|
||||
|
||||
QVector<QChar> VEditSnippetDialog::getAvailableShortcuts() const
|
||||
{
|
||||
QVector<QChar> ret = VSnippet::getAllShortcuts();
|
||||
|
||||
QChar curCh = m_snippet.getShortcut();
|
||||
// Remove those have already been assigned to snippets.
|
||||
for (auto const & snip : m_snippets) {
|
||||
QChar ch = snip.getShortcut();
|
||||
if (ch.isNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch != curCh) {
|
||||
ret.removeOne(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
#ifndef VEDITSNIPPETDIALOG_H
|
||||
#define VEDITSNIPPETDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVector>
|
||||
|
||||
#include "vsnippet.h"
|
||||
|
||||
class VMetaWordLineEdit;
|
||||
class VLineEdit;
|
||||
class QLabel;
|
||||
class QDialogButtonBox;
|
||||
class QComboBox;
|
||||
class QTextEdit;
|
||||
class QCheckBox;
|
||||
|
||||
|
||||
class VEditSnippetDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VEditSnippetDialog(const QString &p_title,
|
||||
const QString &p_info,
|
||||
const QVector<VSnippet> &p_snippets,
|
||||
const VSnippet &p_snippet,
|
||||
QWidget *p_parent = nullptr);
|
||||
|
||||
QString getNameInput() const;
|
||||
|
||||
VSnippet::Type getTypeInput() const;
|
||||
|
||||
QString getCursorMarkInput() const;
|
||||
|
||||
QString getSelectionMarkInput() const;
|
||||
|
||||
QString getContentInput() const;
|
||||
|
||||
QChar getShortcutInput() const;
|
||||
|
||||
bool getAutoIndentInput() const;
|
||||
|
||||
private slots:
|
||||
void handleInputChanged();
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title, const QString &p_info);
|
||||
|
||||
void setContentEditByType();
|
||||
|
||||
QString getContentEditByType() const;
|
||||
|
||||
QVector<QChar> getAvailableShortcuts() const;
|
||||
|
||||
VMetaWordLineEdit *m_nameEdit;
|
||||
QComboBox *m_typeCB;
|
||||
QComboBox *m_shortcutCB;
|
||||
VLineEdit *m_cursorMarkEdit;
|
||||
VLineEdit *m_selectionMarkEdit;
|
||||
QCheckBox *m_autoIndentCB;
|
||||
QTextEdit *m_contentEdit;
|
||||
|
||||
QLabel *m_warnLabel;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
const QVector<VSnippet> &m_snippets;
|
||||
|
||||
const VSnippet &m_snippet;
|
||||
};
|
||||
|
||||
#endif // VEDITSNIPPETDIALOG_H
|
@ -1,516 +0,0 @@
|
||||
#ifndef VEXPORTDIALOG_H
|
||||
#define VEXPORTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QPageLayout>
|
||||
#include <QList>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "vconstants.h"
|
||||
|
||||
class QLabel;
|
||||
class VLineEdit;
|
||||
class QDialogButtonBox;
|
||||
class QPushButton;
|
||||
class QGroupBox;
|
||||
class QPlainTextEdit;
|
||||
class VNotebook;
|
||||
class VDirectory;
|
||||
class VFile;
|
||||
class VCart;
|
||||
class VExporter;
|
||||
class QCheckBox;
|
||||
class VLineEdit;
|
||||
class QProgressBar;
|
||||
|
||||
|
||||
enum class ExportSource
|
||||
{
|
||||
CurrentNote = 0,
|
||||
CurrentFolder,
|
||||
CurrentNotebook,
|
||||
Cart
|
||||
};
|
||||
|
||||
|
||||
enum class ExportFormat
|
||||
{
|
||||
Markdown = 0,
|
||||
HTML,
|
||||
PDF,
|
||||
OnePDF,
|
||||
Custom
|
||||
};
|
||||
|
||||
|
||||
enum class ExportPageNumber
|
||||
{
|
||||
None = 0,
|
||||
Left,
|
||||
Center,
|
||||
Right
|
||||
};
|
||||
|
||||
|
||||
struct ExportHTMLOption
|
||||
{
|
||||
ExportHTMLOption()
|
||||
: m_embedCssStyle(true),
|
||||
m_completeHTML(true),
|
||||
m_embedImages(true),
|
||||
m_mimeHTML(false),
|
||||
m_outlinePanel(true)
|
||||
{
|
||||
}
|
||||
|
||||
ExportHTMLOption(bool p_embedCssStyle,
|
||||
bool p_completeHTML,
|
||||
bool p_embedImages,
|
||||
bool p_mimeHTML,
|
||||
bool p_outlinePanel)
|
||||
: m_embedCssStyle(p_embedCssStyle),
|
||||
m_completeHTML(p_completeHTML),
|
||||
m_embedImages(p_embedImages),
|
||||
m_mimeHTML(p_mimeHTML),
|
||||
m_outlinePanel(p_outlinePanel)
|
||||
{
|
||||
}
|
||||
|
||||
bool m_embedCssStyle;
|
||||
bool m_completeHTML;
|
||||
bool m_embedImages;
|
||||
bool m_mimeHTML;
|
||||
bool m_outlinePanel;
|
||||
};
|
||||
|
||||
|
||||
struct ExportPDFOption
|
||||
{
|
||||
ExportPDFOption()
|
||||
: m_layout(NULL),
|
||||
m_wkhtmltopdf(false),
|
||||
m_wkEnableBackground(true),
|
||||
m_enableTableOfContents(false),
|
||||
m_wkPageNumber(ExportPageNumber::None)
|
||||
{
|
||||
}
|
||||
|
||||
ExportPDFOption(QPageLayout *p_layout,
|
||||
bool p_wkhtmltopdf,
|
||||
const QString &p_wkPath,
|
||||
bool p_wkEnableBackground,
|
||||
bool p_enableTableOfContents,
|
||||
const QString &p_wkTitle,
|
||||
const QString &p_wkTargetFileName,
|
||||
ExportPageNumber p_wkPageNumber,
|
||||
const QString &p_wkExtraArgs)
|
||||
: m_layout(p_layout),
|
||||
m_wkhtmltopdf(p_wkhtmltopdf),
|
||||
m_wkPath(p_wkPath),
|
||||
m_wkEnableBackground(p_wkEnableBackground),
|
||||
m_enableTableOfContents(p_enableTableOfContents),
|
||||
m_wkTitle(p_wkTitle),
|
||||
m_wkTargetFileName(p_wkTargetFileName),
|
||||
m_wkPageNumber(p_wkPageNumber),
|
||||
m_wkExtraArgs(p_wkExtraArgs)
|
||||
{
|
||||
}
|
||||
|
||||
QPageLayout *m_layout;
|
||||
bool m_wkhtmltopdf;
|
||||
QString m_wkPath;
|
||||
bool m_wkEnableBackground;
|
||||
bool m_enableTableOfContents;;
|
||||
QString m_wkTitle;
|
||||
QString m_wkTargetFileName;
|
||||
ExportPageNumber m_wkPageNumber;
|
||||
QString m_wkExtraArgs;
|
||||
};
|
||||
|
||||
|
||||
struct ExportCustomOption
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
#define DEFAULT_SEP ";"
|
||||
#else
|
||||
#define DEFAULT_SEP ":"
|
||||
#endif
|
||||
|
||||
enum SourceFormat
|
||||
{
|
||||
Markdown = 0,
|
||||
HTML
|
||||
};
|
||||
|
||||
ExportCustomOption()
|
||||
: m_srcFormat(SourceFormat::Markdown),
|
||||
m_allInOne(false),
|
||||
m_pdfLike(false),
|
||||
m_folderSep(DEFAULT_SEP)
|
||||
{
|
||||
}
|
||||
|
||||
ExportCustomOption(const QStringList &p_config)
|
||||
: m_srcFormat(SourceFormat::Markdown),
|
||||
m_allInOne(false),
|
||||
m_pdfLike(false),
|
||||
m_folderSep(DEFAULT_SEP)
|
||||
{
|
||||
if (p_config.size() < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_config.at(0).trimmed() != "0") {
|
||||
m_srcFormat = SourceFormat::HTML;
|
||||
}
|
||||
|
||||
m_outputSuffix = p_config.at(1).trimmed();
|
||||
|
||||
m_cmd = p_config.at(2).trimmed();
|
||||
}
|
||||
|
||||
ExportCustomOption(ExportCustomOption::SourceFormat p_srcFormat,
|
||||
const QString &p_outputSuffix,
|
||||
const QString &p_cmd,
|
||||
const QString &p_cssUrl,
|
||||
const QString &p_codeBlockCssUrl,
|
||||
bool p_allInOne,
|
||||
bool p_pdfLike,
|
||||
const QString &p_folderSep,
|
||||
const QString &p_targetFileName)
|
||||
: m_srcFormat(p_srcFormat),
|
||||
m_outputSuffix(p_outputSuffix),
|
||||
m_cssUrl(p_cssUrl),
|
||||
m_codeBlockCssUrl(p_codeBlockCssUrl),
|
||||
m_allInOne(p_allInOne),
|
||||
m_pdfLike(p_pdfLike),
|
||||
m_folderSep(p_folderSep),
|
||||
m_targetFileName(p_targetFileName)
|
||||
{
|
||||
QStringList cmds = p_cmd.split('\n');
|
||||
if (!cmds.isEmpty()) {
|
||||
m_cmd = cmds.first();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList toConfig() const
|
||||
{
|
||||
QStringList config;
|
||||
config << QString::number((int)m_srcFormat);
|
||||
config << m_outputSuffix;
|
||||
config << m_cmd;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
SourceFormat m_srcFormat;
|
||||
QString m_outputSuffix;
|
||||
QString m_cmd;
|
||||
|
||||
QString m_cssUrl;
|
||||
QString m_codeBlockCssUrl;
|
||||
|
||||
bool m_allInOne;
|
||||
bool m_pdfLike;
|
||||
|
||||
QString m_folderSep;
|
||||
QString m_targetFileName;
|
||||
};
|
||||
|
||||
|
||||
struct ExportOption
|
||||
{
|
||||
ExportOption()
|
||||
: m_source(ExportSource::CurrentNote),
|
||||
m_format(ExportFormat::Markdown),
|
||||
m_renderer(MarkdownConverterType::MarkdownIt),
|
||||
m_processSubfolders(true)
|
||||
{
|
||||
}
|
||||
|
||||
ExportOption(ExportSource p_source,
|
||||
ExportFormat p_format,
|
||||
MarkdownConverterType p_renderer,
|
||||
const QString &p_renderBg,
|
||||
const QString &p_renderStyle,
|
||||
const QString &p_renderCodeBlockStyle,
|
||||
bool p_processSubfolders,
|
||||
const ExportPDFOption &p_pdfOpt,
|
||||
const ExportHTMLOption &p_htmlOpt,
|
||||
const ExportCustomOption &p_customOpt)
|
||||
: m_source(p_source),
|
||||
m_format(p_format),
|
||||
m_renderer(p_renderer),
|
||||
m_renderBg(p_renderBg),
|
||||
m_renderStyle(p_renderStyle),
|
||||
m_renderCodeBlockStyle(p_renderCodeBlockStyle),
|
||||
m_processSubfolders(p_processSubfolders),
|
||||
m_pdfOpt(p_pdfOpt),
|
||||
m_htmlOpt(p_htmlOpt),
|
||||
m_customOpt(p_customOpt)
|
||||
{
|
||||
}
|
||||
|
||||
ExportSource m_source;
|
||||
ExportFormat m_format;
|
||||
MarkdownConverterType m_renderer;
|
||||
|
||||
// Background name.
|
||||
QString m_renderBg;
|
||||
|
||||
QString m_renderStyle;
|
||||
QString m_renderCodeBlockStyle;
|
||||
|
||||
// Whether process subfolders recursively when source is CurrentFolder.
|
||||
bool m_processSubfolders;
|
||||
|
||||
ExportPDFOption m_pdfOpt;
|
||||
|
||||
ExportHTMLOption m_htmlOpt;
|
||||
|
||||
ExportCustomOption m_customOpt;
|
||||
};
|
||||
|
||||
|
||||
class VExportDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VExportDialog(VNotebook *p_notebook,
|
||||
VDirectory *p_directory,
|
||||
VFile *p_file,
|
||||
VCart *p_cart,
|
||||
MarkdownConverterType p_renderer,
|
||||
QWidget *p_parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void startExport();
|
||||
|
||||
void handleOutputBrowseBtnClicked();
|
||||
|
||||
void handleWkPathBrowseBtnClicked();
|
||||
|
||||
void handleInputChanged();
|
||||
|
||||
void handleLayoutBtnClicked();
|
||||
|
||||
void handleCurrentFormatChanged(int p_index);
|
||||
|
||||
void handleCurrentSrcChanged(int p_index);
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
QWidget *setupPDFAdvancedSettings();
|
||||
|
||||
QWidget *setupHTMLAdvancedSettings();
|
||||
|
||||
QWidget *setupGeneralAdvancedSettings();
|
||||
|
||||
QWidget *setupCustomAdvancedSettings();
|
||||
|
||||
void initUIFields(MarkdownConverterType p_renderer);
|
||||
|
||||
QString getOutputDirectory() const;
|
||||
|
||||
void appendLogLine(const QString &p_text);
|
||||
|
||||
// Return number of files exported.
|
||||
int doExport(VFile *p_file,
|
||||
const ExportOption &p_opt,
|
||||
const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL,
|
||||
QList<QString> *p_outputFiles = NULL);
|
||||
|
||||
int doExport(VDirectory *p_directory,
|
||||
const ExportOption &p_opt,
|
||||
const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL,
|
||||
QList<QString> *p_outputFiles = NULL);
|
||||
|
||||
int doExport(VNotebook *p_notebook,
|
||||
const ExportOption &p_opt,
|
||||
const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL,
|
||||
QList<QString> *p_outputFiles = NULL);
|
||||
|
||||
int doExport(VCart *p_cart,
|
||||
const ExportOption &p_opt,
|
||||
const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL,
|
||||
QList<QString> *p_outputFiles = NULL);
|
||||
|
||||
int doExportMarkdown(VFile *p_file,
|
||||
const ExportOption &p_opt,
|
||||
const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL,
|
||||
QList<QString> *p_outputFiles = NULL);
|
||||
|
||||
int doExportPDF(VFile *p_file,
|
||||
const ExportOption &p_opt,
|
||||
const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL,
|
||||
QList<QString> *p_outputFiles = NULL);
|
||||
|
||||
int doExportHTML(VFile *p_file,
|
||||
const ExportOption &p_opt,
|
||||
const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL,
|
||||
QList<QString> *p_outputFiles = NULL);
|
||||
|
||||
int doExportPDFAllInOne(const QList<QString> &p_files,
|
||||
const ExportOption &p_opt,
|
||||
const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL);
|
||||
|
||||
int doExportCustomAllInOne(const QList<QString> &p_files,
|
||||
const ExportOption &p_opt,
|
||||
const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL);
|
||||
|
||||
int doExportCustom(VFile *p_file,
|
||||
const ExportOption &p_opt,
|
||||
const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL,
|
||||
QList<QString> *p_outputFiles = NULL);
|
||||
|
||||
// Return false if we could not continue.
|
||||
bool checkUserAction();
|
||||
|
||||
void updatePageLayoutLabel();
|
||||
|
||||
bool checkWkhtmltopdfExecutable(const QString &p_file);
|
||||
|
||||
ExportSource currentSource() const;
|
||||
|
||||
ExportFormat currentFormat() const;
|
||||
|
||||
int outputAsHTML(const QString &p_outputFolder,
|
||||
QString *p_errMsg = NULL,
|
||||
QList<QString> *p_outputFiles = NULL);
|
||||
|
||||
// Collect files to be handled.
|
||||
QList<QString> collectFiles(QString *p_errMsg = NULL);
|
||||
|
||||
QComboBox *m_srcCB;
|
||||
|
||||
QComboBox *m_formatCB;
|
||||
|
||||
QComboBox *m_rendererCB;
|
||||
|
||||
QComboBox *m_renderBgCB;
|
||||
|
||||
QComboBox *m_renderStyleCB;
|
||||
|
||||
QComboBox *m_renderCodeBlockStyleCB;
|
||||
|
||||
VLineEdit *m_outputEdit;
|
||||
|
||||
QGroupBox *m_basicBox;
|
||||
|
||||
QGroupBox *m_settingBox;
|
||||
|
||||
QWidget *m_pdfSettings;
|
||||
|
||||
QWidget *m_htmlSettings;
|
||||
|
||||
QWidget *m_generalSettings;
|
||||
|
||||
QWidget *m_customSettings;
|
||||
|
||||
QPlainTextEdit *m_consoleEdit;
|
||||
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
QPushButton *m_openBtn;
|
||||
|
||||
QPushButton *m_exportBtn;
|
||||
|
||||
QPushButton *m_copyBtn;
|
||||
|
||||
QLabel *m_layoutLabel;
|
||||
|
||||
QCheckBox *m_wkhtmltopdfCB;
|
||||
|
||||
VLineEdit *m_wkPathEdit;
|
||||
|
||||
QPushButton *m_wkPathBrowseBtn;
|
||||
|
||||
VLineEdit *m_wkTitleEdit;
|
||||
|
||||
VLineEdit *m_wkTargetFileNameEdit;
|
||||
|
||||
QCheckBox *m_wkBackgroundCB;
|
||||
|
||||
QCheckBox *m_tableOfContentsCB;
|
||||
|
||||
QComboBox *m_wkPageNumberCB;
|
||||
|
||||
VLineEdit *m_wkExtraArgsEdit;
|
||||
|
||||
QCheckBox *m_embedStyleCB;
|
||||
|
||||
QCheckBox *m_completeHTMLCB;
|
||||
|
||||
QCheckBox *m_embedImagesCB;
|
||||
|
||||
QCheckBox *m_mimeHTMLCB;
|
||||
|
||||
QCheckBox *m_outlinePanelCB;
|
||||
|
||||
QCheckBox *m_subfolderCB;
|
||||
|
||||
QComboBox *m_customSrcFormatCB;
|
||||
|
||||
VLineEdit *m_customSuffixEdit;
|
||||
|
||||
QCheckBox *m_customAllInOneCB;
|
||||
|
||||
QCheckBox *m_customPdfLikeCB;
|
||||
|
||||
QPlainTextEdit *m_customCmdEdit;
|
||||
|
||||
VLineEdit *m_customFolderSepEdit;
|
||||
|
||||
VLineEdit *m_customTargetFileNameEdit;
|
||||
|
||||
VNotebook *m_notebook;
|
||||
|
||||
VDirectory *m_directory;
|
||||
|
||||
VFile *m_file;
|
||||
|
||||
VCart *m_cart;
|
||||
|
||||
QProgressBar *m_proBar;
|
||||
|
||||
QPageLayout m_pageLayout;
|
||||
|
||||
// Whether we are exporting files.
|
||||
bool m_inExport;
|
||||
|
||||
// Asked to stop exporting by user.
|
||||
bool m_askedToStop;
|
||||
|
||||
// Exporter used to export PDF and HTML.
|
||||
VExporter *m_exporter;
|
||||
|
||||
// Last exproted file path.
|
||||
QString m_exportedFile;
|
||||
|
||||
// Last output folder path.
|
||||
static QString s_lastOutputFolder;
|
||||
|
||||
static ExportOption s_opt;
|
||||
};
|
||||
|
||||
inline ExportSource VExportDialog::currentSource() const
|
||||
{
|
||||
return (ExportSource)m_srcCB->currentData().toInt();
|
||||
}
|
||||
|
||||
inline ExportFormat VExportDialog::currentFormat() const
|
||||
{
|
||||
return (ExportFormat)m_formatCB->currentData().toInt();
|
||||
}
|
||||
#endif // VEXPORTDIALOG_H
|
@ -1,160 +0,0 @@
|
||||
#include <QtWidgets>
|
||||
#include "vfileinfodialog.h"
|
||||
#include "vdirectory.h"
|
||||
#include "vnotefile.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "vmetawordlineedit.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VFileInfoDialog::VFileInfoDialog(const QString &title,
|
||||
const QString &info,
|
||||
VDirectory *directory,
|
||||
const VNoteFile *file,
|
||||
QWidget *parent)
|
||||
: QDialog(parent), m_directory(directory), m_file(file)
|
||||
{
|
||||
setupUI(title, info);
|
||||
|
||||
connect(m_nameEdit, &VMetaWordLineEdit::textChanged, this, &VFileInfoDialog::handleInputChanged);
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VFileInfoDialog::setupUI(const QString &p_title, const QString &p_info)
|
||||
{
|
||||
QLabel *infoLabel = NULL;
|
||||
if (!p_info.isEmpty()) {
|
||||
infoLabel = new QLabel(p_info);
|
||||
}
|
||||
|
||||
// File name.
|
||||
QString name = m_file->getName();
|
||||
m_nameEdit = new VMetaWordLineEdit(name);
|
||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
|
||||
m_nameEdit);
|
||||
m_nameEdit->setValidator(validator);
|
||||
int baseStart = 0, baseLength = name.size();
|
||||
int dotIdx = name.lastIndexOf('.');
|
||||
if (dotIdx != -1) {
|
||||
baseLength = dotIdx;
|
||||
}
|
||||
|
||||
// Select without suffix.
|
||||
m_nameEdit->setSelection(baseStart, baseLength);
|
||||
|
||||
// File path.
|
||||
QLineEdit *filePathEdit = new QLineEdit(m_file->fetchPath());
|
||||
filePathEdit->setReadOnly(true);
|
||||
|
||||
// Attachment folder.
|
||||
VLineEdit *attachmentFolderEdit = new VLineEdit(m_file->getAttachmentFolder());
|
||||
attachmentFolderEdit->setPlaceholderText(tr("Will be assigned when adding attachments"));
|
||||
attachmentFolderEdit->setToolTip(tr("The folder to hold attachments of this note"));
|
||||
attachmentFolderEdit->setReadOnly(true);
|
||||
|
||||
// Created time.
|
||||
QString createdTimeStr = VUtils::displayDateTime(m_file->getCreatedTimeUtc().toLocalTime());
|
||||
QLabel *createdTimeLabel = new QLabel(createdTimeStr);
|
||||
|
||||
// Modified time.
|
||||
createdTimeStr = VUtils::displayDateTime(m_file->getModifiedTimeUtc().toLocalTime());
|
||||
QLabel *modifiedTimeLabel = new QLabel(createdTimeStr);
|
||||
modifiedTimeLabel->setToolTip(tr("Last modified time within VNote"));
|
||||
|
||||
// Tags.
|
||||
QLineEdit *tagEdit = new QLineEdit(m_file->getTags().join(", "));
|
||||
QString tagTip = tr("Add tags to a note at the right bottom status bar when it is opened");
|
||||
tagEdit->setPlaceholderText(tagTip);
|
||||
tagEdit->setToolTip(tr("Tags of this note separated by , (%1)").arg(tagTip));
|
||||
tagEdit->setReadOnly(true);
|
||||
|
||||
QFormLayout *topLayout = new QFormLayout();
|
||||
topLayout->addRow(tr("Note &name:"), m_nameEdit);
|
||||
topLayout->addRow(tr("File path:"), filePathEdit);
|
||||
topLayout->addRow(tr("Attachment folder:"), attachmentFolderEdit);
|
||||
topLayout->addRow(tr("Created time:"), createdTimeLabel);
|
||||
topLayout->addRow(tr("Modified time:"), modifiedTimeLabel);
|
||||
topLayout->addRow(tr("Tags:"), tagEdit);
|
||||
|
||||
m_warnLabel = new QLabel();
|
||||
m_warnLabel->setWordWrap(true);
|
||||
m_warnLabel->hide();
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("SpecialBtn", true);
|
||||
m_nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
if (infoLabel) {
|
||||
mainLayout->addWidget(infoLabel);
|
||||
}
|
||||
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_warnLabel);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle(p_title);
|
||||
}
|
||||
|
||||
void VFileInfoDialog::handleInputChanged()
|
||||
{
|
||||
bool showWarnLabel = false;
|
||||
QString name = m_nameEdit->getEvaluatedText();
|
||||
bool nameOk = !name.isEmpty();
|
||||
if (nameOk && name != m_file->getName()) {
|
||||
// Check if the name conflicts with existing note name.
|
||||
// Case-insensitive when creating note.
|
||||
const VNoteFile *file = m_directory->findFile(name, false);
|
||||
QString warnText;
|
||||
if (file && file != m_file) {
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> already exists. "
|
||||
"Please choose another name.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
} else if (m_file->getDocType() != DocType::Unknown
|
||||
&& VUtils::docTypeFromName(name) != m_file->getDocType()) {
|
||||
// Check if the name change the doc type.
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Changing type of the note is not supported. "
|
||||
"Please use the same suffix as the old one.")
|
||||
.arg(g_config->c_warningTextStyle);
|
||||
} else if (!VUtils::checkFileNameLegal(name)) {
|
||||
// Check if evaluated name contains illegal characters.
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> contains illegal characters "
|
||||
"(after magic word evaluation).")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
}
|
||||
|
||||
if (!nameOk) {
|
||||
showWarnLabel = true;
|
||||
m_warnLabel->setText(warnText);
|
||||
}
|
||||
}
|
||||
|
||||
m_warnLabel->setVisible(showWarnLabel);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(nameOk);
|
||||
}
|
||||
|
||||
QString VFileInfoDialog::getNameInput() const
|
||||
{
|
||||
return m_nameEdit->getEvaluatedText();
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#ifndef VFILEINFODIALOG_H
|
||||
#define VFILEINFODIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QLabel;
|
||||
class VMetaWordLineEdit;
|
||||
class QDialogButtonBox;
|
||||
class QString;
|
||||
class VDirectory;
|
||||
class VNoteFile;
|
||||
|
||||
// File information dialog for internal note file.
|
||||
class VFileInfoDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VFileInfoDialog(const QString &title,
|
||||
const QString &info,
|
||||
VDirectory *directory,
|
||||
const VNoteFile *file,
|
||||
QWidget *parent = 0);
|
||||
|
||||
QString getNameInput() const;
|
||||
|
||||
private slots:
|
||||
void handleInputChanged();
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title, const QString &p_info);
|
||||
|
||||
VMetaWordLineEdit *m_nameEdit;
|
||||
QLabel *m_warnLabel;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
VDirectory *m_directory;
|
||||
const VNoteFile *m_file;
|
||||
};
|
||||
|
||||
#endif // VFILEINFODIALOG_H
|
@ -1,314 +0,0 @@
|
||||
#include "vfindreplacedialog.h"
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "utils/viconutils.h"
|
||||
#include "vlineedit.h"
|
||||
|
||||
VFindReplaceDialog::VFindReplaceDialog(QWidget *p_parent)
|
||||
: QWidget(p_parent), m_options(0), m_replaceAvailable(true)
|
||||
{
|
||||
setupUI();
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::setupUI()
|
||||
{
|
||||
QLabel *titleLabel = new QLabel(tr("Find/Replace"));
|
||||
titleLabel->setProperty("TitleLabel", true);
|
||||
m_closeBtn = new QPushButton(VIconUtils::titleIcon(":/resources/icons/close.svg"), "");
|
||||
m_closeBtn->setProperty("TitleBtn", true);
|
||||
QHBoxLayout *titleLayout = new QHBoxLayout();
|
||||
titleLayout->addWidget(titleLabel);
|
||||
titleLayout->addWidget(m_closeBtn);
|
||||
titleLayout->setStretch(0, 1);
|
||||
titleLayout->setStretch(1, 0);
|
||||
titleLayout->setContentsMargins(0, 0, 0, 0);
|
||||
titleLayout->setSpacing(0);
|
||||
QWidget *titleWidget = new QWidget();
|
||||
titleWidget->setObjectName("FindReplaceTitleWidget");
|
||||
titleWidget->setLayout(titleLayout);
|
||||
|
||||
// Find
|
||||
QLabel *findLabel = new QLabel(tr("Find:"));
|
||||
m_findEdit = new VLineEdit();
|
||||
m_findEdit->setPlaceholderText(tr("Enter text to search"));
|
||||
findLabel->setBuddy(m_findEdit);
|
||||
m_findNextBtn = new QPushButton(tr("Find &Next"));
|
||||
m_findNextBtn->setProperty("FlatBtn", true);
|
||||
m_findNextBtn->setDefault(true);
|
||||
m_findPrevBtn = new QPushButton(tr("Find &Previous"));
|
||||
m_findPrevBtn->setProperty("FlatBtn", true);
|
||||
|
||||
// Replace
|
||||
QLabel *replaceLabel = new QLabel(tr("&Replace with:"));
|
||||
m_replaceEdit = new VLineEdit();
|
||||
m_replaceEdit->setPlaceholderText(tr("\\1, \\2 for back reference in regular expression"));
|
||||
replaceLabel->setBuddy(m_replaceEdit);
|
||||
m_replaceBtn = new QPushButton(tr("Replace"));
|
||||
m_replaceBtn->setProperty("FlatBtn", true);
|
||||
m_replaceFindBtn = new QPushButton(tr("Replace && Fin&d"));
|
||||
m_replaceFindBtn->setProperty("FlatBtn", true);
|
||||
m_replaceAllBtn = new QPushButton(tr("Replace A&ll"));
|
||||
m_replaceAllBtn->setProperty("FlatBtn", true);
|
||||
m_advancedBtn = new QPushButton(tr("&Advanced >>>"));
|
||||
m_advancedBtn->setProperty("FlatBtn", true);
|
||||
m_advancedBtn->setCheckable(true);
|
||||
|
||||
// Options
|
||||
m_caseSensitiveCheck = new QCheckBox(tr("&Case sensitive"), this);
|
||||
connect(m_caseSensitiveCheck, &QCheckBox::stateChanged,
|
||||
this, &VFindReplaceDialog::optionBoxToggled);
|
||||
m_wholeWordOnlyCheck = new QCheckBox(tr("&Whole word only"), this);
|
||||
connect(m_wholeWordOnlyCheck, &QCheckBox::stateChanged,
|
||||
this, &VFindReplaceDialog::optionBoxToggled);
|
||||
m_regularExpressionCheck = new QCheckBox(tr("Re&gular expression"), this);
|
||||
connect(m_regularExpressionCheck, &QCheckBox::stateChanged,
|
||||
this, &VFindReplaceDialog::optionBoxToggled);
|
||||
m_incrementalSearchCheck = new QCheckBox(tr("&Incremental search"), this);
|
||||
connect(m_incrementalSearchCheck, &QCheckBox::stateChanged,
|
||||
this, &VFindReplaceDialog::optionBoxToggled);
|
||||
|
||||
QGridLayout *gridLayout = new QGridLayout();
|
||||
gridLayout->addWidget(findLabel, 0, 0);
|
||||
gridLayout->addWidget(m_findEdit, 0, 1);
|
||||
gridLayout->addWidget(m_findNextBtn, 0, 2);
|
||||
gridLayout->addWidget(m_findPrevBtn, 0, 3);
|
||||
gridLayout->addWidget(replaceLabel, 1, 0);
|
||||
gridLayout->addWidget(m_replaceEdit, 1, 1);
|
||||
gridLayout->addWidget(m_replaceBtn, 1, 2);
|
||||
gridLayout->addWidget(m_replaceFindBtn, 1, 3);
|
||||
gridLayout->addWidget(m_replaceAllBtn, 1, 4);
|
||||
gridLayout->addWidget(m_advancedBtn, 1, 5);
|
||||
gridLayout->addWidget(m_caseSensitiveCheck, 2, 1);
|
||||
gridLayout->addWidget(m_wholeWordOnlyCheck, 2, 2);
|
||||
gridLayout->addWidget(m_regularExpressionCheck, 3, 1);
|
||||
gridLayout->addWidget(m_incrementalSearchCheck, 3, 2);
|
||||
gridLayout->setColumnStretch(0, 0);
|
||||
gridLayout->setColumnStretch(1, 4);
|
||||
gridLayout->setColumnStretch(2, 1);
|
||||
gridLayout->setColumnStretch(3, 1);
|
||||
gridLayout->setColumnStretch(4, 1);
|
||||
gridLayout->setColumnStretch(5, 1);
|
||||
gridLayout->setColumnStretch(6, 3);
|
||||
|
||||
QMargins margin = gridLayout->contentsMargins();
|
||||
margin.setLeft(3);
|
||||
gridLayout->setContentsMargins(margin);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addWidget(titleWidget);
|
||||
mainLayout->addLayout(gridLayout);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
setTabOrder(m_findEdit, m_findNextBtn);
|
||||
setTabOrder(m_findNextBtn, m_findPrevBtn);
|
||||
setTabOrder(m_findPrevBtn, m_replaceEdit);
|
||||
setTabOrder(m_replaceEdit, m_replaceBtn);
|
||||
setTabOrder(m_replaceBtn, m_replaceFindBtn);
|
||||
setTabOrder(m_replaceFindBtn, m_replaceAllBtn);
|
||||
setTabOrder(m_replaceAllBtn, m_advancedBtn);
|
||||
setTabOrder(m_advancedBtn, m_caseSensitiveCheck);
|
||||
setTabOrder(m_caseSensitiveCheck, m_wholeWordOnlyCheck);
|
||||
setTabOrder(m_wholeWordOnlyCheck, m_regularExpressionCheck);
|
||||
setTabOrder(m_regularExpressionCheck, m_incrementalSearchCheck);
|
||||
setTabOrder(m_incrementalSearchCheck, m_closeBtn);
|
||||
|
||||
m_caseSensitiveCheck->hide();
|
||||
m_wholeWordOnlyCheck->hide();
|
||||
m_regularExpressionCheck->hide();
|
||||
m_incrementalSearchCheck->hide();
|
||||
|
||||
// Signals
|
||||
connect(m_closeBtn, &QPushButton::clicked,
|
||||
this, &VFindReplaceDialog::closeDialog);
|
||||
connect(m_findEdit, &VLineEdit::textChanged,
|
||||
this, &VFindReplaceDialog::handleFindTextChanged);
|
||||
connect(m_advancedBtn, &QPushButton::toggled,
|
||||
this, &VFindReplaceDialog::advancedBtnToggled);
|
||||
connect(m_findNextBtn, SIGNAL(clicked(bool)),
|
||||
this, SLOT(findNext()));
|
||||
connect(m_findPrevBtn, SIGNAL(clicked(bool)),
|
||||
this, SLOT(findPrevious()));
|
||||
connect(m_replaceBtn, SIGNAL(clicked(bool)),
|
||||
this, SLOT(replace()));
|
||||
connect(m_replaceFindBtn, SIGNAL(clicked(bool)),
|
||||
this, SLOT(replaceFind()));
|
||||
connect(m_replaceAllBtn, SIGNAL(clicked(bool)),
|
||||
this, SLOT(replaceAll()));
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::closeDialog()
|
||||
{
|
||||
if (this->isVisible()) {
|
||||
hide();
|
||||
emit dialogClosed();
|
||||
}
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Escape:
|
||||
event->accept();
|
||||
closeDialog();
|
||||
return;
|
||||
|
||||
case Qt::Key_Return:
|
||||
{
|
||||
int modifiers = event->modifiers();
|
||||
bool shift = false;
|
||||
if (modifiers == Qt::ShiftModifier) {
|
||||
shift = true;
|
||||
} else if (modifiers != Qt::NoModifier) {
|
||||
break;
|
||||
}
|
||||
if (!m_findEdit->hasFocus() && !m_replaceEdit->hasFocus()) {
|
||||
break;
|
||||
}
|
||||
event->accept();
|
||||
if (shift) {
|
||||
findPrevious();
|
||||
} else {
|
||||
findNext();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
QWidget::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::openDialog(QString p_text)
|
||||
{
|
||||
show();
|
||||
if (!p_text.isEmpty()) {
|
||||
m_findEdit->setText(p_text);
|
||||
}
|
||||
m_findEdit->setFocus();
|
||||
m_findEdit->selectAll();
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::handleFindTextChanged(const QString &p_text)
|
||||
{
|
||||
emit findTextChanged(p_text, m_options);
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::advancedBtnToggled(bool p_checked)
|
||||
{
|
||||
if (p_checked) {
|
||||
m_advancedBtn->setText(tr("B&asic <<<"));
|
||||
} else {
|
||||
m_advancedBtn->setText(tr("&Advanced >>>"));
|
||||
}
|
||||
|
||||
m_caseSensitiveCheck->setVisible(p_checked);
|
||||
m_wholeWordOnlyCheck->setVisible(p_checked);
|
||||
m_regularExpressionCheck->setVisible(p_checked);
|
||||
m_incrementalSearchCheck->setVisible(p_checked);
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::optionBoxToggled(int p_state)
|
||||
{
|
||||
QObject *obj = sender();
|
||||
FindOption opt = FindOption::CaseSensitive;
|
||||
if (obj == m_caseSensitiveCheck) {
|
||||
opt = FindOption::CaseSensitive;
|
||||
} else if (obj == m_wholeWordOnlyCheck) {
|
||||
opt = FindOption::WholeWordOnly;
|
||||
} else if (obj == m_regularExpressionCheck) {
|
||||
opt = FindOption::RegularExpression;
|
||||
} else {
|
||||
opt = FindOption::IncrementalSearch;
|
||||
}
|
||||
|
||||
if (p_state) {
|
||||
m_options |= opt;
|
||||
} else {
|
||||
m_options &= ~opt;
|
||||
}
|
||||
emit findOptionChanged(m_options);
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::setOption(FindOption p_opt, bool p_enabled)
|
||||
{
|
||||
if (p_opt == FindOption::CaseSensitive) {
|
||||
m_caseSensitiveCheck->setChecked(p_enabled);
|
||||
} else if (p_opt == FindOption::WholeWordOnly) {
|
||||
m_wholeWordOnlyCheck->setChecked(p_enabled);
|
||||
} else if (p_opt == FindOption::RegularExpression) {
|
||||
m_regularExpressionCheck->setChecked(p_enabled);
|
||||
} else if (p_opt == FindOption::IncrementalSearch) {
|
||||
m_incrementalSearchCheck->setChecked(p_enabled);
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::findNext()
|
||||
{
|
||||
QString text = m_findEdit->text();
|
||||
if (text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
emit findNext(text, m_options, true);
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::findPrevious()
|
||||
{
|
||||
QString text = m_findEdit->text();
|
||||
if (text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
emit findNext(text, m_options, false);
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::replace()
|
||||
{
|
||||
QString text = m_findEdit->text();
|
||||
if (text.isEmpty() || !m_replaceAvailable) {
|
||||
return;
|
||||
}
|
||||
QString replaceText = m_replaceEdit->text();
|
||||
emit replace(text, m_options, replaceText, false);
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::replaceFind()
|
||||
{
|
||||
QString text = m_findEdit->text();
|
||||
if (text.isEmpty() || !m_replaceAvailable) {
|
||||
return;
|
||||
}
|
||||
QString replaceText = m_replaceEdit->text();
|
||||
emit replace(text, m_options, replaceText, true);
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::replaceAll()
|
||||
{
|
||||
QString text = m_findEdit->text();
|
||||
if (text.isEmpty() || !m_replaceAvailable) {
|
||||
return;
|
||||
}
|
||||
QString replaceText = m_replaceEdit->text();
|
||||
emit replaceAll(text, m_options, replaceText);
|
||||
}
|
||||
|
||||
void VFindReplaceDialog::updateState(DocType p_docType, bool p_editMode)
|
||||
{
|
||||
if (p_editMode || p_docType == DocType::Html) {
|
||||
m_wholeWordOnlyCheck->setEnabled(true);
|
||||
m_regularExpressionCheck->setEnabled(true);
|
||||
} else if (p_docType == DocType::Markdown) {
|
||||
m_wholeWordOnlyCheck->setEnabled(false);
|
||||
m_regularExpressionCheck->setEnabled(false);
|
||||
}
|
||||
|
||||
m_replaceAvailable = p_editMode;
|
||||
}
|
||||
|
||||
QString VFindReplaceDialog::textToFind() const
|
||||
{
|
||||
return m_findEdit->text();
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
#ifndef VFINDREPLACEDIALOG_H
|
||||
#define VFINDREPLACEDIALOG_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
#include "vconstants.h"
|
||||
|
||||
class VLineEdit;
|
||||
class QPushButton;
|
||||
class QCheckBox;
|
||||
|
||||
class VFindReplaceDialog : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VFindReplaceDialog(QWidget *p_parent = 0);
|
||||
|
||||
uint options() const;
|
||||
|
||||
void setOption(FindOption p_opt, bool p_enabled);
|
||||
|
||||
// Update the options enabled/disabled state according to current
|
||||
// edit tab.
|
||||
void updateState(DocType p_docType, bool p_editMode);
|
||||
|
||||
QString textToFind() const;
|
||||
|
||||
signals:
|
||||
void dialogClosed();
|
||||
void findTextChanged(const QString &p_text, uint p_options);
|
||||
void findOptionChanged(uint p_options);
|
||||
void findNext(const QString &p_text, uint p_options, bool p_forward);
|
||||
void replace(const QString &p_text, uint p_options,
|
||||
const QString &p_replaceText, bool p_findNext);
|
||||
void replaceAll(const QString &p_text, uint p_options,
|
||||
const QString &p_replaceText);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
|
||||
|
||||
public slots:
|
||||
void closeDialog();
|
||||
void openDialog(QString p_text = "");
|
||||
void findNext();
|
||||
void findPrevious();
|
||||
void replace();
|
||||
void replaceFind();
|
||||
void replaceAll();
|
||||
|
||||
private slots:
|
||||
void handleFindTextChanged(const QString &p_text);
|
||||
void advancedBtnToggled(bool p_checked);
|
||||
void optionBoxToggled(int p_state);
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
// Bit OR of FindOption
|
||||
uint m_options;
|
||||
bool m_replaceAvailable;
|
||||
|
||||
VLineEdit *m_findEdit;
|
||||
VLineEdit *m_replaceEdit;
|
||||
QPushButton *m_findNextBtn;
|
||||
QPushButton *m_findPrevBtn;
|
||||
QPushButton *m_replaceBtn;
|
||||
QPushButton *m_replaceFindBtn;
|
||||
QPushButton *m_replaceAllBtn;
|
||||
QPushButton *m_advancedBtn;
|
||||
QPushButton *m_closeBtn;
|
||||
QCheckBox *m_caseSensitiveCheck;
|
||||
QCheckBox *m_wholeWordOnlyCheck;
|
||||
QCheckBox *m_regularExpressionCheck;
|
||||
QCheckBox *m_incrementalSearchCheck;
|
||||
};
|
||||
|
||||
inline uint VFindReplaceDialog::options() const
|
||||
{
|
||||
return m_options;
|
||||
}
|
||||
#endif // VFINDREPLACEDIALOG_H
|
@ -1,186 +0,0 @@
|
||||
#include "vfixnotebookdialog.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "vconfigmanager.h"
|
||||
#include "vlineedit.h"
|
||||
#include "vnotebook.h"
|
||||
#include "utils/vutils.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VFixNotebookDialog::VFixNotebookDialog(const VNotebook *p_notebook,
|
||||
const QVector<VNotebook *> &p_notebooks,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent), m_notebook(p_notebook), m_notebooks(p_notebooks)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VFixNotebookDialog::setupUI()
|
||||
{
|
||||
QLabel *infoLabel = new QLabel(tr("VNote could not find the root folder of notebook "
|
||||
"<span style=\"%1\">%2</span>. Please specify the new path "
|
||||
"to the root folder if you moved it somewhere, or VNote "
|
||||
"will just remove this notebook.")
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(m_notebook->getName()), this);
|
||||
infoLabel->setWordWrap(true);
|
||||
|
||||
QLabel *nameLabel = new QLabel(m_notebook->getName(), this);
|
||||
|
||||
m_pathEdit = new VLineEdit(m_notebook->getPath(), this);
|
||||
connect(m_pathEdit, &VLineEdit::textChanged,
|
||||
this, &VFixNotebookDialog::handleInputChanged);
|
||||
|
||||
m_browseBtn = new QPushButton(tr("&Browse"), this);
|
||||
connect(m_browseBtn, &QPushButton::clicked,
|
||||
this, &VFixNotebookDialog::handleBrowseBtnClicked);
|
||||
|
||||
m_relativePathCB = new QCheckBox(tr("Use relative path"), this);
|
||||
m_relativePathCB->setToolTip(tr("Use relative path (to VNote's executable) in configuration file"));
|
||||
m_relativePathCB->setChecked(!QDir::isAbsolutePath(m_notebook->getPathInConfig()));
|
||||
connect(m_relativePathCB, &QCheckBox::stateChanged,
|
||||
this, &VFixNotebookDialog::handleInputChanged);
|
||||
|
||||
QHBoxLayout *pathLayout = new QHBoxLayout();
|
||||
pathLayout->addWidget(m_pathEdit);
|
||||
pathLayout->addWidget(m_browseBtn);
|
||||
|
||||
QFormLayout *topLayout = new QFormLayout();
|
||||
topLayout->addRow(tr("Notebook name:"), nameLabel);
|
||||
topLayout->addRow(tr("Notebook root folder:"), pathLayout);
|
||||
topLayout->addRow(m_relativePathCB);
|
||||
|
||||
// Warning label.
|
||||
m_warnLabel = new QLabel(this);
|
||||
m_warnLabel->setWordWrap(true);
|
||||
m_warnLabel->hide();
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("SpecialBtn", true);
|
||||
m_pathEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addWidget(infoLabel);
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_warnLabel);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
|
||||
setLayout(mainLayout);
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setWindowTitle(tr("Fix Notebook"));
|
||||
}
|
||||
|
||||
void VFixNotebookDialog::handleBrowseBtnClicked()
|
||||
{
|
||||
static QString defaultPath;
|
||||
|
||||
if (defaultPath.isEmpty()) {
|
||||
defaultPath = g_config->getVnoteNotebookFolderPath();
|
||||
if (!QFileInfo::exists(defaultPath)) {
|
||||
defaultPath = g_config->getDocumentPathOrHomePath();
|
||||
}
|
||||
}
|
||||
|
||||
QString dirPath = QFileDialog::getExistingDirectory(this,
|
||||
tr("Select Root Folder Of The Notebook"),
|
||||
defaultPath,
|
||||
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
||||
|
||||
if (!dirPath.isEmpty()) {
|
||||
if (m_pathEdit->text() == dirPath) {
|
||||
handleInputChanged();
|
||||
} else {
|
||||
m_pathEdit->setText(dirPath);
|
||||
}
|
||||
|
||||
defaultPath = VUtils::basePathFromPath(dirPath);
|
||||
}
|
||||
}
|
||||
|
||||
void VFixNotebookDialog::handleInputChanged()
|
||||
{
|
||||
QString warnText = tr("<span style=\"%1\">WARNING</span>: The folder chosen is NOT a valid root "
|
||||
"folder of a notebook.")
|
||||
.arg(g_config->c_warningTextStyle);
|
||||
bool pathOk = false;
|
||||
|
||||
QString path = m_pathEdit->text();
|
||||
if (!path.isEmpty()) {
|
||||
if (!QDir::isAbsolutePath(path)) {
|
||||
QString tmp = tr("<span style=\"%1\">WARNING</span>: Please specify absolute path.")
|
||||
.arg(g_config->c_warningTextStyle);
|
||||
m_warnLabel->setText(tmp);
|
||||
} else if (VConfigManager::directoryConfigExist(path)) {
|
||||
pathOk = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (pathOk) {
|
||||
// Check if this path has been in VNote.
|
||||
int idx = -1;
|
||||
for (idx = 0; idx < m_notebooks.size(); ++idx) {
|
||||
if (m_notebooks[idx] != m_notebook
|
||||
&& VUtils::equalPath(m_notebooks[idx]->getPath(), path)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx < m_notebooks.size()) {
|
||||
pathOk = false;
|
||||
QString existText = tr("<span style=\"%1\">WARNING</span>: The folder chosen has already been a root folder "
|
||||
"of existing notebook <span style=\"%2\">%3</span> in VNote.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(m_notebooks[idx]->getName());
|
||||
m_warnLabel->setText(existText);
|
||||
}
|
||||
} else {
|
||||
m_warnLabel->setText(warnText);
|
||||
}
|
||||
|
||||
if (pathOk && isUseRelativePath()) {
|
||||
if (!VUtils::inSameDrive(QCoreApplication::applicationDirPath(), path)) {
|
||||
pathOk = false;
|
||||
QString existText = tr("<span style=\"%1\">WARNING</span>: Please choose a folder in the same drive as "
|
||||
"<span style=\"%2\">%3</span> when relative path is enabled.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(QCoreApplication::applicationDirPath());
|
||||
m_warnLabel->setText(existText);
|
||||
}
|
||||
}
|
||||
|
||||
m_warnLabel->setVisible(!pathOk);
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(pathOk);
|
||||
}
|
||||
|
||||
QString VFixNotebookDialog::getPathInput() const
|
||||
{
|
||||
// absoluteFilePath() to convert the drive to upper case.
|
||||
// cleanPath() to remove duplicate separator, '.', and '..'.
|
||||
QString ret;
|
||||
if (isUseRelativePath()) {
|
||||
// Use relative path in config file.
|
||||
QDir appDir(QCoreApplication::applicationDirPath());
|
||||
ret = QDir::cleanPath(appDir.relativeFilePath(m_pathEdit->text()));
|
||||
} else {
|
||||
ret = QDir::cleanPath(QFileInfo(m_pathEdit->text()).absoluteFilePath());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VFixNotebookDialog::isUseRelativePath() const
|
||||
{
|
||||
return m_relativePathCB->isChecked();
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
#ifndef VFIXNOTEBOOKDIALOG_H
|
||||
#define VFIXNOTEBOOKDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class VNotebook;
|
||||
class VLineEdit;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class QDialogButtonBox;
|
||||
class QCheckBox;
|
||||
|
||||
class VFixNotebookDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VFixNotebookDialog(const VNotebook *p_notebook,
|
||||
const QVector<VNotebook *> &p_notebooks,
|
||||
QWidget *p_parent = nullptr);
|
||||
|
||||
QString getPathInput() const;
|
||||
|
||||
private slots:
|
||||
void handleBrowseBtnClicked();
|
||||
|
||||
void handleInputChanged();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
// Whether relative path will be used in config file.
|
||||
bool isUseRelativePath() const;
|
||||
|
||||
const VNotebook *m_notebook;
|
||||
|
||||
const QVector<VNotebook *> &m_notebooks;
|
||||
|
||||
VLineEdit *m_pathEdit;
|
||||
|
||||
QPushButton *m_browseBtn;
|
||||
|
||||
QCheckBox *m_relativePathCB;
|
||||
|
||||
QLabel *m_warnLabel;
|
||||
|
||||
QDialogButtonBox *m_btnBox;
|
||||
};
|
||||
|
||||
#endif // VFIXNOTEBOOKDIALOG_H
|
@ -1,377 +0,0 @@
|
||||
#include <QtWidgets>
|
||||
#include <QValidator>
|
||||
#include <QRegExp>
|
||||
|
||||
#include "vinsertimagedialog.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "vmetawordlineedit.h"
|
||||
#include "vdownloader.h"
|
||||
#include "vlineedit.h"
|
||||
#include "vconfigmanager.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VInsertImageDialog::VInsertImageDialog(const QString &p_title,
|
||||
const QString &p_imageTitle,
|
||||
const QString &p_imagePath,
|
||||
bool p_browsable,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent),
|
||||
m_browsable(p_browsable)
|
||||
{
|
||||
setupUI(p_title, p_imageTitle, p_imagePath);
|
||||
|
||||
connect(m_imageTitleEdit, &VMetaWordLineEdit::textChanged,
|
||||
this, &VInsertImageDialog::handleInputChanged);
|
||||
|
||||
if (m_browsable) {
|
||||
connect(m_pathEdit, &VLineEdit::editingFinished,
|
||||
this, &VInsertImageDialog::handlePathEditChanged);
|
||||
|
||||
connect(browseBtn, &QPushButton::clicked,
|
||||
this, &VInsertImageDialog::handleBrowseBtnClicked);
|
||||
|
||||
fetchImageFromClipboard();
|
||||
}
|
||||
|
||||
autoCompleteTitleFromPath();
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VInsertImageDialog::setupUI(const QString &p_title,
|
||||
const QString &p_imageTitle,
|
||||
const QString &p_imagePath)
|
||||
{
|
||||
// Path.
|
||||
m_pathEdit = new VLineEdit(p_imagePath);
|
||||
m_pathEdit->setReadOnly(!m_browsable);
|
||||
browseBtn = new QPushButton(tr("&Browse"));
|
||||
browseBtn->setEnabled(m_browsable);
|
||||
|
||||
// Title.
|
||||
m_imageTitleEdit = new VMetaWordLineEdit(p_imageTitle);
|
||||
m_imageTitleEdit->selectAll();
|
||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_imageTitleRegExp),
|
||||
m_imageTitleEdit);
|
||||
m_imageTitleEdit->setValidator(validator);
|
||||
|
||||
// Scale.
|
||||
m_widthSpin = new QSpinBox();
|
||||
m_widthSpin->setMinimum(1);
|
||||
m_widthSpin->setSingleStep(10);
|
||||
m_widthSpin->setSuffix(" px");
|
||||
connect(m_widthSpin, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, [this](int p_val) {
|
||||
if (!m_image) {
|
||||
return;
|
||||
}
|
||||
|
||||
int height = m_image->height() * (1.0 * p_val / m_image->width());
|
||||
m_imageLabel->resize(p_val, height);
|
||||
});
|
||||
|
||||
// 0.1 to 2.0 -> 1 to 20.
|
||||
m_scaleSlider = new QSlider();
|
||||
m_scaleSlider->setOrientation(Qt::Horizontal);
|
||||
m_scaleSlider->setMinimum(1);
|
||||
m_scaleSlider->setMaximum(20);
|
||||
m_scaleSlider->setValue(10);
|
||||
m_scaleSlider->setSingleStep(1);
|
||||
m_scaleSlider->setPageStep(5);
|
||||
connect(m_scaleSlider, &QSlider::valueChanged,
|
||||
this, [this](int p_val) {
|
||||
if (!m_image) {
|
||||
return;
|
||||
}
|
||||
|
||||
int width = m_image->width();
|
||||
qreal factor = 1.0;
|
||||
if (p_val != 10) {
|
||||
factor = p_val / 10.0;
|
||||
width = m_image->width() * factor;
|
||||
}
|
||||
|
||||
m_widthSpin->setValue(width);
|
||||
m_sliderLabel->setText(QString::number(factor) + "x");
|
||||
});
|
||||
|
||||
m_sliderLabel = new QLabel("1x");
|
||||
|
||||
QGridLayout *topLayout = new QGridLayout();
|
||||
topLayout->addWidget(new QLabel(tr("From:")), 0, 0, 1, 1);
|
||||
topLayout->addWidget(m_pathEdit, 0, 1, 1, 3);
|
||||
topLayout->addWidget(browseBtn, 0, 4, 1, 1);
|
||||
topLayout->addWidget(new QLabel(tr("Title:")), 1, 0, 1, 1);
|
||||
topLayout->addWidget(m_imageTitleEdit, 1, 1, 1, 4);
|
||||
topLayout->addWidget(new QLabel(tr("Scaling width:")), 2, 0, 1, 1);
|
||||
topLayout->addWidget(m_widthSpin, 2, 1, 1, 1);
|
||||
topLayout->addWidget(m_scaleSlider, 2, 2, 1, 2);
|
||||
topLayout->addWidget(m_sliderLabel, 2, 4, 1, 1);
|
||||
|
||||
topLayout->setColumnStretch(0, 0);
|
||||
topLayout->setColumnStretch(1, 0);
|
||||
topLayout->setColumnStretch(2, 1);
|
||||
topLayout->setColumnStretch(3, 1);
|
||||
topLayout->setColumnStretch(4, 0);
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
m_btnBox->button(QDialogButtonBox::Ok)->setProperty("SpecialBtn", true);
|
||||
|
||||
m_imageLabel = new QLabel();
|
||||
m_imageLabel->setScaledContents(true);
|
||||
|
||||
m_previewArea = new QScrollArea();
|
||||
m_previewArea->setBackgroundRole(QPalette::Dark);
|
||||
m_previewArea->setWidget(m_imageLabel);
|
||||
int minWidth = 512 * VUtils::calculateScaleFactor();
|
||||
m_previewArea->setMinimumSize(minWidth, minWidth);
|
||||
|
||||
setImageControlsVisible(false);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
mainLayout->addWidget(m_previewArea);
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle(p_title);
|
||||
|
||||
m_imageTitleEdit->setFocus();
|
||||
}
|
||||
|
||||
void VInsertImageDialog::handleInputChanged()
|
||||
{
|
||||
QString title = m_imageTitleEdit->getEvaluatedText();
|
||||
QRegExp reg(VUtils::c_imageTitleRegExp);
|
||||
bool titleOk = reg.exactMatch(title);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(titleOk && m_image);
|
||||
}
|
||||
|
||||
QString VInsertImageDialog::getImageTitleInput() const
|
||||
{
|
||||
return m_imageTitleEdit->getEvaluatedText();
|
||||
}
|
||||
|
||||
QString VInsertImageDialog::getPathInput() const
|
||||
{
|
||||
if (m_tempFile.isNull()) {
|
||||
return m_pathEdit->text();
|
||||
} else {
|
||||
return m_tempFile->fileName();
|
||||
}
|
||||
}
|
||||
|
||||
void VInsertImageDialog::handleBrowseBtnClicked()
|
||||
{
|
||||
QString bpath(m_browsePath);
|
||||
if (bpath.isEmpty()) {
|
||||
bpath = g_config->getImageBrowsePath();
|
||||
if (bpath.isEmpty()) {
|
||||
bpath = QDir::homePath();
|
||||
}
|
||||
}
|
||||
|
||||
QString filePath = QFileDialog::getOpenFileName(this, tr("Select The Image To Be Inserted"),
|
||||
bpath, tr("Images (*.png *.xpm *.jpg *.bmp *.gif *.svg)"));
|
||||
if (filePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update browse path.
|
||||
g_config->setImageBrowsePath(QFileInfo(filePath).path());
|
||||
|
||||
m_imageType = ImageType::LocalFile;
|
||||
|
||||
setPath(filePath);
|
||||
|
||||
autoCompleteTitleFromPath();
|
||||
|
||||
m_imageTitleEdit->setFocus();
|
||||
}
|
||||
|
||||
void VInsertImageDialog::setImage(const QImage &p_image)
|
||||
{
|
||||
if (p_image.isNull()) {
|
||||
m_image.clear();
|
||||
m_imageLabel->clear();
|
||||
|
||||
setImageControlsVisible(false);
|
||||
} else {
|
||||
m_image.reset(new QImage(p_image));
|
||||
|
||||
m_imageLabel->setPixmap(QPixmap::fromImage(*m_image));
|
||||
|
||||
m_imageLabel->adjustSize();
|
||||
|
||||
// Set the scaling widgets.
|
||||
m_scaleSlider->setValue(10);
|
||||
|
||||
int width = m_image->width();
|
||||
m_widthSpin->setMaximum(width * 5);
|
||||
m_widthSpin->setValue(width);
|
||||
|
||||
setImageControlsVisible(true);
|
||||
}
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VInsertImageDialog::imageDownloaded(const QByteArray &data)
|
||||
{
|
||||
setImage(QImage::fromData(data));
|
||||
|
||||
// Try to save it to a temp file.
|
||||
{
|
||||
if (data.isEmpty()) {
|
||||
goto image_data;
|
||||
}
|
||||
|
||||
QString format = QFileInfo(VUtils::purifyUrl(getPathInput())).suffix();
|
||||
if (format.isEmpty()) {
|
||||
goto image_data;
|
||||
}
|
||||
|
||||
m_tempFile.reset(VUtils::createTemporaryFile(format));
|
||||
if (!m_tempFile->open()) {
|
||||
goto image_data;
|
||||
}
|
||||
|
||||
if (m_tempFile->write(data) == -1) {
|
||||
goto image_data;
|
||||
}
|
||||
|
||||
m_imageType = ImageType::LocalFile;
|
||||
m_tempFile->close();
|
||||
return;
|
||||
}
|
||||
|
||||
image_data:
|
||||
m_tempFile.clear();
|
||||
m_imageType = ImageType::ImageData;
|
||||
}
|
||||
|
||||
QImage VInsertImageDialog::getImage() const
|
||||
{
|
||||
if (!m_image) {
|
||||
return QImage();
|
||||
} else return *m_image;
|
||||
}
|
||||
|
||||
void VInsertImageDialog::fetchImageFromClipboard()
|
||||
{
|
||||
if (!m_browsable || !m_pathEdit->text().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(!m_image);
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
const QMimeData *mimeData = clipboard->mimeData();
|
||||
|
||||
QUrl url;
|
||||
|
||||
if (mimeData->hasImage()) {
|
||||
QImage im = qvariant_cast<QImage>(mimeData->imageData());
|
||||
if (im.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setImage(im);
|
||||
m_imageType = ImageType::ImageData;
|
||||
return;
|
||||
} else if (mimeData->hasUrls()) {
|
||||
QList<QUrl> urls = mimeData->urls();
|
||||
if (urls.size() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
url = urls[0];
|
||||
} else if (mimeData->hasText()) {
|
||||
url = QUrl(mimeData->text());
|
||||
}
|
||||
|
||||
if (url.isValid()) {
|
||||
if (url.isLocalFile()) {
|
||||
setPath(url.toLocalFile());
|
||||
} else {
|
||||
setPath(url.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VInsertImageDialog::handlePathEditChanged()
|
||||
{
|
||||
QString text = m_pathEdit->text();
|
||||
QUrl url = QUrl::fromUserInput(text);
|
||||
if (text.isEmpty() || !url.isValid()) {
|
||||
setImage(QImage());
|
||||
return;
|
||||
}
|
||||
|
||||
QImage image;
|
||||
if (url.isLocalFile()) {
|
||||
image = VUtils::imageFromFile(url.toLocalFile());
|
||||
setImage(image);
|
||||
m_imageType = ImageType::LocalFile;
|
||||
} else {
|
||||
setImage(QImage());
|
||||
m_imageType = ImageType::ImageData;
|
||||
VDownloader *downloader = new VDownloader(this);
|
||||
connect(downloader, &VDownloader::downloadFinished,
|
||||
this, &VInsertImageDialog::imageDownloaded);
|
||||
downloader->download(url.toString());
|
||||
}
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VInsertImageDialog::setPath(const QString &p_path)
|
||||
{
|
||||
m_pathEdit->setText(p_path);
|
||||
handlePathEditChanged();
|
||||
}
|
||||
|
||||
void VInsertImageDialog::setImageControlsVisible(bool p_visible)
|
||||
{
|
||||
m_widthSpin->setEnabled(p_visible);
|
||||
m_scaleSlider->setEnabled(p_visible);
|
||||
m_sliderLabel->setEnabled(p_visible);
|
||||
|
||||
m_previewArea->setVisible(p_visible);
|
||||
}
|
||||
|
||||
int VInsertImageDialog::getOverridenWidth() const
|
||||
{
|
||||
int width = m_widthSpin->value();
|
||||
if (m_image && m_image->width() != width) {
|
||||
return width;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VInsertImageDialog::autoCompleteTitleFromPath()
|
||||
{
|
||||
if (!m_imageTitleEdit->text().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString imgPath = m_pathEdit->text();
|
||||
if (imgPath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_imageTitleEdit->setText(QFileInfo(imgPath).baseName());
|
||||
m_imageTitleEdit->selectAll();
|
||||
}
|
||||
|
||||
void VInsertImageDialog::setBrowsePath(const QString &p_path)
|
||||
{
|
||||
m_browsePath = p_path;
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
#ifndef VINSERTIMAGEDIALOG_H
|
||||
#define VINSERTIMAGEDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QImage>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QTemporaryFile>
|
||||
#include <QSharedPointer>
|
||||
|
||||
class QLabel;
|
||||
class VLineEdit;
|
||||
class VMetaWordLineEdit;
|
||||
class QPushButton;
|
||||
class QDialogButtonBox;
|
||||
class QScrollArea;
|
||||
class QSpinBox;
|
||||
class QSlider;
|
||||
|
||||
class VInsertImageDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ImageType
|
||||
{
|
||||
LocalFile = 0,
|
||||
ImageData
|
||||
};
|
||||
|
||||
VInsertImageDialog(const QString &p_title,
|
||||
const QString &p_imageTitle,
|
||||
const QString &p_imagePath,
|
||||
bool p_browsable = true,
|
||||
QWidget *p_parent = nullptr);
|
||||
|
||||
QString getImageTitleInput() const;
|
||||
|
||||
QString getPathInput() const;
|
||||
|
||||
void setImage(const QImage &p_image);
|
||||
|
||||
QImage getImage() const;
|
||||
|
||||
VInsertImageDialog::ImageType getImageType() const;
|
||||
|
||||
// Return 0 if no override.
|
||||
int getOverridenWidth() const;
|
||||
|
||||
void setBrowsePath(const QString &p_path);
|
||||
|
||||
public slots:
|
||||
void imageDownloaded(const QByteArray &data);
|
||||
|
||||
private slots:
|
||||
void handleInputChanged();
|
||||
|
||||
void handleBrowseBtnClicked();
|
||||
|
||||
void handlePathEditChanged();
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title,
|
||||
const QString &p_imageTitle,
|
||||
const QString &p_imagePath);
|
||||
|
||||
void fetchImageFromClipboard();
|
||||
|
||||
void setPath(const QString &p_path);
|
||||
|
||||
void setImageControlsVisible(bool p_visible);
|
||||
|
||||
void autoCompleteTitleFromPath();
|
||||
|
||||
VMetaWordLineEdit *m_imageTitleEdit;
|
||||
VLineEdit *m_pathEdit;
|
||||
QPushButton *browseBtn;
|
||||
|
||||
QSpinBox *m_widthSpin;
|
||||
QSlider *m_scaleSlider;
|
||||
QLabel *m_sliderLabel;
|
||||
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
QLabel *m_imageLabel;
|
||||
QScrollArea *m_previewArea;
|
||||
|
||||
QSharedPointer<QImage> m_image;
|
||||
|
||||
// Whether enable the browse action.
|
||||
bool m_browsable;
|
||||
|
||||
ImageType m_imageType;
|
||||
|
||||
QSharedPointer<QTemporaryFile> m_tempFile;
|
||||
|
||||
// Default path when browsing images to insert.
|
||||
QString m_browsePath;
|
||||
};
|
||||
|
||||
inline VInsertImageDialog::ImageType VInsertImageDialog::getImageType() const
|
||||
{
|
||||
return m_imageType;
|
||||
}
|
||||
|
||||
#endif // VINSERTIMAGEDIALOG_H
|
@ -1,150 +0,0 @@
|
||||
#include "vinsertlinkdialog.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "vmetawordlineedit.h"
|
||||
|
||||
VInsertLinkDialog::VInsertLinkDialog(const QString &p_title,
|
||||
const QString &p_text,
|
||||
const QString &p_info,
|
||||
const QString &p_linkText,
|
||||
const QString &p_linkUrl,
|
||||
bool p_linkTextEmptyAllowed,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent),
|
||||
m_linkTextEmptyAllowed(p_linkTextEmptyAllowed)
|
||||
{
|
||||
setupUI(p_title, p_text, p_info, p_linkText, p_linkUrl);
|
||||
|
||||
fetchLinkFromClipboard();
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VInsertLinkDialog::setupUI(const QString &p_title,
|
||||
const QString &p_text,
|
||||
const QString &p_info,
|
||||
const QString &p_linkText,
|
||||
const QString &p_linkUrl)
|
||||
{
|
||||
QLabel *textLabel = NULL;
|
||||
if (!p_text.isEmpty()) {
|
||||
textLabel = new QLabel(p_text);
|
||||
textLabel->setWordWrap(true);
|
||||
}
|
||||
|
||||
QLabel *infoLabel = NULL;
|
||||
if (!p_info.isEmpty()) {
|
||||
infoLabel = new QLabel(p_info);
|
||||
infoLabel->setWordWrap(true);
|
||||
}
|
||||
|
||||
m_linkTextEdit = new VMetaWordLineEdit(p_linkText);
|
||||
m_linkTextEdit->selectAll();
|
||||
|
||||
m_linkUrlEdit = new VLineEdit(p_linkUrl);
|
||||
m_linkUrlEdit->setToolTip(tr("Absolute or relative path of the link"));
|
||||
|
||||
QFormLayout *inputLayout = new QFormLayout();
|
||||
inputLayout->addRow(tr("&Text:"), m_linkTextEdit);
|
||||
inputLayout->addRow(tr("&URL:"), m_linkUrlEdit);
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("SpecialBtn", true);
|
||||
m_linkTextEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
if (textLabel) {
|
||||
mainLayout->addWidget(textLabel);
|
||||
}
|
||||
|
||||
if (infoLabel) {
|
||||
mainLayout->addWidget(infoLabel);
|
||||
}
|
||||
|
||||
mainLayout->addLayout(inputLayout);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
|
||||
setLayout(mainLayout);
|
||||
setWindowTitle(p_title);
|
||||
|
||||
connect(m_linkTextEdit, &VMetaWordLineEdit::textChanged,
|
||||
this, &VInsertLinkDialog::handleInputChanged);
|
||||
connect(m_linkUrlEdit, &VLineEdit::textChanged,
|
||||
this, &VInsertLinkDialog::handleInputChanged);
|
||||
}
|
||||
|
||||
void VInsertLinkDialog::handleInputChanged()
|
||||
{
|
||||
bool textOk = true;
|
||||
if (m_linkTextEdit->getEvaluatedText().isEmpty()
|
||||
&& !m_linkTextEmptyAllowed) {
|
||||
textOk = false;
|
||||
}
|
||||
|
||||
bool urlOk = true;
|
||||
if (m_linkUrlEdit->text().isEmpty()) {
|
||||
urlOk = false;
|
||||
}
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(textOk && urlOk);
|
||||
}
|
||||
|
||||
void VInsertLinkDialog::fetchLinkFromClipboard()
|
||||
{
|
||||
if (!m_linkUrlEdit->text().isEmpty()
|
||||
|| !m_linkTextEdit->text().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
const QMimeData *mimeData = clipboard->mimeData();
|
||||
|
||||
if (!mimeData->hasText()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString text = mimeData->text();
|
||||
text = text.trimmed();
|
||||
if (text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QUrl url = QUrl::fromUserInput(text);
|
||||
if (url.isValid()) {
|
||||
if (m_linkUrlEdit->text().isEmpty()) {
|
||||
if (url.isLocalFile()) {
|
||||
m_linkUrlEdit->setText(url.toString(QUrl::EncodeSpaces));
|
||||
} else {
|
||||
m_linkUrlEdit->setText(text);
|
||||
}
|
||||
}
|
||||
} else if (m_linkTextEdit->text().isEmpty()) {
|
||||
m_linkTextEdit->setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
QString VInsertLinkDialog::getLinkText() const
|
||||
{
|
||||
return m_linkTextEdit->getEvaluatedText();
|
||||
}
|
||||
|
||||
QString VInsertLinkDialog::getLinkUrl() const
|
||||
{
|
||||
return m_linkUrlEdit->text();
|
||||
}
|
||||
|
||||
void VInsertLinkDialog::showEvent(QShowEvent *p_event)
|
||||
{
|
||||
QDialog::showEvent(p_event);
|
||||
|
||||
if (!m_linkTextEdit->text().isEmpty() && m_linkUrlEdit->text().isEmpty()) {
|
||||
m_linkUrlEdit->setFocus();
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
#ifndef VINSERTLINKDIALOG_H
|
||||
#define VINSERTLINKDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
|
||||
class VMetaWordLineEdit;
|
||||
class VLineEdit;
|
||||
class QDialogButtonBox;
|
||||
class QShowEvent;
|
||||
|
||||
class VInsertLinkDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VInsertLinkDialog(const QString &p_title,
|
||||
const QString &p_text,
|
||||
const QString &p_info,
|
||||
const QString &p_linkText,
|
||||
const QString &p_linkUrl,
|
||||
bool p_linkTextEmptyAllowed,
|
||||
QWidget *p_parent = nullptr);
|
||||
|
||||
QString getLinkText() const;
|
||||
|
||||
QString getLinkUrl() const;
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void handleInputChanged();
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title,
|
||||
const QString &p_text,
|
||||
const QString &p_info,
|
||||
const QString &p_linkText,
|
||||
const QString &p_linkUrl);
|
||||
|
||||
// Infer link text/url from clipboard only when text and url are both empty.
|
||||
void fetchLinkFromClipboard();
|
||||
|
||||
VMetaWordLineEdit *m_linkTextEdit;
|
||||
|
||||
VLineEdit *m_linkUrlEdit;
|
||||
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
bool m_linkTextEmptyAllowed;
|
||||
};
|
||||
|
||||
#endif // VINSERTLINKDIALOG_H
|
@ -1,95 +0,0 @@
|
||||
#include "vinserttabledialog.h"
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QRadioButton>
|
||||
#include <QGridLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
#include <QButtonGroup>
|
||||
|
||||
VInsertTableDialog::VInsertTableDialog(QWidget *p_parent)
|
||||
: QDialog(p_parent),
|
||||
m_alignment(VTable::None)
|
||||
{
|
||||
setupUI();
|
||||
}
|
||||
|
||||
void VInsertTableDialog::setupUI()
|
||||
{
|
||||
m_rowCount = new QSpinBox(this);
|
||||
m_rowCount->setToolTip(tr("Number of rows of the table body"));
|
||||
m_rowCount->setMaximum(1000);
|
||||
m_rowCount->setMinimum(0);
|
||||
|
||||
m_colCount = new QSpinBox(this);
|
||||
m_colCount->setToolTip(tr("Number of columns of the table"));
|
||||
m_colCount->setMaximum(1000);
|
||||
m_colCount->setMinimum(1);
|
||||
|
||||
QRadioButton *noneBtn = new QRadioButton(tr("None"), this);
|
||||
QRadioButton *leftBtn = new QRadioButton(tr("Left"), this);
|
||||
QRadioButton *centerBtn = new QRadioButton(tr("Center"), this);
|
||||
QRadioButton *rightBtn = new QRadioButton(tr("Right"), this);
|
||||
QHBoxLayout *alignLayout = new QHBoxLayout();
|
||||
alignLayout->addWidget(noneBtn);
|
||||
alignLayout->addWidget(leftBtn);
|
||||
alignLayout->addWidget(centerBtn);
|
||||
alignLayout->addWidget(rightBtn);
|
||||
alignLayout->addStretch();
|
||||
|
||||
noneBtn->setChecked(true);
|
||||
|
||||
QButtonGroup *bg = new QButtonGroup(this);
|
||||
bg->addButton(noneBtn, VTable::None);
|
||||
bg->addButton(leftBtn, VTable::Left);
|
||||
bg->addButton(centerBtn, VTable::Center);
|
||||
bg->addButton(rightBtn, VTable::Right);
|
||||
connect(bg, static_cast<void(QButtonGroup::*)(int, bool)>(&QButtonGroup::buttonToggled),
|
||||
this, [this](int p_id, bool p_checked){
|
||||
if (p_checked) {
|
||||
m_alignment = static_cast<VTable::Alignment>(p_id);
|
||||
}
|
||||
});
|
||||
|
||||
QGridLayout *topLayout = new QGridLayout();
|
||||
topLayout->addWidget(new QLabel(tr("Row:")), 0, 0, 1, 1);
|
||||
topLayout->addWidget(m_rowCount, 0, 1, 1, 1);
|
||||
topLayout->addWidget(new QLabel(tr("Column:")), 0, 2, 1, 1);
|
||||
topLayout->addWidget(m_colCount, 0, 3, 1, 1);
|
||||
|
||||
topLayout->addWidget(new QLabel(tr("Alignment:")), 1, 0, 1, 1);
|
||||
topLayout->addLayout(alignLayout, 1, 1, 1, 3);
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("SpecialBtn", true);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
|
||||
setLayout(mainLayout);
|
||||
setWindowTitle(tr("Insert Table"));
|
||||
}
|
||||
|
||||
int VInsertTableDialog::getRowCount() const
|
||||
{
|
||||
return m_rowCount->value();
|
||||
}
|
||||
|
||||
int VInsertTableDialog::getColumnCount() const
|
||||
{
|
||||
return m_colCount->value();
|
||||
}
|
||||
|
||||
VTable::Alignment VInsertTableDialog::getAlignment() const
|
||||
{
|
||||
return m_alignment;
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#ifndef VINSERTTABLEDIALOG_H
|
||||
#define VINSERTTABLEDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "../vtable.h"
|
||||
|
||||
class QDialogButtonBox;
|
||||
class QSpinBox;
|
||||
|
||||
class VInsertTableDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VInsertTableDialog(QWidget *p_parent = nullptr);
|
||||
|
||||
int getRowCount() const;
|
||||
int getColumnCount() const;
|
||||
VTable::Alignment getAlignment() const;
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
QSpinBox *m_rowCount;
|
||||
QSpinBox *m_colCount;
|
||||
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
VTable::Alignment m_alignment;
|
||||
};
|
||||
|
||||
#endif // VINSERTTABLEDIALOG_H
|
@ -1,497 +0,0 @@
|
||||
#include "vkeyboardlayoutmappingdialog.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "vlineedit.h"
|
||||
#include "utils/vkeyboardlayoutmanager.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "utils/viconutils.h"
|
||||
#include "vconfigmanager.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VKeyboardLayoutMappingDialog::VKeyboardLayoutMappingDialog(QWidget *p_parent)
|
||||
: QDialog(p_parent),
|
||||
m_mappingModified(false),
|
||||
m_listenIndex(-1)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
loadAvailableMappings();
|
||||
}
|
||||
|
||||
void VKeyboardLayoutMappingDialog::setupUI()
|
||||
{
|
||||
QString info = tr("Manage keybaord layout mappings to used in shortcuts.");
|
||||
info += "\n";
|
||||
info += tr("Double click an item to set mapping key.");
|
||||
QLabel *infoLabel = new QLabel(info, this);
|
||||
|
||||
// Selector.
|
||||
m_selectorCombo = VUtils::getComboBox(this);
|
||||
connect(m_selectorCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, [this](int p_idx) {
|
||||
loadMappingInfo(m_selectorCombo->itemData(p_idx).toString());
|
||||
});
|
||||
|
||||
// Add.
|
||||
m_addBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/add.svg"), "", this);
|
||||
m_addBtn->setToolTip(tr("New Mapping"));
|
||||
m_addBtn->setProperty("FlatBtn", true);
|
||||
connect(m_addBtn, &QPushButton::clicked,
|
||||
this, &VKeyboardLayoutMappingDialog::newMapping);
|
||||
|
||||
// Delete.
|
||||
m_deleteBtn = new QPushButton(VIconUtils::buttonDangerIcon(":/resources/icons/delete.svg"),
|
||||
"",
|
||||
this);
|
||||
m_deleteBtn->setToolTip(tr("Delete Mapping"));
|
||||
m_deleteBtn->setProperty("FlatBtn", true);
|
||||
connect(m_deleteBtn, &QPushButton::clicked,
|
||||
this, &VKeyboardLayoutMappingDialog::deleteCurrentMapping);
|
||||
|
||||
QHBoxLayout *selectLayout = new QHBoxLayout();
|
||||
selectLayout->addWidget(new QLabel(tr("Keyboard layout mapping:"), this));
|
||||
selectLayout->addWidget(m_selectorCombo);
|
||||
selectLayout->addWidget(m_addBtn);
|
||||
selectLayout->addWidget(m_deleteBtn);
|
||||
selectLayout->addStretch();
|
||||
|
||||
// Name.
|
||||
m_nameEdit = new VLineEdit(this);
|
||||
connect(m_nameEdit, &QLineEdit::textEdited,
|
||||
this, [this](const QString &p_text) {
|
||||
Q_UNUSED(p_text);
|
||||
setModified(true);
|
||||
});
|
||||
|
||||
QHBoxLayout *editLayout = new QHBoxLayout();
|
||||
editLayout->addWidget(new QLabel(tr("Name:"), this));
|
||||
editLayout->addWidget(m_nameEdit);
|
||||
editLayout->addStretch();
|
||||
|
||||
// Tree.
|
||||
m_contentTree = new QTreeWidget(this);
|
||||
m_contentTree->setProperty("ItemBorder", true);
|
||||
m_contentTree->setRootIsDecorated(false);
|
||||
m_contentTree->setColumnCount(2);
|
||||
m_contentTree->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
QStringList headers;
|
||||
headers << tr("Key") << tr("New Key");
|
||||
m_contentTree->setHeaderLabels(headers);
|
||||
|
||||
m_contentTree->installEventFilter(this);
|
||||
|
||||
connect(m_contentTree, &QTreeWidget::itemDoubleClicked,
|
||||
this, [this](QTreeWidgetItem *p_item, int p_column) {
|
||||
Q_UNUSED(p_column);
|
||||
int idx = m_contentTree->indexOfTopLevelItem(p_item);
|
||||
if (m_listenIndex == -1) {
|
||||
// Listen key for this item.
|
||||
setListeningKey(idx);
|
||||
} else if (idx == m_listenIndex) {
|
||||
// Cancel listening key for this item.
|
||||
cancelListeningKey();
|
||||
} else {
|
||||
// Recover previous item.
|
||||
cancelListeningKey();
|
||||
setListeningKey(idx);
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_contentTree, &QTreeWidget::itemClicked,
|
||||
this, [this](QTreeWidgetItem *p_item, int p_column) {
|
||||
Q_UNUSED(p_column);
|
||||
int idx = m_contentTree->indexOfTopLevelItem(p_item);
|
||||
if (idx != m_listenIndex) {
|
||||
cancelListeningKey();
|
||||
}
|
||||
});
|
||||
|
||||
QVBoxLayout *infoLayout = new QVBoxLayout();
|
||||
infoLayout->addLayout(editLayout);
|
||||
infoLayout->addWidget(m_contentTree);
|
||||
|
||||
QGroupBox *box = new QGroupBox(tr("Mapping Information"));
|
||||
box->setLayout(infoLayout);
|
||||
|
||||
// Ok is the default button.
|
||||
QDialogButtonBox *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok
|
||||
| QDialogButtonBox::Apply
|
||||
| QDialogButtonBox::Cancel);
|
||||
connect(btnBox, &QDialogButtonBox::accepted,
|
||||
this, [this]() {
|
||||
if (applyChanges()) {
|
||||
QDialog::accept();
|
||||
}
|
||||
});
|
||||
connect(btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("SpecialBtn", true);
|
||||
|
||||
m_applyBtn = btnBox->button(QDialogButtonBox::Apply);
|
||||
connect(m_applyBtn, &QPushButton::clicked,
|
||||
this, &VKeyboardLayoutMappingDialog::applyChanges);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addWidget(infoLabel);
|
||||
mainLayout->addLayout(selectLayout);
|
||||
mainLayout->addWidget(box);
|
||||
mainLayout->addWidget(btnBox);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle(tr("Keyboard Layout Mappings"));
|
||||
}
|
||||
|
||||
void VKeyboardLayoutMappingDialog::newMapping()
|
||||
{
|
||||
QString name = getNewMappingName();
|
||||
if (!VKeyboardLayoutManager::addLayout(name)) {
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Fail to add mapping <span style=\"%1\">%2</span>.")
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name),
|
||||
tr("Please check the configuration file and try again."),
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
return;
|
||||
}
|
||||
|
||||
loadAvailableMappings();
|
||||
|
||||
setCurrentMapping(name);
|
||||
}
|
||||
|
||||
QString VKeyboardLayoutMappingDialog::getNewMappingName() const
|
||||
{
|
||||
QString name;
|
||||
QString baseName("layout_mapping");
|
||||
int seq = 1;
|
||||
do {
|
||||
name = QString("%1_%2").arg(baseName).arg(QString::number(seq++), 3, '0');
|
||||
} while (m_selectorCombo->findData(name) != -1);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
void VKeyboardLayoutMappingDialog::deleteCurrentMapping()
|
||||
{
|
||||
QString mapping = currentMapping();
|
||||
if (mapping.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Are you sure to delete mapping <span style=\"%1\">%2</span>?")
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(mapping),
|
||||
"",
|
||||
QMessageBox::Ok | QMessageBox::Cancel,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
if (ret != QMessageBox::Ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!VKeyboardLayoutManager::removeLayout(mapping)) {
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Fail to delete mapping <span style=\"%1\">%2</span>.")
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(mapping),
|
||||
tr("Please check the configuration file and try again."),
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
}
|
||||
|
||||
loadAvailableMappings();
|
||||
}
|
||||
|
||||
void VKeyboardLayoutMappingDialog::loadAvailableMappings()
|
||||
{
|
||||
m_selectorCombo->setCurrentIndex(-1);
|
||||
m_selectorCombo->clear();
|
||||
|
||||
QStringList layouts = VKeyboardLayoutManager::availableLayouts();
|
||||
for (auto const & layout : layouts) {
|
||||
m_selectorCombo->addItem(layout, layout);
|
||||
}
|
||||
|
||||
if (m_selectorCombo->count() > 0) {
|
||||
m_selectorCombo->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
static QList<int> keysNeededToMap()
|
||||
{
|
||||
QList<int> keys;
|
||||
|
||||
for (int i = Qt::Key_0; i <= Qt::Key_9; ++i) {
|
||||
keys.append(i);
|
||||
}
|
||||
|
||||
for (int i = Qt::Key_A; i <= Qt::Key_Z; ++i) {
|
||||
keys.append(i);
|
||||
}
|
||||
|
||||
QList<int> addi = g_config->getKeyboardLayoutMappingKeys();
|
||||
for (auto tmp : addi) {
|
||||
if (!keys.contains(tmp)) {
|
||||
keys.append(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
static void recoverTreeItem(QTreeWidgetItem *p_item)
|
||||
{
|
||||
int key = p_item->data(0, Qt::UserRole).toInt();
|
||||
QString text0 = QString("%1 (%2)").arg(VUtils::keyToChar(key, false))
|
||||
.arg(key);
|
||||
p_item->setText(0, text0);
|
||||
|
||||
int newKey = p_item->data(1, Qt::UserRole).toInt();
|
||||
QString text1;
|
||||
if (newKey > 0) {
|
||||
text1 = QString("%1 (%2)").arg(VUtils::keyToChar(newKey, false))
|
||||
.arg(newKey);
|
||||
}
|
||||
|
||||
p_item->setText(1, text1);
|
||||
}
|
||||
|
||||
// @p_newKey, 0 if there is no mapping.
|
||||
static void fillTreeItem(QTreeWidgetItem *p_item, int p_key, int p_newKey)
|
||||
{
|
||||
p_item->setData(0, Qt::UserRole, p_key);
|
||||
p_item->setData(1, Qt::UserRole, p_newKey);
|
||||
recoverTreeItem(p_item);
|
||||
}
|
||||
|
||||
static void setTreeItemMapping(QTreeWidgetItem *p_item, int p_newKey)
|
||||
{
|
||||
p_item->setData(1, Qt::UserRole, p_newKey);
|
||||
}
|
||||
|
||||
static void fillMappingTree(QTreeWidget *p_tree, const QHash<int, int> &p_mappings)
|
||||
{
|
||||
QList<int> keys = keysNeededToMap();
|
||||
|
||||
for (auto key : keys) {
|
||||
int val = 0;
|
||||
auto it = p_mappings.find(key);
|
||||
if (it != p_mappings.end()) {
|
||||
val = it.value();
|
||||
}
|
||||
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(p_tree);
|
||||
fillTreeItem(item, key, val);
|
||||
}
|
||||
}
|
||||
|
||||
static QHash<int, int> retrieveMappingFromTree(QTreeWidget *p_tree)
|
||||
{
|
||||
QHash<int, int> mappings;
|
||||
int cnt = p_tree->topLevelItemCount();
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
QTreeWidgetItem *item = p_tree->topLevelItem(i);
|
||||
int key = item->data(0, Qt::UserRole).toInt();
|
||||
int newKey = item->data(1, Qt::UserRole).toInt();
|
||||
if (newKey > 0) {
|
||||
mappings.insert(key, newKey);
|
||||
}
|
||||
}
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
void VKeyboardLayoutMappingDialog::loadMappingInfo(const QString &p_layout)
|
||||
{
|
||||
setModified(false);
|
||||
|
||||
if (p_layout.isEmpty()) {
|
||||
m_nameEdit->clear();
|
||||
m_contentTree->clear();
|
||||
m_nameEdit->setEnabled(false);
|
||||
m_contentTree->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_nameEdit->setText(p_layout);
|
||||
m_nameEdit->setEnabled(true);
|
||||
|
||||
m_contentTree->clear();
|
||||
if (!p_layout.isEmpty()) {
|
||||
auto mappings = VKeyboardLayoutManager::readLayoutMapping(p_layout);
|
||||
fillMappingTree(m_contentTree, mappings);
|
||||
}
|
||||
m_contentTree->setEnabled(true);
|
||||
}
|
||||
|
||||
void VKeyboardLayoutMappingDialog::updateButtons()
|
||||
{
|
||||
QString mapping = currentMapping();
|
||||
|
||||
m_deleteBtn->setEnabled(!mapping.isEmpty());
|
||||
m_applyBtn->setEnabled(m_mappingModified);
|
||||
}
|
||||
|
||||
QString VKeyboardLayoutMappingDialog::currentMapping() const
|
||||
{
|
||||
return m_selectorCombo->currentData().toString();
|
||||
}
|
||||
|
||||
void VKeyboardLayoutMappingDialog::setCurrentMapping(const QString &p_layout)
|
||||
{
|
||||
return m_selectorCombo->setCurrentIndex(m_selectorCombo->findData(p_layout));
|
||||
}
|
||||
|
||||
bool VKeyboardLayoutMappingDialog::applyChanges()
|
||||
{
|
||||
if (!m_mappingModified) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QString mapping = currentMapping();
|
||||
if (mapping.isEmpty()) {
|
||||
setModified(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the name.
|
||||
QString newName = m_nameEdit->text();
|
||||
if (newName.isEmpty() || newName.toLower() == "global") {
|
||||
// Set back the original name.
|
||||
m_nameEdit->setText(mapping);
|
||||
m_nameEdit->selectAll();
|
||||
m_nameEdit->setFocus();
|
||||
return false;
|
||||
} else if (newName != mapping) {
|
||||
// Rename the mapping.
|
||||
if (!VKeyboardLayoutManager::renameLayout(mapping, newName)) {
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Fail to rename mapping <span style=\"%1\">%2</span>.")
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(mapping),
|
||||
tr("Please check the configuration file and try again."),
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
m_nameEdit->setText(mapping);
|
||||
m_nameEdit->selectAll();
|
||||
m_nameEdit->setFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the combobox.
|
||||
int idx = m_selectorCombo->currentIndex();
|
||||
m_selectorCombo->setItemText(idx, newName);
|
||||
m_selectorCombo->setItemData(idx, newName);
|
||||
|
||||
mapping = newName;
|
||||
}
|
||||
|
||||
// Check the mappings.
|
||||
QHash<int, int> mappings = retrieveMappingFromTree(m_contentTree);
|
||||
if (!VKeyboardLayoutManager::updateLayout(mapping, mappings)) {
|
||||
VUtils::showMessage(QMessageBox::Warning,
|
||||
tr("Warning"),
|
||||
tr("Fail to update mapping <span style=\"%1\">%2</span>.")
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(mapping),
|
||||
tr("Please check the configuration file and try again."),
|
||||
QMessageBox::Ok,
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
return false;
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VKeyboardLayoutMappingDialog::eventFilter(QObject *p_obj, QEvent *p_event)
|
||||
{
|
||||
if (p_obj == m_contentTree) {
|
||||
switch (p_event->type()) {
|
||||
case QEvent::FocusOut:
|
||||
cancelListeningKey();
|
||||
break;
|
||||
|
||||
case QEvent::KeyPress:
|
||||
if (listenKey(static_cast<QKeyEvent *>(p_event))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QDialog::eventFilter(p_obj, p_event);
|
||||
}
|
||||
|
||||
bool VKeyboardLayoutMappingDialog::listenKey(QKeyEvent *p_event)
|
||||
{
|
||||
if (m_listenIndex == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int key = p_event->key();
|
||||
|
||||
if (VUtils::isMetaKey(key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key == Qt::Key_Escape) {
|
||||
cancelListeningKey();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the mapping.
|
||||
QTreeWidgetItem *item = m_contentTree->topLevelItem(m_listenIndex);
|
||||
setTreeItemMapping(item, key);
|
||||
setModified(true);
|
||||
|
||||
// Try next item automatically.
|
||||
int nextIdx = m_listenIndex + 1;
|
||||
cancelListeningKey();
|
||||
|
||||
if (nextIdx < m_contentTree->topLevelItemCount()) {
|
||||
QTreeWidgetItem *item = m_contentTree->topLevelItem(nextIdx);
|
||||
m_contentTree->clearSelection();
|
||||
m_contentTree->setCurrentItem(item);
|
||||
|
||||
setListeningKey(nextIdx);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VKeyboardLayoutMappingDialog::cancelListeningKey()
|
||||
{
|
||||
if (m_listenIndex > -1) {
|
||||
// Recover that item.
|
||||
recoverTreeItem(m_contentTree->topLevelItem(m_listenIndex));
|
||||
|
||||
m_listenIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void VKeyboardLayoutMappingDialog::setListeningKey(int p_idx)
|
||||
{
|
||||
Q_ASSERT(m_listenIndex == -1 && p_idx > -1);
|
||||
m_listenIndex = p_idx;
|
||||
QTreeWidgetItem *item = m_contentTree->topLevelItem(m_listenIndex);
|
||||
item->setText(1, tr("Press key to set mapping"));
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
#ifndef VKEYBOARDLAYOUTMAPPINGDIALOG_H
|
||||
#define VKEYBOARDLAYOUTMAPPINGDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
|
||||
class QDialogButtonBox;
|
||||
class QString;
|
||||
class QTreeWidget;
|
||||
class VLineEdit;
|
||||
class QPushButton;
|
||||
class QComboBox;
|
||||
|
||||
class VKeyboardLayoutMappingDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VKeyboardLayoutMappingDialog(QWidget *p_parent = nullptr);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *p_obj, QEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void newMapping();
|
||||
|
||||
void deleteCurrentMapping();
|
||||
|
||||
// Return true if changes are saved.
|
||||
bool applyChanges();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void loadAvailableMappings();
|
||||
|
||||
void loadMappingInfo(const QString &p_layout);
|
||||
|
||||
void updateButtons();
|
||||
|
||||
QString currentMapping() const;
|
||||
|
||||
void setCurrentMapping(const QString &p_layout);
|
||||
|
||||
QString getNewMappingName() const;
|
||||
|
||||
bool listenKey(QKeyEvent *p_event);
|
||||
|
||||
void cancelListeningKey();
|
||||
|
||||
void setListeningKey(int p_idx);
|
||||
|
||||
void setModified(bool p_modified);
|
||||
|
||||
QComboBox *m_selectorCombo;
|
||||
QPushButton *m_addBtn;
|
||||
QPushButton *m_deleteBtn;
|
||||
VLineEdit *m_nameEdit;
|
||||
QTreeWidget *m_contentTree;
|
||||
QPushButton *m_applyBtn;
|
||||
|
||||
bool m_mappingModified;
|
||||
|
||||
// Index of the item in the tree which is listening key.
|
||||
// -1 for not listening.
|
||||
int m_listenIndex;
|
||||
};
|
||||
|
||||
inline void VKeyboardLayoutMappingDialog::setModified(bool p_modified)
|
||||
{
|
||||
m_mappingModified = p_modified;
|
||||
updateButtons();
|
||||
}
|
||||
#endif // VKEYBOARDLAYOUTMAPPINGDIALOG_H
|
@ -1,113 +0,0 @@
|
||||
#include <QtWidgets>
|
||||
#include "vnewdirdialog.h"
|
||||
#include "vdirectory.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "vmetawordlineedit.h"
|
||||
#include "utils/vutils.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VNewDirDialog::VNewDirDialog(const QString &title,
|
||||
const QString &info,
|
||||
const QString &defaultName,
|
||||
VDirectory *directory,
|
||||
QWidget *parent)
|
||||
: QDialog(parent), title(title), info(info), defaultName(defaultName),
|
||||
m_directory(directory)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
connect(m_nameEdit, &VMetaWordLineEdit::textChanged, this, &VNewDirDialog::handleInputChanged);
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VNewDirDialog::setupUI()
|
||||
{
|
||||
QLabel *infoLabel = NULL;
|
||||
if (!info.isEmpty()) {
|
||||
infoLabel = new QLabel(info);
|
||||
infoLabel->setWordWrap(true);
|
||||
}
|
||||
|
||||
QLabel *nameLabel = new QLabel(tr("Folder &name:"));
|
||||
m_nameEdit = new VMetaWordLineEdit(defaultName);
|
||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
|
||||
m_nameEdit);
|
||||
m_nameEdit->setValidator(validator);
|
||||
m_nameEdit->selectAll();
|
||||
nameLabel->setBuddy(m_nameEdit);
|
||||
|
||||
m_warnLabel = new QLabel();
|
||||
m_warnLabel->setWordWrap(true);
|
||||
m_warnLabel->hide();
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
m_btnBox->button(QDialogButtonBox::Ok)->setProperty("SpecialBtn", true);
|
||||
|
||||
QHBoxLayout *topLayout = new QHBoxLayout();
|
||||
topLayout->addWidget(nameLabel);
|
||||
topLayout->addWidget(m_nameEdit);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
m_nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
if (infoLabel) {
|
||||
mainLayout->addWidget(infoLabel);
|
||||
}
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_warnLabel);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(mainLayout);
|
||||
setWindowTitle(title);
|
||||
}
|
||||
|
||||
void VNewDirDialog::handleInputChanged()
|
||||
{
|
||||
bool showWarnLabel = false;
|
||||
QString name = m_nameEdit->getEvaluatedText();
|
||||
bool nameOk = !name.isEmpty();
|
||||
if (nameOk) {
|
||||
// Check if the name conflicts with existing directory name.
|
||||
// Case-insensitive when creating folder.
|
||||
QString warnText;
|
||||
if (m_directory->findSubDirectory(name, false)) {
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> already exists. "
|
||||
"Please choose another name.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
} else if (!VUtils::checkFileNameLegal(name)) {
|
||||
// Check if evaluated name contains illegal characters.
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> contains illegal characters "
|
||||
"(after magic word evaluation).")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
}
|
||||
|
||||
if (!nameOk) {
|
||||
showWarnLabel = true;
|
||||
m_warnLabel->setText(warnText);
|
||||
}
|
||||
}
|
||||
|
||||
m_warnLabel->setVisible(showWarnLabel);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(nameOk);
|
||||
}
|
||||
|
||||
QString VNewDirDialog::getNameInput() const
|
||||
{
|
||||
return m_nameEdit->getEvaluatedText();
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#ifndef VNEWDIRDIALOG_H
|
||||
#define VNEWDIRDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QLabel;
|
||||
class VMetaWordLineEdit;
|
||||
class QDialogButtonBox;
|
||||
class QString;
|
||||
class VDirectory;
|
||||
|
||||
class VNewDirDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VNewDirDialog(const QString &title,
|
||||
const QString &info,
|
||||
const QString &defaultName,
|
||||
VDirectory *directory,
|
||||
QWidget *parent = 0);
|
||||
|
||||
QString getNameInput() const;
|
||||
|
||||
private slots:
|
||||
void handleInputChanged();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
VMetaWordLineEdit *m_nameEdit;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
QLabel *m_warnLabel;
|
||||
|
||||
QString title;
|
||||
QString info;
|
||||
QString defaultName;
|
||||
|
||||
VDirectory *m_directory;
|
||||
};
|
||||
|
||||
#endif // VNEWDIRDIALOG_H
|
@ -1,285 +0,0 @@
|
||||
#include <QtWidgets>
|
||||
#include "vnewfiledialog.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "vdirectory.h"
|
||||
#include "vmetawordlineedit.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "utils/vmetawordmanager.h"
|
||||
#include "utils/viconutils.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
extern VMetaWordManager *g_mwMgr;
|
||||
|
||||
QString VNewFileDialog::s_lastTemplateFile;
|
||||
|
||||
|
||||
VNewFileDialog::VNewFileDialog(const QString &p_title,
|
||||
const QString &p_info,
|
||||
const QString &p_defaultName,
|
||||
VDirectory *p_directory,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent),
|
||||
m_currentTemplateType(DocType::Unknown),
|
||||
m_directory(p_directory)
|
||||
{
|
||||
setupUI(p_title, p_info, p_defaultName);
|
||||
|
||||
connect(m_nameEdit, &VMetaWordLineEdit::textChanged, this, &VNewFileDialog::handleInputChanged);
|
||||
|
||||
connect(m_templateCB, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &VNewFileDialog::handleCurrentTemplateChanged);
|
||||
|
||||
handleInputChanged();
|
||||
|
||||
tryToSelectLastTemplate();
|
||||
}
|
||||
|
||||
void VNewFileDialog::setupUI(const QString &p_title,
|
||||
const QString &p_info,
|
||||
const QString &p_defaultName)
|
||||
{
|
||||
QLabel *infoLabel = NULL;
|
||||
if (!p_info.isEmpty()) {
|
||||
infoLabel = new QLabel(p_info);
|
||||
}
|
||||
|
||||
// Name.
|
||||
m_nameEdit = new VMetaWordLineEdit(p_defaultName);
|
||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
|
||||
m_nameEdit);
|
||||
m_nameEdit->setValidator(validator);
|
||||
int dotIndex = p_defaultName.lastIndexOf('.');
|
||||
m_nameEdit->setSelection(0, (dotIndex == -1) ? p_defaultName.size() : dotIndex);
|
||||
|
||||
// Template.
|
||||
m_templateCB = VUtils::getComboBox();
|
||||
m_templateCB->setToolTip(tr("Choose a template (magic word supported)"));
|
||||
m_templateCB->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
|
||||
QPushButton *templateBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/manage_template.svg"),
|
||||
"");
|
||||
templateBtn->setToolTip(tr("Manage Templates"));
|
||||
templateBtn->setProperty("FlatBtn", true);
|
||||
connect(templateBtn, &QPushButton::clicked,
|
||||
this, []() {
|
||||
QUrl url = QUrl::fromLocalFile(g_config->getTemplateConfigFolder());
|
||||
QDesktopServices::openUrl(url);
|
||||
});
|
||||
|
||||
QHBoxLayout *tempBtnLayout = new QHBoxLayout();
|
||||
tempBtnLayout->addWidget(m_templateCB);
|
||||
tempBtnLayout->addWidget(templateBtn);
|
||||
tempBtnLayout->addStretch();
|
||||
|
||||
m_templateEdit = new QTextEdit();
|
||||
m_templateEdit->setReadOnly(true);
|
||||
|
||||
QVBoxLayout *templateLayout = new QVBoxLayout();
|
||||
templateLayout->addLayout(tempBtnLayout);
|
||||
templateLayout->addWidget(m_templateEdit);
|
||||
|
||||
m_templateEdit->hide();
|
||||
|
||||
// InsertTitle.
|
||||
m_insertTitleCB = new QCheckBox(tr("Insert note name as title (for Markdown only)"));
|
||||
m_insertTitleCB->setToolTip(tr("Insert note name into the new note as a title"));
|
||||
m_insertTitleCB->setChecked(g_config->getInsertTitleFromNoteName());
|
||||
connect(m_insertTitleCB, &QCheckBox::stateChanged,
|
||||
this, [](int p_state) {
|
||||
g_config->setInsertTitleFromNoteName(p_state == Qt::Checked);
|
||||
});
|
||||
|
||||
QFormLayout *topLayout = new QFormLayout();
|
||||
topLayout->addRow(tr("Note &name:"), m_nameEdit);
|
||||
topLayout->addWidget(m_insertTitleCB);
|
||||
topLayout->addRow(tr("Template:"), templateLayout);
|
||||
|
||||
m_nameEdit->setMinimumWidth(m_insertTitleCB->sizeHint().width());
|
||||
|
||||
m_warnLabel = new QLabel();
|
||||
m_warnLabel->setWordWrap(true);
|
||||
m_warnLabel->hide();
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted,
|
||||
this, [this]() {
|
||||
s_lastTemplateFile = m_templateCB->currentData().toString();
|
||||
QDialog::accept();
|
||||
});
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("SpecialBtn", true);
|
||||
m_templateCB->setMaximumWidth(okBtn->sizeHint().width() * 4);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
if (infoLabel) {
|
||||
mainLayout->addWidget(infoLabel);
|
||||
}
|
||||
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_warnLabel);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle(p_title);
|
||||
}
|
||||
|
||||
void VNewFileDialog::handleInputChanged()
|
||||
{
|
||||
bool showWarnLabel = false;
|
||||
const QString name = m_nameEdit->getEvaluatedText();
|
||||
bool nameOk = !name.isEmpty();
|
||||
if (nameOk) {
|
||||
// Check if the name conflicts with existing note name.
|
||||
// Case-insensitive when creating note.
|
||||
QString warnText;
|
||||
if (m_directory->findFile(name, false)) {
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> already exists. "
|
||||
"Please choose another name.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
} else if (!VUtils::checkFileNameLegal(name)) {
|
||||
// Check if evaluated name contains illegal characters.
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> contains illegal characters "
|
||||
"(after magic word evaluation).")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
}
|
||||
|
||||
if (!nameOk) {
|
||||
showWarnLabel = true;
|
||||
m_warnLabel->setText(warnText);
|
||||
}
|
||||
}
|
||||
|
||||
m_warnLabel->setVisible(showWarnLabel);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(nameOk);
|
||||
|
||||
if (nameOk) {
|
||||
updateTemplates(VUtils::docTypeFromName(name));
|
||||
}
|
||||
}
|
||||
|
||||
QString VNewFileDialog::getNameInput() const
|
||||
{
|
||||
return m_nameEdit->getEvaluatedText();
|
||||
}
|
||||
|
||||
bool VNewFileDialog::getInsertTitleInput() const
|
||||
{
|
||||
return m_insertTitleCB->isEnabled() && m_insertTitleCB->isChecked();
|
||||
}
|
||||
|
||||
void VNewFileDialog::updateTemplates(DocType p_type)
|
||||
{
|
||||
if (m_currentTemplateType == p_type) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentTemplateType = p_type;
|
||||
|
||||
// Clear the combo.
|
||||
m_templateCB->clear();
|
||||
|
||||
// Add None item.
|
||||
m_templateCB->addItem(tr("None"), "None");
|
||||
|
||||
if (m_currentTemplateType == DocType::Unknown) {
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = 1;
|
||||
auto templates = g_config->getNoteTemplates(m_currentTemplateType);
|
||||
for (auto const & tp : templates) {
|
||||
m_templateCB->addItem(tp, tp);
|
||||
m_templateCB->setItemData(idx++, tp, Qt::ToolTipRole);
|
||||
}
|
||||
}
|
||||
|
||||
void VNewFileDialog::handleCurrentTemplateChanged(int p_idx)
|
||||
{
|
||||
if (p_idx == -1) {
|
||||
m_templateEdit->hide();
|
||||
enableInsertTitleCB(false);
|
||||
return;
|
||||
}
|
||||
|
||||
QString file = m_templateCB->itemData(p_idx).toString();
|
||||
if (file == "None") {
|
||||
m_templateEdit->hide();
|
||||
enableInsertTitleCB(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the template file.
|
||||
QString filePath = QDir(g_config->getTemplateConfigFolder()).filePath(file);
|
||||
m_template = VUtils::readFileFromDisk(filePath);
|
||||
DocType type = VUtils::docTypeFromName(file);
|
||||
switch (type) {
|
||||
case DocType::Html:
|
||||
m_templateEdit->setHtml(m_template);
|
||||
break;
|
||||
|
||||
case DocType::Markdown:
|
||||
m_templateEdit->setPlainText(m_template);
|
||||
break;
|
||||
|
||||
default:
|
||||
m_templateEdit->setPlainText(m_template);
|
||||
break;
|
||||
}
|
||||
|
||||
m_templateEdit->show();
|
||||
enableInsertTitleCB(true);
|
||||
}
|
||||
|
||||
void VNewFileDialog::enableInsertTitleCB(bool p_hasTemplate)
|
||||
{
|
||||
m_insertTitleCB->setEnabled(!p_hasTemplate
|
||||
&& VUtils::docTypeFromName(m_nameEdit->getEvaluatedText())
|
||||
== DocType::Markdown);
|
||||
}
|
||||
|
||||
bool VNewFileDialog::isTemplateUsed() const
|
||||
{
|
||||
QString file = m_templateCB->currentData().toString();
|
||||
return !(file.isEmpty() || file == "None");
|
||||
}
|
||||
|
||||
QString VNewFileDialog::getTemplate() const
|
||||
{
|
||||
QString name = m_nameEdit->getEvaluatedText();
|
||||
QHash<QString, QString> overriddenTable;
|
||||
overriddenTable.insert("note", name);
|
||||
overriddenTable.insert("no", QFileInfo(name).completeBaseName());
|
||||
return g_mwMgr->evaluate(m_template, overriddenTable);
|
||||
}
|
||||
|
||||
void VNewFileDialog::tryToSelectLastTemplate()
|
||||
{
|
||||
Q_ASSERT(m_templateCB->count() > 0
|
||||
&& m_templateCB->itemData(0).toString() == "None");
|
||||
if (s_lastTemplateFile.isEmpty() || s_lastTemplateFile == "None") {
|
||||
m_templateCB->setCurrentIndex(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = m_templateCB->findData(s_lastTemplateFile);
|
||||
if (idx != -1) {
|
||||
m_templateCB->setCurrentIndex(idx);
|
||||
} else {
|
||||
s_lastTemplateFile.clear();
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
#ifndef VNEWFILEDIALOG_H
|
||||
#define VNEWFILEDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "vconstants.h"
|
||||
|
||||
class QLabel;
|
||||
class VMetaWordLineEdit;
|
||||
class QDialogButtonBox;
|
||||
class QCheckBox;
|
||||
class VDirectory;
|
||||
class QComboBox;
|
||||
class QTextEdit;
|
||||
|
||||
class VNewFileDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VNewFileDialog(const QString &p_title,
|
||||
const QString &p_info,
|
||||
const QString &p_defaultName,
|
||||
VDirectory *p_directory,
|
||||
QWidget *p_parent = 0);
|
||||
|
||||
QString getNameInput() const;
|
||||
|
||||
bool getInsertTitleInput() const;
|
||||
|
||||
// Whether user choose a note template.
|
||||
bool isTemplateUsed() const;
|
||||
|
||||
// Get the template content (after magic words evaluated) user chose.
|
||||
QString getTemplate() const;
|
||||
|
||||
private slots:
|
||||
void handleInputChanged();
|
||||
|
||||
void handleCurrentTemplateChanged(int p_idx);
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title,
|
||||
const QString &p_info,
|
||||
const QString &p_defaultName);
|
||||
|
||||
// Update the templates according to @p_type.
|
||||
void updateTemplates(DocType p_type);
|
||||
|
||||
void enableInsertTitleCB(bool p_hasTemplate);
|
||||
|
||||
void tryToSelectLastTemplate();
|
||||
|
||||
VMetaWordLineEdit *m_nameEdit;
|
||||
|
||||
QComboBox *m_templateCB;
|
||||
|
||||
// Used for template preview.
|
||||
QTextEdit *m_templateEdit;
|
||||
|
||||
QCheckBox *m_insertTitleCB;
|
||||
|
||||
QPushButton *okBtn;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
QLabel *m_warnLabel;
|
||||
|
||||
// Template content.
|
||||
QString m_template;
|
||||
|
||||
// Doc type of current template.
|
||||
DocType m_currentTemplateType;
|
||||
|
||||
// Last chosen template file.
|
||||
static QString s_lastTemplateFile;
|
||||
|
||||
VDirectory *m_directory;
|
||||
};
|
||||
|
||||
#endif // VNEWFILEDIALOG_H
|
@ -1,405 +0,0 @@
|
||||
#include <QtWidgets>
|
||||
#include <QDir>
|
||||
#include "vnewnotebookdialog.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "vnotebook.h"
|
||||
#include "vmetawordlineedit.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VNewNotebookDialog::VNewNotebookDialog(const QString &title, const QString &info,
|
||||
const QString &defaultName, const QString &defaultPath,
|
||||
const QVector<VNotebook *> &p_notebooks,
|
||||
QWidget *parent)
|
||||
: QDialog(parent),
|
||||
defaultName(defaultName), defaultPath(defaultPath),
|
||||
m_importNotebook(false), m_importExternalProject(false), m_manualPath(false), m_manualName(false),
|
||||
m_notebooks(p_notebooks)
|
||||
{
|
||||
setupUI(title, info);
|
||||
|
||||
connect(m_nameEdit, &VMetaWordLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged);
|
||||
connect(m_pathEdit, &VLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged);
|
||||
connect(browseBtn, &QPushButton::clicked, this, &VNewNotebookDialog::handleBrowseBtnClicked);
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VNewNotebookDialog::setupUI(const QString &p_title, const QString &p_info)
|
||||
{
|
||||
QLabel *infoLabel = NULL;
|
||||
if (!p_info.isEmpty()) {
|
||||
infoLabel = new QLabel(p_info);
|
||||
infoLabel->setWordWrap(true);
|
||||
}
|
||||
|
||||
QLabel *nameLabel = new QLabel(tr("Notebook &name:"));
|
||||
m_nameEdit = new VMetaWordLineEdit(defaultName);
|
||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
|
||||
m_nameEdit);
|
||||
m_nameEdit->setValidator(validator);
|
||||
nameLabel->setBuddy(m_nameEdit);
|
||||
|
||||
QLabel *pathLabel = new QLabel(tr("Notebook &root folder:"));
|
||||
m_pathEdit = new VLineEdit(defaultPath);
|
||||
pathLabel->setBuddy(m_pathEdit);
|
||||
browseBtn = new QPushButton(tr("&Browse"));
|
||||
|
||||
m_relativePathCB = new QCheckBox(tr("Use relative path"));
|
||||
m_relativePathCB->setToolTip(tr("Use relative path (to VNote's executable) in configuration file"));
|
||||
connect(m_relativePathCB, &QCheckBox::stateChanged,
|
||||
this, &VNewNotebookDialog::handleInputChanged);
|
||||
|
||||
QLabel *imageFolderLabel = new QLabel(tr("&Image folder:"));
|
||||
m_imageFolderEdit = new VLineEdit();
|
||||
imageFolderLabel->setBuddy(m_imageFolderEdit);
|
||||
m_imageFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
|
||||
.arg(g_config->getImageFolder()));
|
||||
m_imageFolderEdit->setToolTip(tr("Set the name of the folder to hold images of all the notes in this notebook "
|
||||
"(empty to use global configuration)"));
|
||||
imageFolderLabel->setToolTip(m_imageFolderEdit->toolTip());
|
||||
validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit);
|
||||
m_imageFolderEdit->setValidator(validator);
|
||||
|
||||
QLabel *attachmentFolderLabel = new QLabel(tr("&Attachment folder:"));
|
||||
m_attachmentFolderEdit = new VLineEdit();
|
||||
attachmentFolderLabel->setBuddy(m_attachmentFolderEdit);
|
||||
m_attachmentFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
|
||||
.arg(g_config->getAttachmentFolder()));
|
||||
m_attachmentFolderEdit->setToolTip(tr("Set the name of the folder to hold attachments of all the notes in this notebook "
|
||||
"(empty to use global configuration, read-only once created)"));
|
||||
attachmentFolderLabel->setToolTip(m_attachmentFolderEdit->toolTip());
|
||||
validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_attachmentFolderEdit);
|
||||
m_attachmentFolderEdit->setValidator(validator);
|
||||
|
||||
QGridLayout *topLayout = new QGridLayout();
|
||||
topLayout->addWidget(nameLabel, 0, 0);
|
||||
topLayout->addWidget(m_nameEdit, 0, 1, 1, 2);
|
||||
topLayout->addWidget(pathLabel, 1, 0);
|
||||
topLayout->addWidget(m_pathEdit, 1, 1);
|
||||
topLayout->addWidget(browseBtn, 1, 2);
|
||||
topLayout->addWidget(m_relativePathCB, 2, 1);
|
||||
topLayout->addWidget(imageFolderLabel, 3, 0);
|
||||
topLayout->addWidget(m_imageFolderEdit, 3, 1);
|
||||
topLayout->addWidget(attachmentFolderLabel, 4, 0);
|
||||
topLayout->addWidget(m_attachmentFolderEdit, 4, 1);
|
||||
|
||||
// Warning label.
|
||||
m_warnLabel = new QLabel();
|
||||
m_warnLabel->setWordWrap(true);
|
||||
m_warnLabel->hide();
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("SpecialBtn", true);
|
||||
m_pathEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
if (infoLabel) {
|
||||
mainLayout->addWidget(infoLabel);
|
||||
}
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_warnLabel);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
|
||||
// Will set the parent of above widgets properly.
|
||||
setLayout(mainLayout);
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setWindowTitle(p_title);
|
||||
}
|
||||
|
||||
QString VNewNotebookDialog::getNameInput() const
|
||||
{
|
||||
return m_nameEdit->getEvaluatedText();
|
||||
}
|
||||
|
||||
QString VNewNotebookDialog::getPathInput() const
|
||||
{
|
||||
// absoluteFilePath() to convert the drive to upper case.
|
||||
// cleanPath() to remove duplicate separator, '.', and '..'.
|
||||
QString ret;
|
||||
if (isUseRelativePath()) {
|
||||
// Use relative path in config file.
|
||||
QDir appDir(QCoreApplication::applicationDirPath());
|
||||
ret = QDir::cleanPath(appDir.relativeFilePath(m_pathEdit->text()));
|
||||
} else {
|
||||
ret = QDir::cleanPath(QFileInfo(m_pathEdit->text()).absoluteFilePath());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString VNewNotebookDialog::getImageFolder() const
|
||||
{
|
||||
if (m_imageFolderEdit->isEnabled()) {
|
||||
return m_imageFolderEdit->text();
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
QString VNewNotebookDialog::getAttachmentFolder() const
|
||||
{
|
||||
if (m_attachmentFolderEdit->isEnabled()) {
|
||||
return m_attachmentFolderEdit->text();
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
void VNewNotebookDialog::handleBrowseBtnClicked()
|
||||
{
|
||||
static QString defaultPath;
|
||||
|
||||
if (defaultPath.isEmpty()) {
|
||||
defaultPath = g_config->getVnoteNotebookFolderPath();
|
||||
if (!QFileInfo::exists(defaultPath)) {
|
||||
defaultPath = g_config->getDocumentPathOrHomePath();
|
||||
}
|
||||
}
|
||||
|
||||
QString dirPath = QFileDialog::getExistingDirectory(this,
|
||||
tr("Select Root Folder Of The Notebook"),
|
||||
defaultPath,
|
||||
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
||||
|
||||
if (!dirPath.isEmpty()) {
|
||||
m_manualPath = true;
|
||||
if (m_pathEdit->text() == dirPath) {
|
||||
handleInputChanged();
|
||||
} else {
|
||||
m_pathEdit->setText(dirPath);
|
||||
}
|
||||
|
||||
defaultPath = VUtils::basePathFromPath(dirPath);
|
||||
}
|
||||
}
|
||||
|
||||
bool VNewNotebookDialog::isImportExistingNotebook() const
|
||||
{
|
||||
return m_importNotebook;
|
||||
}
|
||||
|
||||
bool VNewNotebookDialog::isImportExternalProject() const
|
||||
{
|
||||
return m_importExternalProject;
|
||||
}
|
||||
|
||||
void VNewNotebookDialog::showEvent(QShowEvent *event)
|
||||
{
|
||||
m_nameEdit->setFocus();
|
||||
QDialog::showEvent(event);
|
||||
}
|
||||
|
||||
void VNewNotebookDialog::handleInputChanged()
|
||||
{
|
||||
bool pathOk = false;
|
||||
bool configExist = false;
|
||||
bool showWarnLabel = false;
|
||||
|
||||
// User has input some texts.
|
||||
if (m_pathEdit->isModified()) {
|
||||
m_manualPath = true;
|
||||
}
|
||||
|
||||
if (m_nameEdit->isModified()) {
|
||||
m_manualName = true;
|
||||
}
|
||||
|
||||
if (autoComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = m_pathEdit->text();
|
||||
if (!path.isEmpty()) {
|
||||
if (!QDir::isAbsolutePath(path)) {
|
||||
showWarnLabel = true;
|
||||
QString tmp = tr("<span style=\"%1\">WARNING</span>: Please specify absolute path.")
|
||||
.arg(g_config->c_warningTextStyle);
|
||||
m_warnLabel->setText(tmp);
|
||||
} else if (QFileInfo::exists(path)) {
|
||||
QDir dir(path);
|
||||
QStringList files = dir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden);
|
||||
if (!files.isEmpty()) {
|
||||
// Folder is not empty.
|
||||
configExist = VConfigManager::directoryConfigExist(path);
|
||||
if (configExist) {
|
||||
QString infoText = tr("<span style=\"%1\">INFO</span>: The folder chosen seems to be a root "
|
||||
"folder of a notebook created by VNote before. "
|
||||
"VNote will try to import it by reading the configuration file.")
|
||||
.arg("font-weight:bold;");
|
||||
m_warnLabel->setText(infoText);
|
||||
} else {
|
||||
QString warnText = tr("<span style=\"%1\">WARNING</span>: The folder chosen is NOT empty! "
|
||||
"It is highly recommended to use an EMPTY and EXCLUSIVE folder for a new notebook. "
|
||||
"If continue, VNote will try to create a notebook based on existing "
|
||||
"folders and files recursively.")
|
||||
.arg(g_config->c_warningTextStyle);
|
||||
m_warnLabel->setText(warnText);
|
||||
|
||||
m_importExternalProject = true;
|
||||
}
|
||||
|
||||
showWarnLabel = true;
|
||||
}
|
||||
|
||||
pathOk = true;
|
||||
} else {
|
||||
pathOk = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to validate if this is a legal path on the OS.
|
||||
if (pathOk) {
|
||||
pathOk = VUtils::checkPathLegal(path);
|
||||
if (!pathOk) {
|
||||
showWarnLabel = true;
|
||||
QString tmp = tr("<span style=\"%1\">WARNING</span>: The path seems to be illegal. "
|
||||
"Please choose another one.")
|
||||
.arg(g_config->c_warningTextStyle);
|
||||
m_warnLabel->setText(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (pathOk) {
|
||||
// Check if this path has been in VNote.
|
||||
int idx = -1;
|
||||
for (idx = 0; idx < m_notebooks.size(); ++idx) {
|
||||
if (VUtils::equalPath(m_notebooks[idx]->getPath(), path)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx < m_notebooks.size()) {
|
||||
pathOk = false;
|
||||
showWarnLabel = true;
|
||||
QString existText = tr("<span style=\"%1\">WARNING</span>: The folder chosen has already been a root folder "
|
||||
"of existing notebook <span style=\"%2\">%3</span> in VNote.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(m_notebooks[idx]->getName());
|
||||
m_warnLabel->setText(existText);
|
||||
}
|
||||
}
|
||||
|
||||
if (pathOk && isUseRelativePath()) {
|
||||
if (!VUtils::inSameDrive(QCoreApplication::applicationDirPath(), path)) {
|
||||
pathOk = false;
|
||||
showWarnLabel = true;
|
||||
QString existText = tr("<span style=\"%1\">WARNING</span>: Please choose a folder in the same drive as "
|
||||
"<span style=\"%2\">%3</span> when relative path is enabled.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(QCoreApplication::applicationDirPath());
|
||||
m_warnLabel->setText(existText);
|
||||
}
|
||||
}
|
||||
|
||||
QString name = m_nameEdit->getEvaluatedText();
|
||||
bool nameOk = !name.isEmpty();
|
||||
if (pathOk && nameOk) {
|
||||
// Check if the name conflicts with existing notebook name.
|
||||
// Case-insensitive.
|
||||
int idx = -1;
|
||||
for (idx = 0; idx < m_notebooks.size(); ++idx) {
|
||||
if (m_notebooks[idx]->getName().toLower() == name.toLower()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QString warnText;
|
||||
if (idx < m_notebooks.size()) {
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> already exists. "
|
||||
"Please choose another name.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
} else if (!VUtils::checkFileNameLegal(name)) {
|
||||
// Check if evaluated name contains illegal characters.
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> contains illegal characters "
|
||||
"(after magic word evaluation).")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
}
|
||||
|
||||
if (!nameOk) {
|
||||
showWarnLabel = true;
|
||||
m_warnLabel->setText(warnText);
|
||||
}
|
||||
}
|
||||
|
||||
m_warnLabel->setVisible(showWarnLabel);
|
||||
m_importNotebook = configExist;
|
||||
m_imageFolderEdit->setEnabled(!m_importNotebook);
|
||||
m_attachmentFolderEdit->setEnabled(!m_importNotebook);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(nameOk && pathOk);
|
||||
}
|
||||
|
||||
bool VNewNotebookDialog::autoComplete()
|
||||
{
|
||||
QString nameText = m_nameEdit->getEvaluatedText();
|
||||
|
||||
if (m_manualPath) {
|
||||
if (m_manualName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the name according to user-chosen path.
|
||||
QString pathText = m_pathEdit->text();
|
||||
if (!pathText.isEmpty()) {
|
||||
QString autoName = VUtils::directoryNameFromPath(pathText);
|
||||
if (autoName != nameText) {
|
||||
m_nameEdit->setText(autoName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString vnoteFolder = g_config->getVnoteNotebookFolderPath();
|
||||
QString pathText = m_pathEdit->text();
|
||||
if (!pathText.isEmpty()
|
||||
&& !VUtils::equalPath(vnoteFolder, VUtils::basePathFromPath(pathText))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
if (nameText.isEmpty()) {
|
||||
if (m_manualName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get a folder name under vnoteFolder and set it as the name of the notebook.
|
||||
QString name = "vnotebook";
|
||||
name = VUtils::getDirNameWithSequence(vnoteFolder, name);
|
||||
m_nameEdit->setText(name);
|
||||
ret = true;
|
||||
} else {
|
||||
// Use the name as the folder name under vnoteFolder.
|
||||
QString autoPath = QDir::cleanPath(QDir(vnoteFolder).filePath(nameText));
|
||||
if (autoPath != pathText) {
|
||||
m_pathEdit->setText(autoPath);
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VNewNotebookDialog::isUseRelativePath() const
|
||||
{
|
||||
return m_relativePathCB->isChecked();
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
#ifndef VNEWNOTEBOOKDIALOG_H
|
||||
#define VNEWNOTEBOOKDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVector>
|
||||
|
||||
class QLabel;
|
||||
class VLineEdit;
|
||||
class VMetaWordLineEdit;
|
||||
class QPushButton;
|
||||
class QDialogButtonBox;
|
||||
class VNotebook;
|
||||
class QCheckBox;
|
||||
|
||||
class VNewNotebookDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VNewNotebookDialog(const QString &title, const QString &info, const QString &defaultName,
|
||||
const QString &defaultPath, const QVector<VNotebook *> &p_notebooks,
|
||||
QWidget *parent = 0);
|
||||
|
||||
QString getNameInput() const;
|
||||
|
||||
QString getPathInput() const;
|
||||
|
||||
// Whether import existing notebook by reading the config file.
|
||||
bool isImportExistingNotebook() const;
|
||||
|
||||
// Whether import external project
|
||||
bool isImportExternalProject() const;
|
||||
|
||||
// Get the custom image folder for this notebook.
|
||||
// Empty string indicates using global config.
|
||||
QString getImageFolder() const;
|
||||
|
||||
// Get the custom attachment folder for this notebook.
|
||||
// Empty string indicates using global config.
|
||||
QString getAttachmentFolder() const;
|
||||
|
||||
private slots:
|
||||
void handleBrowseBtnClicked();
|
||||
|
||||
// Handle the change of the name and path input.
|
||||
void handleInputChanged();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title, const QString &p_info);
|
||||
|
||||
// Should be called before enableOkButton() when path changed.
|
||||
void checkRootFolder(const QString &p_path);
|
||||
|
||||
// Try to figure out name and path.
|
||||
// Returns true if name or path is modified.
|
||||
bool autoComplete();
|
||||
|
||||
// Whether relative path will be used in config file.
|
||||
bool isUseRelativePath() const;
|
||||
|
||||
VMetaWordLineEdit *m_nameEdit;
|
||||
VLineEdit *m_pathEdit;
|
||||
QPushButton *browseBtn;
|
||||
QCheckBox *m_relativePathCB;
|
||||
QLabel *m_warnLabel;
|
||||
VLineEdit *m_imageFolderEdit;
|
||||
VLineEdit *m_attachmentFolderEdit;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
QString defaultName;
|
||||
QString defaultPath;
|
||||
|
||||
// Whether import existing notebook config file.
|
||||
bool m_importNotebook;
|
||||
|
||||
// Whether import external project
|
||||
bool m_importExternalProject;
|
||||
|
||||
// True if user has change the content of the path edit.
|
||||
bool m_manualPath;
|
||||
|
||||
// True if user has change the content of the name edit.
|
||||
bool m_manualName;
|
||||
|
||||
// All existing notebooks in VNote.
|
||||
const QVector<VNotebook *> &m_notebooks;
|
||||
};
|
||||
|
||||
#endif // VNEWNOTEBOOKDIALOG_H
|
@ -1,167 +0,0 @@
|
||||
#include <QtWidgets>
|
||||
#include "vnotebookinfodialog.h"
|
||||
#include "vnotebook.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "vmetawordlineedit.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VNotebookInfoDialog::VNotebookInfoDialog(const QString &p_title,
|
||||
const QString &p_info,
|
||||
const VNotebook *p_notebook,
|
||||
const QVector<VNotebook *> &p_notebooks,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent), m_notebook(p_notebook),
|
||||
m_notebooks(p_notebooks)
|
||||
{
|
||||
setupUI(p_title, p_info);
|
||||
|
||||
connect(m_nameEdit, &VMetaWordLineEdit::textChanged,
|
||||
this, &VNotebookInfoDialog::handleInputChanged);
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VNotebookInfoDialog::setupUI(const QString &p_title, const QString &p_info)
|
||||
{
|
||||
QLabel *infoLabel = NULL;
|
||||
if (!p_info.isEmpty()) {
|
||||
infoLabel = new QLabel(p_info);
|
||||
}
|
||||
|
||||
m_nameEdit = new VMetaWordLineEdit(m_notebook->getName());
|
||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
|
||||
m_nameEdit);
|
||||
m_nameEdit->setValidator(validator);
|
||||
m_nameEdit->selectAll();
|
||||
|
||||
m_pathEdit = new VLineEdit(m_notebook->getPath());
|
||||
m_pathEdit->setReadOnly(true);
|
||||
|
||||
// Image folder.
|
||||
m_imageFolderEdit = new VLineEdit(m_notebook->getImageFolderConfig());
|
||||
m_imageFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
|
||||
.arg(g_config->getImageFolder()));
|
||||
m_imageFolderEdit->setToolTip(tr("Set the name of the folder to hold images of all the notes in this notebook "
|
||||
"(empty to use global configuration)"));
|
||||
validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit);
|
||||
m_imageFolderEdit->setValidator(validator);
|
||||
|
||||
// Attachment folder.
|
||||
Q_ASSERT(!m_notebook->getAttachmentFolder().isEmpty());
|
||||
m_attachmentFolderEdit = new VLineEdit(m_notebook->getAttachmentFolder());
|
||||
m_attachmentFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
|
||||
.arg(g_config->getAttachmentFolder()));
|
||||
m_attachmentFolderEdit->setToolTip(tr("The folder to hold attachments of all the notes in this notebook"));
|
||||
m_attachmentFolderEdit->setReadOnly(true);
|
||||
|
||||
// Recycle bin folder.
|
||||
VLineEdit *recycleBinFolderEdit = new VLineEdit(m_notebook->getRecycleBinFolder());
|
||||
recycleBinFolderEdit->setReadOnly(true);
|
||||
recycleBinFolderEdit->setToolTip(tr("The folder to hold deleted files from within VNote of all the notes in this notebook"));
|
||||
|
||||
// Created time.
|
||||
QString createdTimeStr = VUtils::displayDateTime(const_cast<VNotebook *>(m_notebook)->getCreatedTimeUtc().toLocalTime());
|
||||
QLabel *createdTimeLabel = new QLabel(createdTimeStr);
|
||||
|
||||
QFormLayout *topLayout = new QFormLayout();
|
||||
topLayout->addRow(tr("Notebook &name:"), m_nameEdit);
|
||||
topLayout->addRow(tr("Notebook &root folder:"), m_pathEdit);
|
||||
topLayout->addRow(tr("&Image folder:"), m_imageFolderEdit);
|
||||
topLayout->addRow(tr("Attachment folder:"), m_attachmentFolderEdit);
|
||||
topLayout->addRow(tr("Recycle bin folder:"), recycleBinFolderEdit);
|
||||
topLayout->addRow(tr("Created time:"), createdTimeLabel);
|
||||
|
||||
// Warning label.
|
||||
m_warnLabel = new QLabel();
|
||||
m_warnLabel->setWordWrap(true);
|
||||
m_warnLabel->hide();
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("SpecialBtn", true);
|
||||
m_pathEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
if (infoLabel) {
|
||||
mainLayout->addWidget(infoLabel);
|
||||
}
|
||||
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_warnLabel);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
|
||||
setLayout(mainLayout);
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
setWindowTitle(p_title);
|
||||
}
|
||||
|
||||
void VNotebookInfoDialog::handleInputChanged()
|
||||
{
|
||||
QString name = m_nameEdit->getEvaluatedText();
|
||||
bool nameOk = !name.isEmpty();
|
||||
bool showWarnLabel = false;
|
||||
|
||||
if (nameOk && name != m_notebook->getName()) {
|
||||
// Check if the name conflicts with existing notebook name.
|
||||
// Case-insensitive.
|
||||
int idx = -1;
|
||||
for (idx = 0; idx < m_notebooks.size(); ++idx) {
|
||||
if (m_notebooks[idx]->getName().toLower() == name.toLower()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QString warnText;
|
||||
if (idx < m_notebooks.size() && m_notebooks[idx] != m_notebook) {
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> already exists. "
|
||||
"Please choose another name.")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
} else if (!VUtils::checkFileNameLegal(name)) {
|
||||
// Check if evaluated name contains illegal characters.
|
||||
nameOk = false;
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> contains illegal characters "
|
||||
"(after magic word evaluation).")
|
||||
.arg(g_config->c_warningTextStyle)
|
||||
.arg(g_config->c_dataTextStyle)
|
||||
.arg(name);
|
||||
}
|
||||
|
||||
if (!nameOk) {
|
||||
showWarnLabel = true;
|
||||
m_warnLabel->setText(warnText);
|
||||
}
|
||||
}
|
||||
|
||||
m_warnLabel->setVisible(showWarnLabel);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(nameOk);
|
||||
}
|
||||
|
||||
QString VNotebookInfoDialog::getName() const
|
||||
{
|
||||
return m_nameEdit->getEvaluatedText();
|
||||
}
|
||||
|
||||
QString VNotebookInfoDialog::getImageFolder() const
|
||||
{
|
||||
return m_imageFolderEdit->text();
|
||||
}
|
||||
|
||||
void VNotebookInfoDialog::showEvent(QShowEvent *p_event)
|
||||
{
|
||||
m_nameEdit->setFocus();
|
||||
QDialog::showEvent(p_event);
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
#ifndef VNOTEBOOKINFODIALOG_H
|
||||
#define VNOTEBOOKINFODIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVector>
|
||||
|
||||
class QLabel;
|
||||
class VLineEdit;
|
||||
class VMetaWordLineEdit;
|
||||
class QDialogButtonBox;
|
||||
class VNotebook;
|
||||
|
||||
class VNotebookInfoDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VNotebookInfoDialog(const QString &p_title,
|
||||
const QString &p_info,
|
||||
const VNotebook *p_notebook,
|
||||
const QVector<VNotebook *> &p_notebooks,
|
||||
QWidget *p_parent = 0);
|
||||
|
||||
QString getName() const;
|
||||
|
||||
// Get the custom image folder for this notebook.
|
||||
// Empty string indicates using global config.
|
||||
QString getImageFolder() const;
|
||||
|
||||
private slots:
|
||||
// Handle the change of the name and path input.
|
||||
void handleInputChanged();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title, const QString &p_info);
|
||||
|
||||
const VNotebook *m_notebook;
|
||||
|
||||
VMetaWordLineEdit *m_nameEdit;
|
||||
VLineEdit *m_pathEdit;
|
||||
VLineEdit *m_imageFolderEdit;
|
||||
// Read-only.
|
||||
VLineEdit *m_attachmentFolderEdit;
|
||||
QLabel *m_warnLabel;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
const QVector<VNotebook *> &m_notebooks;
|
||||
};
|
||||
|
||||
#endif // VNOTEBOOKINFODIALOG_H
|
@ -1,74 +0,0 @@
|
||||
#include "vorphanfileinfodialog.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include "vorphanfile.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "vlineedit.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VOrphanFileInfoDialog::VOrphanFileInfoDialog(const VOrphanFile *p_file, QWidget *p_parent)
|
||||
: QDialog(p_parent), m_file(p_file)
|
||||
{
|
||||
setupUI();
|
||||
|
||||
connect(m_imageFolderEdit, &VLineEdit::textChanged,
|
||||
this, &VOrphanFileInfoDialog::handleInputChanged);
|
||||
|
||||
handleInputChanged();
|
||||
}
|
||||
|
||||
void VOrphanFileInfoDialog::setupUI()
|
||||
{
|
||||
QFormLayout *topLayout = new QFormLayout();
|
||||
|
||||
QLabel *fileLabel = new QLabel(m_file->fetchPath());
|
||||
fileLabel->setTextInteractionFlags(fileLabel->textInteractionFlags() | Qt::TextSelectableByMouse);
|
||||
fileLabel->setWordWrap(true);
|
||||
topLayout->addRow(tr("File:"), fileLabel);
|
||||
|
||||
m_imageFolderEdit = new VLineEdit(m_file->getImageFolder());
|
||||
m_imageFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
|
||||
.arg(g_config->getImageFolderExt()));
|
||||
QString imgFolderTip = tr("Set the path of the image folder to store images "
|
||||
"of this file.\nIf absolute path is used, "
|
||||
"VNote will not manage those images."
|
||||
"(empty to use global configuration)");
|
||||
m_imageFolderEdit->setToolTip(imgFolderTip);
|
||||
topLayout->addRow(tr("&Image folder:"), m_imageFolderEdit);
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setProperty("SpecialBtn", true);
|
||||
m_imageFolderEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
|
||||
setLayout(mainLayout);
|
||||
setWindowTitle(tr("External File Information"));
|
||||
}
|
||||
|
||||
QString VOrphanFileInfoDialog::getImageFolder() const
|
||||
{
|
||||
return QDir::fromNativeSeparators(m_imageFolderEdit->text());
|
||||
}
|
||||
|
||||
void VOrphanFileInfoDialog::handleInputChanged()
|
||||
{
|
||||
bool ok = false;
|
||||
QString imgFolder = m_imageFolderEdit->text();
|
||||
if (imgFolder.isEmpty() || VUtils::checkPathLegal(imgFolder)) {
|
||||
ok = true;
|
||||
}
|
||||
|
||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
okBtn->setEnabled(ok);
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#ifndef VORPHANFILEINFODIALOG_H
|
||||
#define VORPHANFILEINFODIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class VOrphanFile;
|
||||
class QDialogButtonBox;
|
||||
class VLineEdit;
|
||||
|
||||
class VOrphanFileInfoDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
VOrphanFileInfoDialog(const VOrphanFile *p_file, QWidget *p_parent = 0);
|
||||
|
||||
// Get the custom image folder for this external file.
|
||||
// Empty string indicates using global config.
|
||||
QString getImageFolder() const;
|
||||
|
||||
private slots:
|
||||
// Handle the change of the image folder input.
|
||||
void handleInputChanged();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
const VOrphanFile *m_file;
|
||||
|
||||
QDialogButtonBox *m_btnBox;
|
||||
VLineEdit *m_imageFolderEdit;
|
||||
};
|
||||
|
||||
#endif // VORPHANFILEINFODIALOG_H
|
@ -1,110 +0,0 @@
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "vselectdialog.h"
|
||||
#include "utils/vimnavigationforwidget.h"
|
||||
|
||||
#define CANCEL_ID -1
|
||||
|
||||
VSelectDialog::VSelectDialog(const QString &p_title, QWidget *p_parent)
|
||||
: QDialog(p_parent), m_choice(-1)
|
||||
{
|
||||
setupUI(p_title);
|
||||
}
|
||||
|
||||
void VSelectDialog::setupUI(const QString &p_title)
|
||||
{
|
||||
m_list = new QListWidget();
|
||||
m_list->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
m_list->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_list->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
connect(m_list, &QListWidget::itemActivated,
|
||||
this, &VSelectDialog::selectionChosen);
|
||||
|
||||
// Add cancel item.
|
||||
QListWidgetItem *cancelItem = new QListWidgetItem(tr("Cancel"));
|
||||
cancelItem->setData(Qt::UserRole, CANCEL_ID);
|
||||
|
||||
m_list->addItem(cancelItem);
|
||||
m_list->setCurrentRow(0);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
layout->addWidget(m_list);
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
setLayout(layout);
|
||||
setWindowTitle(p_title);
|
||||
}
|
||||
|
||||
void VSelectDialog::addSelection(const QString &p_selectStr, int p_selectID)
|
||||
{
|
||||
Q_ASSERT(p_selectID >= 0);
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem(p_selectStr);
|
||||
item->setData(Qt::UserRole, p_selectID);
|
||||
m_list->insertItem(m_list->count() - 1, item);
|
||||
|
||||
m_list->setCurrentRow(0);
|
||||
}
|
||||
|
||||
void VSelectDialog::selectionChosen(QListWidgetItem *p_item)
|
||||
{
|
||||
m_choice = p_item->data(Qt::UserRole).toInt();
|
||||
if (m_choice == CANCEL_ID) {
|
||||
reject();
|
||||
} else {
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
int VSelectDialog::getSelection() const
|
||||
{
|
||||
return m_choice;
|
||||
}
|
||||
|
||||
void VSelectDialog::updateSize()
|
||||
{
|
||||
Q_ASSERT(m_list->count() > 0);
|
||||
|
||||
int height = 0;
|
||||
for (int i = 0; i < m_list->count(); ++i) {
|
||||
height += m_list->sizeHintForRow(i);
|
||||
}
|
||||
|
||||
height += 2 * m_list->count();
|
||||
int wid = width();
|
||||
m_list->resize(wid, height);
|
||||
resize(wid, height);
|
||||
}
|
||||
|
||||
void VSelectDialog::showEvent(QShowEvent *p_event)
|
||||
{
|
||||
QDialog::showEvent(p_event);
|
||||
|
||||
updateSize();
|
||||
}
|
||||
|
||||
void VSelectDialog::keyPressEvent(QKeyEvent *p_event)
|
||||
{
|
||||
if (VimNavigationForWidget::injectKeyPressEventForVim(m_list,
|
||||
p_event,
|
||||
this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// On Mac OS X, it is `Command+O` to activate an item, instead of Return.
|
||||
#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
|
||||
int key = p_event->key();
|
||||
if (key == Qt::Key_Return || key == Qt::Key_Enter) {
|
||||
p_event->accept();
|
||||
if (auto item = m_list->currentItem()) {
|
||||
selectionChosen(item);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
QDialog::keyPressEvent(p_event);
|
||||
}
|
||||
|
@ -1,43 +0,0 @@
|
||||
#ifndef VSELECTDIALOG_H
|
||||
#define VSELECTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMap>
|
||||
|
||||
class QPushButton;
|
||||
class QMouseEvent;
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class QShowEvent;
|
||||
class QKeyEvent;
|
||||
|
||||
class VSelectDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VSelectDialog(const QString &p_title, QWidget *p_parent = 0);
|
||||
|
||||
// @p_selectID should >= 0.
|
||||
void addSelection(const QString &p_selectStr, int p_selectID);
|
||||
|
||||
int getSelection() const;
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void selectionChosen(QListWidgetItem *p_item);
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title);
|
||||
|
||||
void updateSize();
|
||||
|
||||
int m_choice;
|
||||
|
||||
QListWidget *m_list;
|
||||
};
|
||||
|
||||
#endif // VSELECTDIALOG_H
|
@ -1,421 +0,0 @@
|
||||
#ifndef VSETTINGSDIALOG_H
|
||||
#define VSETTINGSDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
#include <QTabWidget>
|
||||
|
||||
class QDialogButtonBox;
|
||||
class QComboBox;
|
||||
class QGroupBox;
|
||||
class QDoubleSpinBox;
|
||||
class QSpinBox;
|
||||
class QCheckBox;
|
||||
class VLineEdit;
|
||||
class QStackedLayout;
|
||||
class QListWidget;
|
||||
class QPlainTextEdit;
|
||||
class QVBoxLayout;
|
||||
class QFontComboBox;
|
||||
|
||||
class VSettingsDialog;
|
||||
|
||||
class VGeneralTab : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VGeneralTab(QWidget *p_parent = 0);
|
||||
bool loadConfiguration();
|
||||
bool saveConfiguration();
|
||||
|
||||
private:
|
||||
QLayout *setupStartupPagesLayout();
|
||||
|
||||
bool loadLanguage();
|
||||
bool saveLanguage();
|
||||
|
||||
bool loadSystemTray();
|
||||
bool saveSystemTray();
|
||||
|
||||
bool loadStartupPageType();
|
||||
bool saveStartupPageType();
|
||||
|
||||
bool loadQuickAccess();
|
||||
bool saveQuickAccess();
|
||||
|
||||
bool loadKeyboardLayoutMapping();
|
||||
bool saveKeyboardLayoutMapping();
|
||||
|
||||
bool loadOpenGL();
|
||||
bool saveOpenGL();
|
||||
|
||||
// Language
|
||||
QComboBox *m_langCombo;
|
||||
|
||||
// System tray
|
||||
QCheckBox *m_systemTray;
|
||||
|
||||
// Startup page type.
|
||||
QComboBox *m_startupPageTypeCombo;
|
||||
|
||||
// Startup pages.
|
||||
QPlainTextEdit *m_startupPagesEdit;
|
||||
|
||||
// Startup pages add files button.
|
||||
QPushButton *m_startupPagesAddBtn;
|
||||
|
||||
// Quick access note path.
|
||||
VLineEdit *m_quickAccessEdit;
|
||||
|
||||
// Keyboard layout mappings.
|
||||
QComboBox *m_keyboardLayoutCombo;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
// Windows OpenGL.
|
||||
QComboBox *m_openGLCombo;
|
||||
#endif
|
||||
|
||||
static const QVector<QString> c_availableLangs;
|
||||
};
|
||||
|
||||
class VLookTab: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VLookTab(QWidget *p_parent = 0);
|
||||
bool loadConfiguration();
|
||||
bool saveConfiguration();
|
||||
|
||||
private:
|
||||
bool loadToolBarIconSize();
|
||||
bool saveToolBarIconSize();
|
||||
|
||||
// Tool bar icon size.
|
||||
QSpinBox *m_tbIconSizeSpin;
|
||||
};
|
||||
|
||||
class VReadEditTab : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VReadEditTab(VSettingsDialog *p_dlg, QWidget *p_parent = 0);
|
||||
|
||||
bool loadConfiguration();
|
||||
bool saveConfiguration();
|
||||
|
||||
private:
|
||||
bool loadWebZoomFactor();
|
||||
bool saveWebZoomFactor();
|
||||
|
||||
bool loadSwapFile();
|
||||
bool saveSwapFile();
|
||||
|
||||
bool loadAutoSave();
|
||||
bool saveAutoSave();
|
||||
|
||||
void showTipsAboutAutoSave();
|
||||
|
||||
bool loadKeyMode();
|
||||
bool saveKeyMode();
|
||||
|
||||
bool loadFlashAnchor();
|
||||
bool saveFlashAnchor();
|
||||
|
||||
bool loadEditorZoomDelta();
|
||||
bool saveEditorZoomDelta();
|
||||
|
||||
bool loadEditorFontFamily();
|
||||
bool saveEditorFontFamily();
|
||||
|
||||
VSettingsDialog *m_settingsDlg;
|
||||
|
||||
// Web zoom factor.
|
||||
QCheckBox *m_customWebZoom;
|
||||
QDoubleSpinBox *m_webZoomFactorSpin;
|
||||
|
||||
// Web flash anchor.
|
||||
QCheckBox *m_flashAnchor;
|
||||
|
||||
// Swap file.
|
||||
QCheckBox *m_swapFile;
|
||||
|
||||
// Auto save.
|
||||
QCheckBox *m_autoSave;
|
||||
|
||||
// Key mode.
|
||||
QComboBox *m_keyModeCB;
|
||||
|
||||
// Smart IM in Vim mode.
|
||||
QCheckBox *m_smartIM;
|
||||
|
||||
// Editor zoom delta.
|
||||
QSpinBox *m_editorZoomDeltaSpin;
|
||||
|
||||
// Editor font family.
|
||||
QCheckBox *m_customEditorFont;
|
||||
QFontComboBox *m_editorFontFamilyCB;
|
||||
|
||||
QGroupBox *m_readBox;
|
||||
QGroupBox *m_editBox;
|
||||
};
|
||||
|
||||
class VNoteManagementTab : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VNoteManagementTab(QWidget *p_parent = 0);
|
||||
bool loadConfiguration();
|
||||
bool saveConfiguration();
|
||||
|
||||
private slots:
|
||||
void customImageFolderChanged(int p_state);
|
||||
void customImageFolderExtChanged(int p_state);
|
||||
void customAttachmentFolderChanged(int p_state);
|
||||
|
||||
private:
|
||||
bool loadImageFolder();
|
||||
bool saveImageFolder();
|
||||
|
||||
bool loadImageFolderExt();
|
||||
bool saveImageFolderExt();
|
||||
|
||||
bool loadAttachmentFolder();
|
||||
bool saveAttachmentFolder();
|
||||
|
||||
bool loadSingleClickOpen();
|
||||
bool saveSingleClickOpen();
|
||||
|
||||
QGroupBox *m_noteBox;
|
||||
QGroupBox *m_externalBox;
|
||||
|
||||
// Image folder.
|
||||
QCheckBox *m_customImageFolder;
|
||||
VLineEdit *m_imageFolderEdit;
|
||||
|
||||
// Image folder of External File.
|
||||
QCheckBox *m_customImageFolderExt;
|
||||
VLineEdit *m_imageFolderEditExt;
|
||||
|
||||
// Attachment folder.
|
||||
QCheckBox *m_customAttachmentFolder;
|
||||
VLineEdit *m_attachmentFolderEdit;
|
||||
|
||||
// Single click to open note in current tab.
|
||||
QCheckBox *m_singleClickOpen;
|
||||
};
|
||||
|
||||
class VMarkdownTab : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VMarkdownTab(QWidget *p_parent = 0);
|
||||
bool loadConfiguration();
|
||||
bool saveConfiguration();
|
||||
|
||||
private:
|
||||
bool loadOpenMode();
|
||||
bool saveOpenMode();
|
||||
|
||||
bool loadHeadingSequence();
|
||||
bool saveHeadingSequence();
|
||||
|
||||
bool loadColorColumn();
|
||||
bool saveColorColumn();
|
||||
|
||||
bool loadCodeBlockCopyButton();
|
||||
bool saveCodeBlockCopyButton();
|
||||
|
||||
bool loadMathJax();
|
||||
bool saveMathJax();
|
||||
|
||||
bool loadPlantUML();
|
||||
bool savePlantUML();
|
||||
|
||||
bool loadGraphviz();
|
||||
bool saveGraphviz();
|
||||
|
||||
// Default note open mode for markdown.
|
||||
QComboBox *m_openModeCombo;
|
||||
|
||||
// Whether enable heading sequence.
|
||||
QComboBox *m_headingSequenceTypeCombo;
|
||||
QComboBox *m_headingSequenceLevelCombo;
|
||||
|
||||
// Color column in code block.
|
||||
VLineEdit *m_colorColumnEdit;
|
||||
|
||||
// Copy button in code block.
|
||||
QCheckBox *m_codeBlockCopyButtonCB;
|
||||
|
||||
// MathJax.
|
||||
VLineEdit *m_mathjaxConfigEdit;
|
||||
|
||||
// PlantUML.
|
||||
QComboBox *m_plantUMLModeCombo;
|
||||
VLineEdit *m_plantUMLServerEdit;
|
||||
VLineEdit *m_plantUMLJarEdit;
|
||||
|
||||
// Graphviz.
|
||||
QCheckBox *m_graphvizCB;
|
||||
VLineEdit *m_graphvizDotEdit;
|
||||
};
|
||||
|
||||
class VMiscTab : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VMiscTab(QWidget *p_parent = 0);
|
||||
bool loadConfiguration();
|
||||
bool saveConfiguration();
|
||||
|
||||
private:
|
||||
bool loadMatchesInPage();
|
||||
bool saveMatchesInPage();
|
||||
|
||||
// Highlight matches in page.
|
||||
QCheckBox *m_matchesInPageCB;
|
||||
};
|
||||
|
||||
class VImageHostingTab : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VImageHostingTab(QWidget *p_parent = 0);
|
||||
bool loadConfiguration();
|
||||
bool saveConfiguration();
|
||||
|
||||
private:
|
||||
bool loadGithubPersonalAccessToken();
|
||||
bool saveGithubPersonalAccessToken();
|
||||
|
||||
bool loadGithubReposName();
|
||||
bool saveGithubReposName();
|
||||
|
||||
bool loadGithubUserName();
|
||||
bool saveGithubUserName();
|
||||
|
||||
bool loadGithubKeepImgScale();
|
||||
bool saveGithubKeepImgScale();
|
||||
|
||||
bool loadGithubDoNotReplaceLink();
|
||||
bool saveGithubDoNotReplaceLink();
|
||||
|
||||
bool loadWechatAppid();
|
||||
bool saveWechatAppid();
|
||||
|
||||
bool loadWechatSecret();
|
||||
bool saveWechatSecret();
|
||||
|
||||
bool loadMarkdown2WechatToolUrl();
|
||||
bool saveMarkdown2WechatToolUrl();
|
||||
|
||||
bool loadWechatKeepImgScale();
|
||||
bool saveWechatKeepImgScale();
|
||||
|
||||
bool loadWechatDoNotReplaceLink();
|
||||
bool saveWechatDoNotReplaceLink();
|
||||
|
||||
bool loadTencentAccessDomainName();
|
||||
bool saveTencentAccessDomainName();
|
||||
|
||||
bool loadTencentSecretId();
|
||||
bool saveTencentSecretId();
|
||||
|
||||
bool loadTencentSecretKey();
|
||||
bool saveTencentSecretKey();
|
||||
|
||||
bool loadTencentKeepImgScale();
|
||||
bool saveTencentKeepImgScale();
|
||||
|
||||
bool loadTencentDoNotReplaceLink();
|
||||
bool saveTencentDoNotReplaceLink();
|
||||
|
||||
bool loadGiteePersonalAccessToken();
|
||||
bool saveGiteePersonalAccessToken();
|
||||
|
||||
bool loadGiteeReposName();
|
||||
bool saveGiteeReposName();
|
||||
|
||||
bool loadGiteeUserName();
|
||||
bool saveGiteeUserName();
|
||||
|
||||
bool loadGiteeKeepImgScale();
|
||||
bool saveGiteeKeepImgScale();
|
||||
|
||||
bool loadGiteeDoNotReplaceLink();
|
||||
bool saveGiteeDoNotReplaceLink();
|
||||
|
||||
// Github configuration edit.
|
||||
VLineEdit *m_githubPersonalAccessTokenEdit;
|
||||
VLineEdit *m_githubRepoNameEdit;
|
||||
VLineEdit *m_githubUserNameEdit;
|
||||
QCheckBox *m_githubKeepImgScaleCB;
|
||||
QCheckBox *m_githubDoNotReplaceLinkCB;
|
||||
|
||||
// Gitee configuration edit.
|
||||
VLineEdit *m_giteePersonalAccessTokenEdit;
|
||||
VLineEdit *m_giteeRepoNameEdit;
|
||||
VLineEdit *m_giteeUserNameEdit;
|
||||
QCheckBox *m_giteeKeepImgScaleCB;
|
||||
QCheckBox *m_giteeDoNotReplaceLinkCB;
|
||||
|
||||
// Wechat configuration edit.
|
||||
VLineEdit *m_wechatAppidEdit;
|
||||
VLineEdit *m_wechatSecretEdit;
|
||||
VLineEdit *m_markdown2WechatToolUrlEdit;
|
||||
QCheckBox *m_wechatKeepImgScaleCB;
|
||||
QCheckBox *m_wechatDoNotReplaceLinkCB;
|
||||
|
||||
// Tencent configuration edit.
|
||||
VLineEdit *m_tencentAccessDomainNameEdit;
|
||||
VLineEdit *m_tencentSecretIdEdit;
|
||||
VLineEdit *m_tencentSecretKeyEdit;
|
||||
QCheckBox *m_tencentKeepImgScaleCB;
|
||||
QCheckBox *m_tencentDoNotReplaceLinkCB;
|
||||
};
|
||||
|
||||
class VSettingsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VSettingsDialog(QWidget *p_parent = 0);
|
||||
|
||||
void setNeedUpdateEditorFont(bool p_need);
|
||||
bool getNeedUpdateEditorFont() const;
|
||||
|
||||
private slots:
|
||||
void saveConfiguration();
|
||||
|
||||
void resetVNote();
|
||||
|
||||
void resetLayout();
|
||||
|
||||
private:
|
||||
void loadConfiguration();
|
||||
|
||||
void addTab(QWidget *p_widget, const QString &p_label);
|
||||
|
||||
QStackedLayout *m_tabs;
|
||||
QListWidget *m_tabList;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
// Reset all the configuration of VNote.
|
||||
QPushButton *m_resetVNoteBtn;
|
||||
|
||||
// Reset the layout.
|
||||
QPushButton *m_resetLayoutBtn;
|
||||
|
||||
bool m_needUpdateEditorFont;
|
||||
};
|
||||
|
||||
inline void VSettingsDialog::setNeedUpdateEditorFont(bool p_need)
|
||||
{
|
||||
m_needUpdateEditorFont = p_need;
|
||||
}
|
||||
|
||||
inline bool VSettingsDialog::getNeedUpdateEditorFont() const
|
||||
{
|
||||
return m_needUpdateEditorFont;
|
||||
}
|
||||
#endif // VSETTINGSDIALOG_H
|
@ -1,251 +0,0 @@
|
||||
#include "vsortdialog.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
VSortDialog::VSortDialog(const QString &p_title,
|
||||
const QString &p_info,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent)
|
||||
{
|
||||
setupUI(p_title, p_info);
|
||||
}
|
||||
|
||||
void VSortDialog::setupUI(const QString &p_title, const QString &p_info)
|
||||
{
|
||||
QLabel *infoLabel = NULL;
|
||||
if (!p_info.isEmpty()) {
|
||||
infoLabel = new QLabel(p_info);
|
||||
infoLabel->setWordWrap(true);
|
||||
}
|
||||
|
||||
m_treeWidget = new VTreeWidget();
|
||||
m_treeWidget->setRootIsDecorated(false);
|
||||
m_treeWidget->setSelectionMode(QAbstractItemView::ContiguousSelection);
|
||||
m_treeWidget->setDragDropMode(QAbstractItemView::InternalMove);
|
||||
connect(m_treeWidget, &VTreeWidget::rowsMoved,
|
||||
this, [this](int p_first, int p_last, int p_row) {
|
||||
Q_UNUSED(p_first);
|
||||
Q_UNUSED(p_last);
|
||||
QTreeWidgetItem *item = m_treeWidget->topLevelItem(p_row);
|
||||
if (item) {
|
||||
m_treeWidget->setCurrentItem(item);
|
||||
|
||||
// Select all items back.
|
||||
int cnt = p_last - p_first + 1;
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
QTreeWidgetItem *it = m_treeWidget->topLevelItem(p_row + i);
|
||||
if (it) {
|
||||
it->setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Buttons for top/up/down/bottom.
|
||||
m_topBtn = new QPushButton(tr("&Top"));
|
||||
m_topBtn->setToolTip(tr("Move selected items to top"));
|
||||
connect(m_topBtn, &QPushButton::clicked,
|
||||
this, [this]() {
|
||||
this->handleMoveOperation(MoveOperation::Top);
|
||||
});
|
||||
|
||||
m_upBtn = new QPushButton(tr("&Up"));
|
||||
m_upBtn->setToolTip(tr("Move selected items up"));
|
||||
connect(m_upBtn, &QPushButton::clicked,
|
||||
this, [this]() {
|
||||
this->handleMoveOperation(MoveOperation::Up);
|
||||
});
|
||||
|
||||
m_downBtn = new QPushButton(tr("&Down"));
|
||||
m_downBtn->setToolTip(tr("Move selected items down"));
|
||||
connect(m_downBtn, &QPushButton::clicked,
|
||||
this, [this]() {
|
||||
this->handleMoveOperation(MoveOperation::Down);
|
||||
});
|
||||
|
||||
m_bottomBtn = new QPushButton(tr("&Bottom"));
|
||||
m_bottomBtn->setToolTip(tr("Move selected items to bottom"));
|
||||
connect(m_bottomBtn, &QPushButton::clicked,
|
||||
this, [this]() {
|
||||
this->handleMoveOperation(MoveOperation::Bottom);
|
||||
});
|
||||
|
||||
QVBoxLayout *btnLayout = new QVBoxLayout;
|
||||
btnLayout->addWidget(m_topBtn);
|
||||
btnLayout->addWidget(m_upBtn);
|
||||
btnLayout->addWidget(m_downBtn);
|
||||
btnLayout->addWidget(m_bottomBtn);
|
||||
btnLayout->addStretch();
|
||||
|
||||
QHBoxLayout *midLayout = new QHBoxLayout;
|
||||
midLayout->addWidget(m_treeWidget);
|
||||
midLayout->addLayout(btnLayout);
|
||||
|
||||
// Ok is the default button.
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
m_btnBox->button(QDialogButtonBox::Ok)->setProperty("SpecialBtn", true);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
if (infoLabel) {
|
||||
mainLayout->addWidget(infoLabel);
|
||||
}
|
||||
|
||||
mainLayout->addLayout(midLayout);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
|
||||
setLayout(mainLayout);
|
||||
setWindowTitle(p_title);
|
||||
}
|
||||
|
||||
void VSortDialog::treeUpdated()
|
||||
{
|
||||
int cols = m_treeWidget->columnCount();
|
||||
for (int i = 0; i < cols; ++i) {
|
||||
m_treeWidget->resizeColumnToContents(i);
|
||||
}
|
||||
|
||||
QHeaderView *header = m_treeWidget->header();
|
||||
if (header) {
|
||||
header->setStretchLastSection(true);
|
||||
}
|
||||
|
||||
// We just need single level.
|
||||
int cnt = m_treeWidget->topLevelItemCount();
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
QTreeWidgetItem *item = m_treeWidget->topLevelItem(i);
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsDropEnabled);
|
||||
}
|
||||
|
||||
m_treeWidget->sortByColumn(-1);
|
||||
m_treeWidget->setSortingEnabled(true);
|
||||
}
|
||||
|
||||
void VSortDialog::handleMoveOperation(MoveOperation p_op)
|
||||
{
|
||||
const QList<QTreeWidgetItem *> selectedItems = m_treeWidget->selectedItems();
|
||||
if (selectedItems.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int first = m_treeWidget->topLevelItemCount();
|
||||
int last = -1;
|
||||
for (auto const & it : selectedItems) {
|
||||
int idx = m_treeWidget->indexOfTopLevelItem(it);
|
||||
Q_ASSERT(idx > -1);
|
||||
if (idx < first) {
|
||||
first = idx;
|
||||
}
|
||||
|
||||
if (idx > last) {
|
||||
last = idx;
|
||||
}
|
||||
}
|
||||
|
||||
Q_ASSERT(first <= last && (last - first + 1) == selectedItems.size());
|
||||
QTreeWidgetItem *firstItem = NULL;
|
||||
|
||||
switch (p_op) {
|
||||
case MoveOperation::Top:
|
||||
if (first == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_treeWidget->clearSelection();
|
||||
|
||||
// Insert item[last] to index 0 repeatedly.
|
||||
for (int i = last - first; i >= 0; --i) {
|
||||
QTreeWidgetItem *item = m_treeWidget->takeTopLevelItem(last);
|
||||
Q_ASSERT(item);
|
||||
m_treeWidget->insertTopLevelItem(0, item);
|
||||
item->setSelected(true);
|
||||
}
|
||||
|
||||
firstItem = m_treeWidget->topLevelItem(0);
|
||||
|
||||
break;
|
||||
|
||||
case MoveOperation::Up:
|
||||
if (first == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_treeWidget->clearSelection();
|
||||
|
||||
// Insert item[last] to index (first -1) repeatedly.
|
||||
for (int i = last - first; i >= 0; --i) {
|
||||
QTreeWidgetItem *item = m_treeWidget->takeTopLevelItem(last);
|
||||
Q_ASSERT(item);
|
||||
m_treeWidget->insertTopLevelItem(first - 1, item);
|
||||
item->setSelected(true);
|
||||
}
|
||||
|
||||
firstItem = m_treeWidget->topLevelItem(first - 1);
|
||||
|
||||
break;
|
||||
|
||||
case MoveOperation::Down:
|
||||
if (last == m_treeWidget->topLevelItemCount() - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_treeWidget->clearSelection();
|
||||
|
||||
// Insert item[first] to index (last) repeatedly.
|
||||
for (int i = last - first; i >= 0; --i) {
|
||||
QTreeWidgetItem *item = m_treeWidget->takeTopLevelItem(first);
|
||||
Q_ASSERT(item);
|
||||
m_treeWidget->insertTopLevelItem(last + 1, item);
|
||||
item->setSelected(true);
|
||||
|
||||
if (!firstItem) {
|
||||
firstItem = item;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MoveOperation::Bottom:
|
||||
if (last == m_treeWidget->topLevelItemCount() - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_treeWidget->clearSelection();
|
||||
|
||||
// Insert item[first] to the last of the tree repeatedly.
|
||||
for (int i = last - first; i >= 0; --i) {
|
||||
QTreeWidgetItem *item = m_treeWidget->takeTopLevelItem(first);
|
||||
Q_ASSERT(item);
|
||||
m_treeWidget->addTopLevelItem(item);
|
||||
item->setSelected(true);
|
||||
|
||||
if (!firstItem) {
|
||||
firstItem = item;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstItem) {
|
||||
m_treeWidget->setCurrentItem(firstItem);
|
||||
m_treeWidget->scrollToItem(firstItem);
|
||||
}
|
||||
}
|
||||
|
||||
QVector<QVariant> VSortDialog::getSortedData() const
|
||||
{
|
||||
int cnt = m_treeWidget->topLevelItemCount();
|
||||
QVector<QVariant> data(cnt);
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
QTreeWidgetItem *item = m_treeWidget->topLevelItem(i);
|
||||
Q_ASSERT(item);
|
||||
data[i] = item->data(0, Qt::UserRole);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#ifndef VSORTDIALOG_H
|
||||
#define VSORTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVector>
|
||||
|
||||
#include "vtreewidget.h"
|
||||
|
||||
class QPushButton;
|
||||
class QDialogButtonBox;
|
||||
|
||||
class VSortDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VSortDialog(const QString &p_title,
|
||||
const QString &p_info,
|
||||
QWidget *p_parent = 0);
|
||||
|
||||
QTreeWidget *getTreeWidget() const;
|
||||
|
||||
// Called after updating the m_treeWidget.
|
||||
void treeUpdated();
|
||||
|
||||
// Get user data of column 0 from sorted items.
|
||||
QVector<QVariant> getSortedData() const;
|
||||
|
||||
private:
|
||||
enum MoveOperation { Top, Up, Down, Bottom };
|
||||
|
||||
private slots:
|
||||
void handleMoveOperation(MoveOperation p_op);
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_title, const QString &p_info);
|
||||
|
||||
VTreeWidget *m_treeWidget;
|
||||
QPushButton *m_topBtn;
|
||||
QPushButton *m_upBtn;
|
||||
QPushButton *m_downBtn;
|
||||
QPushButton *m_bottomBtn;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
};
|
||||
|
||||
inline QTreeWidget *VSortDialog::getTreeWidget() const
|
||||
{
|
||||
return m_treeWidget;
|
||||
}
|
||||
|
||||
#endif // VSORTDIALOG_H
|
@ -1,76 +0,0 @@
|
||||
#include "vtipsdialog.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QWebEngineView>
|
||||
|
||||
#include "vconfigmanager.h"
|
||||
#include "vmarkdownconverter.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "vnote.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VTipsDialog::VTipsDialog(const QString &p_tipFile,
|
||||
const QString &p_actionText,
|
||||
TipsDialogFunc p_action,
|
||||
QWidget *p_parent)
|
||||
: QDialog(p_parent), m_actionBtn(NULL), m_action(p_action)
|
||||
{
|
||||
setupUI(p_actionText);
|
||||
|
||||
readFile(p_tipFile);
|
||||
}
|
||||
|
||||
void VTipsDialog::setupUI(const QString &p_actionText)
|
||||
{
|
||||
m_viewer = VUtils::getWebEngineView(g_config->getBaseBackground());
|
||||
m_viewer->setContextMenuPolicy(Qt::NoContextMenu);
|
||||
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
|
||||
m_okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||
|
||||
if (!p_actionText.isEmpty()) {
|
||||
Q_ASSERT(m_action != nullptr);
|
||||
m_actionBtn = m_btnBox->addButton(p_actionText, QDialogButtonBox::ActionRole);
|
||||
m_actionBtn->setProperty("SpecialBtn", true);
|
||||
m_actionBtn->setDefault(true);
|
||||
|
||||
connect(m_actionBtn, &QPushButton::clicked,
|
||||
this, [this]() {
|
||||
m_action();
|
||||
});
|
||||
}
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addWidget(m_viewer);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
|
||||
setLayout(mainLayout);
|
||||
setWindowTitle(tr("VNote Tips"));
|
||||
}
|
||||
|
||||
void VTipsDialog::readFile(const QString &p_tipFile)
|
||||
{
|
||||
QString content = VUtils::readFileFromDisk(p_tipFile);
|
||||
VMarkdownConverter mdConverter;
|
||||
QString toc;
|
||||
QString html = mdConverter.generateHtml(content,
|
||||
g_config->getMarkdownExtensions(),
|
||||
toc);
|
||||
html = VUtils::generateSimpleHtmlTemplate(html);
|
||||
// Add a base URL to enable it to access local style files.
|
||||
m_viewer->setHtml(html, QUrl("qrc:/resources"));
|
||||
}
|
||||
|
||||
void VTipsDialog::showEvent(QShowEvent *p_event)
|
||||
{
|
||||
QDialog::showEvent(p_event);
|
||||
|
||||
if (m_actionBtn) {
|
||||
m_actionBtn->setFocus();
|
||||
} else {
|
||||
m_okBtn->setFocus();
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#ifndef VTIPSDIALOG_H
|
||||
#define VTIPSDIALOG_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QDialogButtonBox;
|
||||
class QWebEngineView;
|
||||
class QPushButton;
|
||||
class QShowEvent;
|
||||
|
||||
typedef std::function<void()> TipsDialogFunc;
|
||||
|
||||
class VTipsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VTipsDialog(const QString &p_tipFile,
|
||||
const QString &p_actionText = QString(),
|
||||
TipsDialogFunc p_action = nullptr,
|
||||
QWidget *p_parent = nullptr);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void setupUI(const QString &p_actionText);
|
||||
|
||||
void readFile(const QString &p_tipFile);
|
||||
|
||||
QWebEngineView *m_viewer;
|
||||
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
QPushButton *m_actionBtn;
|
||||
|
||||
QPushButton *m_okBtn;
|
||||
|
||||
TipsDialogFunc m_action;
|
||||
};
|
||||
|
||||
#endif // VTIPSDIALOG_H
|
@ -1,183 +0,0 @@
|
||||
#include "vupdater.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QTimer>
|
||||
#include <QWebEngineView>
|
||||
|
||||
#include "vconfigmanager.h"
|
||||
#include "vdownloader.h"
|
||||
#include "vmarkdownconverter.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "vnote.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
VUpdater::VUpdater(QWidget *p_parent)
|
||||
: QDialog(p_parent)
|
||||
{
|
||||
setupUI();
|
||||
}
|
||||
|
||||
void VUpdater::setupUI()
|
||||
{
|
||||
QImage img(":/resources/icons/vnote_update.svg");
|
||||
QSize imgSize(128, 128);
|
||||
QLabel *imgLabel = new QLabel();
|
||||
imgLabel->setPixmap(QPixmap::fromImage(img.scaled(imgSize)));
|
||||
|
||||
m_versionLabel = new QLabel(tr("Current Version: v%1")
|
||||
.arg(g_config->c_version));
|
||||
|
||||
m_proLabel = new QLabel(tr("Checking for updates..."));
|
||||
m_proLabel->setOpenExternalLinks(true);
|
||||
m_proBar = new QProgressBar();
|
||||
m_proBar->setTextVisible(false);
|
||||
|
||||
m_descriptionWV = VUtils::getWebEngineView(g_config->getBaseBackground());
|
||||
m_descriptionWV->setContextMenuPolicy(Qt::NoContextMenu);
|
||||
m_descriptionWV->setHtml(VUtils::generateSimpleHtmlTemplate(VNote::s_sloganTemplate),
|
||||
QUrl("qrc:/resources"));
|
||||
|
||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||
m_btnBox->button(QDialogButtonBox::Ok)->setProperty("SpecialBtn", true);
|
||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
|
||||
QVBoxLayout *verLayout = new QVBoxLayout();
|
||||
verLayout->addStretch();
|
||||
verLayout->addWidget(m_versionLabel);
|
||||
verLayout->addStretch();
|
||||
verLayout->addWidget(m_proLabel);
|
||||
verLayout->addWidget(m_proBar);
|
||||
|
||||
QHBoxLayout *topLayout = new QHBoxLayout();
|
||||
topLayout->addWidget(imgLabel);
|
||||
topLayout->addLayout(verLayout);
|
||||
topLayout->addStretch();
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(m_descriptionWV, 1);
|
||||
mainLayout->addWidget(m_btnBox);
|
||||
|
||||
m_proLabel->hide();
|
||||
m_proBar->hide();
|
||||
|
||||
setLayout(mainLayout);
|
||||
setWindowTitle(tr("VNote Update"));
|
||||
}
|
||||
|
||||
void VUpdater::showEvent(QShowEvent *p_event)
|
||||
{
|
||||
QDialog::showEvent(p_event);
|
||||
|
||||
QTimer *timer = new QTimer(this);
|
||||
timer->setSingleShot(true);
|
||||
timer->setInterval(1000);
|
||||
connect(timer, &QTimer::timeout,
|
||||
this, [this]() {
|
||||
this->checkUpdates();
|
||||
});
|
||||
|
||||
timer->start();
|
||||
}
|
||||
|
||||
void VUpdater::checkUpdates()
|
||||
{
|
||||
// Change UI.
|
||||
m_proLabel->setText(tr("Checking for updates..."));
|
||||
m_proLabel->show();
|
||||
|
||||
m_proBar->setEnabled(true);
|
||||
m_proBar->setMinimum(0);
|
||||
m_proBar->setMaximum(100);
|
||||
m_proBar->reset();
|
||||
m_proBar->show();
|
||||
|
||||
QString url("https://api.github.com/repos/tamlok/vnote/releases/latest");
|
||||
VDownloader *downloader = new VDownloader(this);
|
||||
connect(downloader, &VDownloader::downloadFinished,
|
||||
this, &VUpdater::parseResult);
|
||||
downloader->download(url);
|
||||
|
||||
m_proBar->setValue(20);
|
||||
}
|
||||
|
||||
// Return if @p_latestVersion is newer than p_curVersion.
|
||||
// They are both in format xx.xx.xx.xx
|
||||
bool isNewerVersion(const QString &p_curVersion, const QString &p_latestVersion)
|
||||
{
|
||||
QStringList curList = p_curVersion.split('.', QString::SkipEmptyParts);
|
||||
QStringList latestList = p_latestVersion.split('.', QString::SkipEmptyParts);
|
||||
|
||||
int i = 0;
|
||||
for (; i < curList.size() && i < latestList.size(); ++i) {
|
||||
int a = curList[i].toInt();
|
||||
int b = latestList[i].toInt();
|
||||
|
||||
if (a > b) {
|
||||
return false;
|
||||
} else if (a < b) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (i < curList.size()) {
|
||||
// 1.2.1 vs 1.2
|
||||
return false;
|
||||
} else if (i < latestList.size()) {
|
||||
// 1.2 vs 1.2.1
|
||||
return true;
|
||||
} else {
|
||||
// 1.2 vs 1.2
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void VUpdater::parseResult(const QByteArray &p_data)
|
||||
{
|
||||
m_proBar->setValue(40);
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(p_data);
|
||||
QJsonObject json = jsonDoc.object();
|
||||
|
||||
if (jsonDoc.isNull() || json.empty()) {
|
||||
m_proBar->setEnabled(false);
|
||||
m_proLabel->setText(tr(":( Fail to check for updates.\n"
|
||||
"Please try it later."));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_proBar->setValue(100);
|
||||
|
||||
QString tag = json["tag_name"].toString();
|
||||
if (tag.startsWith('v') && tag.size() > 3) {
|
||||
tag = tag.right(tag.size() - 1);
|
||||
}
|
||||
|
||||
QString releaseName = json["name"].toString();
|
||||
QString releaseUrl = json["html_url"].toString();
|
||||
QString body = json["body"].toString();
|
||||
|
||||
m_versionLabel->setText(tr("Current Version: v%1\nLatest Version: v%2")
|
||||
.arg(g_config->c_version).arg(tag));
|
||||
if (isNewerVersion(g_config->c_version, tag)) {
|
||||
m_proLabel->setText(tr("<span style=\"font-weight: bold;\">Updates Available!</span><br/>"
|
||||
"Please visit <a href=\"%1\">GitHub Releases</a> to download the latest version.")
|
||||
.arg(releaseUrl));
|
||||
} else {
|
||||
m_proLabel->setText(tr("VNote is already the latest version."));
|
||||
}
|
||||
|
||||
QString mdText = "# " + releaseName + "\n" + body;
|
||||
VMarkdownConverter mdConverter;
|
||||
QString toc;
|
||||
QString html = mdConverter.generateHtml(mdText,
|
||||
g_config->getMarkdownExtensions(),
|
||||
toc);
|
||||
html = VUtils::generateSimpleHtmlTemplate(html);
|
||||
m_descriptionWV->setHtml(html, QUrl("qrc:/resources"));
|
||||
m_proBar->hide();
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#ifndef VUPDATER_H
|
||||
#define VUPDATER_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QByteArray>
|
||||
|
||||
class QLabel;
|
||||
class QDialogButtonBox;
|
||||
class QWebEngineView;
|
||||
class QProgressBar;
|
||||
class QShowEvent;
|
||||
|
||||
class VUpdater : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VUpdater(QWidget *p_parent = 0);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
// Calling to Github api got responses.
|
||||
void parseResult(const QByteArray &p_data);
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
// Fetch the latest release info from Github.
|
||||
void checkUpdates();
|
||||
|
||||
QLabel *m_versionLabel;
|
||||
QWebEngineView *m_descriptionWV;
|
||||
QDialogButtonBox *m_btnBox;
|
||||
|
||||
// Progress label and bar.
|
||||
QLabel *m_proLabel;
|
||||
QProgressBar *m_proBar;
|
||||
};
|
||||
|
||||
#endif // VUPDATER_H
|
@ -1 +0,0 @@
|
||||
#include "isearchengine.h"
|