Compare commits

...

237 Commits

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

* fix cache action

* use 2022 for 6.8

* fix vs

* fix linjux

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

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

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

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

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

* remove win32

* add more modules

* fix ci

* try fix linux-ci

* fix linux ci

* try fix rcc

* fix rcc path

* tree

* add back qt 5

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

* adj name and complete quick create

* del unused

* del unused

* adj name

* adj to const

* adj name 2

* adj

* fix

---------

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

* format

* reset

* adj

* adj isProcess

* adj func name

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

* adj

* Update src/widgets/pdfviewwindow.cpp

---------

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

* restore
2023-01-06 10:27:58 +08:00
chendapao
f764fec458
fix/selectdialog_QListWidget_to_ListWidget (#2320) 2023-01-04 14:40:36 +08:00
Le Tan
0cb502520f PdfViewer: set useWorkerFetch to false 2023-01-01 09:47:18 +08:00
Le Tan
b4e38409a9 release v3.16.0 2023-01-01 09:47:18 +08:00
Le Tan
03ffb12ff1 refine mindmap 2023-01-01 09:47:18 +08:00
Le Tan
31d4ad151e
Dev (#2316)
* refine mindmap

* update release

* release v3.16.0
2022-12-30 18:29:54 +08:00
Le Tan
5229be4687 add MindMap editor 2022-12-25 13:09:49 +08:00
chendapao
e76c6829f7
feature/add_history_del (#2297)
* feature/add_history_del

* del debug

* adj 1

* adj 2

* adj name

* adj to call by address

* adj to call by address

* adj to call by address

* adj func name

* adj code grammar

* adj per
2022-12-23 22:53:56 +08:00
Le Tan
e7a7895c6e embed pdfviewer reader 2022-12-18 14:20:30 +08:00
Le Tan
7f2cfe4f52
Pdf view (#2312)
* Draft: feature/pdf_view (#2268)

* build base code

* simple run successful

* add save session

* add pdfjs resource

* simple pdf viewer

Co-authored-by: chendapao <feloxx@163.com>
2022-12-17 18:52:37 +08:00
chendapao
7f27745303
fix/adj_snippet_state_store (#2300)
* fix/adj_snippet_state_store

* revert
2022-11-27 15:56:27 +08:00
Le Tan
4002cbca1b
fix macos ci (#2301) 2022-11-27 11:51:38 +08:00
两双筷子
f2c7abcc6f
修复因边距导致代码块与行号对不齐的问题 (#2294)
* 修改代码块边距

* Update src/data/extra/themes/vue-light/web.css

Co-authored-by: Le Tan <tamlokveer@gmail.com>
2022-11-21 22:46:45 +08:00
chendapao
e47b813b69
feature/add_shortcuts_paste_to_md (#2277)
* feature/add_shortcuts_paste_to_md

* feature/add_shortcuts_paste_to_md

* submodule update

* adj
2022-10-12 22:54:09 +08:00
Le Tan
0f635fba7d ci-win: do not package ico 2022-10-11 14:01:04 +08:00
Le Tan
9a10b210ec ViewWindow: add shortcut Ctrl_G,V to alternate view mode 2022-10-09 12:05:33 +08:00
两双筷子
038de48d54
Update highlight (#2270) 2022-10-08 21:54:47 +08:00
Le Tan
89cd40d0cf NotebookExplorer: support view mode for notbooks 2022-10-07 21:02:00 +08:00
Le Tan
3d77c29393 [Release] test CI 12 2022-10-04 21:13:51 +08:00
Le Tan
aea0da5570 [Release] test CI 13 2022-10-04 18:56:40 +08:00
Le Tan
7a88d1ef99 [Release] test CI 12 2022-10-04 17:15:01 +08:00
Le Tan
ee2dc9ccc5 [Release] test CI 11 2022-10-04 16:33:43 +08:00
Le Tan
73bb88e61f [Release] test CI 10 2022-10-04 15:54:56 +08:00
Le Tan
2846e590d5 [Release] test CI 9 2022-10-04 13:42:39 +08:00
Le Tan
cdf7091ae1 [Release] test CI 8 2022-10-04 10:36:19 +08:00
Le Tan
bed49cf038 [Release] test CI 7 2022-10-04 10:26:48 +08:00
Le Tan
4120ea2601 [Release] test CI 6 2022-10-04 09:39:03 +08:00
Le Tan
0e9b6894c7 [Release] test CI5 2022-10-04 09:36:43 +08:00
Le Tan
ff6031209f [Release] test CI 4 2022-10-03 20:52:17 +08:00
Le Tan
7e5ea84f37 [Release] test CI 3 2022-10-03 20:00:00 +08:00
Le Tan
e87be24e5d [Release] test ci 3 2022-10-03 19:27:56 +08:00
Le Tan
8141d58652 [Release] test CI 2 2022-10-03 17:50:48 +08:00
Le Tan
d6631280e5 [Release] test CI 2022-10-03 17:23:19 +08:00
Le Tan
0da7200898 [Release] v3.15.1
A fake release to test CI.
2022-10-03 16:00:50 +08:00
Le Tan
ed87ea3df1
Update ci-win.yml (#2265)
* Update ci-win.yml

* Update ci-win.yml

* Update ci-win.yml

* fix win

* release
2022-10-03 14:58:11 +08:00
Le Tan
749c4006a2 MarkdownEditor: bug fix about typing markers 2022-09-24 07:20:27 +08:00
Le Tan
66ed2cdda1 CI: remove private Qt 5.15.x build 2022-09-24 06:52:08 +08:00
Le Tan
3739cfd0ef add two themes 2022-09-24 06:49:05 +08:00
Le Tan
815ef927cb MainWindow: fix content area expand 2022-09-24 06:49:05 +08:00
chendapao
1d94617837
Fix/quick access open mode (#2239)
* fix/quick_access_open_mode

* fix/quick_access_open_mode

* adj quick access open mode location

* adj quick access open mode location
2022-09-17 18:07:15 +08:00
uRick
a25b75f828
add vx-idea theme (#2236) 2022-09-12 11:21:45 +08:00
chendapao
e92ea5061a
feature/adj_linux_build (#2234) 2022-09-06 15:04:00 +08:00
两双筷子
d9f009953f
添加vue暗黑主题 (#2232) 2022-09-05 20:58:30 +08:00
Le Tan
08fe335a80 check name conflicts when loading nodes 2022-08-28 21:58:57 +08:00
Le Tan
73ebcaeb90 fix read mode search 2022-08-27 21:41:40 +08:00
Le Tan
6de3663269
fix CI (#2225) 2022-08-26 22:19:15 +08:00
Le Tan
12302d375d release v3.15.0 2022-08-26 20:57:20 +08:00
Le Tan
490634b85a update translations 2022-08-26 20:54:12 +08:00
Le Tan
cc0689d946 NotebookSelector: support dynamic icons for notebooks 2022-08-26 20:45:18 +08:00
Le Tan
fe4b6ca72e refine themes 2022-08-25 21:26:40 +08:00
Le Tan
791a5da245 support word count 2022-08-25 20:31:01 +08:00
Le Tan
2a91577521 add abck tab icon 2022-08-24 20:16:12 +08:00
Le Tan
08c597513c support edit/read mode 2022-08-19 21:31:53 +08:00
Le Tan
3c64b86297
use Qt 5.15.4 for mac CI (#2219) 2022-08-19 20:12:50 +08:00
Le Tan
a37d03fd02 fix vew-light theme 2022-08-19 13:48:54 +08:00
两双筷子
12eda471fc
添加 Vue Light 主题 (#2218) 2022-08-18 23:21:29 +08:00
zz of NSFOCUS Security Team
5877366b77
update DockWidgetHelper chinese translation (#2217)
* update DockWidgetHelper chinese translation

* Update src/data/core/translations/vnote_zh_CN.ts

Co-authored-by: henices <henices@163.com>
Co-authored-by: Le Tan <tamlokveer@gmail.com>
2022-08-18 16:15:37 +08:00
Le Tan
9b78d38726
use qt 5.15.5 for windows (#2216) 2022-08-18 13:08:12 +08:00
Le Tan
b78bd5abef refine themes 2022-08-17 18:36:01 +08:00
Le Tan
5865519402 update vtextedit 2022-08-12 17:52:27 +08:00
Le Tan
4f1959501d Add open windows panel 2022-08-11 19:10:28 +08:00
Le Tan
12e18934df fix ci 2022-08-04 21:56:34 +08:00
Le Tan
05e8648d25
try fix win ci (#2202)
* try fix win ci

* add 5.15 for win

* fix

* refine README
2022-08-04 21:03:45 +08:00
Le Tan
e9c8225255 release v3.14.0 2022-08-02 21:11:25 +08:00
Le Tan
14475330da
fix CI (#2197)
* fix CI

* fix win
2022-08-02 05:10:13 +08:00
zz of NSFOCUS Security Team
53e2b3bfa8
Fix export html/pdf problem in Qt 5.15.5 (#2199)
maybe related https://github.com/vnotex/vnote/issues/1942   export PDF cpu 100%

```
Critical:(markdownviewer.js:103) Uncaught TypeError: Failed to delete an indexed property from 'NodeList': Index property deleter is not supported.
[520896:520896:0801/171207.286744:INFO:CONSOLE(103)] "Uncaught TypeError: Failed to delete an indexed property from 'NodeList': Index property deleter is not supported.", source: file:///home/henices/.local/share/VNote/VNote/web/js/prism.js (103)
```
2022-08-01 21:49:09 +08:00
Le Tan
8197012bcb refine icons and pure theme 2022-07-31 10:14:31 +08:00
Le Tan
719c7339cf refine pure theme 2022-07-26 19:46:37 +08:00
Le Tan
21893fdff8 more fixes 2022-07-21 21:46:37 +08:00
Le Tan
9a3c69870c IconUtils: transform to lower case for comparison 2022-07-18 08:59:41 +08:00
Le Tan
30f73cb004 NavigtionMode: fix bug when input method is enabled 2022-07-14 22:11:01 +08:00
chendapao
3025e8e01c
feature/adj_mono_icons_render (#2174)
* feature/adj_mono_icons_render

* adj mono check

* del space

* feature/adj_mono_icons_render

* adj color match

* adj color hex match

* adj color hex match

* simple

* simple

* feature/adj_mono_icons_render

* feature/adj_mono_icons_render

* back QRegExp
2022-07-14 08:51:45 +08:00
Le Tan
e5f7a23157 fix path 2022-07-10 09:32:41 +08:00
chendapao
8c0c056c47
feature/add_viewarea_shortcut_close_other_right (#2173)
* feature/add_viewarea_shortcut_close_other_right

* delete default

* adj multi

* del blank line

* add close all and close left

* adj

* adj

* del unless
2022-07-10 10:06:38 +08:00
Le Tan
17af3b8dc0 SessionConfig: add external media default path 2022-06-25 14:45:49 +08:00
chendapao
82ec6beeb5
fix/add_custom_icons (#2167) 2022-06-24 22:35:37 +08:00
chendapao
1318427bb7
feature/add_custom_icons (#2145)
* first boold

* adj

* base spec amendment

* add monochrome optional fields to the theme

* add monochrome optional fields to the theme

* adj default value
2022-06-21 18:31:25 +08:00
Le Tan
29b2093ef0 release v3.13.1 2022-06-19 16:06:57 +08:00
Le Tan
c08296bc3b UnitedEntry: fix key press event 2022-06-19 14:38:12 +08:00
Le Tan
cd53eedc7f UnitedEntry: bug fix 2022-06-17 22:17:47 +08:00
Le Tan
e0b07b8aba UnitedEntry: fix the popup hiding input method issue on macOS 2022-06-17 21:06:09 +08:00
Le Tan
8678468998 fix shortcuts 2022-06-14 21:28:32 +08:00
Le Tan
d33b539659
fix win ci (#2141) 2022-05-29 20:55:22 +08:00
Le Tan
09776f07cd use qt 5.12.11 on Windows 2022-05-27 17:23:20 +08:00
Le Tan
f762204512 downgrade Qt version to 5.12 2022-05-26 21:43:08 +08:00
chendapao
06b9269b84
fix shortcut copy conflict (#2112) 2022-04-25 16:46:58 +08:00
chendapao
243a03e142
add notebooknodeexplorer shortcut new note folder and toolbar (#2101) 2022-04-16 10:10:08 +08:00
chendapao
dfa4e59737
add notebooknodeexplorer shortcut cut (#2100) 2022-04-16 10:09:15 +08:00
chendapao
cf0146050e
add global shortcut wakeup (#2097)
* add global shortcut wakeup

* del useless

* add ann

* upgrade git mod

* add short text

* update

* Simplify logic and add QKeySequence non-null judgment.

* Simplify logic.

* Simplify logic.
2022-04-16 09:51:13 +08:00
Le Tan
18080d174c add QHotkey 2022-04-11 12:54:10 +08:00
chendapao
34f3bb7a08
add copy paste properties short text (#2096) 2022-04-10 07:09:40 +08:00
chendapao
09789590a2
add notebooknodeexplorer shortcut properties (#2094)
* impl properties

* repeat logic merge to function

* code format

* properties only 1 node

* change function name
2022-04-07 22:47:21 +08:00
chendapao
0cadea231a
add notebooknodeexplorer shortcut copy paste (#2088)
* add notebooknodeexplorer shortcut copy paste

* simplify code and add isActionFromMaster function

* add const

* adj ann

* adj return
2022-04-06 12:45:16 +08:00
Le Tan
bbab5cc223 release v3.13.0 2022-04-03 13:12:37 +08:00
Le Tan
fe3280e02e update translations 2022-04-03 13:09:53 +08:00
Le Tan
a31a7a32a5 MindMap: preparations 2022-04-01 21:28:49 +08:00
Le Tan
b13771e34f fix CI on macOS 2022-04-01 21:28:17 +08:00
Le Tan
cc8ee46efe SettingsDialog: finer grain restart prompt 2022-04-01 21:28:01 +08:00
Le Tan
1ca899d8ab Revert "use Qt 5.15.3"
This reverts commit 5a8eb1e7aa3f9dc224727e4dc157b5ee54d975c1.
2022-03-31 21:22:35 +08:00
Le Tan
8a1bd930eb Fixes
1. PlantUmlWebService override;
2. Shortcut to clear highlights;
3. Save ViewArea session on minimized to system tray;
4. Prompt for restart after Settings
2022-03-31 21:21:15 +08:00
Le Tan
ecce8d13c1 MarkdownEditor: support RichPaste as default 2022-03-31 12:10:22 +08:00
Le Tan
5a8eb1e7aa use Qt 5.15.3 2022-03-30 21:26:08 +08:00
Le Tan
cadbab25bb FileAssociations: add System for system default program 2022-03-30 21:18:30 +08:00
Le Tan
dba9fb30e8 UnitedEntry: support United Entry 2022-03-30 21:18:15 +08:00
Le Tan
2908148c00 $$: no need to add blank line before $$ start 2022-03-10 20:20:20 +08:00
Le Tan
8be34ade30 KateVi: skip Ctrl+X as well as Ctrl+C 2022-03-08 20:12:33 +08:00
Le Tan
52d389a5e7 MarkdownEditor: fix image url encoding 2022-03-07 20:24:09 +08:00
Le Tan
f9767bf7f7 update vtextedit 2022-03-03 13:41:46 +08:00
Le Tan
d25c142bfa NotebookSelector: use ComboBox 2022-03-02 10:57:16 +08:00
Le Tan
bdd935d9c2 use windows-2019 to fix CI on windows 2022-02-26 16:18:08 +08:00
Le Tan
eab367cf6f Qt5.15: hide unused actions in web viewer 2022-02-23 17:02:31 +08:00
Le Tan
486950c1aa Searcher: use async first phase search 2022-02-22 19:49:22 +08:00
Le Tan
0e4442f513 release v3.12.888 2022-01-31 09:25:57 +08:00
Le Tan
401b1934e2 update translations 2022-01-31 09:20:35 +08:00
Le Tan
6ea5995c12 NewNoteDialog: remember default file type 2022-01-31 09:15:13 +08:00
Le Tan
b3a385693c support file associations and open with built-in editors 2022-01-30 19:49:51 +08:00
Le Tan
737f9e51d8
upgrade Qt to 5.15.2 (#2031)
upgrade Qt to 5.15.2
2022-01-27 12:49:06 +08:00
Le Tan
ef7b28b6b3 QuickAccess: support folder 2022-01-26 21:16:38 +08:00
Le Tan
993d522e15 add line ending settings for configs 2022-01-26 19:14:33 +08:00
Le Tan
969db5c3db update libs/vtextedit 2022-01-24 21:22:41 +08:00
Le Tan
8b0210eb5d refine themes 2022-01-24 20:44:06 +08:00
Le Tan
5da6a81d4b fix shortcuts in key sequence with input method 2022-01-21 21:01:31 +08:00
Le Tan
36d4248a32 release v3.12.0 2022-01-19 16:58:02 +08:00
Le Tan
b1dc1eefe6 update translations 2022-01-19 16:47:05 +08:00
Le Tan
2630999acf NotebookExplorer: support separated node explorer 2022-01-18 20:07:26 +08:00
Le Tan
a4a5dea3d7 fixes
* MarkdownViewWindow: add switch for code block line number
* MarkodwnViewWindow: fix Turndown <style> issue
* MarkdownViewWindow: add config for overridding MathJax script
* SortDialog: fix sorting issue of date
* FramelessMainWindow: fix StayOnTop issue
2022-01-10 20:06:45 +08:00
Le Tan
09dba18994 fix QDockWidget 2022-01-08 16:01:05 +08:00
Le Tan
a5671e20e5 Theme: use a FFFFFF background for pure theme 2022-01-08 09:14:04 +08:00
Le Tan
5ba1ba5b97 refine theme VSCode-dark 2022-01-07 21:25:06 +08:00
Le Tan
eff1a81125 MarkdownViewWindow: use web to highlight code block in editor 2022-01-07 20:43:08 +08:00
YiChuJun
727fade948
add dark themes (#2009)
* 新增dark主题

* 新增dark主题
2022-01-05 15:14:51 +08:00
Le Tan
126091c080 release v3.11.0 2021-12-25 09:50:51 +08:00
Le Tan
7a017e30ea refine task 2021-12-23 21:00:01 +08:00
Le Tan
293d04e2c3 fix theme 2021-12-22 20:49:10 +08:00
Le Tan
70c984353c Task: refine task 2021-12-22 20:49:00 +08:00
Nriver
87ed9250ef
add solarized-dark and solarized-light themes (#1978) 2021-12-21 20:26:24 +08:00
YaoXin
ba9a0ba459
CI Increase cache QT (#1975) 2021-12-19 16:16:19 +08:00
Le Tan
f50a834ae7 Settings: refine searching 2021-12-10 20:34:07 +08:00
Le Tan
301dace730 add FakeAccessibleInterface to fix crash of Youdao Dict 2021-12-09 21:12:31 +08:00
chendapao
b369612070
setting tree match complete (#1932) 2021-12-09 18:40:51 +08:00
tootal
911392deab
Feature Task System (#1947)
* add task system code

* enable ci build

* Revert "enable ci build"

This reverts commit 8c457a22e44e64c7d87804fc3c76ee778c1c3b6f.
2021-12-09 18:40:14 +08:00
Le Tan
fa238b3e22 MainWindow: decide DPI on the screen where vnote starts 2021-12-06 21:04:13 +08:00
Le Tan
956842f102 support equation begin in MathJax 2021-12-06 21:03:54 +08:00
Le Tan
04a93de5a5 add --disable-gpu as known cmd option 2021-11-26 20:48:27 +08:00
Le Tan
04451257d2 MarkdownEditor: align highlight syntax with read mode 2021-11-25 21:09:18 +08:00
Le Tan
2d4d4609da MarkdownEditor: fix
1. Fix issue that duplicated images would not be processed when copied;
2. Add suffix `*.nasm` to Intel ASM syntax.
2021-11-22 20:40:48 +08:00
Le Tan
ff7cf0f24e export: fix wkhtmltopdf table-of-contents in Chinese 2021-11-19 21:56:28 +08:00
Le Tan
54576543e9 fix update_version.py 2021-11-19 07:59:21 +08:00
Le Tan
14c54ab275 release v3.10.1 2021-11-17 20:51:43 +08:00
Le Tan
ecf1f8dbbe support print 2021-11-17 20:50:26 +08:00
Le Tan
6884e1ff6a MarkdownViewWindow: fix view mode 2021-11-16 21:12:20 +08:00
Le Tan
258adf10da fix tab stop width 2021-11-15 21:13:14 +08:00
Le Tan
3a4334ffd2 release v3.10.0 2021-11-13 17:35:34 +08:00
Le Tan
4d41421fb8 support input method on macOS in tag popup 2021-11-13 17:24:56 +08:00
Le Tan
e2fda18dbd MarkdownViewWindow: fix EditPreview 2021-11-12 07:53:30 +08:00
Le Tan
6b1f00880e MarkdownEditor: support EditPreview mode 2021-11-10 20:52:24 +08:00
Le Tan
86df5c39a2 TextEditor: add config for highlighting tab and trailing space 2021-11-08 09:28:18 +08:00
1068 changed files with 149614 additions and 22803 deletions

View File

@ -8,14 +8,21 @@ on:
# Allows you to run this workflow manually from the Actions tab.
workflow_dispatch:
inputs:
debug_enabled:
type: boolean
description: 'Run the build with tmate debugging enabled'
required: false
default: false
env:
VNOTE_VER: 3.9.0
VNOTE_VER: 3.19.2
CMAKE_VER: 3.24.3
jobs:
build-linux:
name: Build On Ubuntu
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
@ -30,75 +37,86 @@ jobs:
git submodule sync --recursive
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
- name: Install a Fresh CMake
run: |
wget --no-verbose https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-Linux-x86_64.sh
chmod +x cmake-${CMAKE_VER}-Linux-x86_64.sh
mkdir ${{runner.workspace}}/cmake
sudo ./cmake-${CMAKE_VER}-Linux-x86_64.sh --skip-license --prefix=${{runner.workspace}}/cmake
sudo rm -f /usr/local/bin/cmake /usr/local/bin/cpack
sudo ln -s ${{runner.workspace}}/cmake/bin/cmake /usr/local/bin/cmake
sudo ln -s ${{runner.workspace}}/cmake/bin/cpack /usr/local/bin/cpack
- name: Install linuxdeploy
uses: miurahr/install-linuxdeploy-action@v1
with:
plugins: qt appimage
- name: Install dependencies
- name: Install Dependencies
run: |
sudo DEBIAN_FRONTEND=noninteractive apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y libfcitx-qt5-dev tree
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y libfcitx5-qt-dev fcitx-libs-dev extra-cmake-modules libxkbcommon-dev
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tree
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y fuse libxcb-cursor-dev
python3 -m pip config set global.break-system-packages true
- name: Cache Qt
id: cache-qt
uses: actions/cache@v4
with:
path: ${{runner.workspace}}/Qt
key: ${{ runner.os }}-QtCache-6.8
- name: Install Qt
uses: jurplel/install-qt-action@v2
uses: jurplel/install-qt-action@v3
with:
version: 5.12.10
version: 6.8.3
target: desktop
modules: qtwebchannel qtwebengine qtsvg qtlocation qttools qttranslations
tools: tools_openssl_x64,1.1.1-4,qt.tools.openssl.gcc_64
modules: 'qtwebengine qtwebchannel qtpositioning qtpdf qtimageformats qt5compat qtserialport'
tools: 'tools_opensslv3_src'
cache: 'true'
- name: Compile OpenSSLV3
run: |
cd ${Qt6_DIR}/../../Tools/OpenSSLv3/src
./Configure
make -j2
sudo make install
- name: Create Build Dir
run: mkdir build
working-directory: ${{runner.workspace}}
- name: Compile qt5ct
- name: Compile fcitxqt5
run: |
wget -c https://excellmedia.dl.sourceforge.net/project/qt5ct/qt5ct-1.1.tar.bz2
tar xf qt5ct-1.*.tar.bz2
cd qt5ct-1.*/
QT_SELECT=5 qmake
git clone https://github.com/fcitx/fcitx-qt5
cd fcitx-qt5
mkdir build && cd build
cmake -DENABLE_QT5=OFF -DENABLE_QT6=ON ..
make -j2
sudo make install
working-directory: ${{runner.workspace}}/build
- name: Compile qt6ct
run: |
git clone https://github.com/trialuser02/qt6ct qt6ct.git
cd qt6ct.git
qmake
make -j$(nproc) && sudo make install
working-directory: ${{runner.workspace}}/build
- name: Configure Project
run: |
qmake -v
cp /usr/lib/x86_64-linux-gnu/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so ./
sudo chmod +rwx ./libfcitxplatforminputcontextplugin.so
cp ./libfcitxplatforminputcontextplugin.so $Qt5_Dir/plugins/platforminputcontexts
qmake CONFIG+=release -spec linux-g++-64 ${GITHUB_WORKSPACE}/vnote.pro
cmake --version
cmake ${GITHUB_WORKSPACE}
working-directory: ${{runner.workspace}}/build
- name: Build Project
run: make -j$(nproc)
working-directory: ${{runner.workspace}}/build
- name: Install Project
run: |
mkdir AppDir
make install INSTALL_ROOT=${{runner.workspace}}/build/AppDir
tree AppDir
working-directory: ${{runner.workspace}}/build
- name: Package Project
run: |
# Move the lib out to avoid duplication
mv AppDir/usr/lib ./
LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH
# Copy translations
mkdir -p AppDir/usr/translations
cp $Qt5_Dir/translations/qt_zh_CN.qm AppDir/usr/translations
# Package qt5ct (EXTRA_QT_PLUGINS seems not work)
# EXTRA_QT_PLUGINS="platformthemes/libqt5ct.so;styles/libqt5ct-style.so"
mkdir -p AppDir/usr/plugins/platformthemes
mkdir -p AppDir/usr/plugins/styles
cp $Qt5_Dir/plugins/platformthemes/* AppDir/usr/plugins/platformthemes/
cp $Qt5_Dir/plugins/styles/* AppDir/usr/plugins/styles/
# Package libssl.so and libcrypto.so
Qt5_Tools=$Qt5_Dir/../../Tools
linuxdeploy-x86_64.AppImage --appdir ./AppDir --plugin qt --output appimage -l $Qt5_Tools/OpenSSL/binary/lib/libcrypto.so.1.1 -l $Qt5_Tools/OpenSSL/binary/lib/libssl.so.1.1
tree AppDir
# Remove the libqsqlmimer.so as libmimerapi.so is not deployed with Qt6
rm ${{runner.workspace}}/Qt/6.*/gcc_64/plugins/sqldrivers/libqsqlmimer.so
cmake --build . --target pack
working-directory: ${{runner.workspace}}/build
- name: Fix Package
@ -115,15 +133,14 @@ jobs:
linuxdeploy-plugin-appimage-x86_64.AppImage --appdir=./squashfs-root
mv VNote*.AppImage ../
popd
mv VNote*.AppImage vnote-linux-x64_v${{env.VNOTE_VER}}.AppImage
cp vnote-linux-x64_v${{env.VNOTE_VER}}.AppImage vnote-linux-x64.AppImage
mv VNote*.AppImage VNote-${{env.VNOTE_VER}}-linux-x64.AppImage
working-directory: ${{runner.workspace}}/build
- name: Archive Artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: vnote-linux-x64_v${{env.VNOTE_VER}}
path: ${{runner.workspace}}/build/vnote-linux-x64_v${{env.VNOTE_VER}}.AppImage
name: VNote-${{env.VNOTE_VER}}-linux-x64.AppImage
path: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-linux-x64.AppImage
- name: Update Tag
if: github.ref == 'refs/heads/master'
@ -136,6 +153,17 @@ jobs:
uses: johnwbyrd/update-release@v1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: ${{runner.workspace}}/build/vnote-linux-x64.AppImage
files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-linux-x64.AppImage
release: Continuous Build
tag: continuous-build
- name: Release
if: github.ref == 'refs/heads/master' && startsWith(github.event.head_commit.message, '[Release]')
uses: ncipollo/release-action@v1.11.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-linux-x64.AppImage
commit: master
tag: v${{env.VNOTE_VER}}
allowUpdates: true
draft: true

View File

@ -8,16 +8,34 @@ on:
# Allows you to run this workflow manually from the Actions tab.
workflow_dispatch:
inputs:
debug_enabled:
type: boolean
description: 'Run the build with tmate debugging enabled'
required: false
default: false
env:
VNOTE_VER: 3.9.0
VNOTE_VER: 3.19.2
CMAKE_VER: 3.24.3
jobs:
build-linux:
build:
environment: Mac-code-sign
name: Build On MacOS
runs-on: macos-latest
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
config:
- name: "Build on Arm64"
os: macos-latest
arch: universal
qt: 6.8.3
runs-on: ${{matrix.config.os}}
steps:
# Checks-out your repository under $GITHUB_WORKSPACE.
- uses: actions/checkout@v2
@ -32,19 +50,50 @@ jobs:
- name: Install Dependencies
run: |
brew install tree
brew install tree libiodbc libpq
- name: Fix SQL
run: |
sudo mkdir -p /usr/local/opt/libiodbc/lib
sudo ln -s /opt/homebrew/opt/libiodbc/lib/libiodbc.2.dylib /usr/local/opt/libiodbc/lib/libiodbc.2.dylib
sudo mkdir -p /Applications/Postgres.app/Contents/Versions/14/lib
sudo ln -s /opt/homebrew/Cellar/libpq/16.3/lib/libpq.5.dylib /Applications/Postgres.app/Contents/Versions/14/lib/libpq.5.dylib
- name: Install a fresh CMake
run: |
wget --no-verbose https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-macos-universal.tar.gz
tar xzf cmake-${CMAKE_VER}-macos-universal.tar.gz
sudo rm -f /usr/local/bin/cmake /usr/local/bin/cpack
sudo ln -s ${{runner.workspace}}/cmake-${CMAKE_VER}-macos-universal/CMake.app/Contents/bin/cmake /usr/local/bin/cmake
sudo ln -s ${{runner.workspace}}/cmake-${CMAKE_VER}-macos-universal/CMake.app/Contents/bin/cpack /usr/local/bin/cpack
working-directory: ${{runner.workspace}}
- name: Install macdeployqtfix
run: |
git clone https://github.com/tamlok/macdeployqtfix.git macdeployqtfix --depth=1
working-directory: ${{runner.workspace}}
- name: Install Qt
uses: jurplel/install-qt-action@v2
- name: Install optool
run: |
wget --no-verbose https://github.com/alexzielenski/optool/releases/download/0.1/optool.zip
unzip ./optool.zip
sudo ln -s ./optool /usr/local/bin/optool
working-directory: ${{runner.workspace}}
- name: Cache Qt
id: cache-qt
uses: actions/cache@v4
with:
version: 5.12.10
path: ${{runner.workspace}}/Qt
key: ${{ runner.os }}-QtCache-6.8
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{matrix.config.qt}}
target: desktop
modules: qtwebchannel qtwebengine qtsvg qtlocation qttools qttranslations
modules: 'qtwebengine qtwebchannel qtpositioning qtpdf qtimageformats qt5compat qtserialport'
cache: 'true'
- name: Create Build Dir
run: mkdir build
@ -53,76 +102,111 @@ jobs:
- name: Configure Project
run: |
qmake -v
qmake CONFIG+=release ${GITHUB_WORKSPACE}/vnote.pro
cmake --version
cmake -DMACDEPLOYQTFIX_EXECUTABLE=${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" ${GITHUB_WORKSPACE}
working-directory: ${{runner.workspace}}/build
- name: Build Project
run: make -j4
working-directory: ${{runner.workspace}}/build
- name: Deploy VSyntaxHighlighting Framework
run: |
my_lib_name=VSyntaxHighlighting
my_lib_framework=${my_lib_name}.framework
my_lib_dir=./libs/vtextedit/src/libs/syntax-highlighting
frameworks_dir=./src/vnote.app/Contents/Frameworks
mkdir -p ${frameworks_dir}
cp -R ${my_lib_dir}/${my_lib_framework} ${frameworks_dir}
# Keep only required SQL drivers
rm ${{env.Qt6_DIR}}/plugins/sqldrivers/libqsqlmimer.dylib
rm ${{env.Qt6_DIR}}/plugins/sqldrivers/libqsqlodbc.dylib
rm ${{env.Qt6_DIR}}/plugins/sqldrivers/libqsqlpsql.dylib
# Build the project
cmake --build . --target pack
# Fix Qt frameworks
python3 ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py ./src/VNote.app/Contents/MacOS/VNote ${{env.Qt6_DIR}}/../..
# Only delete rpaths that exist to avoid errors
for rpath in $(otool -l ./src/VNote.app/Contents/MacOS/VNote | awk '/LC_RPATH/ {getline; getline; print $2}' | grep 'vnote'); do
echo "Checking rpath: $rpath"
if otool -l ./src/VNote.app/Contents/MacOS/VNote | grep -q "$rpath"; then
echo "Deleting rpath: $rpath"
install_name_tool -delete_rpath "$rpath" ./src/VNote.app/Contents/MacOS/VNote
else
echo "Rpath not found: $rpath"
fi
done
for rpath in $(otool -l ./src/VNote.app/Contents/Frameworks/libVTextEdit.dylib | awk '/LC_RPATH/ {getline; getline; print $2}' | grep 'vnote'); do
echo "Checking rpath: $rpath"
if otool -l ./src/VNote.app/Contents/Frameworks/libVTextEdit.dylib | grep -q "$rpath"; then
echo "Deleting rpath: $rpath"
install_name_tool -delete_rpath "$rpath" ./src/VNote.app/Contents/Frameworks/libVTextEdit.dylib
else
echo "Rpath not found: $rpath"
fi
done
# Run macdeployqtfix again to ensure all dependencies are properly fixed
python3 ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py ./src/VNote.app/Contents/MacOS/VNote ${{env.Qt6_DIR}}/../..
working-directory: ${{runner.workspace}}/build
- name: Deploy VTextEdit Framework
- name: Codesign Bundle
# Extract the secrets we defined earlier as environment variables
env:
MACOS_CERTIFICATE: ${{ secrets.CLI_MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.CLI_MACOS_CERTIFICATE_PWD }}
MACOS_CERTIFICATE_NAME: ${{ secrets.CLI_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.CLI_MACOS_CERTIFICATE }}
run: |
my_lib_name=VTextEdit
my_lib_framework=${my_lib_name}.framework
my_lib_dir=./libs/vtextedit/src/editor
frameworks_dir=./src/vnote.app/Contents/Frameworks
mkdir -p ${frameworks_dir}
cp -R ${my_lib_dir}/${my_lib_framework} ${frameworks_dir}
# Turn our base64-encoded certificate back to a regular .p12 file
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
# We need to create a new keychain, otherwise using the certificate will prompt
# with a UI dialog asking for the certificate password, which we can't
# use in a headless CI environment
security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain
echo "Codesigning main app bundle"
codesign --force --deep -s "$MACOS_CERTIFICATE_NAME" --entitlements ${{github.workspace}}/package/entitlements.xml --options runtime ./src/VNote.app
codesign -v -vvv ./src/VNote.app
hdiutil create -volname "VNote" -srcfolder ./src/VNote.app -ov -format UDZO VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
codesign --force --deep -s "$MACOS_CERTIFICATE_NAME" --entitlements ${{github.workspace}}/package/entitlements.xml --options runtime ./VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
codesign -v -vvv ./VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
working-directory: ${{runner.workspace}}/build
- name: Cleanup rpath
- name: "Notarize Bundle"
# Extract the secrets we defined earlier as environment variables
env:
PROD_MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.CLI_MACOS_NOTARY_USER }}
PROD_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.CLI_MACOS_TEAM_ID }}
PROD_MACOS_NOTARIZATION_PWD: ${{ secrets.CLI_MACOS_NOTARY_PWD }}
run: |
app_target=./src/vnote.app/Contents/MacOS/vnote
install_name_tool -delete_rpath ${PWD}/src/../libs/vtextedit/src/editor ${app_target}
install_name_tool -delete_rpath ${PWD}/src/../libs/vtextedit/src/libs/syntax-highlighting ${app_target}
working-directory: ${{runner.workspace}}/build
# Store the notarization credentials so that we can prevent a UI password dialog
# from blocking the CI
echo "Create keychain profile"
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" --password "$PROD_MACOS_NOTARIZATION_PWD"
- name: Mac Deploy
run: |
pushd src
macdeployqt vnote.app
# Here we send the notarization request to the Apple's Notarization service, waiting for the result.
# This typically takes a few seconds inside a CI environment, but it might take more depending on the App
# characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if
# you're curious
echo "Notarize app"
xcrun notarytool submit "${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg" --keychain-profile "notarytool-profile" --wait
# Remove unused sql drivers
rm vnote.app/Contents/Plugins/sqldrivers/libqsqlodbc.dylib
rm vnote.app/Contents/Plugins/sqldrivers/libqsqlpsql.dylib
# Finally, we need to "attach the staple" to our executable, which will allow our app to be
# validated by macOS even when an internet connection is not available.
echo "Attach staple"
xcrun stapler staple "${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg"
python ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py vnote.app/Contents/MacOS/vnote $Qt5_Dir
# Fix Helpers/QtWebEngineProcess.app
pushd vnote.app/Contents/Frameworks/QtWebEngineCore.framework/Versions/5/Helpers
python ${{runner.workspace}}/macdeployqtfix/macdeployqtfix.py QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess $Qt5_Dir
popd
# Enable tmate debugging of manually-triggered workflows if the input option was provided
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
popd
tree ./
working-directory: ${{runner.workspace}}/build
- name: Package Project
run: |
mkdir -p distrib/vnote
pushd distrib/vnote
mv ../../src/vnote.app ./
ln -s /Applications ./Applications
popd
sleep 1m
hdiutil create -srcfolder ./distrib/vnote -format UDBZ vnote-mac-x64_v${{env.VNOTE_VER}}.dmg
cp vnote-mac-x64_v${{env.VNOTE_VER}}.dmg vnote-mac-x64.dmg
working-directory: ${{runner.workspace}}/build
- name: Archive Artifacts
uses: actions/upload-artifact@v2
- name: Archive DMG
uses: actions/upload-artifact@v4
with:
name: vnote-mac-x64_v${{env.VNOTE_VER}}
path: ${{runner.workspace}}/build/vnote-mac-x64_v${{env.VNOTE_VER}}.dmg
name: VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}
path: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
- name: Update Tag
if: github.ref == 'refs/heads/master'
@ -135,6 +219,17 @@ jobs:
uses: johnwbyrd/update-release@v1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: ${{runner.workspace}}/build/vnote-mac-x64.dmg
files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
release: Continuous Build
tag: continuous-build
- name: Release
if: github.ref == 'refs/heads/master' && startsWith(github.event.head_commit.message, '[Release]')
uses: ncipollo/release-action@v1.11.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-mac-${{matrix.config.arch}}.dmg
commit: master
tag: v${{env.VNOTE_VER}}
allowUpdates: true
draft: true

View File

@ -8,29 +8,44 @@ on:
# Allows you to run this workflow manually from the Actions tab.
workflow_dispatch:
inputs:
debug_enabled:
type: boolean
description: 'Run the build with tmate debugging enabled'
required: false
default: false
env:
VNOTE_VER: 3.9.0
VNOTE_VER: 3.19.2
jobs:
build:
name: ${{ matrix.config.name }}
runs-on: windows-latest
runs-on: windows-${{ matrix.config.vs_version }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
config:
- {
name: "Build On Win64",
arch: win64_msvc2017_64,
platform: x64
}
- {
name: "Build On Win32",
arch: win32_msvc2017,
platform: x86
}
- name: "Build on Win64 Qt 5.15"
arch: win64_msvc2019_64
vs_version: 2019
vs_cmd: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Auxiliary\\Build\\vcvars64.bat"
qt: 5.15.2
qt_modules: qtwebengine
qt_tools: tools_opensslv3_x64
qt_major: 5
suffix: "-windows7"
- name: "Build on Win64 Qt 6"
arch: win64_msvc2022_64
vs_version: 2022
vs_cmd: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Auxiliary\\Build\\vcvars64.bat"
qt: 6.8.3
qt_modules: "qtwebengine qtwebchannel qtpositioning qtpdf qtimageformats qt5compat"
qt_tools: tools_opensslv3_x64
qt_major: 6
suffix: ""
steps:
# Checks-out your repository under $GITHUB_WORKSPACE.
@ -45,65 +60,72 @@ jobs:
git submodule sync --recursive
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
- name: Install Qt
uses: jurplel/install-qt-action@v2
- name: Cache Qt
id: cache-qt
uses: actions/cache@v4
with:
version: 5.12.10
path: ${{runner.workspace}}/Qt
key: ${{runner.os}}-${{matrix.config.arch}}-QtCache-${{matrix.config.qt}}
- name: Install Qt Official Build
uses: jurplel/install-qt-action@v3
with:
version: ${{matrix.config.qt}}
target: desktop
arch: ${{matrix.config.arch}}
modules: qtwebchannel qtwebengine qtsvg qtlocation qttools qttranslations
tools: tools_openssl_${{matrix.config.platform}},1.1.1-4,qt.tools.openssl.win_${{matrix.config.platform}}
modules: ${{matrix.config.qt_modules}}
tools: ${{matrix.config.qt_tools}}
cache: 'true'
- name: Create Build Dir
shell: bash
run: mkdir build
working-directory: ${{runner.workspace}}
- name: Clone OpenSSL
- name: Clone OpenSSL on 5.15
shell: bash
if: ${{startsWith(matrix.config.qt, '5.15')}}
run: |
git clone https://github.com/tamlok/openssl-utils.git openssl-utils.git --depth=1
working-directory: ${{runner.workspace}}/build
# Enable tmate debugging of manually-triggered workflows if the input option was provided
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
- name: Configure and Build Project
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" ${{matrix.config.platform}}
qmake -r -spec win32-msvc CONFIG-=debug CONFIG+=release %GITHUB_WORKSPACE%\vnote.pro
nmake
cmake --version
call "${{matrix.config.vs_cmd}}"
cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DQT_DEFAULT_MAJOR_VERSION=${{matrix.config.qt_major}} -DOPENSSL_EXTRA_LIB_DIR=${{runner.workspace}}\build\openssl-utils.git\1.1.1j\Win_x64 %GITHUB_WORKSPACE%
cmake --build .
cmake --build . --target=pack
7z x VNote*.zip -o*
dir
working-directory: ${{runner.workspace}}/build
- name: Package Project
shell: cmd
- name: Rename on 5.15
shell: bash
if: ${{startsWith(matrix.config.qt, '5.15')}}
run: |
mkdir "%DISTRIB_PATH%"
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" ${{matrix.config.platform}}
windeployqt.exe --dir "%DISTRIB_PATH%" .\src\release\vnote.exe
copy .\src\release\vnote.exe "%DISTRIB_PATH%\vnote.exe"
copy .\src\release\vnote_extra.rcc "%DISTRIB_PATH%\vnote_extra.rcc"
copy .\libs\vtextedit\src\libs\syntax-highlighting\release\VSyntaxHighlighting.dll "%DISTRIB_PATH%\VSyntaxHighlighting.dll"
copy .\libs\vtextedit\src\editor\release\VTextEdit.dll "%DISTRIB_PATH%\VTextEdit.dll"
rem set qt_dir=%Qt5_Dir:/=\%
rem for %%I in ("%qt_dir%\..\..") do set "qt_topdir=%%~fI"
rem set openssl_dir=%qt_topdir%\Tools\OpenSSL\Win_${{matrix.config.platform}}\bin
set openssl_dir=openssl-utils.git\1.1.1g\Win_${{matrix.config.platform}}
copy %openssl_dir%\lib*.dll "%DISTRIB_PATH%\"
copy "%GITHUB_WORKSPACE%\README.md" "%DISTRIB_PATH%\README.md"
copy "%GITHUB_WORKSPACE%\COPYING.LESSER" "%DISTRIB_PATH%\COPYING.LESSER"
echo %GITHUB_SHA% > "%DISTRIB_PATH%\commit"
del /F /Q "%DISTRIB_PATH%\translations\qt_*.qm"
7z a vnote-win-${{matrix.config.platform}}.zip "%DISTRIB_PATH%"
mv VNote-${{env.VNOTE_VER}}-win64 VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}
mv VNote-${{env.VNOTE_VER}}-win64.zip VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}.zip
working-directory: ${{runner.workspace}}/build
env:
DISTRIB_PATH: ${{runner.workspace}}/build/distrib/vnote
- name: Archive Artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: vnote-win-${{matrix.config.platform}}_v${{env.VNOTE_VER}}
path: ${{env.DISTRIB_PATH}}
env:
DISTRIB_PATH: ${{runner.workspace}}/build/distrib/vnote
name: VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}
path: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}
- name: Archive Installer
if: ${{!startsWith(matrix.config.qt, '5.15')}}
uses: actions/upload-artifact@v4
with:
name: VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}.msi
path: ${{runner.workspace}}/build/VNote*.msi
- name: Update Tag
if: github.ref == 'refs/heads/master'
@ -117,6 +139,19 @@ jobs:
uses: johnwbyrd/update-release@v1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: ${{runner.workspace}}/build/vnote-win-${{matrix.config.platform}}.zip
# glob not supported
files: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}.zip
release: Continuous Build
tag: continuous-build
- name: Release
if: github.ref == 'refs/heads/master' && startsWith(github.event.head_commit.message, '[Release]')
uses: ncipollo/release-action@v1.11.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
# glob not supported
artifacts: ${{runner.workspace}}/build/VNote-${{env.VNOTE_VER}}-win64${{matrix.config.suffix}}
commit: master
tag: v${{env.VNOTE_VER}}
allowUpdates: true
draft: true

12
.gitignore vendored
View File

@ -2,4 +2,16 @@
*.pro.user.*
.ccls
compile_commands.json
compile_commands.json.*
compile_flags.txt
.cache
.tasks
.vimspector.json
GPATH
GRTAGS
GTAGS
aqtinstall.log
tags
CMakeLists.txt.user
build
.DS_Store

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "libs/vtextedit"]
path = libs/vtextedit
url = https://github.com/vnotex/vtextedit.git
[submodule "libs/QHotkey"]
path = libs/QHotkey
url = https://github.com/vnotex/QHotkey.git

26
CMakeLists.txt Normal file
View File

@ -0,0 +1,26 @@
cmake_minimum_required (VERSION 3.20)
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.1" CACHE STRING "Minimum OS X deployment version")
project(VNote
VERSION 3.19.2
DESCRIPTION "A pleasant note-taking platform"
HOMEPAGE_URL "https://app.vnote.fun"
LANGUAGES C CXX)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type, defaults to Release")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(QHOTKEY_INSTALL OFF CACHE BOOL "Disable installing QHotKey" FORCE)
add_subdirectory(libs)
add_subdirectory(src)
# TODO: find a better way to organize tests
# add_subdirectory(tests)

View File

@ -1,11 +1,13 @@
# VNote
![CI-Windows](https://github.com/vnotex/vnote/workflows/CI-Windows/badge.svg) ![CI-Linux](https://github.com/vnotex/vnote/workflows/CI-Linux/badge.svg) ![CI-MacOS](https://github.com/vnotex/vnote/workflows/CI-MacOS/badge.svg)
![CI-Windows](https://github.com/vnotex/vnote/actions/workflows/ci-win.yml/badge.svg?branch=master) ![CI-Linux](https://github.com/vnotex/vnote/actions/workflows/ci-linux.yml/badge.svg?branch=master) ![CI-MacOS](https://github.com/vnotex/vnote/actions/workflows/ci-macos.yml/badge.svg?branch=master)
[简体中文](README_zh_CN.md)
[Project on Gitee](https://gitee.com/vnotex/vnote)
A pleasant note-taking platform.
For more information, please visit [**VNote's Home Page**](https://vnotex.github.io/vnote) or [Home Page on Gitee](https://tamlok.gitee.io/vnote).
For more information, please visit [**VNote's Home Page**](https://vnotex.github.io/vnote).
![VNote](pics/vnote.png)
@ -18,39 +20,22 @@ Utilizing Qt, VNote could run on **Linux**, **Windows**, and **macOS**.
![Main](pics/main.png)
![Main2](pics/main2.png)
## Downloads
Continuous builds on `master` branch could be found at the [Continuous Build](https://github.com/vnotex/vnote/releases/tag/continuous-build) release.
Latest stable builds could be found at the [latest release](https://github.com/vnotex/vnote/releases/latest). Alternative download services are available:
* [Tianyi Netdisk](https://cloud.189.cn/t/Av67NvmEJVBv)
* [Baidu Netdisk](https://pan.baidu.com/s/1Fou1flmBsQUQ8Qs9V_M6Aw) with the code `note`
* [Baidu Netdisk](https://pan.baidu.com/s/1lX69oMBw8XuJshQDN3HiHw?pwd=f8fk)
## Supports
* [GitHub Issues](https://github.com/vnotex/vnote/issues);
* Email: `tamlokveer at gmail.com`;
* [Slack](https://join.slack.com/t/vnote/shared_invite/enQtNDg2MzY0NDg3NzI4LTVhMzBlOTY0YzVhMmQyMTFmZDdhY2M3MDQxYTBjOTA2Y2IxOGRiZjg2NzdhMjkzYmUyY2VkMWJlZTNhMTQyODU);
* [Telegram](https://t.me/vnotex);
* WeChat Public Account: vnotex;
## Donate
You could help VNote's development in many ways.
* Keep monitoring VNote and sending feedback for improvement.
* Spread and promote VNote to your friends. Popularity is a strong power to drive developers.
* Participate in the development of VNote and send [Pull Request](https://github.com/vnotex/vnote/pulls) to make VNote perfect.
* Last, really appreciate your donations to VNote if VNote does help.
**PayPal**: [PayPal.Me/vnotemd](https://www.paypal.me/vnotemd)
**Alipay**: `tamlokveer@gmail.com`
<img src="pics/alipay.png" width="256px" height="256px" />
**WeChat**
<img src="pics/wechat_pay.png" width="256px" height="256px" />
Thank [users who donated to VNote](https://github.com/vnotex/vnote/wiki/Donate-List)!
## License

View File

@ -1,11 +1,13 @@
# VNote
![CI-Windows](https://github.com/vnotex/vnote/workflows/CI-Windows/badge.svg) ![CI-Linux](https://github.com/vnotex/vnote/workflows/CI-Linux/badge.svg) ![CI-MacOS](https://github.com/vnotex/vnote/workflows/CI-MacOS/badge.svg)
![CI-Windows](https://github.com/vnotex/vnote/actions/workflows/ci-win.yml/badge.svg?branch=master) ![CI-Linux](https://github.com/vnotex/vnote/actions/workflows/ci-linux.yml/badge.svg?branch=master) ![CI-MacOS](https://github.com/vnotex/vnote/actions/workflows/ci-macos.yml/badge.svg?branch=master)
[English](README.md)
[Gitee托管项目](https://gitee.com/vnotex/vnote)
一个舒适的笔记平台!
更多信息,请访问 [VNote 主页](https://tamlok.gitee.io/vnote) 或者[由 Gitee 托管的主页](https://tamlok.gitee.io/vnote)
更多信息,请访问[VNote主页](https://vnotex.github.io/vnote)
![VNote](pics/vnote.png)
@ -18,38 +20,21 @@ VNote 不是一个简单的 Markdown 编辑器。通过提供强大的笔记管
![主界面](pics/main.png)
![主界面2](pics/main2.png)
## 下载
基于`master`分支的[持续构建版本发布](https://github.com/vnotex/vnote/releases/tag/continuous-build)。
最新的[稳定版本发布](https://github.com/vnotex/vnote/releases/latest)。其他下载选项:
* [天翼云盘](https://cloud.189.cn/t/Av67NvmEJVBv)
* [百度云盘](https://pan.baidu.com/s/1Fou1flmBsQUQ8Qs9V_M6Aw) 提取码 `note`
* [百度云盘](https://pan.baidu.com/s/1lX69oMBw8XuJshQDN3HiHw?pwd=f8fk)
## 支持
* [GitHub Issues](https://github.com/vnotex/vnote/issues)
* 邮件:`tamlokveer at gmail.com`
* [Slack](https://join.slack.com/t/vnote/shared_invite/enQtNDg2MzY0NDg3NzI4LTVhMzBlOTY0YzVhMmQyMTFmZDdhY2M3MDQxYTBjOTA2Y2IxOGRiZjg2NzdhMjkzYmUyY2VkMWJlZTNhMTQyODU)
* [Telegram](https://t.me/vnotex)
* 微信公众号: vnotex
## 捐赠
有很多方式可以帮助 VNote 的开发:
* 持续关注 VNote 并反馈问题以帮助改进;
* 推荐 VNote 给朋友,口碑传播;
* 参与 VNote 的开发,发起 [拉取请求](https://github.com/vnotex/vnote/pulls) 一起改进 VNote
* 如果 VNote 真的好用,可以考虑捐赠;
**PayPal**: [PayPal.Me/vnotemd](https://www.paypal.me/vnotemd)
**支付宝**: `tamlokveer@gmail.com`
<img src="pics/alipay.png" width="256px" height="256px" />
**微信**
<img src="pics/wechat_pay.png" width="256px" height="256px" />
* 微信公众号:`vnotex`
感谢这些[捐赠用户](https://github.com/vnotex/vnote/wiki/Donate-List)

15
SECURITY.md Normal file
View File

@ -0,0 +1,15 @@
# Security Policy
## Supported Versions
Only the latest version is supported with security updates.
## Reporting a Vulnerability
Please [contact support](mailto:tamlokveer@gmail.com) **with a proof of concept** that shows the security vulnerability. Please do not contact us without this proof of concept, as we cannot fix anything without this.
For general opinions on what makes an app more or less secure, please use the forum.
## Bounty
We **do not** offer a bounty for discovering vulnerabilities, please do not ask. We can however credit you and link to your website/profile in the changelog and release announcement.

View File

@ -1,4 +1,105 @@
# Changes
## v3.19.2
* Codesign MacOS Bundle
* Fix toolbar expansion button style
* Support hot-reloading of theme via --watch-themes option
## v3.19.1
* Fix toolbar button in Qt 6.8
## v3.19.0
* Add VSCode-sytle editor shortcuts
## v3.18.1
* Fix crash caused by Qt6 change
* Fix XSS protection exemption
* Check link before open
## v3.18.0
* Upgrade to Qt6
* Support MacOS universal build
* Upgrade Mermaid, Flowchart.js, and markdown-it
* Markdown-it
* Fix XSS protection and turn it on by default
* Support mark by `==xx==`
## v3.17.0
* Quick note: create note in given scheme (@feloxx)
* MarkdownEditor: support inserting multiple images (@feloxx)
* Mermaid: upgrade and fix preview issue (@ygcaicn)
* Flowchart.js: upgrade
## v3.16.0
* Support reading PDF format
* Support Ming Map editor in suffix `*.emind`
* Support "View By" for notebooks selector
* ViewWindow: add shortcut Ctrl+G,V to alternate among view modes
* Bug fixes
## v3.15.1
* Add two themes
* Bug fixes
## v3.15.0
* Editor supports Word Count
* Add Open Windows panel
* Theme: add Vue-light theme
* Support default open mode
* NotebookSelector: support dynamic icons for notebooks
## v3.14.0
* Theme: support custom icons
* Theme: refine icons
* NavigationMode: fix issue for input method
## v3.13.1
* Shortcuts for Copy/Paste/Properties in node explorer
* Global shortcut to call out main window
* UnitedEntry: bug fix for macOS
## v3.13.0
* United Entry: migration of Universal Entry
## v3.12.888
* Fix shortcuts in key sequence with input method (like `Ctrl+G, E`)
* Add line ending settings for config files
* FindAndReplace: fix zero-length search
* QuickAccess: support folders
* Upgrade to Qt 5.15.2
* Support file associations
* NewNoteDialog: remember default file type
## v3.12.0
* NotebookExplorer: support separate node explorer
* Theme: add user-provided VSCode-Dark theme
* MarkdownEditor: use web to highlight code blocks
* MarkdownViewWindow
* Add switch for code block line number
* Fix ParseToMarkdown `<style>` issue
* Add config for overridding MathJax script
* SortDialog: fix sorting issue of date
* FramelessMainWindow: fix StayOnTop issue
## v3.11.0
* Task: support a simple task system (@tootal)
* Theme: add user-provided Solarized-Dark and Solarized-Light themes
* Export: fix wkhtmltopdf table-of-contents translation
* Support equation begin in MathJax
* MainWindow: decide DPI on the screen vnote starts
* Settings: support searching
* Fix crash caused by Youdao Dict
## v3.10.1
* MarkdownEditor: fix view mode issue
* Support print
* Refine icons
## v3.10.0
* MarkdownEditor
* Support side-by-side edit with preview
* Support config for highlighting whitespace
* Tag: fix input method issue on macOS
## v3.9.0
* Remove recycle bin node (now recycle bin is just a simple folder)
* Quick Access: support removing items directly

2
libs/CMakeLists.txt Normal file
View File

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

1
libs/QHotkey Submodule

@ -0,0 +1 @@
Subproject commit 8abe0b2280533af57f423f5785acc4d9d4d73ab8

View File

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

@ -1 +1 @@
Subproject commit 6c2ff0e78aedb6d4a107cd4825473d47813596cc
Subproject commit 50b1421793af3882ddc62ad4e6cf5537e1d7906f

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.cs.debugger</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>
</dict>
</plist>

BIN
package/banner_text.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

24
package/entitlements.xml Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.cs.debugger</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>
</dict>
</plist>

110
package/lgpl-3.0.rtf Normal file
View File

@ -0,0 +1,110 @@
{\rtf1\ansi\deff3\adeflang1025
{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Liberation Serif{\*\falt Times New Roman};}{\f4\fswiss\fprq2\fcharset0 Liberation Sans{\*\falt Arial};}{\f5\froman\fprq0\fcharset128 Helvetica{\*\falt Arial};}{\f6\fnil\fprq2\fcharset0 Droid Sans Fallback;}{\f7\fnil\fprq2\fcharset0 FreeSans;}{\f8\fswiss\fprq0\fcharset128 FreeSans;}}
{\colortbl;\red0\green0\blue0;\red0\green0\blue128;\red128\green128\blue128;}
{\stylesheet{\s0\snext0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033 Normal;}
{\*\cs15\snext15\cf2\ul\ulc0\langfe255\alang255\lang255 Internet Link;}
{\s16\sbasedon0\snext17\sb240\sa120\keepn\dbch\af6\dbch\af7\afs28\loch\f4\fs28 Heading;}
{\s17\sbasedon0\snext17\sl288\slmult1\sb0\sa140 Text Body;}
{\s18\sbasedon17\snext18\sl288\slmult1\sb0\sa140\dbch\af8 List;}
{\s19\sbasedon0\snext19\sb120\sa120\noline\i\dbch\af8\afs24\ai\fs24 Caption;}
{\s20\sbasedon0\snext20\noline\dbch\af8 Index;}
}{\info{\creatim\yr0\mo0\dy0\hr0\min0}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}{\comment LibreOffice}{\vern67241986}}\deftab720
\viewscale100
{\*\pgdsctbl
{\pgdsc0\pgdscuse451\pgwsxn12240\pghsxn15840\marglsxn1800\margrsxn1800\margtsxn1440\margbsxn1440\pgdscnxt0 Default Style;}}
\formshade{\*\pgdscno0}\paperh15840\paperw12240\margl1800\margr1800\margt1440\margb1440\sectd\sbknone\sectunlocked1\pgndec\pgwsxn12240\pghsxn15840\marglsxn1800\margrsxn1800\margtsxn1440\margbsxn1440\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc
\pgndec\pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs28\loch\f5
GNU LESSER GENERAL PUBLIC LICENSE}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
Version 3, 29 June 2007}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
Copyright \u169\'3f 2007 Free Software Foundation, Inc. <}{{\field{\*\fldinst HYPERLINK "https://fsf.org/" }{\fldrslt {\cf2\ul\ulc0\langfe255\alang255\lang255\ul\ulc0\rtlch \ltrch\loch\loch\f5
https://fsf.org/}{}}}\rtlch \ltrch\loch\loch\f5
>}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
0. Additional Definitions.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
As used herein, \uc2 \u8220\'81\'67this License\u8221\'81\'68 refers to version 3 of the GNU Lesser General Public License, and the \u8220\'81\'67GNU GPL\u8221\'81\'68 refers to version 3 of the GNU General Public License.\uc1 }
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\f5
\uc2 \u8220\'81\'67\uc1 }{\rtlch \ltrch\loch\loch\f5
The Library\uc2 \u8221\'81\'68 refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.\uc1 }
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
An \uc2 \u8220\'81\'67Application\u8221\'81\'68 is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.\uc1 }
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
A \uc2 \u8220\'81\'67Combined Work\u8221\'81\'68 is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the \u8220\'81\'67Linked Version\u8221\'81\'68.\uc1 }
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
The \uc2 \u8220\'81\'67Minimal Corresponding Source\u8221\'81\'68 for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.\uc1 }
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
The \uc2 \u8220\'81\'67Corresponding Application Code\u8221\'81\'68 for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.\uc1 }
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
1. Exception to Section 3 of the GNU GPL.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
2. Conveying Modified Versions.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
\tab a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
\tab b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
3. Object Code Incorporating Material from Library Header Files.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
\tab a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
\tab b) Accompany the object code with a copy of the GNU GPL and this license document.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
4. Combined Works.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
\tab a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
\tab b) Accompany the Combined Work with a copy of the GNU GPL and this license document.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
\tab c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
\tab d) Do one of the following:}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li720\ri0\lin720\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8211\'3f}{\rtlch \ltrch\loch\loch\f5
\tab 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li720\ri0\lin720\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8211\'3f}{\rtlch \ltrch\loch\loch\f5
\tab 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
\tab e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
5. Combined Libraries.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
\tab a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li360\ri0\lin360\rin0\fi-360\sb0\sa0{\rtlch \ltrch\loch\f5
\u8226\'3f}{\rtlch \ltrch\loch\loch\f5
\tab b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\b\rtlch \ltrch\loch\fs24\loch\f5
6. Revised Versions of the GNU Lesser General Public License.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.}
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License \uc2 \u8220\'81\'67or any later version\u8221\'81\'68 applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.\uc1 }
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\fs24\lang1033\ql\li0\ri0\lin0\rin0\fi0\sb0\sa180{\rtlch \ltrch\loch\loch\f5
If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.}
\par }

2
package/qt.conf Normal file
View File

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

BIN
package/wix_banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
package/wix_dialog.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
pics/main2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

26
privacy_policy.md Normal file
View File

@ -0,0 +1,26 @@
# Privacy Policy
## Introduction
Welcome to VNote! We value your privacy and are committed to protecting your personal information. This Privacy Policy explains how we handle your information when you use our application.
## Information We Collect
VNote does not collect any personal or usage data from its users. Your interactions with the app remain private and are not stored or shared.
## How We Use Your Information
Since VNote does not collect any user data, we do not use, store, or process any personal information.
## Sharing Your Information
As VNote does not collect any user data, we do not share any personal information with third parties.
## Security
While VNote does not collect any user data, we still implement security measures to ensure the integrity and safety of the application itself.
## Your Choices
Since no data is collected, there are no choices or actions required from users regarding their personal information.
## Children's Privacy
Our services are not intended for children under the age of 13. We do not knowingly collect personal information from children under 13.
## Changes to This Privacy Policy
We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on our app. You are advised to review this Privacy Policy periodically for any changes.

View File

@ -1,33 +0,0 @@
@echo off
rem Update .ccls project file for ccls LPS and compile_flags.txt for clangd
if "%~1"=="" (
echo missing argument: the location of Qt's include directory
EXIT /B 0
)
set qt_inc=%~1
set qt_inc=%qt_inc:\=\\%
(
echo clang
echo -fcxx-exceptions
echo -std=c++14
echo -Isrc\\core
echo -Isrc
echo -Ilibs\\vtextedit\\src\\editor\\include
echo -Ilibs\\vtitlebar\\src
echo -I%qt_inc%
echo -I%qt_inc%\\QtCore
echo -I%qt_inc%\\QtWebEngineWidgets
echo -I%qt_inc%\\QtSvg
echo -I%qt_inc%\\QtPrintSupport
echo -I%qt_inc%\\QtWidgets
echo -I%qt_inc%\\QtWebEngineCore
echo -I%qt_inc%\\QtGui
echo -I%qt_inc%\\QtWebChannel
echo -I%qt_inc%\\QtNetwork
echo -I%qt_inc%\\QtTest
) > ".ccls"
copy /Y .ccls compile_flags.txt

View File

@ -1,30 +0,0 @@
#!/bin/sh
if [ -n "$1" ]; then
echo Qt include directory: $1
else
echo Please specify the Qt include directory.
exit
fi
ccls_file=".ccls"
echo clang > $ccls_file
echo -fcxx-exceptions >> $ccls_file
echo -std=c++14 >> $ccls_file
echo -Isrc/core >> $ccls_file
echo -Isrc >> $ccls_file
echo -Ilibs/vtextedit/src/editor/include >> $ccls_file
echo -Ilibs/vtitlebar/src >> $ccls_file
echo -I$1 >> $ccls_file
echo -I$1/QtCore >> $ccls_file
echo -I$1/QtWebEngineWidgets >> $ccls_file
echo -I$1/QtSvg >> $ccls_file
echo -I$1/QtPrintSupport >> $ccls_file
echo -I$1/QtWidgets >> $ccls_file
echo -I$1/QtWebEngineCore >> $ccls_file
echo -I$1/QtGui >> $ccls_file
echo -I$1/QtWebChannel >> $ccls_file
echo -I$1/QtNetwork >> $ccls_file
echo -I$1/QtTest >> $ccls_file
cp -f .ccls compile_flags.txt

View File

@ -7,8 +7,14 @@ if len(sys.argv) < 2:
exit
newVersion = sys.argv[1]
shortVersion = re.match('^(\\d+\\.\\d+).', newVersion).group(1)
print("New version: {0}".format(newVersion))
# CMakeList
regExp = re.compile('(\\s+)VERSION \\S+')
for line in fileinput.input(['CMakeLists.txt'], inplace = True):
print(regExp.sub('\\1VERSION ' + newVersion, line), end='')
# vnotex.json
regExp = re.compile('(\\s+)"version" : "\\S+"')
for line in fileinput.input(['src/data/core/vnotex.json'], inplace = True):
@ -20,10 +26,14 @@ for line in fileinput.input(['.github/workflows/ci-win.yml', '.github/workflows/
print(regExp.sub('\\1VNOTE_VER: ' + newVersion, line), end='')
# Info.plist
regExp = re.compile('(\\s+)<string>\\d\\.\\d\\.\\d</string>')
regExp = re.compile('(\\s+)<string>(?!10\\.15)\\d+\\.\\d+</string>')
for line in fileinput.input(['src/data/core/Info.plist'], inplace = True):
print(regExp.sub('\\1<string>' + shortVersion + '</string>', line), end='')
regExp = re.compile('(\\s+)<string>\\d+\\.\\d+\\.\\d+</string>')
for line in fileinput.input(['src/data/core/Info.plist'], inplace = True):
print(regExp.sub('\\1<string>' + newVersion + '</string>', line), end='')
regExp = re.compile('(\\s+)<string>\\d\\.\\d\\.\\d\\.\\d</string>')
regExp = re.compile('(\\s+)<string>\\d+\\.\\d+\\.\\d+\\.\\d+</string>')
for line in fileinput.input(['src/data/core/Info.plist'], inplace = True):
print(regExp.sub('\\1<string>' + newVersion + '.1</string>', line), end='')

179
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,179 @@
cmake_minimum_required(VERSION 3.20)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_INSTALL_BINDIR "." CACHE STRING "Binary dir for install")
set(QT_DEFAULT_MAJOR_VERSION 6 CACHE STRING "Qt version to use (5 or 6), defaults to 6")
find_package(Qt${QT_DEFAULT_MAJOR_VERSION} REQUIRED COMPONENTS Core Gui Network PrintSupport Sql Svg Widgets WebChannel WebEngineWidgets LinguistTools)
find_package(Qt${QT_DEFAULT_MAJOR_VERSION} OPTIONAL_COMPONENTS Core5Compat)
if ((QT_DEFAULT_MAJOR_VERSION GREATER 5))
qt_standard_project_setup()
else()
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
endif()
# Application icon on Windows
set(VX_APP_ICON_RC_WIN data/core/icons/vnote.rc)
# The MACOSX_BUNDLE_ICON_FILE variable is added to the Info.plist
# generated by CMake. This variable contains the .icns file name,
# without the path.
set(MACOSX_BUNDLE_ICON_FILE vnote.icns)
# And the following tells CMake where to find and install the file itself.
set(VX_APP_ICON_MACOS data/core/icons/vnote.icns)
set_source_files_properties(${VX_APP_ICON_MACOS} PROPERTIES
MACOSX_PACKAGE_LOCATION "Resources")
# Translations
set(VX_TS_FILES data/core/translations/vnote_zh_CN.ts
data/core/translations/vnote_ja.ts)
if((QT_DEFAULT_MAJOR_VERSION EQUAL 6))
if((Qt6Widgets_VERSION VERSION_GREATER_EQUAL 6.7.0))
qt_add_lupdate(TS_FILES ${VX_TS_FILES}
SOURCE_TARGETS vnote)
else()
qt_add_lupdate(vnote TS_FILES ${VX_TS_FILES})
endif()
endif()
# Generate .qm files from .ts files (lrelease)
set_source_files_properties(${VX_TS_FILES} PROPERTIES
OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
qt_add_translation(VX_QM_FILES ${VX_TS_FILES})
add_custom_target(lrelease DEPENDS ${VX_QM_FILES})
# TODO: VTextEdit translations
list(APPEND VX_QM_FILES
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qdialogbuttonbox_zh_CN.qm
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtbase_ja.qm
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtbase_zh_CN.qm
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtv_ja.qm
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qtv_zh_CN.qm
${CMAKE_CURRENT_LIST_DIR}/data/core/translations/qwebengine_zh_CN.qm
)
# Resources
set(VX_RESOURCE_FILES data/core/core.qrc)
set(VX_EXTRA_RESOURCE_FILES_RCC ${CMAKE_CURRENT_BINARY_DIR}/vnote_extra.rcc)
qt_add_binary_resources(VX_EXTRA_RESOURCE data/extra/extra.qrc DESTINATION ${VX_EXTRA_RESOURCE_FILES_RCC} OPTIONS -compress 9)
add_executable(vnote WIN32 MACOSX_BUNDLE
application.cpp application.h
commandlineoptions.cpp commandlineoptions.h
fakeaccessible.cpp fakeaccessible.h
main.cpp
${VX_APP_ICON_RC_WIN} ${VX_APP_ICON_MACOS} ${VX_RESOURCE_FILES}
)
add_dependencies(vnote VX_EXTRA_RESOURCE)
set(VX_LIBS_FOLDER ../libs)
target_include_directories(vnote PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
find_program(GOLD_LINKER "ld.gold")
if (NOT ${GOLD_LINKER} STREQUAL GOLD_LINKER-NOTFOUND)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold")
endif()
target_compile_definitions(vnote PRIVATE
QT_MESSAGELOGCONTEXT
)
add_subdirectory(core)
add_subdirectory(export)
add_subdirectory(imagehost)
add_subdirectory(search)
add_subdirectory(snippet)
add_subdirectory(task)
add_subdirectory(unitedentry)
add_subdirectory(utils)
add_subdirectory(widgets)
target_link_libraries(vnote PRIVATE
Qt::Core
Qt::Gui
Qt::Network
Qt::PrintSupport
Qt::Sql
Qt::Svg
Qt::WebChannel
Qt::WebEngineWidgets
Qt::Widgets
VTextEdit
qhotkey
)
if((QT_DEFAULT_MAJOR_VERSION GREATER 5))
target_link_libraries(vnote PRIVATE
Qt::Core5Compat
)
endif()
# Copy the qt.conf on Windows
if(WIN32)
add_custom_command(TARGET vnote POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_SOURCE_DIR}/package/qt.conf" $<TARGET_FILE_DIR:vnote>)
endif()
# Installation
if (WIN32)
install(TARGETS vnote RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES "${PROJECT_SOURCE_DIR}/package/qt.conf" DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES ${VX_EXTRA_RESOURCE_FILES_RCC} DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES ${VX_QM_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/translations" OPTIONAL)
elseif(APPLE)
set(CMAKE_MACOSX_RPATH ON)
# TODO: declare install for macOS if necessary. For packing, we will manually copy files into
# the src/vnote.app bundle.
# The generated Info.plist will be overridden.
set_target_properties(vnote
PROPERTIES
OUTPUT_NAME "${PROJECT_NAME}"
MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}"
MACOSX_BUNDLE_INFO_STRING "${PROJECT_DESCRIPTION}"
MACOSX_BUNDLE_GUI_IDENTIFIER "fun.vnote.vnote"
MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
MACOSX_BUNDLE_COPYRIGHT "Distributed under LGPL-3.0 license. Copyright (c) 2024 app.vnote.fun"
)
else()
install(TARGETS vnote
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(FILES ${VX_EXTRA_RESOURCE_FILES_RCC} DESTINATION ${CMAKE_INSTALL_DATADIR})
install(FILES ${VX_QM_FILES} DESTINATION "${CMAKE_INSTALL_DATADIR}/translations" OPTIONAL)
set(desktop.path applications)
set(desktop.files data/core/vnote.desktop)
set(icon16.path icons/hicolor/16x16/apps)
set(icon16.files data/core/logo/16x16/vnote.png)
set(icon32.path icons/hicolor/32x32/apps)
set(icon32.files data/core/logo/32x32/vnote.png)
set(icon48.path icons/hicolor/48x48/apps)
set(icon48.files data/core/logo/48x48/vnote.png)
set(icon64.path icons/hicolor/64x64/apps)
set(icon64.files data/core/logo/64x64/vnote.png)
set(icon128.path icons/hicolor/128x128/apps)
set(icon128.files data/core/logo/128x128/vnote.png)
set(icon256.path icons/hicolor/256x256/apps)
set(icon256.files data/core/logo/256x256/vnote.png)
set(iconsvg.path icons/hicolor/scalable/apps)
set(iconsvg.files data/core/logo/vnote.svg)
foreach(item IN ITEMS desktop icon16 icon32 icon48 icon64 icon128 icon256 iconsvg)
install(FILES ${CMAKE_CURRENT_LIST_DIR}/${${item}.files}
DESTINATION ${CMAKE_INSTALL_DATADIR}/${${item}.path}
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
endforeach()
endif()
include(${CMAKE_CURRENT_LIST_DIR}/Packaging.cmake)

View File

@ -0,0 +1,16 @@
message(STATUS "VX_APPIMAGE_DEST_DIR ${VX_APPIMAGE_DEST_DIR}")
message(STATUS "VX_APPIMAGE_DESKTOP_FILE ${VX_APPIMAGE_DESKTOP_FILE}")
execute_process(
COMMAND ${CMAKE_MAKE_PROGRAM} DESTDIR=${VX_APPIMAGE_DEST_DIR} install
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
execute_process(
COMMAND env QMAKE=${QMAKE_EXECUTABLE} LD_LIBRARY_PATH=/usr/local/lib64:$ENV{LD_LIBRARY_PATH} "${LINUXDEPLOY_EXECUTABLE}" --plugin=qt --output=appimage
--appdir=${VX_APPIMAGE_DEST_DIR} -e ${CMAKE_CURRENT_BINARY_DIR}/vnote -d ${VX_APPIMAGE_DESKTOP_FILE}
-i ${CMAKE_CURRENT_LIST_DIR}/data/core/logo/64x64/vnote.png
-l ${QT_PLUGINS_DIR}/platformthemes/libqgtk3.so
-l /usr/local/lib64/libcrypto.so.3
-l /usr/local/lib64/libssl.so.3
# --exclude-library option does not work as expected
# --exclude-library=libssl.so.1.1,libcrypto.so.1.1,libnss3.so,libnssutil3.so
WORKING_DIRECTORY ${CPACK_PACKAGE_DIRECTORY})

View File

@ -0,0 +1,6 @@
execute_process(COMMAND "optool" strip -t ${CMAKE_CURRENT_BINARY_DIR}/VNote.app
WORKING_DIRECTORY ${CPACK_PACKAGE_DIRECTORY}
)
execute_process(COMMAND "${MACDEPLOYQT_EXECUTABLE}" ${CMAKE_CURRENT_BINARY_DIR}/VNote.app -always-overwrite -verbose=1
WORKING_DIRECTORY ${CPACK_PACKAGE_DIRECTORY}
)

165
src/Packaging.cmake Normal file
View File

@ -0,0 +1,165 @@
# from: https://github.com/miurahr/cmake-qt-packaging-example
find_package(Qt${QT_DEFAULT_MAJOR_VERSION} REQUIRED COMPONENTS Core)
get_target_property(QMAKE_EXECUTABLE Qt::qmake IMPORTED_LOCATION)
get_filename_component(QT_BIN_DIR "${QMAKE_EXECUTABLE}" DIRECTORY)
execute_process(COMMAND ${QMAKE_EXECUTABLE} -query QT_VERSION OUTPUT_VARIABLE QT_VERSION)
set(QT_TOOLS_DIR "${QT_BIN_DIR}/../../../Tools")
cmake_path(NORMAL_PATH QT_TOOLS_DIR OUTPUT_VARIABLE QT_TOOLS_DIR)
set(QT_PLUGINS_DIR "${QT_BIN_DIR}/../plugins")
cmake_path(NORMAL_PATH QT_PLUGINS_DIR OUTPUT_VARIABLE QT_PLUGINS_DIR)
# To use the specific version of Qt
set(WINDEPLOYQT_EXECUTABLE "${QT_BIN_DIR}/windeployqt.exe")
find_program(LINUXDEPLOY_EXECUTABLE linuxdeploy linuxdeploy-x86_64.AppImage HINTS "${QT_BIN_DIR}")
find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${QT_BIN_DIR}")
find_program(MACDEPLOYQTFIX_EXECUTABLE macdeployqtfix.py HINTS "${QT_BIN_DIR}")
find_package(Python)
function(windeployqt target)
# Bundle Library Files
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER)
if ((QT_DEFAULT_MAJOR_VERSION GREATER 5))
if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG")
set(WINDEPLOYQT_ARGS --debug)
else()
set(WINDEPLOYQT_ARGS --release)
endif()
endif()
add_custom_target(deploy
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/"
COMMAND "${CMAKE_COMMAND}" -E
env PATH="${QT_BIN_DIR}" "${WINDEPLOYQT_EXECUTABLE}"
${WINDEPLOYQT_ARGS}
--no-quick-import
--no-opengl-sw
--no-compiler-runtime
--translations zh_CN,ja
--dir "${CMAKE_CURRENT_BINARY_DIR}/winqt/"
$<TARGET_FILE:${target}>
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/generic/"
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/styles/"
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/qmltooling/"
COMMENT "Deploying Qt..."
DEPENDS vnote lrelease
)
add_dependencies(pack deploy)
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/winqt/" DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
set(OPENSSL_ROOT_DIR "${QT_TOOLS_DIR}/OpenSSL/Win_x64" CACHE STRING "OpenSSL dir")
file(GLOB OPENSSL_LIBS_FILES "${OPENSSL_ROOT_DIR}/bin/lib*.dll")
cmake_path(NORMAL_PATH OPENSSL_LIBS_FILES OUTPUT_VARIABLE OPENSSL_LIBS_FILES)
install(FILES ${OPENSSL_LIBS_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
message(STATUS "OpenSSLExtraLIBDIR:${OPENSSL_EXTRA_LIB_DIR}")
file(GLOB OPENSSL_EXTRA_LIB_FILES "${OPENSSL_EXTRA_LIB_DIR}/lib*.dll")
cmake_path(NORMAL_PATH OPENSSL_EXTRA_LIB_FILES OUTPUT_VARIABLE OPENSSL_EXTRA_LIB_FILES)
message(STATUS "OpenSSLExtraLibFiles:${OPENSSL_EXTRA_LIB_FILES}")
install(FILES ${OPENSSL_EXTRA_LIB_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE)
include(InstallRequiredSystemLibraries)
endfunction()
set(CPACK_PACKAGE_VENDOR "VNoteX")
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_CONTACT "Le Tan <tamlokveer@gmail.com>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_DESCRIPTION}")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.md")
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING.LESSER")
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}")
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}")
# Start menu entry on Windows
set(CPACK_PACKAGE_EXECUTABLES "vnote" "VNote")
# Desktop link on Windows
set(CPACK_CREATE_DESKTOP_LINKS "vnote")
set(CPACK_STRIP_FILES TRUE)
# WIX generator
set(CPACK_WIX_UPGRADE_GUID BA25F337-991A-4893-9D8A-AD5E89BAF5C4)
set(CPACK_WIX_PRODUCT_GUID BA25F337-991A-4893-9D8A-AD5E89BAF5C4)
set(CPACK_WIX_LICENSE_RTF "${PROJECT_SOURCE_DIR}/package/lgpl-3.0.rtf")
set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_LIST_DIR}/data/core/icons/vnote.ico")
set(CPACK_WIX_UI_BANNER "${PROJECT_SOURCE_DIR}/package/wix_banner.png")
set(CPACK_WIX_UI_DIALOG "${PROJECT_SOURCE_DIR}/package/wix_dialog.png")
#------------------------------------------------------------------------------
# include CPack, so we get target for packages
set(CPACK_OUTPUT_CONFIG_FILE "${CMAKE_BINARY_DIR}/BundleConfig.cmake")
add_custom_target(pack
COMMAND ${CMAKE_CPACK_COMMAND} "--config" "${CMAKE_BINARY_DIR}/BundleConfig.cmake" "--verbose"
COMMENT "Running CPACK. Please wait..."
DEPENDS vnote)
add_dependencies(pack lrelease)
set(CPACK_GENERATOR)
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_LIST_DIR}/data/core/logo/64x64/vnote.png")
if(WIN32)
find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${QT_BIN_DIR}" DOC "Path to the windeployqt utility")
list(APPEND CPACK_GENERATOR ZIP)
message(STATUS "Package generation - Windows - Zip")
find_program(WIX_EXECUTABLE wix HINTS "${QT_BIN_DIR}" DOC "Path to the WiX utility")
if (NOT WIX_EXECUTABLE-NOTFOUND)
list(APPEND CPACK_GENERATOR WIX)
message(STATUS "Package generation - Windows - WiX")
endif()
windeployqt(vnote)
elseif(APPLE)
# Manually copy resources.
set(VX_BUNDLE_CONTENTS_DIR $<TARGET_FILE_DIR:vnote>/..)
add_custom_target(deploy
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_LIST_DIR}/data/core/Info.plist" ${VX_BUNDLE_CONTENTS_DIR}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${VX_EXTRA_RESOURCE_FILES_RCC} ${VX_BUNDLE_CONTENTS_DIR}/Resources
COMMAND ${CMAKE_COMMAND} -E make_directory ${VX_BUNDLE_CONTENTS_DIR}/Resources/translations
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${VX_QM_FILES} ${VX_BUNDLE_CONTENTS_DIR}/Resources/translations
COMMENT "Copying resources into bundle Contents ${VX_BUNDLE_CONTENTS_DIR}"
DEPENDS vnote lrelease
)
add_dependencies(pack deploy)
message(STATUS "MACDeployQtExecutable: ${MACDEPLOYQT_EXECUTABLE}")
if (MACDEPLOYQT_EXECUTABLE)
message(STATUS "Package generation - MacOS - DMG")
list(APPEND CPACK_GENERATOR External)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CPackMacDeployQt.cmake.in "${CMAKE_BINARY_DIR}/CPackExternal.cmake")
set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_BINARY_DIR}/CPackExternal.cmake")
include(InstallRequiredSystemLibraries)
endif()
else()
message(STATUS "LinuxDeployExecutable: ${LINUXDEPLOY_EXECUTABLE}")
if(LINUXDEPLOY_EXECUTABLE)
message(STATUS "Package generation - Linux - AppImage")
list(APPEND CPACK_GENERATOR External)
set(VX_APPIMAGE_DEST_DIR "${CPACK_PACKAGE_DIRECTORY}/_CPack_Packages/Linux/External/AppImage")
set(VX_APPIMAGE_DESKTOP_FILE "${VX_APPIMAGE_DEST_DIR}${CMAKE_INSTALL_PREFIX}/share/applications/vnote.desktop")
configure_file(${CMAKE_CURRENT_LIST_DIR}/CPackLinuxDeployQt.cmake.in "${CMAKE_BINARY_DIR}/CPackExternal.cmake")
set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_BINARY_DIR}/CPackExternal.cmake")
endif()
endif()
include(CPack)

View File

@ -2,6 +2,11 @@
#include <QFileOpenEvent>
#include <QDebug>
#include <QDir>
#include <QStyle>
#include <QFileSystemWatcher>
#include <QTimer>
#include <core/vnotex.h>
using namespace vnotex;
@ -10,6 +15,53 @@ Application::Application(int &p_argc, char **p_argv)
{
}
void Application::watchThemeFolder(const QString &p_themeFolderPath)
{
if (p_themeFolderPath.isEmpty()) {
return;
}
// Initialize watchers only when needed
if (!m_styleWatcher) {
m_styleWatcher = new QFileSystemWatcher(this);
}
if (!m_reloadTimer) {
m_reloadTimer = new QTimer(this);
m_reloadTimer->setSingleShot(true);
m_reloadTimer->setInterval(500); // 500ms debounce delay
connect(m_reloadTimer, &QTimer::timeout,
this, &Application::reloadThemeResources);
// Connect file watcher to timer
connect(m_styleWatcher, &QFileSystemWatcher::directoryChanged,
m_reloadTimer, qOverload<>(&QTimer::start));
connect(m_styleWatcher, &QFileSystemWatcher::fileChanged,
m_reloadTimer, qOverload<>(&QTimer::start));
}
// Watch the theme folder and its files
m_styleWatcher->addPath(p_themeFolderPath);
// Also watch individual files in the theme folder
QDir themeDir(p_themeFolderPath);
QStringList files = themeDir.entryList(QDir::Files);
for (const QString &file : files) {
m_styleWatcher->addPath(themeDir.filePath(file));
}
}
void Application::reloadThemeResources()
{
VNoteX::getInst().getThemeMgr().refreshCurrentTheme();
auto stylesheet = VNoteX::getInst().getThemeMgr().fetchQtStyleSheet();
if (!stylesheet.isEmpty()) {
setStyleSheet(stylesheet);
style()->unpolish(this);
style()->polish(this);
}
}
bool Application::event(QEvent *p_event)
{
// On macOS, we need this to open file from Finder.

View File

@ -1,8 +1,10 @@
#ifndef APPLICATION_H
#define APPLICATION_H
#include <QApplication>
class QFileSystemWatcher;
class QTimer;
namespace vnotex
{
class Application : public QApplication
@ -11,11 +13,21 @@ namespace vnotex
public:
Application(int &p_argc, char **p_argv);
// Set up theme folder watcher for hot-reload
void watchThemeFolder(const QString &p_themeFolderPath);
// Reload the theme resources (stylesheet, icons, etc)
void reloadThemeResources();
signals:
void openFileRequested(const QString &p_filePath);
protected:
bool event(QEvent *p_event) Q_DECL_OVERRIDE;
private:
QFileSystemWatcher *m_styleWatcher = nullptr;
QTimer *m_reloadTimer = nullptr;
};
}

View File

@ -22,18 +22,28 @@ CommandLineOptions::ParseResult CommandLineOptions::parse(const QStringList &p_a
const QCommandLineOption verboseOpt("verbose", MainWindow::tr("Print more logs."));
parser.addOption(verboseOpt);
const QCommandLineOption logStderrOpt("log-stderr", MainWindow::tr("Log to stderr."));
parser.addOption(logStderrOpt);
const QCommandLineOption watchThemesOpt("watch-themes", MainWindow::tr("Watch theme folder for changes."));
parser.addOption(watchThemesOpt);
// WebEngine options.
// No need to handle them. Just add them to the parser to avoid parse error.
{
QCommandLineOption webRemoteDebuggingPortOpt("remote-debugging-port",
MainWindow::tr("WebEngine remote debugging port."),
"port_number");
MainWindow::tr("port_number"));
webRemoteDebuggingPortOpt.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(webRemoteDebuggingPortOpt);
QCommandLineOption webNoSandboxOpt("no-sandbox", MainWindow::tr("WebEngine without sandbox."));
webNoSandboxOpt.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(webNoSandboxOpt);
QCommandLineOption webDisableGpu("disable-gpu", MainWindow::tr("WebEngine with GPU disabled."));
webDisableGpu.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(webDisableGpu);
}
if (!parser.parse(p_arguments)) {
@ -59,5 +69,13 @@ CommandLineOptions::ParseResult CommandLineOptions::parse(const QStringList &p_a
m_verbose = true;
}
if (parser.isSet(logStderrOpt)) {
m_logToStderr = true;
}
if (parser.isSet(watchThemesOpt)) {
m_watchThemes = true;
}
return ParseResult::Ok;
}

View File

@ -25,6 +25,11 @@ public:
QStringList m_pathsToOpen;
bool m_verbose = false;
bool m_logToStderr = false;
// Whether to watch theme folder for changes
bool m_watchThemes = false;
};
#endif // COMMANDLINEOPTIONS_H

87
src/core/CMakeLists.txt Normal file
View File

@ -0,0 +1,87 @@
target_sources(vnote PRIVATE
buffer/buffer.cpp buffer/buffer.h
buffer/bufferprovider.cpp buffer/bufferprovider.h
buffer/filebufferprovider.cpp buffer/filebufferprovider.h
buffer/filetypehelper.cpp buffer/filetypehelper.h
buffer/ibufferfactory.h
buffer/markdownbuffer.cpp buffer/markdownbuffer.h
buffer/markdownbufferfactory.cpp buffer/markdownbufferfactory.h
buffer/mindmapbuffer.cpp buffer/mindmapbuffer.h
buffer/mindmapbufferfactory.cpp buffer/mindmapbufferfactory.h
buffer/nodebufferprovider.cpp buffer/nodebufferprovider.h
buffer/pdfbuffer.cpp buffer/pdfbuffer.h
buffer/pdfbufferfactory.cpp buffer/pdfbufferfactory.h
buffer/textbuffer.cpp buffer/textbuffer.h
buffer/textbufferfactory.cpp buffer/textbufferfactory.h
buffer/urlbasedbufferprovider.h
buffermgr.cpp buffermgr.h
clipboarddata.cpp clipboarddata.h
configmgr.cpp configmgr.h
coreconfig.cpp coreconfig.h
editorconfig.cpp editorconfig.h
events.h
exception.h
externalfile.cpp externalfile.h
file.cpp file.h
filelocator.h
fileopenparameters.h
global.cpp global.h
historyitem.cpp historyitem.h
historymgr.cpp historymgr.h
htmltemplatehelper.cpp htmltemplatehelper.h
iconfig.h
location.h
logger.cpp logger.h
mainconfig.cpp mainconfig.h
markdowneditorconfig.cpp markdowneditorconfig.h
mindmapeditorconfig.cpp mindmapeditorconfig.h
namebasedserver.h
noncopyable.h
notebook/bundlenotebook.cpp notebook/bundlenotebook.h
notebook/bundlenotebookfactory.cpp notebook/bundlenotebookfactory.h
notebook/externalnode.cpp notebook/externalnode.h
notebook/historyi.h
notebook/inotebookfactory.h
notebook/node.cpp notebook/node.h
notebook/nodeparameters.cpp notebook/nodeparameters.h
notebook/notebook.cpp notebook/notebook.h
notebook/notebookdatabaseaccess.cpp notebook/notebookdatabaseaccess.h
notebook/notebookparameters.cpp notebook/notebookparameters.h
notebook/notebooktagmgr.cpp notebook/notebooktagmgr.h
notebook/tag.cpp notebook/tag.h
notebook/tagi.h
notebook/vxnode.cpp notebook/vxnode.h
notebook/vxnodefile.cpp notebook/vxnodefile.h
notebookbackend/inotebookbackend.cpp notebookbackend/inotebookbackend.h
notebookbackend/inotebookbackendfactory.h
notebookbackend/localnotebookbackend.cpp notebookbackend/localnotebookbackend.h
notebookbackend/localnotebookbackendfactory.cpp notebookbackend/localnotebookbackendfactory.h
notebookconfigmgr/bundlenotebookconfigmgr.cpp notebookconfigmgr/bundlenotebookconfigmgr.h
notebookconfigmgr/inotebookconfigmgr.cpp notebookconfigmgr/inotebookconfigmgr.h
notebookconfigmgr/inotebookconfigmgrfactory.h
notebookconfigmgr/notebookconfig.cpp notebookconfigmgr/notebookconfig.h
notebookconfigmgr/vxnodeconfig.cpp notebookconfigmgr/vxnodeconfig.h
notebookconfigmgr/vxnotebookconfigmgr.cpp notebookconfigmgr/vxnotebookconfigmgr.h
notebookconfigmgr/vxnotebookconfigmgrfactory.cpp notebookconfigmgr/vxnotebookconfigmgrfactory.h
notebookmgr.cpp notebookmgr.h
pdfviewerconfig.cpp pdfviewerconfig.h
quickaccesshelper.cpp quickaccesshelper.h
sessionconfig.cpp sessionconfig.h
singleinstanceguard.cpp singleinstanceguard.h
templatemgr.cpp templatemgr.h
texteditorconfig.cpp texteditorconfig.h
theme.cpp theme.h
thememgr.cpp thememgr.h
versioncontroller/dummyversioncontroller.cpp versioncontroller/dummyversioncontroller.h
versioncontroller/dummyversioncontrollerfactory.cpp versioncontroller/dummyversioncontrollerfactory.h
versioncontroller/iversioncontroller.h
versioncontroller/iversioncontrollerfactory.h
versioncontroller/versioncontrollerserver.cpp versioncontroller/versioncontrollerserver.h
vnotex.cpp vnotex.h
webresource.h
widgetconfig.cpp widgetconfig.h
)
target_include_directories(vnote PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)

View File

@ -358,7 +358,7 @@ void Buffer::writeBackupFile()
QString Buffer::generateBackupFileHead() const
{
return QString("vnotex_backup_file %1|").arg(getContentPath());
return QStringLiteral("vnotex_backup_file %1|").arg(getContentPath());
}
void Buffer::checkBackupFileOfPreviousSession()
@ -373,7 +373,7 @@ void Buffer::checkBackupFileOfPreviousSession()
QDir backupDir(backupDirPath);
QStringList backupFiles;
{
const QString nameFilter = QString("%1*%2").arg(getName(), config.getBackupFileExtension());
const QString nameFilter = QStringLiteral("%1*%2").arg(getName(), config.getBackupFileExtension());
backupFiles = backupDir.entryList(QStringList(nameFilter),
QDir::Files | QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot);
}

View File

@ -1,22 +0,0 @@
SOURCES += \
$$PWD/buffer.cpp \
$$PWD/bufferprovider.cpp \
$$PWD/filebufferprovider.cpp \
$$PWD/markdownbuffer.cpp \
$$PWD/markdownbufferfactory.cpp \
$$PWD/filetypehelper.cpp \
$$PWD/nodebufferprovider.cpp \
$$PWD/textbuffer.cpp \
$$PWD/textbufferfactory.cpp
HEADERS += \
$$PWD/bufferprovider.h \
$$PWD/buffer.h \
$$PWD/filebufferprovider.h \
$$PWD/ibufferfactory.h \
$$PWD/markdownbuffer.h \
$$PWD/markdownbufferfactory.h \
$$PWD/filetypehelper.h \
$$PWD/nodebufferprovider.h \
$$PWD/textbuffer.h \
$$PWD/textbufferfactory.h

View File

@ -16,6 +16,7 @@ QDateTime BufferProvider::getLastModifiedFromFile() const
bool BufferProvider::checkFileChangedOutside() const
{
// TODO: support non-local URLs.
QFileInfo info(getContentPath());
if (!info.exists() || m_lastModified != info.lastModified()) {
return true;

View File

@ -5,6 +5,8 @@
#include <utils/fileutils.h>
#include "buffer.h"
#include <core/configmgr.h>
#include <core/coreconfig.h>
using namespace vnotex;
@ -18,26 +20,42 @@ bool FileType::isMarkdown() const
return m_type == Type::Markdown;
}
QString FileTypeHelper::s_systemDefaultProgram = QStringLiteral("System");
FileTypeHelper::FileTypeHelper()
{
setupBuiltInTypes();
reload();
}
// TODO: read configuration file.
void FileTypeHelper::reload()
{
setupBuiltInTypes();
setupSuffixTypeMap();
}
void FileTypeHelper::setupBuiltInTypes()
{
m_fileTypes.clear();
const auto &coreConfig = ConfigMgr::getInst().getCoreConfig();
{
FileType type;
type.m_type = FileType::Markdown;
type.m_displayName = Buffer::tr("Markdown");
type.m_typeName = QStringLiteral("Markdown");
type.m_displayName = Buffer::tr("Markdown");
auto suffixes = coreConfig.findFileTypeSuffix(type.m_typeName);
if (suffixes && !suffixes->isEmpty()) {
type.m_suffixes = *suffixes;
} else {
type.m_suffixes << QStringLiteral("md")
<< QStringLiteral("mkd")
<< QStringLiteral("rmd")
<< QStringLiteral("markdown");
}
m_fileTypes.push_back(type);
}
@ -46,7 +64,47 @@ void FileTypeHelper::setupBuiltInTypes()
type.m_type = FileType::Text;
type.m_typeName = QStringLiteral("Text");
type.m_displayName = Buffer::tr("Text");
auto suffixes = coreConfig.findFileTypeSuffix(type.m_typeName);
if (suffixes && !suffixes->isEmpty()) {
type.m_suffixes = *suffixes;
} else {
type.m_suffixes << QStringLiteral("txt") << QStringLiteral("text") << QStringLiteral("log");
}
m_fileTypes.push_back(type);
}
{
FileType type;
type.m_type = FileType::Pdf;
type.m_typeName = QStringLiteral("PDF");
type.m_displayName = Buffer::tr("Portable Document Format");
type.m_isNewable = false;
auto suffixes = coreConfig.findFileTypeSuffix(type.m_typeName);
if (suffixes && !suffixes->isEmpty()) {
type.m_suffixes = *suffixes;
} else {
type.m_suffixes << QStringLiteral("pdf");
}
m_fileTypes.push_back(type);
}
{
FileType type;
type.m_type = FileType::MindMap;
type.m_typeName = QStringLiteral("MindMap");
type.m_displayName = Buffer::tr("Mind Map");
auto suffixes = coreConfig.findFileTypeSuffix(type.m_typeName);
if (suffixes && !suffixes->isEmpty()) {
type.m_suffixes = *suffixes;
} else {
type.m_suffixes << QStringLiteral("emind");
}
m_fileTypes.push_back(type);
}
@ -88,10 +146,10 @@ const FileType &FileTypeHelper::getFileTypeBySuffix(const QString &p_suffix) con
}
}
#define ADD(x, y) m_suffixTypeMap.insert((x), (y))
void FileTypeHelper::setupSuffixTypeMap()
{
m_suffixTypeMap.clear();
for (int i = 0; i < m_fileTypes.size(); ++i) {
for (const auto &suffix : m_fileTypes[i].m_suffixes) {
if (m_suffixTypeMap.contains(suffix)) {
@ -109,11 +167,13 @@ const QVector<FileType> &FileTypeHelper::getAllFileTypes() const
const FileType &FileTypeHelper::getFileType(int p_type) const
{
Q_ASSERT(p_type < m_fileTypes.size());
if (p_type >= m_fileTypes.size()) {
p_type = FileType::Others;
}
return m_fileTypes[p_type];
}
const FileTypeHelper &FileTypeHelper::getInst()
FileTypeHelper &FileTypeHelper::getInst()
{
static FileTypeHelper helper;
return helper;

View File

@ -15,9 +15,15 @@ namespace vnotex
{
Markdown = 0,
Text,
Pdf,
MindMap,
Others
};
QString preferredSuffix() const;
bool isMarkdown() const;
// Type.
int m_type = -1;
@ -27,11 +33,11 @@ namespace vnotex
QStringList m_suffixes;
QString preferredSuffix() const;
bool isMarkdown() const;
// Whether we can new this type of file.
bool m_isNewable = true;
};
// Only handle built-in editors.
class FileTypeHelper
{
public:
@ -47,7 +53,11 @@ namespace vnotex
bool checkFileType(const QString &p_filePath, int p_type) const;
static const FileTypeHelper &getInst();
void reload();
static FileTypeHelper &getInst();
static QString s_systemDefaultProgram;
private:
FileTypeHelper();

View File

@ -18,6 +18,8 @@ namespace vnotex
virtual Buffer *createBuffer(const BufferParameters &p_parameters,
QObject *p_parent) = 0;
virtual bool isBufferCreatedByFactory(const Buffer *p_buffer) const = 0;
};
} // ns vnotex

View File

@ -9,3 +9,8 @@ Buffer *MarkdownBufferFactory::createBuffer(const BufferParameters &p_parameters
{
return new MarkdownBuffer(p_parameters, p_parent);
}
bool MarkdownBufferFactory::isBufferCreatedByFactory(const Buffer *p_buffer) const
{
return dynamic_cast<const MarkdownBuffer *>(p_buffer) != nullptr;
}

View File

@ -11,6 +11,8 @@ namespace vnotex
public:
Buffer *createBuffer(const BufferParameters &p_parameters,
QObject *p_parent) Q_DECL_OVERRIDE;
bool isBufferCreatedByFactory(const Buffer *p_buffer) const Q_DECL_OVERRIDE;
};
} // vnotex

View File

@ -0,0 +1,17 @@
#include "mindmapbuffer.h"
#include <widgets/mindmapviewwindow.h>
using namespace vnotex;
MindMapBuffer::MindMapBuffer(const BufferParameters &p_parameters,
QObject *p_parent)
: Buffer(p_parameters, p_parent)
{
}
ViewWindow *MindMapBuffer::createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent)
{
Q_UNUSED(p_paras);
return new MindMapViewWindow(p_parent);
}

View File

@ -0,0 +1,21 @@
#ifndef MINDMAPBUFFER_H
#define MINDMAPBUFFER_H
#include "buffer.h"
namespace vnotex
{
class MindMapBuffer : public Buffer
{
Q_OBJECT
public:
MindMapBuffer(const BufferParameters &p_parameters,
QObject *p_parent = nullptr);
protected:
ViewWindow *createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras,
QWidget *p_parent) Q_DECL_OVERRIDE;
};
}
#endif // MINDMAPBUFFER_H

View File

@ -0,0 +1,16 @@
#include "mindmapbufferfactory.h"
#include "mindmapbuffer.h"
using namespace vnotex;
Buffer *MindMapBufferFactory::createBuffer(const BufferParameters &p_parameters,
QObject *p_parent)
{
return new MindMapBuffer(p_parameters, p_parent);
}
bool MindMapBufferFactory::isBufferCreatedByFactory(const Buffer *p_buffer) const
{
return dynamic_cast<const MindMapBuffer *>(p_buffer) != nullptr;
}

View File

@ -0,0 +1,19 @@
#ifndef MINDMAPBUFFERFACTORY_H
#define MINDMAPBUFFERFACTORY_H
#include "ibufferfactory.h"
namespace vnotex
{
// Buffer factory for MindMap file.
class MindMapBufferFactory : public IBufferFactory
{
public:
Buffer *createBuffer(const BufferParameters &p_parameters,
QObject *p_parent) Q_DECL_OVERRIDE;
bool isBufferCreatedByFactory(const Buffer *p_buffer) const Q_DECL_OVERRIDE;
};
}
#endif // MINDMAPBUFFERFACTORY_H

View File

@ -0,0 +1,17 @@
#include "pdfbuffer.h"
#include <widgets/pdfviewwindow.h>
using namespace vnotex;
PdfBuffer::PdfBuffer(const BufferParameters &p_parameters,
QObject *p_parent)
: Buffer(p_parameters, p_parent)
{
}
ViewWindow *PdfBuffer::createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent)
{
Q_UNUSED(p_paras);
return new PdfViewWindow(p_parent);
}

View File

@ -0,0 +1,21 @@
#ifndef PDFBUFFER_H
#define PDFBUFFER_H
#include "buffer.h"
namespace vnotex
{
class PdfBuffer : public Buffer
{
Q_OBJECT
public:
PdfBuffer(const BufferParameters &p_parameters,
QObject *p_parent = nullptr);
protected:
ViewWindow *createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras,
QWidget *p_parent) Q_DECL_OVERRIDE;
};
} // ns vnotex
#endif // PDFBUFFER_H

View File

@ -0,0 +1,19 @@
#include "pdfbufferfactory.h"
#include "pdfbuffer.h"
#include "urlbasedbufferprovider.h"
using namespace vnotex;
Buffer *PdfBufferFactory::createBuffer(const BufferParameters &p_parameters,
QObject *p_parent)
{
BufferParameters paras;
paras.m_provider = QSharedPointer<UrlBasedBufferProvider>::create(p_parameters.m_provider);
return new PdfBuffer(paras, p_parent);
}
bool PdfBufferFactory::isBufferCreatedByFactory(const Buffer *p_buffer) const
{
return dynamic_cast<const PdfBuffer *>(p_buffer) != nullptr;
}

View File

@ -0,0 +1,19 @@
#ifndef PDFBUFFERFACTORY_H
#define PDFBUFFERFACTORY_H
#include "ibufferfactory.h"
namespace vnotex
{
// Buffer factory for Pdf file.
class PdfBufferFactory : public IBufferFactory
{
public:
Buffer *createBuffer(const BufferParameters &p_parameters,
QObject *p_parent) Q_DECL_OVERRIDE;
bool isBufferCreatedByFactory(const Buffer *p_buffer) const Q_DECL_OVERRIDE;
};
} // vnotex
#endif // PDFBUFFERFACTORY_H

View File

@ -9,3 +9,8 @@ Buffer *TextBufferFactory::createBuffer(const BufferParameters &p_parameters,
{
return new TextBuffer(p_parameters, p_parent);
}
bool TextBufferFactory::isBufferCreatedByFactory(const Buffer *p_buffer) const
{
return dynamic_cast<const TextBuffer *>(p_buffer) != nullptr;
}

View File

@ -11,6 +11,8 @@ namespace vnotex
public:
Buffer *createBuffer(const BufferParameters &p_parameters,
QObject *p_parent) Q_DECL_OVERRIDE;
bool isBufferCreatedByFactory(const Buffer *p_buffer) const Q_DECL_OVERRIDE;
};
}

View File

@ -0,0 +1,132 @@
#ifndef URLBASEDBUFFERPROVIDER_H
#define URLBASEDBUFFERPROVIDER_H
#include "bufferprovider.h"
#include <QSharedPointer>
namespace vnotex
{
// A wrapper provider to provide URL-based buffer (instead of content-based).
class UrlBasedBufferProvider : public BufferProvider
{
Q_OBJECT
public:
// Will own @p_provider.
UrlBasedBufferProvider(const QSharedPointer<BufferProvider> &p_provider, QObject *p_parent = nullptr)
: BufferProvider(p_parent),
m_provider(p_provider)
{
}
Buffer::ProviderType getType() const Q_DECL_OVERRIDE {
return m_provider->getType();
}
bool match(const Node *p_node) const Q_DECL_OVERRIDE {
return m_provider->match(p_node);
}
bool match(const QString &p_filePath) const Q_DECL_OVERRIDE {
return m_provider->match(p_filePath);
}
QString getName() const Q_DECL_OVERRIDE {
return m_provider->getName();
}
QString getPath() const Q_DECL_OVERRIDE {
return m_provider->getPath();
}
QString getContentPath() const Q_DECL_OVERRIDE {
return m_provider->getContentPath();
}
QString getResourcePath() const Q_DECL_OVERRIDE {
return m_provider->getResourcePath();
}
void write(const QString &p_content) Q_DECL_OVERRIDE {
Q_UNUSED(p_content);
}
QString read() const Q_DECL_OVERRIDE {
const_cast<UrlBasedBufferProvider *>(this)->m_lastModified = getLastModifiedFromFile();
return QString();
}
QString fetchImageFolderPath() Q_DECL_OVERRIDE {
return m_provider->fetchImageFolderPath();
}
bool isChildOf(const Node *p_node) const Q_DECL_OVERRIDE {
return m_provider->isChildOf(p_node);
}
Node *getNode() const Q_DECL_OVERRIDE {
return m_provider->getNode();
}
QString getAttachmentFolder() const Q_DECL_OVERRIDE {
return m_provider->getAttachmentFolder();
}
QString fetchAttachmentFolderPath() Q_DECL_OVERRIDE {
return m_provider->fetchAttachmentFolderPath();
}
QStringList addAttachment(const QString &p_destFolderPath, const QStringList &p_files) Q_DECL_OVERRIDE {
return m_provider->addAttachment(p_destFolderPath, p_files);
}
QString newAttachmentFile(const QString &p_destFolderPath, const QString &p_name) Q_DECL_OVERRIDE {
return m_provider->newAttachmentFile(p_destFolderPath, p_name);
}
QString newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name) Q_DECL_OVERRIDE {
return m_provider->newAttachmentFolder(p_destFolderPath, p_name);
}
QString renameAttachment(const QString &p_path, const QString &p_name) Q_DECL_OVERRIDE {
return m_provider->renameAttachment(p_path, p_name);
}
void removeAttachment(const QStringList &p_paths) Q_DECL_OVERRIDE {
m_provider->removeAttachment(p_paths);
}
QString insertImage(const QString &p_srcImagePath, const QString &p_imageFileName) Q_DECL_OVERRIDE {
return m_provider->insertImage(p_srcImagePath, p_imageFileName);
}
QString insertImage(const QImage &p_image, const QString &p_imageFileName) Q_DECL_OVERRIDE {
return m_provider->insertImage(p_image, p_imageFileName);
}
void removeImage(const QString &p_imagePath) Q_DECL_OVERRIDE {
m_provider->removeImage(p_imagePath);
}
bool isAttachmentSupported() const Q_DECL_OVERRIDE {
return m_provider->isAttachmentSupported();
}
bool isTagSupported() const Q_DECL_OVERRIDE {
return m_provider->isTagSupported();
}
bool isReadOnly() const Q_DECL_OVERRIDE {
return true;
}
QSharedPointer<File> getFile() const Q_DECL_OVERRIDE {
return m_provider->getFile();
}
private:
QSharedPointer<BufferProvider> m_provider;
};
}
#endif // URLBASEDBUFFERPROVIDER_H

View File

@ -7,18 +7,25 @@
#include <buffer/filetypehelper.h>
#include <buffer/markdownbufferfactory.h>
#include <buffer/textbufferfactory.h>
#include <buffer/pdfbufferfactory.h>
#include <buffer/mindmapbufferfactory.h>
#include <buffer/buffer.h>
#include <buffer/nodebufferprovider.h>
#include <buffer/filebufferprovider.h>
#include <utils/widgetutils.h>
#include <utils/processutils.h>
#include "notebookmgr.h"
#include "vnotex.h"
#include "externalfile.h"
#include "sessionconfig.h"
#include "configmgr.h"
#include "fileopenparameters.h"
using namespace vnotex;
QMap<QString, QString> BufferMgr::s_suffixToFileType;
BufferMgr::BufferMgr(QObject *p_parent)
: QObject(p_parent)
{
@ -47,6 +54,14 @@ void BufferMgr::initBufferServer()
// Text.
auto textFactory = QSharedPointer<TextBufferFactory>::create();
m_bufferServer->registerItem(helper.getFileType(FileType::Text).m_typeName, textFactory);
// Pdf.
auto pdfFactory = QSharedPointer<PdfBufferFactory>::create();
m_bufferServer->registerItem(helper.getFileType(FileType::Pdf).m_typeName, pdfFactory);
// MindMap.
auto mindMapFactory = QSharedPointer<MindMapBufferFactory>::create();
m_bufferServer->registerItem(helper.getFileType(FileType::MindMap).m_typeName, mindMapFactory);
}
void BufferMgr::open(Node *p_node, const QSharedPointer<FileOpenParameters> &p_paras)
@ -60,18 +75,33 @@ void BufferMgr::open(Node *p_node, const QSharedPointer<FileOpenParameters> &p_p
}
if (!p_node->checkExists()) {
auto msg = QString("Failed to open node that does not exist (%1)").arg(p_node->fetchAbsolutePath());
auto msg = QStringLiteral("Failed to open node that does not exist (%1)").arg(p_node->fetchAbsolutePath());
qWarning() << msg;
VNoteX::getInst().showStatusMessageShort(msg);
return;
}
const auto nodePath = p_node->fetchAbsolutePath();
auto fileType = p_paras->m_fileType;
if (fileType.isEmpty()) {
// Check if we need to open it with external program by default according to the suffix.
fileType = findFileTypeByFile(nodePath);
if (openWithExternalProgram(nodePath, fileType)) {
return;
}
}
auto buffer = findBuffer(p_node);
if (!buffer) {
auto nodePath = p_node->fetchAbsolutePath();
if (!buffer || !isSameTypeBuffer(buffer, fileType)) {
auto nodeFile = p_node->getContentFile();
Q_ASSERT(nodeFile);
auto fileType = nodeFile->getContentType().m_typeName;
if (fileType.isEmpty()) {
fileType = nodeFile->getContentType().m_typeName;
} else if (fileType != nodeFile->getContentType().m_typeName) {
nodeFile->setContentType(FileTypeHelper::getInst().getFileTypeByName(fileType).m_type);
}
auto factory = m_bufferServer->getItem(fileType);
if (!factory) {
// No factory to open this file type.
@ -96,9 +126,14 @@ void BufferMgr::open(const QString &p_filePath, const QSharedPointer<FileOpenPar
return;
}
// Check if it is requested to open with external program.
if (openWithExternalProgram(p_filePath, p_paras->m_fileType)) {
return;
}
QFileInfo finfo(p_filePath);
if (!finfo.exists()) {
auto msg = QString("Failed to open file that does not exist (%1)").arg(p_filePath);
auto msg = QStringLiteral("Failed to open file that does not exist (%1)").arg(p_filePath);
qWarning() << msg;
VNoteX::getInst().showStatusMessageShort(msg);
WidgetUtils::openUrlByDesktop(QUrl::fromUserInput(p_filePath));
@ -123,11 +158,25 @@ void BufferMgr::open(const QString &p_filePath, const QSharedPointer<FileOpenPar
return;
}
auto fileType = p_paras->m_fileType;
if (fileType.isEmpty()) {
// Check if we need to open it with external program by default according to the suffix.
fileType = findFileTypeByFile(p_filePath);
if (openWithExternalProgram(p_filePath, fileType)) {
return;
}
}
auto buffer = findBuffer(p_filePath);
if (!buffer) {
if (!buffer || !isSameTypeBuffer(buffer, fileType)) {
// Open it as external file.
auto externalFile = QSharedPointer<ExternalFile>::create(p_filePath);
auto fileType = externalFile->getContentType().m_typeName;
if (fileType.isEmpty()) {
fileType = externalFile->getContentType().m_typeName;
} else if (fileType != externalFile->getContentType().m_typeName) {
externalFile->setContentType(FileTypeHelper::getInst().getFileTypeByName(fileType).m_type);
}
auto factory = m_bufferServer->getItem(fileType);
if (!factory) {
// No factory to open this file type.
@ -188,3 +237,72 @@ void BufferMgr::addBuffer(Buffer *p_buffer)
p_buffer->deleteLater();
});
}
bool BufferMgr::openWithExternalProgram(const QString &p_filePath, const QString &p_name) const
{
if (p_name.isEmpty()) {
return false;
}
if (p_name == FileTypeHelper::s_systemDefaultProgram) {
// Open it by system default program.
qInfo() << "file will be opened by default program" << p_filePath;
WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(p_filePath));
return true;
}
if (auto pro = ConfigMgr::getInst().getSessionConfig().findExternalProgram(p_name)) {
const auto command = pro->fetchCommand(p_filePath);
qDebug() << "external program" << command;
if (!command.isEmpty()) {
ProcessUtils::startDetached(command);
}
return true;
}
return false;
}
bool BufferMgr::isSameTypeBuffer(const Buffer *p_buffer, const QString &p_typeName) const
{
if (p_typeName.isEmpty()) {
return true;
}
auto factory = m_bufferServer->getItem(p_typeName);
Q_ASSERT(factory);
if (factory) {
return factory->isBufferCreatedByFactory(p_buffer);
}
return true;
}
void BufferMgr::updateSuffixToFileType(const QVector<CoreConfig::FileTypeSuffix> &p_fileTypeSuffixes)
{
s_suffixToFileType.clear();
for (const auto &fts : p_fileTypeSuffixes) {
for (const auto &suf : fts.m_suffixes) {
auto it = s_suffixToFileType.find(suf);
if (it != s_suffixToFileType.end()) {
qWarning() << "suffix conflicts for file types" << fts.m_name << it.value();
it.value() = fts.m_name;
} else {
s_suffixToFileType.insert(suf, fts.m_name);
}
}
}
}
QString BufferMgr::findFileTypeByFile(const QString &p_filePath)
{
QFileInfo fi(p_filePath);
auto suffix = fi.suffix().toLower();
auto it = s_suffixToFileType.find(suffix);
if (it != s_suffixToFileType.end()) {
return it.value();
} else {
return QString();
}
}

View File

@ -5,8 +5,10 @@
#include <QScopedPointer>
#include <QSharedPointer>
#include <QVector>
#include <QMap>
#include "namebasedserver.h"
#include "coreconfig.h"
namespace vnotex
{
@ -30,6 +32,8 @@ namespace vnotex
void open(const QString &p_filePath, const QSharedPointer<FileOpenParameters> &p_paras);
static void updateSuffixToFileType(const QVector<CoreConfig::FileTypeSuffix> &p_fileTypeSuffixes);
signals:
void bufferRequested(Buffer *p_buffer, const QSharedPointer<FileOpenParameters> &p_paras);
@ -42,10 +46,19 @@ namespace vnotex
void addBuffer(Buffer *p_buffer);
bool openWithExternalProgram(const QString &p_filePath, const QString &p_name) const;
bool isSameTypeBuffer(const Buffer *p_buffer, const QString &p_typeName) const;
static QString findFileTypeByFile(const QString &p_filePath);
QSharedPointer<NameBasedServer<IBufferFactory>> m_bufferServer;
// Managed by QObject.
QVector<Buffer *> m_buffers;
// Mapping from suffix to file type or external program name.
static QMap<QString, QString> s_suffixToFileType;
};
} // ns vnotex

View File

@ -63,14 +63,14 @@ void ClipboardData::fromJson(const QJsonObject &p_jobj)
|| !p_jobj.contains(c_action)
|| !p_jobj.contains(c_data)) {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
QStringLiteral("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
return;
}
auto idRet = stringToID(p_jobj[c_instanceId].toString());
if (!idRet.first) {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
QStringLiteral("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
return;
}
m_instanceId = idRet.second;
@ -79,7 +79,7 @@ void ClipboardData::fromJson(const QJsonObject &p_jobj)
m_action = intToAction(act);
if (m_action == Action::Invalid) {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
QStringLiteral("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
return;
}

View File

@ -36,6 +36,10 @@ const QString ConfigMgr::c_configFileName = "vnotex.json";
const QString ConfigMgr::c_sessionFileName = "session.json";
const QString ConfigMgr::c_userFilesFolder = "user_files";
const QString ConfigMgr::c_appFilesFolder = "vnotex_files";
const QJsonObject &ConfigMgr::Settings::getJson() const
{
return m_jobj;
@ -68,18 +72,22 @@ ConfigMgr::ConfigMgr(bool p_isUnitTest, QObject *p_parent)
qWarning() << "failed to init ConfigMgr for UnitTest";
return;
}
m_appConfigFolderPath = m_dirForUnitTest->filePath("vnotex_files");
m_userConfigFolderPath = m_dirForUnitTest->filePath("user_files");
FileUtils::copyFile(getConfigFilePath(Source::Default), PathUtils::concatenateFilePath(m_appConfigFolderPath, c_configFileName));
} else {
QDir dir(m_dirForUnitTest->path());
dir.mkdir(c_appFilesFolder);
dir.mkdir(c_userFilesFolder);
m_appConfigFolderPath = m_dirForUnitTest->filePath(c_appFilesFolder);
m_userConfigFolderPath = m_dirForUnitTest->filePath(c_userFilesFolder);
return;
}
locateConfigFolder();
bool needUpdate = checkAppConfig();
if (needUpdate) {
checkUserConfig();
}
}
m_config->init();
m_sessionConfig->init();
@ -106,8 +114,7 @@ void ConfigMgr::locateConfigFolder()
qInfo() << "app folder" << appDirPath;
// Check app config.
{
const QString configFolderName("vnotex_files");
QString folderPath(appDirPath + '/' + configFolderName);
QString folderPath(appDirPath + '/' + c_appFilesFolder);
if (QDir(folderPath).exists()) {
// Config folder in app/.
m_appConfigFolderPath = PathUtils::cleanPath(folderPath);
@ -118,8 +125,7 @@ void ConfigMgr::locateConfigFolder()
// Check user config.
{
const QString configFolderName("user_files");
QString folderPath(appDirPath + '/' + configFolderName);
QString folderPath(appDirPath + '/' + c_userFilesFolder);
if (QDir(folderPath).exists()) {
// Config folder in app/.
m_userConfigFolderPath = PathUtils::cleanPath(folderPath);
@ -185,12 +191,11 @@ bool ConfigMgr::checkAppConfig()
// Load extra data.
splash->showMessage("Loading extra resource data");
const QString extraRcc(PathUtils::concatenateFilePath(QCoreApplication::applicationDirPath(),
QStringLiteral("vnote_extra.rcc")));
const QString extraRcc("app:vnote_extra.rcc");
bool ret = QResource::registerResource(extraRcc);
if (!ret) {
Exception::throwOne(Exception::Type::FailToReadFile,
QString("failed to register resource file %1").arg(extraRcc));
QStringLiteral("failed to register resource file %1").arg(extraRcc));
}
auto cleanup = qScopeGuard([extraRcc]() {
QResource::unregisterResource(extraRcc);
@ -227,6 +232,12 @@ bool ConfigMgr::checkAppConfig()
FileUtils::copyDir(extraDataRoot + QStringLiteral("/themes"),
appConfigDir.filePath(QStringLiteral("themes")));
// Copy tasks.
qApp->processEvents();
splash->showMessage("Copying tasks");
FileUtils::copyDir(extraDataRoot + QStringLiteral("/tasks"),
appConfigDir.filePath(QStringLiteral("tasks")));
// Copy docs.
qApp->processEvents();
splash->showMessage("Copying docs");
@ -377,6 +388,18 @@ QString ConfigMgr::getUserThemeFolder() const
return folderPath;
}
QString ConfigMgr::getAppTaskFolder() const
{
return PathUtils::concatenateFilePath(m_appConfigFolderPath, QStringLiteral("tasks"));
}
QString ConfigMgr::getUserTaskFolder() const
{
auto folderPath = PathUtils::concatenateFilePath(m_userConfigFolderPath, QStringLiteral("tasks"));
QDir().mkpath(folderPath);
return folderPath;
}
QString ConfigMgr::getAppWebStylesFolder() const
{
return PathUtils::concatenateFilePath(m_appConfigFolderPath, QStringLiteral("web-styles"));
@ -546,3 +569,46 @@ QString ConfigMgr::getApplicationVersion()
return appVersion;
}
void ConfigMgr::initAppPrefixPath()
{
// Support QFile("app:abc.txt").
QStringList potential_dirs;
auto app_dir_path = QCoreApplication::applicationDirPath();
qInfo() << "app prefix path: " << app_dir_path;
potential_dirs << app_dir_path;
#if defined(Q_OS_LINUX)
QDir localBinDir(app_dir_path);
if (localBinDir.exists("../local/bin/vnote")) {
auto app_dir_path2 = localBinDir.cleanPath(localBinDir.filePath("../local/share"));
qInfo() << "app prefix path: " << app_dir_path2;
potential_dirs << app_dir_path2;
}
if (localBinDir.exists("../share")) {
auto app_dir_path3 = localBinDir.cleanPath(localBinDir.filePath("../share"));
qInfo() << "app prefix path: " << app_dir_path3;
potential_dirs << app_dir_path3;
}
#elif defined(Q_OS_MACOS)
QDir localBinDir(app_dir_path);
if (localBinDir.exists("../Resources")) {
auto app_dir_path2 = localBinDir.cleanPath(localBinDir.filePath("../Resources"));
qInfo() << "app prefix path: " << app_dir_path2;
potential_dirs << app_dir_path2;
}
#endif
QDir::setSearchPaths("app", potential_dirs);
}
QJsonValue ConfigMgr::parseAndReadConfig(const QString &p_exp) const
{
if (p_exp.startsWith(QStringLiteral("main."))) {
return Utils::parseAndReadJson(m_config->toJson(), p_exp.mid(5));
} else if (p_exp.startsWith(QStringLiteral("session."))) {
return Utils::parseAndReadJson(m_sessionConfig->toJson(), p_exp.mid(8));
} else {
return QJsonValue();
}
}

View File

@ -74,6 +74,10 @@ namespace vnotex
QString getUserThemeFolder() const;
QString getAppTaskFolder() const;
QString getUserTaskFolder() const;
QString getAppWebStylesFolder() const;
QString getUserWebStylesFolder() const;
@ -102,6 +106,9 @@ namespace vnotex
QString getConfigFilePath(Source p_src) const;
// Parse exp like "[main|session].core.shortcuts.FullScreen" and return the config value.
QJsonValue parseAndReadConfig(const QString &p_exp) const;
// Called at boostrap without QApplication instance.
static QString locateSessionConfigFilePathAtBootstrap();
@ -113,6 +120,8 @@ namespace vnotex
static QString getApplicationVersion();
static void initAppPrefixPath();
static void initForUnitTest();
static const QString c_orgName;
@ -164,6 +173,10 @@ namespace vnotex
// Name of the session config file.
static const QString c_sessionFileName;
static const QString c_userFilesFolder;
static const QString c_appFilesFolder;
};
} // ns vnotex

View File

@ -1,71 +0,0 @@
INCLUDEPATH *= $$PWD
include($$PWD/notebookbackend/notebookbackend.pri)
include($$PWD/versioncontroller/versioncontroller.pri)
include($$PWD/notebookconfigmgr/notebookconfigmgr.pri)
include($$PWD/notebook/notebook.pri)
include($$PWD/buffer/buffer.pri)
SOURCES += \
$$PWD/buffermgr.cpp \
$$PWD/configmgr.cpp \
$$PWD/coreconfig.cpp \
$$PWD/editorconfig.cpp \
$$PWD/externalfile.cpp \
$$PWD/file.cpp \
$$PWD/historyitem.cpp \
$$PWD/historymgr.cpp \
$$PWD/htmltemplatehelper.cpp \
$$PWD/logger.cpp \
$$PWD/mainconfig.cpp \
$$PWD/markdowneditorconfig.cpp \
$$PWD/quickaccesshelper.cpp \
$$PWD/singleinstanceguard.cpp \
$$PWD/templatemgr.cpp \
$$PWD/texteditorconfig.cpp \
$$PWD/vnotex.cpp \
$$PWD/thememgr.cpp \
$$PWD/notebookmgr.cpp \
$$PWD/theme.cpp \
$$PWD/sessionconfig.cpp \
$$PWD/clipboarddata.cpp \
$$PWD/widgetconfig.cpp
HEADERS += \
$$PWD/buffermgr.h \
$$PWD/configmgr.h \
$$PWD/coreconfig.h \
$$PWD/editorconfig.h \
$$PWD/events.h \
$$PWD/externalfile.h \
$$PWD/file.h \
$$PWD/filelocator.h \
$$PWD/fileopenparameters.h \
$$PWD/historyitem.h \
$$PWD/historymgr.h \
$$PWD/htmltemplatehelper.h \
$$PWD/location.h \
$$PWD/logger.h \
$$PWD/mainconfig.h \
$$PWD/markdowneditorconfig.h \
$$PWD/noncopyable.h \
$$PWD/quickaccesshelper.h \
$$PWD/singleinstanceguard.h \
$$PWD/iconfig.h \
$$PWD/templatemgr.h \
$$PWD/texteditorconfig.h \
$$PWD/vnotex.h \
$$PWD/thememgr.h \
$$PWD/global.h \
$$PWD/namebasedserver.h \
$$PWD/exception.h \
$$PWD/notebookmgr.h \
$$PWD/theme.h \
$$PWD/sessionconfig.h \
$$PWD/clipboarddata.h \
$$PWD/webresource.h \
$$PWD/widgetconfig.h

View File

@ -3,6 +3,9 @@
#include <QMetaEnum>
#include <QLocale>
#include <utils/utils.h>
#include <buffer/filetypehelper.h>
using namespace vnotex;
#define READSTR(key) readString(appObj, userObj, (key))
@ -10,6 +13,17 @@ using namespace vnotex;
#define READBOOL(key) readBool(appObj, userObj, (key))
#define READSTRLIST(key) readStringList(appObj, userObj, (key))
CoreConfig::FileTypeSuffix::FileTypeSuffix(const QString &p_name, const QStringList &p_suffixes)
: m_name(p_name),
m_suffixes(p_suffixes)
{
}
bool CoreConfig::FileTypeSuffix::operator==(const FileTypeSuffix &p_other) const
{
return m_name == p_other.m_name && m_suffixes == p_other.m_suffixes;
}
QStringList CoreConfig::s_availableLocales;
CoreConfig::CoreConfig(ConfigMgr *p_mgr, IConfig *p_topConfig)
@ -43,14 +57,16 @@ void CoreConfig::init(const QJsonObject &p_app,
loadShortcuts(appObj, userObj);
m_shortcutLeaderKey = READSTR(QStringLiteral("shortcut_leader_key"));
m_toolBarIconSize = READINT(QStringLiteral("toolbar_icon_size"));
if (m_toolBarIconSize <= 0) {
m_toolBarIconSize = 16;
m_toolBarIconSize = 18;
}
m_docksTabBarIconSize = READINT(QStringLiteral("docks_tabbar_icon_size"));
if (m_docksTabBarIconSize <= 0) {
m_docksTabBarIconSize = 20;
m_docksTabBarIconSize = 18;
}
loadNoteManagement(appObj, userObj);
@ -65,6 +81,20 @@ void CoreConfig::init(const QJsonObject &p_app,
}
m_perNotebookHistoryEnabled = READBOOL(QStringLiteral("per_notebook_history"));
{
auto lineEnding = READSTR(QStringLiteral("line_ending"));
m_lineEnding = stringToLineEndingPolicy(lineEnding);
}
{
auto mode = READSTR(QStringLiteral("default_open_mode"));
m_defaultOpenMode = stringToViewWindowMode(mode);
}
loadFileTypeSuffixes(appObj, userObj);
loadUnitedEntry(appObj, userObj);
}
QJsonObject CoreConfig::toJson() const
@ -73,12 +103,17 @@ QJsonObject CoreConfig::toJson() const
obj[QStringLiteral("theme")] = m_theme;
obj[QStringLiteral("locale")] = m_locale;
obj[QStringLiteral("shortcuts")] = saveShortcuts();
obj[QStringLiteral("shortcut_leader_key")] = m_shortcutLeaderKey;
obj[QStringLiteral("toolbar_icon_size")] = m_toolBarIconSize;
obj[QStringLiteral("docks_tabbar_icon_size")] = m_docksTabBarIconSize;
obj[QStringLiteral("recover_last_session_on_start")] = m_recoverLastSessionOnStartEnabled;
obj[QStringLiteral("check_for_updates_on_start")] = m_checkForUpdatesOnStartEnabled;
obj[QStringLiteral("history_max_count")] = m_historyMaxCount;
obj[QStringLiteral("per_notebook_history")] = m_perNotebookHistoryEnabled;
obj[QStringLiteral("line_ending")] = lineEndingPolicyToString(m_lineEnding);
obj[QStringLiteral("file_type_suffixes")] = saveFileTypeSuffixes();
obj[QStringLiteral("united_entry")] = saveUnitedEntry();
obj[QStringLiteral("default_open_mode")] = viewWindowModeToString(m_defaultOpenMode);
return obj;
}
@ -216,3 +251,149 @@ void CoreConfig::setPerNotebookHistoryEnabled(bool p_enabled)
{
updateConfig(m_perNotebookHistoryEnabled, p_enabled, this);
}
const QString &CoreConfig::getShortcutLeaderKey() const
{
return m_shortcutLeaderKey;
}
LineEndingPolicy CoreConfig::getLineEndingPolicy() const
{
return m_lineEnding;
}
void CoreConfig::setLineEndingPolicy(LineEndingPolicy p_ending)
{
updateConfig(m_lineEnding, p_ending, this);
}
void CoreConfig::loadFileTypeSuffixes(const QJsonObject &p_app, const QJsonObject &p_user)
{
m_fileTypeSuffixes.clear();
QJsonArray arr;
if (p_user.contains(QStringLiteral("file_type_suffixes"))) {
arr = p_user[QStringLiteral("file_type_suffixes")].toArray();
} else {
arr = p_app[QStringLiteral("file_type_suffixes")].toArray();
}
m_fileTypeSuffixes.reserve(arr.size());
bool hasSystemDefined = false;
for (int i = 0; i < arr.size(); ++i) {
const auto obj = arr[i].toObject();
const auto name = obj[QStringLiteral("name")].toString();
if (name.isEmpty()) {
continue;
}
if (!hasSystemDefined && name == FileTypeHelper::s_systemDefaultProgram) {
hasSystemDefined = true;
}
const auto suffixes = readStringList(obj, QStringLiteral("suffixes"));
m_fileTypeSuffixes.push_back(FileTypeSuffix(name, Utils::toLower(suffixes)));
}
if (!hasSystemDefined) {
m_fileTypeSuffixes.push_back(FileTypeSuffix(FileTypeHelper::s_systemDefaultProgram, QStringList()));
}
}
QJsonArray CoreConfig::saveFileTypeSuffixes() const
{
QJsonArray arr;
for (const auto &fts : m_fileTypeSuffixes) {
QJsonObject obj;
obj[QStringLiteral("name")] = fts.m_name;
writeStringList(obj, QStringLiteral("suffixes"), fts.m_suffixes);
arr.push_back(obj);
}
return arr;
}
void CoreConfig::loadUnitedEntry(const QJsonObject &p_app, const QJsonObject &p_user)
{
QJsonObject unitedObj;
if (p_user.contains(QStringLiteral("united_entry"))) {
unitedObj = p_user[QStringLiteral("united_entry")].toObject();
} else {
unitedObj = p_app[QStringLiteral("united_entry")].toObject();
}
m_unitedEntryAlias = unitedObj[QStringLiteral("alias")].toArray();
}
QJsonObject CoreConfig::saveUnitedEntry() const
{
QJsonObject unitedObj;
unitedObj[QStringLiteral("alias")] = m_unitedEntryAlias;
return unitedObj;
}
const QVector<CoreConfig::FileTypeSuffix> &CoreConfig::getFileTypeSuffixes() const
{
return m_fileTypeSuffixes;
}
void CoreConfig::setFileTypeSuffixes(const QVector<CoreConfig::FileTypeSuffix> &p_fileTypeSuffixes)
{
updateConfig(m_fileTypeSuffixes, p_fileTypeSuffixes, this);
}
const QStringList *CoreConfig::findFileTypeSuffix(const QString &p_name) const
{
if (p_name.isEmpty()) {
return nullptr;
}
for (const auto &fts : m_fileTypeSuffixes) {
if (fts.m_name == p_name) {
return &fts.m_suffixes;
}
}
return nullptr;
}
const QJsonArray &CoreConfig::getUnitedEntryAlias() const
{
return m_unitedEntryAlias;
}
void CoreConfig::setUnitedEntryAlias(const QJsonArray &p_alias)
{
updateConfig(m_unitedEntryAlias, p_alias, this);
}
ViewWindowMode CoreConfig::getDefaultOpenMode() const
{
return m_defaultOpenMode;
}
void CoreConfig::setDefaultOpenMode(ViewWindowMode p_mode)
{
updateConfig(m_defaultOpenMode, p_mode, this);
}
ViewWindowMode CoreConfig::stringToViewWindowMode(const QString &p_mode)
{
if (p_mode == "edit") {
return ViewWindowMode::Edit;
}
return ViewWindowMode::Read;
}
QString CoreConfig::viewWindowModeToString(ViewWindowMode p_mode)
{
switch (p_mode) {
case ViewWindowMode::Edit:
return "edit";
default:
return "read";
}
}

View File

@ -7,6 +7,8 @@
#include <QString>
#include <QStringList>
#include "global.h"
namespace vnotex
{
class CoreConfig : public IConfig
@ -20,8 +22,12 @@ namespace vnotex
ExpandContentArea,
Settings,
NewNote,
NewQuickNote,
NewFolder,
CloseTab,
CloseAllTabs,
CloseOtherTabs,
CloseTabsToTheLeft,
CloseTabsToTheRight,
NavigationDock,
OutlineDock,
@ -29,6 +35,7 @@ namespace vnotex
SnippetDock,
LocationListDock,
HistoryDock,
WindowsDock,
TagDock,
Search,
NavigationMode,
@ -66,10 +73,29 @@ namespace vnotex
MoveOneSplitUp,
MoveOneSplitRight,
OpenLastClosedFile,
UnitedEntry,
Copy,
Paste,
Cut,
Properties,
Global_WakeUp,
MaxShortcut
};
Q_ENUM(Shortcut)
struct FileTypeSuffix
{
FileTypeSuffix() = default;
FileTypeSuffix(const QString &p_name, const QStringList &p_suffixes);
bool operator==(const FileTypeSuffix &p_other) const;
QString m_name;
QStringList m_suffixes;
};
CoreConfig(ConfigMgr *p_mgr, IConfig *p_topConfig);
void init(const QJsonObject &p_app, const QJsonObject &p_user) Q_DECL_OVERRIDE;
@ -108,6 +134,22 @@ namespace vnotex
bool isPerNotebookHistoryEnabled() const;
void setPerNotebookHistoryEnabled(bool p_enabled);
const QString &getShortcutLeaderKey() const;
LineEndingPolicy getLineEndingPolicy() const;
void setLineEndingPolicy(LineEndingPolicy p_ending);
const QVector<FileTypeSuffix> &getFileTypeSuffixes() const;
void setFileTypeSuffixes(const QVector<FileTypeSuffix> &p_fileTypeSuffixes);
const QStringList *findFileTypeSuffix(const QString &p_name) const;
const QJsonArray &getUnitedEntryAlias() const;
void setUnitedEntryAlias(const QJsonArray &p_alias);
ViewWindowMode getDefaultOpenMode() const;
void setDefaultOpenMode(ViewWindowMode p_mode);
private:
friend class MainConfig;
@ -117,6 +159,17 @@ namespace vnotex
QJsonObject saveShortcuts() const;
void loadFileTypeSuffixes(const QJsonObject &p_app, const QJsonObject &p_user);
QJsonArray saveFileTypeSuffixes() const;
void loadUnitedEntry(const QJsonObject &p_app, const QJsonObject &p_user);
QJsonObject saveUnitedEntry() const;
static ViewWindowMode stringToViewWindowMode(const QString &p_mode);
static QString viewWindowModeToString(ViewWindowMode p_mode);
// Theme name.
QString m_theme;
@ -126,8 +179,11 @@ namespace vnotex
QString m_shortcuts[Shortcut::MaxShortcut];
// Leader key of shortcuts defined in m_shortctus.
QString m_shortcutLeaderKey;
// Icon size of MainWindow tool bar.
int m_toolBarIconSize = 16;
int m_toolBarIconSize = 18;
// Icon size of MainWindow QDockWidgets tab bar.
int m_docksTabBarIconSize = 20;
@ -143,7 +199,15 @@ namespace vnotex
int m_historyMaxCount = 100;
// Whether store history in each notebook.
bool m_perNotebookHistoryEnabled = true;
bool m_perNotebookHistoryEnabled = false;
LineEndingPolicy m_lineEnding = LineEndingPolicy::LF;
QVector<FileTypeSuffix> m_fileTypeSuffixes;
QJsonArray m_unitedEntryAlias;
ViewWindowMode m_defaultOpenMode = ViewWindowMode::Read;
static QStringList s_availableLocales;
};

View File

@ -5,6 +5,8 @@
#include "texteditorconfig.h"
#include "markdowneditorconfig.h"
#include "pdfviewerconfig.h"
#include "mindmapeditorconfig.h"
#include <vtextedit/viconfig.h>
@ -41,7 +43,9 @@ QJsonObject EditorConfig::ImageHostItem::toJson() const
EditorConfig::EditorConfig(ConfigMgr *p_mgr, IConfig *p_topConfig)
: IConfig(p_mgr, p_topConfig),
m_textEditorConfig(new TextEditorConfig(p_mgr, p_topConfig)),
m_markdownEditorConfig(new MarkdownEditorConfig(p_mgr, p_topConfig, m_textEditorConfig))
m_markdownEditorConfig(new MarkdownEditorConfig(p_mgr, p_topConfig, m_textEditorConfig)),
m_pdfViewerConfig(new PdfViewerConfig(p_mgr, p_topConfig)),
m_mindMapEditorConfig(new MindMapEditorConfig(p_mgr, p_topConfig))
{
m_sessionName = QStringLiteral("editor");
}
@ -65,6 +69,8 @@ void EditorConfig::init(const QJsonObject &p_app,
m_textEditorConfig->init(appObj, userObj);
m_markdownEditorConfig->init(appObj, userObj);
m_pdfViewerConfig->init(appObj, userObj);
m_mindMapEditorConfig->init(appObj, userObj);
}
void EditorConfig::loadCore(const QJsonObject &p_app, const QJsonObject &p_user)
@ -148,9 +154,15 @@ QJsonObject EditorConfig::toJson() const
QJsonObject obj;
obj[m_textEditorConfig->getSessionName()] = m_textEditorConfig->toJson();
obj[m_markdownEditorConfig->getSessionName()] = m_markdownEditorConfig->toJson();
obj[m_pdfViewerConfig->getSessionName()] = m_pdfViewerConfig->toJson();
obj[m_mindMapEditorConfig->getSessionName()] = m_mindMapEditorConfig->toJson();
obj[QStringLiteral("core")] = saveCore();
obj[QStringLiteral("image_host")] = saveImageHost();
// In UT, it may be nullptr.
if (m_viConfig) {
obj[QStringLiteral("vi")] = m_viConfig->toJson();
}
return obj;
}
@ -174,6 +186,26 @@ const MarkdownEditorConfig &EditorConfig::getMarkdownEditorConfig() const
return *m_markdownEditorConfig;
}
PdfViewerConfig &EditorConfig::getPdfViewerConfig()
{
return *m_pdfViewerConfig;
}
const PdfViewerConfig &EditorConfig::getPdfViewerConfig() const
{
return *m_pdfViewerConfig;
}
MindMapEditorConfig &EditorConfig::getMindMapEditorConfig()
{
return *m_mindMapEditorConfig;
}
const MindMapEditorConfig &EditorConfig::getMindMapEditorConfig() const
{
return *m_mindMapEditorConfig;
}
int EditorConfig::getToolBarIconSize() const
{
return m_toolBarIconSize;
@ -217,44 +249,6 @@ EditorConfig::AutoSavePolicy EditorConfig::stringToAutoSavePolicy(const QString
}
}
QString EditorConfig::lineEndingPolicyToString(LineEndingPolicy p_ending) const
{
switch (p_ending) {
case LineEndingPolicy::Platform:
return QStringLiteral("platform");
case LineEndingPolicy::File:
return QStringLiteral("file");
case LineEndingPolicy::LF:
return QStringLiteral("lf");
case LineEndingPolicy::CRLF:
return QStringLiteral("crlf");
case LineEndingPolicy::CR:
return QStringLiteral("cr");
}
return QStringLiteral("platform");
}
LineEndingPolicy EditorConfig::stringToLineEndingPolicy(const QString &p_str) const
{
auto ending = p_str.toLower();
if (ending == QStringLiteral("file")) {
return LineEndingPolicy::File;
} else if (ending == QStringLiteral("lf")) {
return LineEndingPolicy::LF;
} else if (ending == QStringLiteral("crlf")) {
return LineEndingPolicy::CRLF;
} else if (ending == QStringLiteral("cr")) {
return LineEndingPolicy::CR;
} else {
return LineEndingPolicy::Platform;
}
}
EditorConfig::AutoSavePolicy EditorConfig::getAutoSavePolicy() const
{
return m_autoSavePolicy;

View File

@ -19,6 +19,8 @@ namespace vnotex
{
class TextEditorConfig;
class MarkdownEditorConfig;
class PdfViewerConfig;
class MindMapEditorConfig;
class EditorConfig : public IConfig
{
@ -53,13 +55,19 @@ namespace vnotex
TypeTable,
TypeMark,
Outline,
RichPaste,
AltPaste,
FindAndReplace,
FindNext,
FindPrevious,
ApplySnippet,
Tag,
Debug,
Print,
ClearHighlights,
WordCount,
Attachment,
AlternateViewMode,
ParseToMarkdownAndPaste,
MaxShortcut
};
Q_ENUM(Shortcut)
@ -99,6 +107,12 @@ namespace vnotex
MarkdownEditorConfig &getMarkdownEditorConfig();
const MarkdownEditorConfig &getMarkdownEditorConfig() const;
PdfViewerConfig &getPdfViewerConfig();
const PdfViewerConfig &getPdfViewerConfig() const;
MindMapEditorConfig &getMindMapEditorConfig();
const MindMapEditorConfig &getMindMapEditorConfig() const;
void init(const QJsonObject &p_app, const QJsonObject &p_user) Q_DECL_OVERRIDE;
QJsonObject toJson() const Q_DECL_OVERRIDE;
@ -148,9 +162,6 @@ namespace vnotex
QString autoSavePolicyToString(AutoSavePolicy p_policy) const;
AutoSavePolicy stringToAutoSavePolicy(const QString &p_str) const;
QString lineEndingPolicyToString(LineEndingPolicy p_ending) const;
LineEndingPolicy stringToLineEndingPolicy(const QString &p_str) const;
void loadImageHost(const QJsonObject &p_app, const QJsonObject &p_user);
QJsonObject saveImageHost() const;
@ -173,6 +184,10 @@ namespace vnotex
QScopedPointer<MarkdownEditorConfig> m_markdownEditorConfig;
QScopedPointer<PdfViewerConfig> m_pdfViewerConfig;
QScopedPointer<MindMapEditorConfig> m_mindMapEditorConfig;
bool m_spellCheckAutoDetectLanguageEnabled = false;
QString m_spellCheckDefaultDictionary;

View File

@ -47,40 +47,46 @@ namespace vnotex
{
switch (p_type) {
case Type::InvalidPath:
return QString("InvalidPath");
return QStringLiteral("InvalidPath");
case Type::FailToCreateDir:
return QString("FailToCreateDir");
return QStringLiteral("FailToCreateDir");
case Type::FailToWriteFile:
return QString("FailToWriteFile");
return QStringLiteral("FailToWriteFile");
case Type::FailToReadFile:
return QString("FailToReadFile");
return QStringLiteral("FailToReadFile");
case Type::FailToRenameFile:
return QString("FailToRenameFile");
return QStringLiteral("FailToRenameFile");
case Type::FailToCopyFile:
return QString("FailToCopyFile");
return QStringLiteral("FailToCopyFile");
case Type::FailToCopyDir:
return QString("FailToCopyDir");
return QStringLiteral("FailToCopyDir");
case Type::FailToRemoveFile:
return QString("FailToRemoveFile");
return QStringLiteral("FailToRemoveFile");
case Type::FailToRemoveDir:
return QString("FailToRemoveDir");
return QStringLiteral("FailToRemoveDir");
case Type::FileMissingOnDisk:
return QString("FileMissingOnDisk");
return QStringLiteral("FileMissingOnDisk");
case Type::EssentialFileMissing:
return QString("EssentialFileMissing");
return QStringLiteral("EssentialFileMissing");
case Type::FileExistsOnCreate:
return QStringLiteral("FileExistsOnCreate");
case Type::DirExistsOnCreate:
return QStringLiteral("DirExistsOnCreate");
case Type::InvalidArgument:
return QString("InvalidArgument");
return QStringLiteral("InvalidArgument");
}
return QString::number(static_cast<int>(p_type));

View File

@ -59,7 +59,6 @@ namespace vnotex
const FileType &getContentType() const;
protected:
void setContentType(int p_type);
private:

View File

@ -50,6 +50,9 @@ namespace vnotex
// Whether should save this file into session.
bool m_sessionEnabled = true;
// Whether specify the built-in file type to open as or the external program to open with.
QString m_fileType;
std::function<void()> m_hooks[Hook::MaxHook];
};
}

3
src/core/global.cpp Normal file
View File

@ -0,0 +1,3 @@
#include "global.h"
using namespace vnotex;

View File

@ -94,8 +94,6 @@ namespace vnotex
{
Read,
Edit,
FullPreview,
FocusPreview,
Invalid
};
@ -141,6 +139,65 @@ namespace vnotex
CRLF,
CR
};
inline QString lineEndingPolicyToString(LineEndingPolicy p_ending)
{
switch (p_ending) {
case LineEndingPolicy::Platform:
return QStringLiteral("platform");
case LineEndingPolicy::File:
return QStringLiteral("file");
case LineEndingPolicy::LF:
return QStringLiteral("lf");
case LineEndingPolicy::CRLF:
return QStringLiteral("crlf");
case LineEndingPolicy::CR:
return QStringLiteral("cr");
}
return QStringLiteral("platform");
}
inline LineEndingPolicy stringToLineEndingPolicy(const QString &p_str)
{
auto ending = p_str.toLower();
if (ending == QStringLiteral("file")) {
return LineEndingPolicy::File;
} else if (ending == QStringLiteral("lf")) {
return LineEndingPolicy::LF;
} else if (ending == QStringLiteral("crlf")) {
return LineEndingPolicy::CRLF;
} else if (ending == QStringLiteral("cr")) {
return LineEndingPolicy::CR;
} else {
return LineEndingPolicy::Platform;
}
}
enum Role
{
// Qt::UserRole = 0x0100
UserRole2 = 0x0101,
HighlightsRole = 0x0102,
// Used for comparison.
ComparisonRole = 0x0103
};
enum ViewOrder
{
OrderedByConfiguration = 0,
OrderedByName,
OrderedByNameReversed,
OrderedByCreatedTime,
OrderedByCreatedTimeReversed,
OrderedByModifiedTime,
OrderedByModifiedTimeReversed,
ViewOrderMax
};
} // ns vnotex
Q_DECLARE_OPERATORS_FOR_FLAGS(vnotex::FindOptions);

View File

@ -94,6 +94,16 @@ const QVector<QSharedPointer<HistoryItemFull>> &HistoryMgr::getHistory() const
return m_history;
}
void HistoryMgr::removeFromHistory(const QString &p_itemPath)
{
for (int i = m_history.size() - 1; i >= 0; --i) {
if (m_history[i]->m_item.m_path == p_itemPath) {
m_history.remove(i);
break;
}
}
}
void HistoryMgr::add(const QString &p_path,
int p_lineNumber,
ViewWindowMode p_mode,
@ -116,13 +126,7 @@ void HistoryMgr::add(const QString &p_path,
// Maintain the combined queue.
{
for (int i = m_history.size() - 1; i >= 0; --i) {
if (m_history[i]->m_item.m_path == item.m_path) {
// Erase it.
m_history.remove(i);
break;
}
}
removeFromHistory(item.m_path);
auto fullItem = QSharedPointer<HistoryItemFull>::create();
fullItem->m_item = item;
@ -156,16 +160,35 @@ void HistoryMgr::add(const QString &p_path,
emit historyUpdated();
}
void HistoryMgr::insertHistoryItem(QVector<HistoryItem> &p_history, const HistoryItem &p_item)
void HistoryMgr::remove(const QVector<QString> &p_paths, Notebook *p_notebook)
{
for(const QString &p_itemPath : p_paths) {
if (p_notebook && m_perNotebookHistoryEnabled && p_notebook->history()) {
p_notebook->history()->removeHistory(p_itemPath);
} else {
auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
sessionConfig.removeHistory(p_itemPath);
}
removeFromHistory(p_itemPath);
}
emit historyUpdated();
}
void HistoryMgr::removeHistoryItem(QVector<HistoryItem> &p_history, const QString &p_itemPath)
{
for (int i = p_history.size() - 1; i >= 0; --i) {
if (p_history[i].m_path == p_item.m_path) {
// Erase it.
if (p_history[i].m_path == p_itemPath) {
p_history.remove(i);
break;
}
}
}
void HistoryMgr::insertHistoryItem(QVector<HistoryItem> &p_history, const HistoryItem &p_item)
{
removeHistoryItem(p_history, p_item.m_path);
p_history.append(p_item);
const int maxHistoryCount = ConfigMgr::getInst().getCoreConfig().getHistoryMaxCount();

View File

@ -55,10 +55,14 @@ namespace vnotex
bool p_readOnly,
Notebook *p_notebook);
void remove(const QVector<QString> &p_paths, Notebook *p_notebook);
void clear();
LastClosedFile popLastClosedFile();
static void removeHistoryItem(QVector<HistoryItem> &p_history, const QString &p_itemPath);
static void insertHistoryItem(QVector<HistoryItem> &p_history, const HistoryItem &p_item);
signals:
@ -72,6 +76,8 @@ namespace vnotex
// Sorted by last accessed time ascendingly.
QVector<QSharedPointer<HistoryItemFull>> m_history;
void removeFromHistory(const QString &p_itemPath);
QVector<LastClosedFile> m_lastClosedFiles;
const bool m_perNotebookHistoryEnabled = false;

View File

@ -3,6 +3,8 @@
#include <QDebug>
#include <core/markdowneditorconfig.h>
#include <core/pdfviewerconfig.h>
#include <core/mindmapeditorconfig.h>
#include <core/configmgr.h>
#include <utils/utils.h>
#include <utils/fileutils.h>
@ -16,27 +18,34 @@ using namespace vnotex;
HtmlTemplateHelper::Template HtmlTemplateHelper::s_markdownViewerTemplate;
QString WebGlobalOptions::toJavascriptObject() const
HtmlTemplateHelper::Template HtmlTemplateHelper::s_pdfViewerTemplate;
HtmlTemplateHelper::Template HtmlTemplateHelper::s_mindMapEditorTemplate;
QString MarkdownWebGlobalOptions::toJavascriptObject() const
{
return QStringLiteral("window.vxOptions = {\n")
+ QString("webPlantUml: %1,\n").arg(Utils::boolToString(m_webPlantUml))
+ QString("webGraphviz: %1,\n").arg(Utils::boolToString(m_webGraphviz))
+ QString("constrainImageWidthEnabled: %1,\n").arg(Utils::boolToString(m_constrainImageWidthEnabled))
+ QString("imageAlignCenterEnabled: %1,\n").arg(Utils::boolToString(m_imageAlignCenterEnabled))
+ QString("protectFromXss: %1,\n").arg(Utils::boolToString(m_protectFromXss))
+ QString("htmlTagEnabled: %1,\n").arg(Utils::boolToString(m_htmlTagEnabled))
+ QString("autoBreakEnabled: %1,\n").arg(Utils::boolToString(m_autoBreakEnabled))
+ QString("linkifyEnabled: %1,\n").arg(Utils::boolToString(m_linkifyEnabled))
+ QString("indentFirstLineEnabled: %1,\n").arg(Utils::boolToString(m_indentFirstLineEnabled))
+ QString("sectionNumberEnabled: %1,\n").arg(Utils::boolToString(m_sectionNumberEnabled))
+ QString("transparentBackgroundEnabled: %1,\n").arg(Utils::boolToString(m_transparentBackgroundEnabled))
+ QString("scrollable: %1,\n").arg(Utils::boolToString(m_scrollable))
+ QString("bodyWidth: %1,\n").arg(m_bodyWidth)
+ QString("bodyHeight: %1,\n").arg(m_bodyHeight)
+ QString("transformSvgToPngEnabled: %1,\n").arg(Utils::boolToString(m_transformSvgToPngEnabled))
+ QString("mathJaxScale: %1,\n").arg(m_mathJaxScale)
+ QString("removeCodeToolBarEnabled: %1,\n").arg(Utils::boolToString(m_removeCodeToolBarEnabled))
+ QString("sectionNumberBaseLevel: %1\n").arg(m_sectionNumberBaseLevel)
+ QStringLiteral("webPlantUml: %1,\n").arg(Utils::boolToString(m_webPlantUml))
+ QStringLiteral("plantUmlWebService: '%1',\n").arg(m_plantUmlWebService)
+ QStringLiteral("webGraphviz: %1,\n").arg(Utils::boolToString(m_webGraphviz))
+ QStringLiteral("mathJaxScript: '%1',\n").arg(m_mathJaxScript)
+ QStringLiteral("constrainImageWidthEnabled: %1,\n").arg(Utils::boolToString(m_constrainImageWidthEnabled))
+ QStringLiteral("imageAlignCenterEnabled: %1,\n").arg(Utils::boolToString(m_imageAlignCenterEnabled))
+ QStringLiteral("protectFromXss: %1,\n").arg(Utils::boolToString(m_protectFromXss))
+ QStringLiteral("htmlTagEnabled: %1,\n").arg(Utils::boolToString(m_htmlTagEnabled))
+ QStringLiteral("autoBreakEnabled: %1,\n").arg(Utils::boolToString(m_autoBreakEnabled))
+ QStringLiteral("linkifyEnabled: %1,\n").arg(Utils::boolToString(m_linkifyEnabled))
+ QStringLiteral("indentFirstLineEnabled: %1,\n").arg(Utils::boolToString(m_indentFirstLineEnabled))
+ QStringLiteral("codeBlockLineNumberEnabled: %1,\n").arg(Utils::boolToString(m_codeBlockLineNumberEnabled))
+ QStringLiteral("sectionNumberEnabled: %1,\n").arg(Utils::boolToString(m_sectionNumberEnabled))
+ QStringLiteral("transparentBackgroundEnabled: %1,\n").arg(Utils::boolToString(m_transparentBackgroundEnabled))
+ QStringLiteral("scrollable: %1,\n").arg(Utils::boolToString(m_scrollable))
+ QStringLiteral("bodyWidth: %1,\n").arg(m_bodyWidth)
+ QStringLiteral("bodyHeight: %1,\n").arg(m_bodyHeight)
+ QStringLiteral("transformSvgToPngEnabled: %1,\n").arg(Utils::boolToString(m_transformSvgToPngEnabled))
+ QStringLiteral("mathJaxScale: %1,\n").arg(m_mathJaxScale)
+ QStringLiteral("removeCodeToolBarEnabled: %1,\n").arg(Utils::boolToString(m_removeCodeToolBarEnabled))
+ QStringLiteral("sectionNumberBaseLevel: %1\n").arg(m_sectionNumberBaseLevel)
+ QStringLiteral("}");
}
@ -74,7 +83,7 @@ static QString fillStyleTag(const QString &p_styleFile)
return "";
}
auto url = PathUtils::pathToUrl(p_styleFile);
return QString("<link rel=\"stylesheet\" type=\"text/css\" href=\"%1\">\n").arg(url.toString());
return QStringLiteral("<link rel=\"stylesheet\" type=\"text/css\" href=\"%1\">\n").arg(url.toString());
}
static QString fillScriptTag(const QString &p_scriptFile)
@ -83,7 +92,7 @@ static QString fillScriptTag(const QString &p_scriptFile)
return "";
}
auto url = PathUtils::pathToUrl(p_scriptFile);
return QString("<script type=\"text/javascript\" src=\"%1\"></script>\n").arg(url.toString());
return QStringLiteral("<script type=\"text/javascript\" src=\"%1\"></script>\n").arg(url.toString());
}
static void fillThemeStyles(QString &p_template, const QString &p_webStyleSheetFile, const QString &p_highlightStyleSheetFile)
@ -98,7 +107,7 @@ static void fillThemeStyles(QString &p_template, const QString &p_webStyleSheetF
}
}
static void fillGlobalOptions(QString &p_template, const WebGlobalOptions &p_opts)
static void fillGlobalOptions(QString &p_template, const MarkdownWebGlobalOptions &p_opts)
{
p_template.replace(QStringLiteral("/* VX_GLOBAL_OPTIONS_PLACEHOLDER */"),
p_opts.toJavascriptObject());
@ -184,7 +193,7 @@ void HtmlTemplateHelper::updateMarkdownViewerTemplate(const MarkdownEditorConfig
s_markdownViewerTemplate.m_revision = p_config.revision();
Paras paras;
MarkdownParas paras;
const auto &themeMgr = VNoteX::getInst().getThemeMgr();
paras.m_webStyleSheetFile = themeMgr.getFile(Theme::File::WebStyleSheet);
paras.m_highlightStyleSheetFile = themeMgr.getFile(Theme::File::HighlightStyleSheet);
@ -192,7 +201,8 @@ void HtmlTemplateHelper::updateMarkdownViewerTemplate(const MarkdownEditorConfig
s_markdownViewerTemplate.m_template = generateMarkdownViewerTemplate(p_config, paras);
}
QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, const Paras &p_paras)
QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config,
const MarkdownParas &p_paras)
{
const auto &viewerResource = p_config.getViewerResource();
const auto templateFile = ConfigMgr::getInst().getUserOrAppFile(viewerResource.m_template);
@ -209,9 +219,11 @@ QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorC
fillThemeStyles(htmlTemplate, p_paras.m_webStyleSheetFile, p_paras.m_highlightStyleSheetFile);
{
WebGlobalOptions opts;
MarkdownWebGlobalOptions opts;
opts.m_webPlantUml = p_config.getWebPlantUml();
opts.m_plantUmlWebService = p_config.getPlantUmlWebService();
opts.m_webGraphviz = p_config.getWebGraphviz();
opts.m_mathJaxScript = p_config.getMathJaxScript();
opts.m_sectionNumberEnabled = p_config.getSectionNumberMode() == MarkdownEditorConfig::SectionNumberMode::Read;
opts.m_sectionNumberBaseLevel = p_config.getSectionNumberBaseLevel();
opts.m_constrainImageWidthEnabled = p_config.getConstrainImageWidthEnabled();
@ -221,6 +233,7 @@ QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorC
opts.m_autoBreakEnabled = p_config.getAutoBreakEnabled();
opts.m_linkifyEnabled = p_config.getLinkifyEnabled();
opts.m_indentFirstLineEnabled = p_config.getIndentFirstLineEnabled();
opts.m_codeBlockLineNumberEnabled = p_config.getCodeBlockLineNumberEnabled();
opts.m_transparentBackgroundEnabled = p_paras.m_transparentBackgroundEnabled;
opts.m_scrollable = p_paras.m_scrollable;
opts.m_bodyWidth = p_paras.m_bodyWidth;
@ -236,7 +249,7 @@ QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorC
return htmlTemplate;
}
QString HtmlTemplateHelper::generateExportTemplate(const MarkdownEditorConfig &p_config,
QString HtmlTemplateHelper::generateMarkdownExportTemplate(const MarkdownEditorConfig &p_config,
bool p_addOutlinePanel)
{
auto exportResource = p_config.getExportResource();
@ -245,7 +258,7 @@ QString HtmlTemplateHelper::generateExportTemplate(const MarkdownEditorConfig &p
try {
htmlTemplate = FileUtils::readTextFile(templateFile);
} catch (Exception &p_e) {
qWarning() << "failed to read export HTML template" << templateFile << p_e.what();
qWarning() << "failed to read Markdown export HTML template" << templateFile << p_e.what();
return errorPage();
}
@ -287,7 +300,7 @@ void HtmlTemplateHelper::fillTitle(QString &p_template, const QString &p_title)
{
if (!p_title.isEmpty()) {
p_template.replace("<!-- VX_TITLE_PLACEHOLDER -->",
QString("<title>%1</title>").arg(HtmlUtils::escapeHtml(p_title)));
QStringLiteral("<title>%1</title>").arg(HtmlUtils::escapeHtml(p_title)));
}
}
@ -316,3 +329,75 @@ QString HtmlTemplateHelper::errorPage()
return VNoteX::tr("Failed to load HTML template. Check the logs for details. "
"Try deleting the user configuration file and the default configuration file.");
}
const QString &HtmlTemplateHelper::getPdfViewerTemplate()
{
return s_pdfViewerTemplate.m_template;
}
const QString &HtmlTemplateHelper::getPdfViewerTemplatePath()
{
return s_pdfViewerTemplate.m_templatePath;
}
void HtmlTemplateHelper::updatePdfViewerTemplate(const PdfViewerConfig &p_config, bool p_force)
{
if (!p_force && p_config.revision() == s_pdfViewerTemplate.m_revision) {
return;
}
s_pdfViewerTemplate.m_revision = p_config.revision();
generatePdfViewerTemplate(p_config, s_pdfViewerTemplate);
}
void HtmlTemplateHelper::generatePdfViewerTemplate(const PdfViewerConfig &p_config, Template& p_template)
{
const auto &viewerResource = p_config.getViewerResource();
p_template.m_templatePath = ConfigMgr::getInst().getUserOrAppFile(viewerResource.m_template);
try {
p_template.m_template = FileUtils::readTextFile(p_template.m_templatePath);
} catch (Exception &p_e) {
qWarning() << "failed to read HTML template" << p_template.m_templatePath << p_e.what();
p_template.m_template = errorPage();
return;
}
fillResources(p_template.m_template, viewerResource);
}
const QString &HtmlTemplateHelper::getMindMapEditorTemplate()
{
return s_mindMapEditorTemplate.m_template;
}
void HtmlTemplateHelper::updateMindMapEditorTemplate(const MindMapEditorConfig &p_config, bool p_force)
{
if (!p_force && p_config.revision() == s_mindMapEditorTemplate.m_revision) {
return;
}
s_mindMapEditorTemplate.m_revision = p_config.revision();
generateMindMapEditorTemplate(p_config,
QString() /* Use empty theme style for now */,
s_mindMapEditorTemplate);
}
void HtmlTemplateHelper::generateMindMapEditorTemplate(const MindMapEditorConfig &p_config,
const QString &p_webStyleSheetFile,
Template& p_template)
{
const auto &editorResource = p_config.getEditorResource();
p_template.m_templatePath = ConfigMgr::getInst().getUserOrAppFile(editorResource.m_template);
try {
p_template.m_template = FileUtils::readTextFile(p_template.m_templatePath);
} catch (Exception &p_e) {
qWarning() << "failed to read HTML template" << p_template.m_templatePath << p_e.what();
p_template.m_template = errorPage();
return;
}
fillThemeStyles(p_template.m_template, p_webStyleSheetFile, QString());
fillResources(p_template.m_template, editorResource);
}

View File

@ -6,15 +6,21 @@
namespace vnotex
{
class MarkdownEditorConfig;
class PdfViewerConfig;
class MindMapEditorConfig;
struct WebResource;
// Global options to be passed to Web side at the very beginning.
struct WebGlobalOptions
// Global options to be passed to Web side at the very beginning for Markdown.
struct MarkdownWebGlobalOptions
{
bool m_webPlantUml = true;
QString m_plantUmlWebService;
bool m_webGraphviz = true;
QString m_mathJaxScript;
bool m_sectionNumberEnabled = true;
int m_sectionNumberBaseLevel = 2;
@ -33,6 +39,8 @@ namespace vnotex
bool m_indentFirstLineEnabled = false;
bool m_codeBlockLineNumberEnabled = true;
// Force to use transparent background.
bool m_transparentBackgroundEnabled = false;
@ -61,7 +69,7 @@ namespace vnotex
class HtmlTemplateHelper
{
public:
struct Paras
struct MarkdownParas
{
QString m_webStyleSheetFile;
@ -84,14 +92,16 @@ namespace vnotex
HtmlTemplateHelper() = delete;
// For MarkdownViewer.
static const QString &getMarkdownViewerTemplate();
static void updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, bool p_force = false);
static QString generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, const Paras &p_paras);
static QString generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, const MarkdownParas &p_paras);
static QString generateExportTemplate(const MarkdownEditorConfig &p_config,
static QString generateMarkdownExportTemplate(const MarkdownEditorConfig &p_config,
bool p_addOutlinePanel);
// For common HTML content manipulation.
static void fillTitle(QString &p_template, const QString &p_title);
static void fillStyleContent(QString &p_template, const QString &p_styles);
@ -104,17 +114,37 @@ namespace vnotex
static void fillOutlinePanel(QString &p_template, WebResource &p_exportResource, bool p_addOutlinePanel);
private:
static QString errorPage();
// For PdfViewer.
static const QString &getPdfViewerTemplate();
static void updatePdfViewerTemplate(const PdfViewerConfig &p_config, bool p_force = false);
static const QString &getPdfViewerTemplatePath();
// For MindMapEditor.
static const QString &getMindMapEditorTemplate();
static void updateMindMapEditorTemplate(const MindMapEditorConfig &p_config, bool p_force = false);
private:
struct Template
{
int m_revision = -1;
QString m_template;
QString m_templatePath;
};
// Template for MarkdownViewer.
static QString errorPage();
static void generatePdfViewerTemplate(const PdfViewerConfig &p_config, Template& p_template);
static void generateMindMapEditorTemplate(const MindMapEditorConfig &p_config,
const QString &p_webStyleSheetFile,
Template& p_template);
static Template s_markdownViewerTemplate;
static Template s_pdfViewerTemplate;
static Template s_mindMapEditorTemplate;
};
}

View File

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

View File

@ -8,19 +8,27 @@ using namespace vnotex;
QFile Logger::s_file;
bool Logger::s_debugLog = false;
bool Logger::s_verbose = false;
void Logger::init(bool p_debugLog)
bool Logger::s_logToStderr = false;
void Logger::init(bool p_verbose, bool p_logToStderr)
{
s_debugLog = p_debugLog;
s_verbose = p_verbose;
s_logToStderr = p_logToStderr;
#if defined(QT_NO_DEBUG)
if (!s_logToStderr) {
s_file.setFileName(ConfigMgr::getInst().getLogFile());
if (s_file.size() >= 5 * 1024 * 1024) {
s_file.open(QIODevice::WriteOnly | QIODevice::Text);
} else {
s_file.open(QIODevice::Append | QIODevice::Text);
}
}
#else
// Always log to stderr in debug.
s_logToStderr = true;
#endif
qInstallMessageHandler(Logger::log);
@ -44,7 +52,7 @@ static QString getFileName(const char *p_file)
void Logger::log(QtMsgType p_type, const QMessageLogContext &p_context, const QString &p_msg)
{
#if defined(QT_NO_DEBUG)
if (!s_debugLog && p_type == QtDebugMsg) {
if (!s_verbose && p_type == QtDebugMsg) {
return;
}
#endif
@ -76,16 +84,16 @@ void Logger::log(QtMsgType p_type, const QMessageLogContext &p_context, const QS
QString fileName = getFileName(p_context.file);
#if defined(QT_NO_DEBUG)
if (!s_logToStderr) {
QTextStream stream(&s_file);
stream << header << (QString("(%1:%2) ").arg(fileName).arg(p_context.line))
stream << header << (QStringLiteral("(%1:%2) ").arg(fileName).arg(p_context.line))
<< localMsg << "\n";
if (p_type == QtFatalMsg) {
s_file.close();
abort();
}
#else
} else {
std::string fileStr = fileName.toStdString();
const char *file = fileStr.c_str();
@ -114,5 +122,5 @@ void Logger::log(QtMsgType p_type, const QMessageLogContext &p_context, const QS
}
fflush(stderr);
#endif
}
}

View File

@ -13,14 +13,16 @@ namespace vnotex
public:
Logger() = delete;
static void init(bool p_debugLog);
static void init(bool p_verbose, bool p_logToStderr);
private:
static void log(QtMsgType p_type, const QMessageLogContext &p_context, const QString &p_msg);
static QFile s_file;
static bool s_debugLog;
static bool s_verbose;
static bool s_logToStderr;
};
}

View File

@ -7,6 +7,7 @@
#include "coreconfig.h"
#include "editorconfig.h"
#include "widgetconfig.h"
#include "texteditorconfig.h"
#include "markdowneditorconfig.h"
using namespace vnotex;
@ -118,5 +119,4 @@ QString MainConfig::getVersion(const QJsonObject &p_jobj)
void MainConfig::doVersionSpecificOverride()
{
// In a new version, we may want to change one value by force.
m_coreConfig->m_docksTabBarIconSize = 26;
}

View File

@ -35,10 +35,14 @@ void MarkdownEditorConfig::init(const QJsonObject &p_app, const QJsonObject &p_u
m_plantUmlCommand = READSTR(QStringLiteral("plantuml_command"));
m_plantUmlWebService = READSTR(QStringLiteral("plantuml_web_service"));
m_webGraphviz = READBOOL(QStringLiteral("web_graphviz"));
m_graphvizExe = READSTR(QStringLiteral("graphviz_exe"));
m_mathJaxScript = READSTR(QStringLiteral("mathjax_script"));
m_prependDotInRelativeLink = READBOOL(QStringLiteral("prepend_dot_in_relative_link"));
m_confirmBeforeClearObsoleteImages = READBOOL(QStringLiteral("confirm_before_clear_obsolete_images"));
m_insertFileNameAsTitle = READBOOL(QStringLiteral("insert_file_name_as_title"));
@ -58,6 +62,7 @@ void MarkdownEditorConfig::init(const QJsonObject &p_app, const QJsonObject &p_u
m_autoBreakEnabled = READBOOL(QStringLiteral("auto_break"));
m_linkifyEnabled = READBOOL(QStringLiteral("linkify"));
m_indentFirstLineEnabled = READBOOL(QStringLiteral("indent_first_line"));
m_codeBlockLineNumberEnabled = READBOOL(QStringLiteral("code_block_line_number"));
m_smartTableEnabled = READBOOL(QStringLiteral("smart_table"));
m_smartTableInterval = READINT(QStringLiteral("smart_table_interval"));
@ -73,6 +78,10 @@ void MarkdownEditorConfig::init(const QJsonObject &p_app, const QJsonObject &p_u
m_inplacePreviewSources |= stringToInplacePreviewSource(src);
}
}
m_editViewMode = stringToEditViewMode(READSTR(QStringLiteral("edit_view_mode")));
m_richPasteByDefaultEnabled = READBOOL(QStringLiteral("rich_paste_by_default"));
}
QJsonObject MarkdownEditorConfig::toJson() const
@ -83,8 +92,10 @@ QJsonObject MarkdownEditorConfig::toJson() const
obj[QStringLiteral("web_plantuml")] = m_webPlantUml;
obj[QStringLiteral("plantuml_jar")] = m_plantUmlJar;
obj[QStringLiteral("plantuml_command")] = m_plantUmlCommand;
obj[QStringLiteral("plantuml_web_service")] = m_plantUmlWebService;
obj[QStringLiteral("web_graphviz")] = m_webGraphviz;
obj[QStringLiteral("graphviz_exe")] = m_graphvizExe;
obj[QStringLiteral("mathjax_script")] = m_mathJaxScript;
obj[QStringLiteral("prepend_dot_in_relative_link")] = m_prependDotInRelativeLink;
obj[QStringLiteral("confirm_before_clear_obsolete_images")] = m_confirmBeforeClearObsoleteImages;
obj[QStringLiteral("insert_file_name_as_title")] = m_insertFileNameAsTitle;
@ -103,6 +114,7 @@ QJsonObject MarkdownEditorConfig::toJson() const
obj[QStringLiteral("auto_break")] = m_autoBreakEnabled;
obj[QStringLiteral("linkify")] = m_linkifyEnabled;
obj[QStringLiteral("indent_first_line")] = m_indentFirstLineEnabled;
obj[QStringLiteral("code_block_line_number")] = m_codeBlockLineNumberEnabled;
obj[QStringLiteral("smart_table")] = m_smartTableEnabled;
obj[QStringLiteral("smart_table_interval")] = m_smartTableInterval;
obj[QStringLiteral("spell_check")] = m_spellCheckEnabled;
@ -122,6 +134,10 @@ QJsonObject MarkdownEditorConfig::toJson() const
obj[QStringLiteral("inplace_preview_sources")] = srcs.join(QLatin1Char(';'));
}
obj[QStringLiteral("edit_view_mode")] = editViewModeToString(m_editViewMode);
obj[QStringLiteral("rich_paste_by_default")] = m_richPasteByDefaultEnabled;
return obj;
}
@ -225,6 +241,16 @@ const QString &MarkdownEditorConfig::getPlantUmlCommand() const
return m_plantUmlCommand;
}
const QString &MarkdownEditorConfig::getPlantUmlWebService() const
{
return m_plantUmlWebService;
}
void MarkdownEditorConfig::setPlantUmlWebService(const QString &p_service)
{
updateConfig(m_plantUmlWebService, p_service, this);
}
bool MarkdownEditorConfig::getWebGraphviz() const
{
return m_webGraphviz;
@ -245,6 +271,16 @@ void MarkdownEditorConfig::setGraphvizExe(const QString &p_exe)
updateConfig(m_graphvizExe, p_exe, this);
}
const QString &MarkdownEditorConfig::getMathJaxScript() const
{
return m_mathJaxScript;
}
void MarkdownEditorConfig::setMathJaxScript(const QString &p_script)
{
updateConfig(m_mathJaxScript, p_script, this);
}
bool MarkdownEditorConfig::getPrependDotInRelativeLink() const
{
return m_prependDotInRelativeLink;
@ -257,9 +293,7 @@ bool MarkdownEditorConfig::getConfirmBeforeClearObsoleteImages() const
void MarkdownEditorConfig::setConfirmBeforeClearObsoleteImages(bool p_confirm)
{
updateConfig(m_confirmBeforeClearObsoleteImages,
p_confirm,
this);
updateConfig(m_confirmBeforeClearObsoleteImages, p_confirm, this);
}
bool MarkdownEditorConfig::getInsertFileNameAsTitle() const
@ -367,6 +401,16 @@ void MarkdownEditorConfig::setIndentFirstLineEnabled(bool p_enabled)
updateConfig(m_indentFirstLineEnabled, p_enabled, this);
}
bool MarkdownEditorConfig::getCodeBlockLineNumberEnabled() const
{
return m_codeBlockLineNumberEnabled;
}
void MarkdownEditorConfig::setCodeBlockLineNumberEnabled(bool p_enabled)
{
updateConfig(m_codeBlockLineNumberEnabled, p_enabled, this);
}
QString MarkdownEditorConfig::sectionNumberModeToString(SectionNumberMode p_mode) const
{
switch (p_mode) {
@ -519,3 +563,44 @@ void MarkdownEditorConfig::setInplacePreviewSources(InplacePreviewSources p_src)
{
updateConfig(m_inplacePreviewSources, p_src, this);
}
QString MarkdownEditorConfig::editViewModeToString(EditViewMode p_mode) const
{
switch (p_mode) {
case EditViewMode::EditPreview:
return QStringLiteral("editpreview");
default:
return QStringLiteral("editonly");
}
}
MarkdownEditorConfig::EditViewMode MarkdownEditorConfig::stringToEditViewMode(const QString &p_str) const
{
auto mode = p_str.toLower();
if (mode == QStringLiteral("editpreview")) {
return EditViewMode::EditPreview;
} else {
return EditViewMode::EditOnly;
}
}
MarkdownEditorConfig::EditViewMode MarkdownEditorConfig::getEditViewMode() const
{
return m_editViewMode;
}
void MarkdownEditorConfig::setEditViewMode(EditViewMode p_mode)
{
updateConfig(m_editViewMode, p_mode, this);
}
bool MarkdownEditorConfig::getRichPasteByDefaultEnabled() const
{
return m_richPasteByDefaultEnabled;
}
void MarkdownEditorConfig::setRichPasteByDefaultEnabled(bool p_enabled)
{
updateConfig(m_richPasteByDefaultEnabled, p_enabled, this);
}

View File

@ -39,6 +39,12 @@ namespace vnotex
};
Q_DECLARE_FLAGS(InplacePreviewSources, InplacePreviewSource);
enum EditViewMode
{
EditOnly,
EditPreview
};
MarkdownEditorConfig(ConfigMgr *p_mgr,
IConfig *p_topConfig,
const QSharedPointer<TextEditorConfig> &p_textEditorConfig);
@ -64,12 +70,18 @@ namespace vnotex
const QString &getPlantUmlCommand() const;
const QString &getPlantUmlWebService() const;
void setPlantUmlWebService(const QString &p_service);
bool getWebGraphviz() const;
void setWebGraphviz(bool p_enabled);
const QString &getGraphvizExe() const;
void setGraphvizExe(const QString &p_exe);
const QString &getMathJaxScript() const;
void setMathJaxScript(const QString &p_script);
bool getPrependDotInRelativeLink() const;
bool getConfirmBeforeClearObsoleteImages() const;
@ -116,6 +128,9 @@ namespace vnotex
bool getIndentFirstLineEnabled() const;
void setIndentFirstLineEnabled(bool p_enabled);
bool getCodeBlockLineNumberEnabled() const;
void setCodeBlockLineNumberEnabled(bool p_enabled);
bool getSmartTableEnabled() const;
void setSmartTableEnabled(bool p_enabled);
@ -130,6 +145,12 @@ namespace vnotex
InplacePreviewSources getInplacePreviewSources() const;
void setInplacePreviewSources(InplacePreviewSources p_src);
EditViewMode getEditViewMode() const;
void setEditViewMode(EditViewMode p_mode);
bool getRichPasteByDefaultEnabled() const;
void setRichPasteByDefaultEnabled(bool p_enabled);
private:
friend class MainConfig;
@ -148,6 +169,9 @@ namespace vnotex
QString inplacePreviewSourceToString(InplacePreviewSource p_src) const;
InplacePreviewSource stringToInplacePreviewSource(const QString &p_str) const;
QString editViewModeToString(EditViewMode p_mode) const;
EditViewMode stringToEditViewMode(const QString &p_str) const;
QSharedPointer<TextEditorConfig> m_textEditorConfig;
WebResource m_viewerResource;
@ -164,11 +188,17 @@ namespace vnotex
// %1: the format to render in.
QString m_plantUmlCommand;
// PlantUml Web service to override that in plantuml.js file.
QString m_plantUmlWebService;
bool m_webGraphviz = true;
// Graphviz executable file.
QString m_graphvizExe;
// MathJax script to override that in mathjax.js file.
QString m_mathJaxScript;
// Whether prepend a dot in front of the relative link, like images.
bool m_prependDotInRelativeLink = false;
@ -190,7 +220,7 @@ namespace vnotex
// Whether enable image width constraint.
bool m_constrainImageWidthEnabled = true;
bool m_imageAlignCenterEnabled = true;
bool m_imageAlignCenterEnabled = false;
// Whether enable in-place preview width constraint.
bool m_constrainInplacePreviewWidthEnabled = false;
@ -201,7 +231,7 @@ namespace vnotex
bool m_fetchImagesInParseAndPaste = true;
// Whether protect from Cross-Site Scripting.
bool m_protectFromXss = false;
bool m_protectFromXss = true;
// Whether allow HTML tag in Markdown source.
bool m_htmlTagEnabled = true;
@ -215,6 +245,9 @@ namespace vnotex
// Whether indent the first line of a paragraph.
bool m_indentFirstLineEnabled = false;
// Whether enable code block line number in read mode.
bool m_codeBlockLineNumberEnabled = true;
bool m_smartTableEnabled = true;
// Interval time to do smart table format.
@ -227,6 +260,11 @@ namespace vnotex
QString m_editorOverriddenFontFamily;
InplacePreviewSources m_inplacePreviewSources = InplacePreviewSource::NoInplacePreview;
// View mode in edit mode.
EditViewMode m_editViewMode = EditViewMode::EditOnly;
bool m_richPasteByDefaultEnabled = true;
};
}

View File

@ -0,0 +1,61 @@
#include "mindmapeditorconfig.h"
#include "mainconfig.h"
#define READSTR(key) readString(appObj, userObj, (key))
#define READBOOL(key) readBool(appObj, userObj, (key))
#define READINT(key) readInt(appObj, userObj, (key))
using namespace vnotex;
MindMapEditorConfig::MindMapEditorConfig(ConfigMgr *p_mgr, IConfig *p_topConfig)
: IConfig(p_mgr, p_topConfig)
{
m_sessionName = QStringLiteral("mindmap_editor");
}
void MindMapEditorConfig::init(const QJsonObject &p_app,
const QJsonObject &p_user)
{
const auto appObj = p_app.value(m_sessionName).toObject();
const auto userObj = p_user.value(m_sessionName).toObject();
loadEditorResource(appObj, userObj);
}
QJsonObject MindMapEditorConfig::toJson() const
{
QJsonObject obj;
obj[QStringLiteral("editor_resource")] = saveEditorResource();
return obj;
}
void MindMapEditorConfig::loadEditorResource(const QJsonObject &p_app, const QJsonObject &p_user)
{
const QString name(QStringLiteral("editor_resource"));
if (MainConfig::isVersionChanged()) {
bool needOverride = p_app[QStringLiteral("override_editor_resource")].toBool();
if (needOverride) {
qInfo() << "override \"editor_resource\" in user configuration due to version change";
m_editorResource.init(p_app[name].toObject());
return;
}
}
if (p_user.contains(name)) {
m_editorResource.init(p_user[name].toObject());
} else {
m_editorResource.init(p_app[name].toObject());
}
}
QJsonObject MindMapEditorConfig::saveEditorResource() const
{
return m_editorResource.toJson();
}
const WebResource &MindMapEditorConfig::getEditorResource() const
{
return m_editorResource;
}

View File

@ -0,0 +1,31 @@
#ifndef MINDMAPEDITORCONFIG_H
#define MINDMAPEDITORCONFIG_H
#include "iconfig.h"
#include "webresource.h"
namespace vnotex
{
class MindMapEditorConfig : public IConfig
{
public:
MindMapEditorConfig(ConfigMgr *p_mgr, IConfig *p_topConfig);
void init(const QJsonObject &p_app, const QJsonObject &p_user) Q_DECL_OVERRIDE;
QJsonObject toJson() const Q_DECL_OVERRIDE;
const WebResource &getEditorResource() const;
private:
friend class MainConfig;
void loadEditorResource(const QJsonObject &p_app, const QJsonObject &p_user);
QJsonObject saveEditorResource() const;
WebResource m_editorResource;
};
}
#endif // MINDMAPEDITORCONFIG_H

View File

@ -93,7 +93,7 @@ void BundleNotebook::remove()
// Remove notebook root folder if it is empty.
if (!FileUtils::removeDirIfEmpty(getRootFolderAbsolutePath())) {
qInfo() << QString("root folder of notebook (%1) is not empty and needs manual clean up")
qInfo() << QStringLiteral("root folder of notebook (%1) is not empty and needs manual clean up")
.arg(getRootFolderAbsolutePath());
}
}
@ -108,6 +108,13 @@ const QVector<HistoryItem> &BundleNotebook::getHistory() const
return m_history;
}
void BundleNotebook::removeHistory(const QString &p_itemPath)
{
HistoryMgr::removeHistoryItem(m_history, p_itemPath);
updateNotebookConfig();
}
void BundleNotebook::addHistory(const HistoryItem &p_item)
{
HistoryItem item(p_item);

View File

@ -49,6 +49,8 @@ namespace vnotex
const QVector<HistoryItem> &getHistory() const Q_DECL_OVERRIDE;
void removeHistory(const QString &p_itemPath) Q_DECL_OVERRIDE;
void addHistory(const HistoryItem &p_item) Q_DECL_OVERRIDE;
void clearHistory() Q_DECL_OVERRIDE;

View File

@ -40,7 +40,7 @@ static void checkRootFolderForNewNotebook(const NotebookParameters &p_paras)
qCritical() << msg;
throw Exception(Exception::Type::InvalidPath, msg);
} else if (p_paras.m_ensureEmptyRootFolder && !PathUtils::isEmptyDir(p_paras.m_rootFolderPath)) {
QString msg = QString("local root folder must be empty: %1 (%2)")
QString msg = QStringLiteral("local root folder must be empty: %1 (%2)")
.arg(p_paras.m_rootFolderPath, PathUtils::absolutePath(p_paras.m_rootFolderPath));
qCritical() << msg;
throw Exception(Exception::Type::InvalidPath, msg);
@ -88,7 +88,7 @@ void BundleNotebookFactory::checkParameters(const NotebookParameters &p_paras) c
auto configMgr = dynamic_cast<BundleNotebookConfigMgr *>(p_paras.m_notebookConfigMgr.data());
if (!configMgr) {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("Invalid notebook configuration manager"));
QStringLiteral("Invalid notebook configuration manager"));
}
}

View File

@ -17,6 +17,8 @@ namespace vnotex
virtual void addHistory(const HistoryItem &p_item) = 0;
virtual void removeHistory(const QString &p_itemPath) = 0;
virtual void clearHistory() = 0;
};
}

View File

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

View File

@ -136,6 +136,16 @@ QString Notebook::getRootFolderAbsolutePath() const
return PathUtils::absolutePath(m_rootFolderPath);
}
QString Notebook::getConfigFolderAbsolutePath() const
{
const auto &folderPath = m_configMgr->getConfigFolderPath();
if (folderPath.isEmpty()) {
return QString();
}
return getBackend()->getFullPath(folderPath);
}
const QIcon &Notebook::getIcon() const
{
return m_icon;
@ -244,7 +254,7 @@ QSharedPointer<Node> Notebook::copyNodeAsChildOf(const QSharedPointer<Node> &p_s
if (Node::isAncestor(p_src.data(), p_dest)) {
Exception::throwOne(Exception::Type::InvalidArgument,
QString("source (%1) is the ancestor of destination (%2)")
QStringLiteral("source (%1) is the ancestor of destination (%2)")
.arg(p_src->fetchPath(), p_dest->fetchPath()));
return nullptr;
}

View File

@ -54,6 +54,9 @@ namespace vnotex
QString getRootFolderAbsolutePath() const;
// Get the absolute path of the config folder if applicable.
QString getConfigFolderAbsolutePath() const;
const QIcon &getIcon() const;
void setIcon(const QIcon &p_icon);

View File

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

View File

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

View File

@ -49,6 +49,8 @@ namespace vnotex
public:
bool addNode(Node *p_node, bool p_ignoreId);
bool addNodeRecursively(Node *p_node, bool p_ignoreId);
// Whether there is a record with the same ID in DB and has the same path.
bool existsNode(const Node *p_node);

View File

@ -187,8 +187,8 @@ bool NotebookTagMgr::updateNodeTags(Node *p_node)
auto db = m_notebook->getDatabaseAccess();
// Make sure the node exists in DB.
if (!db->addNode(p_node, false)) {
qWarning() << "failed to add node to DB" << p_node->fetchPath() << p_node->getId();
if (!db->addNodeRecursively(p_node, false)) {
qWarning() << "failed to add node to DB" << p_node->fetchPath() << p_node->getId() << (p_node->getParent() ? p_node->getParent()->getId() : -1);
return false;
}

View File

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

View File

@ -6,9 +6,13 @@
#include <QJsonObject>
#include <QJsonDocument>
#include <vtextedit/textutils.h>
#include <utils/pathutils.h>
#include "exception.h"
#include <utils/fileutils.h>
#include "exception.h"
#include "coreconfig.h"
#include "configmgr.h"
using namespace vnotex;
@ -48,7 +52,7 @@ void LocalNotebookBackend::makePath(const QString &p_dirPath)
QDir dir(getRootPath());
if (!dir.mkpath(p_dirPath)) {
Exception::throwOne(Exception::Type::FailToCreateDir,
QString("fail to create directory: %1").arg(p_dirPath));
QStringLiteral("fail to create directory: %1").arg(p_dirPath));
}
}
@ -66,7 +70,40 @@ void LocalNotebookBackend::writeFile(const QString &p_filePath, const QString &p
void LocalNotebookBackend::writeFile(const QString &p_filePath, const QJsonObject &p_jobj)
{
writeFile(p_filePath, QJsonDocument(p_jobj).toJson());
const auto &coreConfig = ConfigMgr::getInst().getCoreConfig();
auto data = QJsonDocument(p_jobj).toJson();
vte::LineEnding before = vte::LineEnding::LF;
QString text;
switch (coreConfig.getLineEndingPolicy()) {
case LineEndingPolicy::Platform:
#if defined(Q_OS_WIN)
text = QString::fromUtf8(data);
vte::TextUtils::transformLineEnding(text, before, vte::LineEnding::CRLF);
#endif
break;
case LineEndingPolicy::File:
// Not supported.
Q_FALLTHROUGH();
case LineEndingPolicy::LF:
break;
case LineEndingPolicy::CRLF:
text = QString::fromUtf8(data);
vte::TextUtils::transformLineEnding(text, before, vte::LineEnding::CRLF);
break;
case LineEndingPolicy::CR:
text = QString::fromUtf8(data);
vte::TextUtils::transformLineEnding(text, before, vte::LineEnding::CR);
break;
}
if (!text.isEmpty()) {
writeFile(p_filePath, text);
} else {
writeFile(p_filePath, data);
}
}
QString LocalNotebookBackend::readTextFile(const QString &p_filePath)

View File

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

View File

@ -106,3 +106,8 @@ int BundleNotebookConfigMgr::getCodeVersion() const
{
return 3;
}
QString BundleNotebookConfigMgr::getConfigFolderPath() const
{
return c_configFolderName;
}

View File

@ -28,6 +28,8 @@ namespace vnotex
int getCodeVersion() const Q_DECL_OVERRIDE;
QString getConfigFolderPath() const Q_DECL_OVERRIDE;
static const QString &getConfigFolderName();
static const QString &getConfigName();

View File

@ -87,6 +87,8 @@ namespace vnotex
// Version of the config processing code.
virtual int getCodeVersion() const = 0;
virtual QString getConfigFolderPath() const = 0;
private:
QSharedPointer<INotebookBackend> m_backend;

View File

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

View File

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

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