Compare commits

..

551 Commits

Author SHA1 Message Date
e4bb8cfa17
!172 修复简陋的锁定图标
Merge pull request !172 from Sky/release-v1.1.14
2024-04-10 12:03:12 +00:00
Sky
98ee99168b 修复简陋的锁定图标 2024-04-10 20:00:21 +08:00
格物方能致知
4d065afe98
!170 修复文件树由于缓存问题导致新增的文件无法被显示 初步添加文件树菜单
Merge pull request !170 from 格物方能致知/develop
2023-10-24 10:57:51 +00:00
gewuyou
2c3416fde5 🐛 修复文件树由于缓存问题导致新增的文件无法被显示 🚩初步添加文件树菜单 2023-10-24 18:50:25 +08:00
格物方能致知
51749cf81b
!169 完善文件树打开逻辑
Merge pull request !169 from 格物方能致知/develop
2023-10-18 07:30:18 +00:00
gewuyou
9037223584 🚩 完善文件树打开逻辑 2023-10-18 15:26:19 +08:00
格物方能致知
3aea4f319a
!168 完善右键上下文菜单功能
Merge pull request !168 from 格物方能致知/develop
2023-10-14 15:14:48 +00:00
gewuyou
cae04fe2a0 🚩 完善右键上下文菜单功能 2023-10-14 23:11:59 +08:00
格物方能致知
6a5298b0fa
!167 修复 BUG 新建超过10个默认标签页后,序号错误
Merge pull request !167 from 格物方能致知/develop
2023-10-14 11:06:36 +00:00
gewuyou
6b5449a6d5 🐛 修复 BUG 新建超过10个默认标签页后,序号错误 2023-10-14 19:03:32 +08:00
格物方能致知
31a736c6b8
update .gitee/ISSUE_TEMPLATE/bug.yml.
Signed-off-by: 格物方能致知 <1063891901@qq.com>
2023-10-14 11:03:00 +00:00
格物方能致知
950b15880e
!166 ♻️ ️ 优化性能重构并优化标签页右键菜单
Merge pull request !166 from 格物方能致知/refactor-I884CX
2023-10-14 10:38:18 +00:00
gewuyou
694cd2a448 ♻️ ️ 优化性能重构并优化标签页右键菜单 2023-10-14 18:33:29 +08:00
格物方能致知
5659fc8591
!165 ️ 优化快捷键初始化
Merge pull request !165 from 格物方能致知/develop
2023-10-13 08:42:19 +00:00
gewuyou
fb486a87ba ️ 优化快捷键初始化 2023-10-13 16:36:25 +08:00
格物方能致知
5fef22d0db
!164 尝试修复文件图标渲染问题
Merge pull request !164 from 格物方能致知/develop
2023-10-11 14:11:24 +00:00
gewuyou
7e745797f9 🐛 尝试修复文件图标渲染问题 2023-10-11 22:09:19 +08:00
2486b9bbb5
!163 refactor: #I8719S 重构项目结构
Merge pull request !163 from Luke/refactor-I8719S
2023-10-11 01:27:14 +00:00
92b47af415 重构项目结构 2023-10-11 02:46:00 +08:00
36ac4c87f1 重构项目结构 2023-10-11 02:43:52 +08:00
96fa1434fa 增加文档说明 2023-10-11 02:43:20 +08:00
11c833bb54 增加 ConfigController.java 的注释 2023-10-11 02:13:40 +08:00
32f8a0c189 增加 AbstractCacheManager.java 的注释 2023-10-11 02:11:37 +08:00
db004a23b0 增加 ConfigController.java 的注释 2023-10-11 02:11:33 +08:00
9c3473a166 增加 BuildPanel.java 的注释 2023-10-11 02:08:34 +08:00
70dc245d1e 增加 ApplicationCacheManager.java 的注释 2023-10-11 02:08:26 +08:00
8cd73016c5 增加 AppConstants.java 的注释 2023-10-11 02:08:20 +08:00
cf453a012c 增加 AbstractMenuBuilder.java 的注释 2023-10-11 02:08:16 +08:00
b624d11c7d 增加 AbstractFunctionChildrenBox.java 的注释 2023-10-11 02:08:11 +08:00
eb7a599e03 增加 AbstractPaneStage.java 的注释 2023-10-11 02:08:05 +08:00
dbc2c9270d 增加 AbstractVerticalBox.java 的注释 2023-10-11 02:07:58 +08:00
e47ffd017c 增加 BorderPaneAble.java 的注释 2023-10-11 02:07:52 +08:00
2b0ffd1a22 增加 BaseConfigController.java 的注释 2023-10-11 02:07:49 +08:00
b9fdb14bcd 增加 ApplicationManager.java 的注释 2023-10-11 02:07:45 +08:00
7d4123ba50 增加 AppConfig.java 的注释 2023-10-11 02:07:39 +08:00
464532b509 增加 AbstractHorizontalBox.java 的注释 2023-10-11 02:07:35 +08:00
c13f846b99 增加 AbstractBorderPane.java 的注释 2023-10-11 02:07:28 +08:00
b895d0c234 增加 CmdTerminalBox.java 的注释 2023-10-11 02:07:22 +08:00
60dad5389b 增加 ContextMenuBuilder.java 的注释 2023-10-11 02:07:16 +08:00
d6a9319cd7 增加 ControllerAble.java 的注释 2023-10-11 02:07:09 +08:00
956e1cc64f 增加 CustomTitleBarBox.java 的注释 2023-10-11 02:07:01 +08:00
df3737d23e 增加 DebugBox.java 的注释 2023-10-11 02:06:55 +08:00
84753f8fdd 增加 HorizontalBoxAble.java 的注释 2023-10-11 02:06:49 +08:00
49682f8cd5 增加 MainBorderPaneManager.java 的注释 2023-10-11 02:06:43 +08:00
d4220b2acc 增加 MenuBuilder.java 的注释 2023-10-11 02:06:32 +08:00
0f8cc95e40 增加 PluginConfig.java 的注释 2023-10-11 02:06:25 +08:00
cac5a365f5 增加 RunBox.java 的注释 2023-10-11 02:06:19 +08:00
a3f77bc6fe 增加 RunTopMenu.java 的注释 2023-10-11 02:06:14 +08:00
d4f02c84c7 增加 SplitPaneItemConstants.java 的注释 2023-10-11 02:06:07 +08:00
b5fe6c8451 增加 TextConstants.java 的注释 2023-10-11 02:06:01 +08:00
2b0dce4640 增加 ThreadPoolManager.java 的注释 2023-10-11 02:05:48 +08:00
0402d31630 增加 UiResourceBundle.java 的注释 2023-10-11 02:05:43 +08:00
c9c6152df2 增加 UserConfig.java 的注释 2023-10-11 02:05:37 +08:00
8e2d7c53de 增加 VerticalBoxAble.java 的注释 2023-10-11 02:05:29 +08:00
格物方能致知
4fdefea1bd
!162 尝试修复文件图标渲染问题
Merge pull request !162 from 格物方能致知/develop
2023-10-10 12:46:59 +00:00
gewuyou
0d191ef104 🐛 尝试修复文件图标渲染问题 2023-10-10 20:45:23 +08:00
格物方能致知
559387eecc
!161 完善文件图标支持
Merge pull request !161 from 格物方能致知/develop
2023-10-10 10:14:21 +00:00
gewuyou
0a94a9f7d1 🚩 完善文件图标支持 2023-10-10 14:52:54 +08:00
gewuyou
c036127f36 🚩 完善文件图标支持 2023-10-10 14:49:37 +08:00
格物方能致知
eb3559115e
!160 完善文件图标支持
Merge pull request !160 from 格物方能致知/develop
2023-10-10 06:21:30 +00:00
gewuyou
e469b8ba89 🚩 完善文件图标支持 2023-10-10 14:09:55 +08:00
gewuyou
6920774e64 🚩 完善文件图标支持 2023-10-10 14:07:44 +08:00
格物方能致知
b45c22c275
!159 完善文件图标支持
Merge pull request !159 from 格物方能致知/develop
2023-10-09 14:18:41 +00:00
gewuyou
69f0381167 🚩 完善文件图标支持 2023-10-09 22:15:31 +08:00
格物方能致知
c8ea5622e0
!158 完善标签页右键菜单的图标支持
Merge pull request !158 from 格物方能致知/feature-I86AJF
2023-10-09 11:18:49 +00:00
gewuyou
e85340ba06 👔 完善标签页右键菜单的图标支持 2023-10-09 19:15:49 +08:00
gewuyou
6fee8fe4ee 👔 完善标签页右键菜单的图标支持 2023-10-09 18:50:35 +08:00
格物方能致知
48589365b7
!157 完善标签页右键菜单的图标支持
Merge pull request !157 from 格物方能致知/develop
2023-10-08 14:58:29 +00:00
gewuyou
249333b961 👔 完善标签页右键菜单的图标支持 2023-10-08 22:56:26 +08:00
格物方能致知
e75fa0e3da
!156 完善标签页右键菜单
Merge pull request !156 from 格物方能致知/develop
2023-10-08 14:14:38 +00:00
gewuyou
e58ab2e3dc 👔 完善标签页右键菜单 2023-10-08 22:12:25 +08:00
gewuyou
0e3b7dcaab 👔 完善标签页右键菜单 2023-10-08 21:35:12 +08:00
格物方能致知
ac015b4679
!155 完善标签页右键菜单
Merge pull request !155 from 格物方能致知/develop
2023-10-08 13:11:55 +00:00
gewuyou
9b6ec0734e Merge branch 'release-v1.1.14' of https://gitee.com/jcnc-org/JNotepad into develop
# Conflicts:
#	src/main/java/org/jcnc/jnotepad/common/constants/AppConstants.java
#	src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java
2023-10-08 21:10:03 +08:00
gewuyou
6e2889453c 👔 完善标签页右键菜单 2023-10-08 21:07:45 +08:00
9e93d537a6 Merge remote-tracking branch 'JCNC主仓库/release-v1.1.14' into release-v1.1.14 2023-10-08 20:59:20 +08:00
1870bf6b90 版本更新常量 2023-10-08 20:58:32 +08:00
8c135f968a
!154 feature: #I869YQ 增加tab打开资源管理器功能
Merge pull request !154 from Luke/feature-I869YQ
2023-10-08 12:48:19 +00:00
eca42274c0 增加tab打开资源管理器功能 2023-10-08 20:45:47 +08:00
格物方能致知
11e1f8c607
!152 初步添加标签页右键菜单
Merge pull request !152 from 格物方能致知/develop
2023-10-07 17:25:15 +00:00
gewuyou
ac150126d8 🚩 初步添加标签页右键菜单 2023-10-07 23:34:04 +08:00
034f895b25
更新readme
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-10-07 09:42:45 +00:00
格物方能致知
e8a02a176d
!151 修改启动类名称
Merge pull request !151 from 格物方能致知/develop
2023-10-07 05:00:52 +00:00
gewuyou
dbdec091e1 🔨 修改启动类名称 2023-10-07 12:59:23 +08:00
格物方能致知
86729c12aa
!150 修改名称过时的api
Merge pull request !150 from 格物方能致知/develop
2023-10-07 01:58:56 +00:00
gewuyou
9d765f2f5c 🔨 修改名称过时的API 2023-10-07 09:55:17 +08:00
gewuyou
d9aaa17551 Merge branch 'release-v1.1.14' of https://gitee.com/jcnc-org/JNotepad into develop
# Conflicts:
#	src/main/java/org/jcnc/jnotepad/views/root/top/menubar/menu/RunTopMenu.java
2023-10-07 09:47:42 +08:00
gewuyou
fc0eec2932 🔨 修改名称过时的API 2023-10-07 09:26:06 +08:00
654ddf8c83
!149 更新readme
Merge pull request !149 from Luke/release-v1.1.13
2023-10-06 19:40:23 +00:00
8cf24ef669 更新最新截图 2023-10-07 03:36:27 +08:00
5a5a9ba319 更新最新截图 2023-10-07 03:33:59 +08:00
03324543a3 国际化 2023-10-07 03:13:20 +08:00
6053b23fce 使用国际化 2023-10-07 02:54:03 +08:00
7d7e50c25d 增加日志 2023-10-07 02:47:46 +08:00
f773ccb67c 修复uibug 2023-10-07 02:45:09 +08:00
edb9bab320
!148 feature: #I85N2Z 完善编译运行界面
Merge pull request !148 from cccqyu/feature-I85N2Z
2023-10-06 17:23:08 +00:00
cccqy
761c2cca8b 完善编译运行界面 2023-10-07 01:20:50 +08:00
70d97cd6e9
!146 移动开发者页面到帮助,移除插件示例按钮
Merge pull request !146 from 格物方能致知/refactor-I85JM0
2023-10-06 03:43:56 +00:00
8f639ce292
!147 feature: #I85JRT 增加编译代码的功能
Merge pull request !147 from Luke/feature-I85JRT
2023-10-05 17:44:34 +00:00
6dff6baa8e 增加编译功能 2023-10-06 01:41:20 +08:00
gewuyou
7736971546 解决冲突 2023-10-05 22:46:53 +08:00
gewuyou
fe32898fef Merge branch 'release-v1.1.13' of https://gitee.com/jcnc-org/JNotepad into develop
# Conflicts:
#	src/main/java/org/jcnc/jnotepad/component/stage/setting/DeveloperDebugStage.java
#	src/main/java/org/jcnc/jnotepad/component/stage/setting/SetStage.java
#	src/main/java/org/jcnc/jnotepad/views/root/top/menubar/TopMenuBar.java
#	src/main/resources/i18n/i18n.properties
2023-10-05 22:39:12 +08:00
gewuyou
3034568047 🚚 移动开发者页面到帮助,移除插件示例按钮 2023-10-05 22:38:36 +08:00
f01f466cef
!145 hotfix: #I85J1P 删除setStage的模态.换一种实现方法
Merge pull request !145 from Luke/hotfix-I85J1P
2023-10-05 11:29:31 +00:00
80712f59d7 删除setStage的模态.换一种实现方法 2023-10-05 19:23:14 +08:00
52672dcda8
!143 feaure: #I85IYZ 增加运行菜单
Merge pull request !143 from Luke/feature-I85IYZ
2023-10-05 11:02:36 +00:00
52c8f13355 Merge remote-tracking branch '我的仓库/release-v1.1.13' into release-v1.1.13 2023-10-05 18:58:14 +08:00
b39e633efb 创建运行菜单 2023-10-05 18:57:53 +08:00
格物方能致知
962a6bd1e9
!142 更新ReadMe
Merge pull request !142 from 格物方能致知/develop
2023-10-05 10:24:59 +00:00
gewuyou
b99db6f5d9 更新ReadMe 2023-10-05 18:23:40 +08:00
c87c0db667 创建运行菜单 2023-10-05 17:24:38 +08:00
格物方能致知
f25d40463c
!141 添加 选择文件根路径的功能
Merge pull request !141 from 格物方能致知/feature-I85IGY
2023-10-05 07:52:11 +00:00
gewuyou
901f7f6910 Merge branch 'release-v1.1.13' of https://gitee.com/jcnc-org/JNotepad into develop 2023-10-05 15:47:58 +08:00
gewuyou
ad73d3a71c 🚩 完善 选择文件根路径的功能 2023-10-05 15:28:33 +08:00
305b10768b 更新readme 2023-10-05 15:15:10 +08:00
0df7e85d3c add linux-1.png 2023-10-05 15:12:38 +08:00
292463e445 add linux-1.png 2023-10-05 15:01:02 +08:00
格物方能致知
79dc83b2c1
!140 添加 选择文件根路径的功能
Merge pull request !140 from 格物方能致知/develop
2023-10-05 05:26:56 +00:00
gewuyou
778bb5bb62 🚩 添加 选择文件根路径的功能 2023-10-05 12:42:05 +08:00
gewuyou
981fa41f25 一些小优化 2023-10-05 11:03:13 +08:00
325819f92e
!139 refactor: #I85GVX 重构项目整体结构
Merge pull request !139 from Luke/refactor-I85GVX
2023-10-04 17:04:58 +00:00
87c90072f8 重构项目的UI为component
重构项目整体结构
2023-10-05 01:00:42 +08:00
70a9538e2c Merge remote-tracking branch 'JCNC主仓库/release-v1.1.13' into release-v1.1.13 2023-10-05 00:00:11 +08:00
10cd8e374e 更新最新截图 2023-10-04 23:59:04 +08:00
1c9839c925
!138 feature: #I85GSG 增加powershell 的UI组件
Merge pull request !138 from Luke/feature-I85GSG
2023-10-04 15:43:21 +00:00
2b3eb0d474 增加powershell 的UI组件 2023-10-04 23:41:05 +08:00
格物方能致知
c69fc58fb4
!137 ♻️ 重构代码 重构顶部菜单栏管理类,减少耦合方便扩展 修复 BUG 修复重复点击设置按钮导致重复创建设置页面
Merge pull request !137 from 格物方能致知/refactor-I85G9E-fix-I85G9B
2023-10-04 10:37:50 +00:00
gewuyou
8608ad6e75 ♻️ 重构代码 重构顶部菜单栏管理类,减少耦合方便扩展 🐛 修复 BUG 修复重复点击设置按钮导致重复创建设置页面 2023-10-04 18:25:31 +08:00
9e239a9756
!136 feature: #I85EOB:增加设置页面选项
Merge pull request !136 from Luke/Issue-I85EOB
2023-10-03 19:11:51 +00:00
09a7ec5adf Issue-I85EOB: 增加设置页面的选项 2023-10-04 03:08:49 +08:00
格物方能致知
f02332332e
!135 暂时将日志输出改在用户目录
Merge pull request !135 from 格物方能致知/develop
2023-10-03 16:07:18 +00:00
gewuyou
f9812e2d63 暂时将日志输出改在用户目录 2023-10-04 00:05:16 +08:00
格物方能致知
32b20809f9
!134 为文件树添加图标,修改项目结构
Merge pull request !134 from 格物方能致知/develop
2023-10-03 05:24:01 +00:00
gewuyou
429fb3cc07 解决冲突 2023-10-03 13:22:13 +08:00
gewuyou
f6063568bb Merge branch 'release-v1.1.13' of https://gitee.com/jcnc-org/JNotepad into develop
# Conflicts:
#	src/main/java/org/jcnc/jnotepad/views/manager/DirectorySidebarManager.java
#	src/main/java/org/jcnc/jnotepad/views/root/center/main/center/directory/DirectorySidebarPane.java
2023-10-03 13:17:23 +08:00
gewuyou
b542a9322b 🚩 为文件树添加图标,修改项目结构 2023-10-03 13:10:50 +08:00
gewuyou
8a94fb67ec 🚩 为文件树添加图标,修改项目结构 2023-10-03 13:07:21 +08:00
4f20a7c3d0
!133 feature: #I85CW5 修改默认Satge大小
Merge pull request !133 from Luke/feature-I85CW5
2023-10-03 00:51:52 +00:00
1ff182c58c 修改Satge的默认大小 2023-10-03 08:45:41 +08:00
c52f2b44f5
!132 feature-I85CQR
Merge pull request !132 from cccqyu/feature-I85CQR
2023-10-02 23:43:42 +00:00
cccqy
50941aedd8 增加文件树与文本框区域滑动大小功能
修改文件树显示与隐藏逻辑
修改错误代码注释
2023-10-03 05:46:46 +08:00
格物方能致知
8a3404b5d7
!131 添加自动打开上次打开的文件夹逻辑
Merge pull request !131 from gewuyou/feature-I85CM5
2023-10-02 14:26:05 +00:00
gewuyou
ab947ce163 🚩 添加自动打开上次打开的文件夹逻辑 2023-10-02 22:24:15 +08:00
格物方能致知
3d54349bdc
!130 细微修改与解决冲突
Merge pull request !130 from 格物方能致知/develop
2023-10-02 14:08:04 +00:00
gewuyou
566e08eb11 细微修改 2023-10-02 22:06:41 +08:00
gewuyou
49ea34ac7a Merge branch 'release-v1.1.13' of https://gitee.com/jcnc-org/JNotepad into develop 2023-10-02 21:32:38 +08:00
gewuyou
82bd720d0e 细微修改 2023-10-02 21:32:22 +08:00
格物方能致知
c03e1d8474
!129 Issues-#I82EK4
Merge pull request !129 from cccqyu/Issues-I82EK4
2023-10-02 13:30:38 +00:00
cccqyu
292d136c10 Merge branch 'release-v1.1.13' of gitee.com:jcnc-org/JNotepad into Issues-I82EK4
Signed-off-by: cccqyu <11255974+cccqyu@user.noreply.gitee.com>
2023-10-02 13:13:17 +00:00
cccqy
b4936bbbda ### https://gitee.com/jcnc-org/JNotepad/issues/I82EK4
#I82EK4
### 增加侧边文件树按钮 打开文件夹
2023-10-02 21:03:34 +08:00
格物方能致知
8d914fa2d0
!128 添加监测已打开的文件状态功能逻辑
Merge pull request !128 from 格物方能致知/feature-I85BHW
2023-10-02 12:33:50 +00:00
gewuyou
239d69ddc3 🚩 添加监测已打开的文件状态功能逻辑 2023-10-02 20:29:12 +08:00
89a0ce87b9
!127 增加侧边栏按钮,提高示例代码和TODU
Merge pull request !127 from Luke/release-v1.1.13
2023-10-02 00:47:36 +00:00
66f30eccf9 修改侧边栏方向 2023-10-02 08:46:03 +08:00
a2cb2406a1 增加侧边栏按钮 2023-10-02 08:43:40 +08:00
格物方能致知
a9f6f6936d
!126 ♻️ 重构代码 解耦并重构顶部栏,侧边栏,关于子菜单项代码 修复第一次启动时找不到缓存报错的问题
Merge pull request !126 from 格物方能致知/refactor-I85A0L
2023-10-01 08:29:56 +00:00
gewuyou
7a7af09614 ♻️ 重构代码 解耦并重构顶部栏,侧边栏,关于子菜单项代代码 2023-10-01 14:24:34 +08:00
格物方能致知
a0bf563e08
!125 为项目添加缓存逻辑
Merge pull request !125 from 格物方能致知/feature-I8577D
2023-09-30 15:07:56 +00:00
gewuyou
9711e7b2f1 🐛 修复 BUG 修复错误的监听器设置方法导致的文件数据覆盖问题 2023-09-30 22:05:29 +08:00
gewuyou
3d69d8ac0e Merge branch 'release-v1.1.13' of https://gitee.com/jcnc-org/JNotepad into develop 2023-09-30 20:39:33 +08:00
gewuyou
35551de78b 🚩 实现缓存逻辑缓存逻辑 2023-09-30 20:38:48 +08:00
6d03104990
!124 增加关于页面,增加提示框
Merge pull request !124 from Luke/release-v1.1.13
2023-09-29 22:27:06 +00:00
c63066e26e 完善提示框位置 2023-09-30 06:23:41 +08:00
c547a29b46 增加提示框 2023-09-30 06:17:40 +08:00
c7c27d785a 增加按钮点击事件 2023-09-30 05:01:39 +08:00
7a330ec3f3 完善关于页面 2023-09-30 04:50:59 +08:00
02165abde2 增加帮助菜单 2023-09-30 02:57:16 +08:00
e4d5d8a129
!123 fea:增加重启方法和更新开发者调试页面
Merge pull request !123 from Luke/release-v1.1.13
2023-09-29 17:59:51 +00:00
ddafba2f01 更新开发者调试页面 2023-09-30 01:56:12 +08:00
a16b957730 增加开发者调试的重启按钮 2023-09-30 01:41:11 +08:00
f99ed4e706 增加重启程序的方法 2023-09-30 01:34:42 +08:00
34a42310ab 增加重启程序的方法 2023-09-30 01:34:25 +08:00
1b2c08473b 更新jlink的打包方法,提高打包速度 2023-09-30 01:15:47 +08:00
格物方能致知
003fd36e9a
!122 初步编写项目缓存逻辑
Merge pull request !122 from 格物方能致知/develop
2023-09-29 16:06:31 +00:00
gewuyou
b02ab2ac99 🚩 编写项目缓存逻辑 2023-09-30 00:00:44 +08:00
c385b9429f
!121 增加代码高亮和代码折叠功能
Merge pull request !121 from Luke/release-v1.1.13
2023-09-27 19:19:34 +00:00
23407cfa63 增加最新截图 2023-09-28 03:14:50 +08:00
430e9fb690 增加注释 2023-09-28 03:14:40 +08:00
47da48ba20 修复bug 2023-09-28 02:51:40 +08:00
03f663deba 增加语法高亮 2023-09-28 02:44:33 +08:00
格物方能致知
c537fa5046
!120 小错误修复
Merge pull request !120 from 格物方能致知/develop
2023-09-27 02:36:18 +00:00
gewuyou
4a6b43d594 小错误修复 2023-09-27 10:33:34 +08:00
c5d8e80e19
!119 ♻️ 重构代码 创建单例组件管理类,将单例组件初始化与单例组件分离
Merge pull request !119 from 格物方能致知/refactor-I84M61
2023-09-26 15:34:03 +00:00
gewuyou
14353f8a02 ♻️ 重构代码 创建单例组件管理类,将单例组件初始化与单例组件分离 2023-09-26 23:09:09 +08:00
格物方能致知
7ce8879e69
!118 ♻️ 重构代码 创建单例组件管理类,将单例组件初始化与单例组件分离
Merge pull request !118 from 格物方能致知/develop
2023-09-26 13:46:01 +00:00
gewuyou
f61d308a90 ♻️ 重构代码 创建单例组件管理类,将单例组件初始化与单例组件分离 2023-09-26 21:42:45 +08:00
格物方能致知
0f7103c643
!117 修复 BUG 引入依赖没有模块化导致的jlink失败的问题
Merge pull request !117 from 格物方能致知/develop
2023-09-25 03:50:48 +00:00
gewuyou
bf5284e6de 🐛 修复 BUG 引入依赖没有模块化导致的jlink失败的问题 2023-09-25 11:46:51 +08:00
格物方能致知
c084fe34cb
!116 完善插件逻辑
Merge pull request !116 from 格物方能致知/develop
2023-09-24 10:40:38 +00:00
gewuyou
e505f2a6ce 完善插件逻辑 2023-09-24 18:36:21 +08:00
685f9256e2
!115 完善插件逻辑
Merge pull request !115 from 格物方能致知/develop
2023-09-24 03:40:52 +00:00
gewuyou
0fffcb6828 完善插件逻辑 2023-09-24 11:39:17 +08:00
格物方能致知
ea82f9b177
!114 完善插件功能
Merge pull request !114 from 格物方能致知/develop
2023-09-23 16:58:46 +00:00
gewuyou
3d88d7d2b4 完善插件页面 2023-09-24 00:56:21 +08:00
gewuyou
7af369c1c7 Merge branch 'release-v1.1.13' of https://gitee.com/jcnc-org/JNotepad into develop
# Conflicts:
#	src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java
2023-09-23 23:31:13 +08:00
gewuyou
97fbdd0d14 完善插件页面 2023-09-23 23:28:02 +08:00
5d75c79477
!113 完善插件管理器:设置页面和逻辑
Merge pull request !113 from Luke/release-v1.1.13
2023-09-23 15:11:54 +00:00
15be8c44f2 增加插件设置按钮 2023-09-23 23:10:20 +08:00
b66a5d9647 更新ui逻辑 2023-09-23 23:01:22 +08:00
b4e38e866c 增加按钮逻辑 2023-09-23 22:49:38 +08:00
gewuyou
a353b4f283 Merge branch 'release-v1.1.13' of https://gitee.com/jcnc-org/JNotepad into develop
# Conflicts:
#	src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java
2023-09-23 22:41:48 +08:00
gewuyou
5963084649 提交修改 2023-09-23 22:38:17 +08:00
834770159c
!112 完善插件管理器UI
Merge pull request !112 from Luke/release-v1.1.13
2023-09-23 14:30:32 +00:00
518ec4d12c 增加底部栏 2023-09-23 22:27:53 +08:00
5e35ba94c7 增加安装按钮 2023-09-23 22:19:51 +08:00
gewuyou
6b16395bbd Merge branch 'release-v1.1.13' of https://gitee.com/Luke-Skywalker-Xu/JNotepad into develop 2023-09-23 22:10:46 +08:00
a5c31a81ec 完善插件管理页面 2023-09-23 22:06:52 +08:00
gewuyou
367ef836e4 插件页面编写 2023-09-23 21:26:37 +08:00
格物方能致知
5aa7229097
!111 ♻️ 重构代码 重构顶部菜单栏与侧边工具栏
Merge pull request !111 from 格物方能致知/refactor-I83SVR
2023-09-23 11:39:09 +00:00
gewuyou
d4120994b5 修正提交 2023-09-23 16:37:30 +08:00
gewuyou
dc71382e71 Merge branch 'release-v1.1.13' of https://gitee.com/jcnc-org/JNotepad into develop
# Conflicts:
#	src/main/java/org/jcnc/jnotepad/views/root/top/menu/TopMenuBar.java
2023-09-23 16:33:36 +08:00
ed835f9158
!110 完善插件管理页面
Merge pull request !110 from Luke/release-v1.1.13
2023-09-23 08:32:33 +00:00
gewuyou
0f2eee7d5c ♻️ 重构代码 重构顶部菜单栏与侧边工具栏 2023-09-23 16:29:09 +08:00
gewuyou
13f2f6703d Merge branch 'release-v1.1.13' of https://gitee.com/jcnc-org/JNotepad into develop
# Conflicts:
#	src/main/java/org/jcnc/jnotepad/views/root/top/menu/TopMenuBar.java
2023-09-23 16:19:21 +08:00
gewuyou
d2565799ef ♻️ 重构代码 重构顶部菜单栏与侧边工具栏 2023-09-23 16:16:18 +08:00
b5b5cb0606 完善插件管理页面 2023-09-23 11:50:21 +08:00
7bf7065ca7 增加插件布局和图片 2023-09-23 10:26:35 +08:00
efe7747cb3 创建专属于每个插件的CustomSplitPane内容 2023-09-23 09:25:08 +08:00
dfd8a18e00
!109 feature-更新管理插件的页面显示
Merge pull request !109 from Luke/release-v1.1.13
2023-09-22 17:51:04 +00:00
95d87102b1 修改插件管理器的ui错误 2023-09-23 01:49:13 +08:00
1aa84a16c0 修改插件管理器的ui错误 2023-09-23 01:42:20 +08:00
2f18b49410 修复readme错误 2023-09-23 01:40:28 +08:00
adc2ba7d54 Merge remote-tracking branch 'origin/release-v1.1.13' into release-v1.1.13 2023-09-23 01:39:51 +08:00
24ecfafa80 更新插件系统UI 2023-09-23 01:39:38 +08:00
格物方能致知
dbe5d83ff6
删除文件 src/main/resources/test 2023-09-21 09:04:09 +00:00
bf5b652ff3
!108 test
Merge pull request !108 from coderch/release-v1.1.13
2023-09-21 08:19:10 +00:00
BestYetToCome
8339d71bc0 'test' 2023-09-21 15:42:14 +08:00
BestYetToCome
ba67161862 test 2023-09-21 15:41:35 +08:00
4da14c06cc
!107 ♻️ 重构代码 重构LunchApp,将应用资源加载与初始化与启动类剥离
Merge pull request !107 from 格物方能致知/refactor-I831QL
2023-09-20 11:55:52 +00:00
gewuyou
3ab4c4f77e ♻️ 重构代码 重构LunchApp,将应用资源加载与初始化与启动类剥离 2023-09-20 19:20:19 +08:00
529236e212
删除外链
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-09-20 00:14:00 +00:00
745e14a96e
!106 修复jlink错误问题
Merge pull request !106 from Luke/release-v1.1.13
2023-09-19 16:09:10 +00:00
86819258fa 修复jlink错误问题 2023-09-20 00:08:12 +08:00
格物方能致知
2d538c7867
!105 修复 插件不存在时的加载逻辑
Merge pull request !105 from 格物方能致知/develop
2023-09-19 11:29:38 +00:00
gewuyou
a6a56cd8b2 🐛 修复 插件不存在时的加载逻辑 2023-09-19 19:25:48 +08:00
7e1884ce6a
!104 更新readme
Merge pull request !104 from Luke/release-v1.1.13
2023-09-19 07:52:20 +00:00
237f836d4b 更新readme截图 2023-09-19 15:46:34 +08:00
fb1c7d030e
修改readme
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-09-19 07:39:46 +00:00
6398b2f7fe
!103 ♻️ 重构插件类加载逻辑 修复 导入插件错误 添加工具栏插件支持
Merge pull request !103 from 格物方能致知/develop
2023-09-19 07:08:50 +00:00
gewuyou
399ef925a5 ♻️ 重构插件类加载逻辑 🐛 修复 导入插件错误 添加工具栏插件支持 2023-09-19 14:36:57 +08:00
gewuyou
7c1a313023 🔥 移除暂时用不到的接口方法 2023-09-18 18:26:24 +08:00
gewuyou
26977a0876 Merge branch 'release-v1.1.13' of https://gitee.com/jcnc-org/JNotepad into develop 2023-09-18 08:23:15 +08:00
ad35950edf
!102 feature:增加终端功能
Merge pull request !102 from Luke/release-v1.1.13
2023-09-17 16:26:58 +00:00
2965a9283d 增加本地终端 2023-09-18 00:22:15 +08:00
e561439b4d 增加本地终端 2023-09-17 23:59:41 +08:00
425c4ce6bd 增加本地终端 2023-09-17 23:30:24 +08:00
gewuyou
98010e4411 Merge branch 'release-v1.1.13' of https://gitee.com/jcnc-org/JNotepad into develop 2023-09-17 21:24:30 +08:00
gewuyou
9f4c734b40 重新格式化代码 2023-09-17 21:22:57 +08:00
5202fc200e
!101 fix:状态栏的行和列无法根据光标更新
Merge pull request !101 from Luke/release-v1.1.13
2023-09-17 12:51:54 +00:00
88eee51941 fix:行号不正常更新的bug 2023-09-17 20:50:57 +08:00
afbbdc650c fix:修改字体为等宽字体 2023-09-17 20:42:29 +08:00
b3c39137d5
!100 fix:修复文本编辑器和上方距离过近的bug
Merge pull request !100 from Luke/release-v1.1.13
2023-09-17 12:35:42 +00:00
ee393300f9 fix:修改文本编辑器组件的边距 2023-09-17 20:33:42 +08:00
9a865aa81e
!99 fix:修复错别字
Merge pull request !99 from Luke/release-v1.1.13
2023-09-17 12:25:08 +00:00
f445c321ba fix:修复错别字 2023-09-17 20:24:22 +08:00
28d3da172a
!98 fix:修复iss模板和设置按钮ui
Merge pull request !98 from Luke/release-v1.1.13
2023-09-17 12:21:26 +00:00
24503f8a9c 更新iss模板 2023-09-17 20:17:41 +08:00
d3326a1ed7 设置设置按钮的大小 2023-09-17 20:10:06 +08:00
d18af26ec3
!97 ♻️ 重构插件配置文件控制类
Merge pull request !97 from 格物方能致知/develop
2023-09-17 04:56:53 +00:00
gewuyou
9303b023f2 ♻️ 重构插件配置文件控制类 2023-09-17 11:20:48 +08:00
850e5b509f
!96 初步实现插件功能
Merge pull request !96 from 格物方能致知/develop
2023-09-16 15:20:35 +00:00
gewuyou
2d9f964163 初步实现插件功能 2023-09-16 15:56:58 +08:00
格物方能致知
8605aaf726
!95 ♻️ 重构代码 重构样式
Merge pull request !95 from 格物方能致知/refactor-I815GM
2023-09-13 11:18:14 +00:00
gewuyou
bd3bff528c ♻️ 重构代码 重构样式 2023-09-13 19:13:55 +08:00
格物方能致知
32689ddbb3
!93 修复行号bug
Merge pull request !93 from 格物方能致知/fix-I7W7F6
2023-09-13 04:41:09 +00:00
gewuyou
46d859ef30 🚩新建分支尝试修复行号bug 2023-09-13 11:46:40 +08:00
87d00a29fb
更新错别字
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-09-12 15:55:34 +00:00
b261bc22d9
更新readme
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-09-12 15:54:52 +00:00
a11d17e5fe
更新readme
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-09-12 15:51:51 +00:00
格物方能致知
28db9f2950
!91 修复 BUG 出现滚动条后,退格可能会导致行号错位
Merge pull request !91 from 格物方能致知/fix-I7UK2L
2023-09-12 14:35:19 +00:00
gewuyou
0af9dd3dbd 🐛 修复 BUG 出现滚动条后,退格可能会导致行号错位 2023-09-12 18:44:10 +08:00
194d71cf91
!90 ♻️ 重构代码 重构应用对话框类
Merge pull request !90 from 格物方能致知/refactor-I80I19
2023-09-11 15:21:11 +00:00
gewuyou
967ad9343c ♻️ 重构代码 重构应用对话框类 2023-09-11 23:11:20 +08:00
gewuyou
df7140ff34 ♻️ 重构代码 重构应用对话框类 添加依赖 添加lombok依赖减少实体类代码量 2023-09-11 21:32:38 +08:00
a06a89a0e4
!89 更新readme
Merge pull request !89 from Luke/release-v1.1.13
2023-09-10 17:10:41 +00:00
6767679715 更新readme文档-3 2023-09-11 01:09:53 +08:00
8138cf021e
更新readme
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-09-10 17:07:33 +00:00
dad4597c22
更新readme
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-09-10 17:03:18 +00:00
95344ca299
!88 更新readme
Merge pull request !88 from Luke/release-v1.1.13
2023-09-10 16:58:42 +00:00
0ea829f2c7 更新readme文档-2 2023-09-11 00:57:52 +08:00
2154aef937 更新readme文档-1 2023-09-11 00:39:41 +08:00
格物方能致知
a637b30d6f
!87 ️ 优化开发者调试页面
Merge pull request !87 from 格物方能致知/develop
2023-09-10 03:10:07 +00:00
gewuyou
38797e36fd ️ 优化开发者调试页面 2023-09-10 11:08:07 +08:00
格物方能致知
a606dbc188
!86 ️ 优化弹窗工具类,并为图标设置颜色样式
Merge pull request !86 from 格物方能致知/develop
2023-09-09 15:45:59 +00:00
gewuyou
efd6e9880e ️ 优化弹窗工具类,并为图标设置颜色样式 2023-09-09 23:44:39 +08:00
5be9a5543c
!85 ♻️ 重构代码 初步重构项目结构
Merge pull request !85 from 格物方能致知/refactor-I7ZZHP
2023-09-09 13:33:16 +00:00
gewuyou
26bb88422d ♻️ 重构代码 初步重构项目结构 2023-09-09 19:25:36 +08:00
c7e55abab2
!84 ♻️ 重构代码 初步重构项目结构
Merge pull request !84 from 格物方能致知/refactor-I7ZZHP
2023-09-09 11:12:58 +00:00
gewuyou
f5c2510ab4 Merge branch 'develop' of https://gitee.com/gewuyou/JNotepad into develop 2023-09-09 18:57:05 +08:00
gewuyou
c60bfdd598 ♻️ 重构代码 初步重构项目结构 2023-09-09 18:56:28 +08:00
格物方能致知
3449808666
!83 ⬆️ 依赖升级 jackson-databind
Merge pull request !83 from 格物方能致知/develop
2023-09-09 09:34:35 +00:00
格物方能致知
b3c107eeb6
update README.md.
Signed-off-by: 格物方能致知 <1063891901@qq.com>
2023-09-09 09:33:43 +00:00
gewuyou
fd60cbd9d8 ⬆️ 依赖升级 jackson-databind 2023-09-09 17:28:07 +08:00
7afdeeaf5d
!82 修复 BUG 重命名标签页改为相同名称标签页卡死问题
Merge pull request !82 from 格物方能致知/fix-I7ZWAW
2023-09-09 02:39:58 +00:00
gewuyou
12b3959d5a 🐛 修复 BUG 重命名标签页改为相同名称标签页卡死问题 2023-09-09 10:20:46 +08:00
a619406005
!81 !80fix:修复重命名同样的文件没有提示的bug
Merge pull request !81 from Luke/release-v1.1.12
2023-09-08 17:17:53 +00:00
1e752c3ba9 fix:修复重命名同样的文件名没有提示的bug 2023-09-09 01:16:03 +08:00
b3923c3cfe
!80 fix:修复重命名同样的文件没有提示的bug
Merge pull request !80 from Luke/release-v1.1.12
2023-09-08 16:22:10 +00:00
be1baff33a
!79 修复 BUG 修复删除已有的文本后新建名字不会重新重置的bug
Merge pull request !79 from 格物方能致知/fix-I7ZT6X
2023-09-08 16:22:05 +00:00
9cdf93a913 fix:修复重命名同样的文件没有提示的bug 2023-09-09 00:17:57 +08:00
gewuyou
da92b918e4 🐛 修复 BUG 修复删除已有的文本后新建名字不会重新重置的bug 2023-09-09 00:10:08 +08:00
d1a19a75fa
!78 更新windows截图
Merge pull request !78 from Luke/release-v1.1.12
2023-09-08 15:35:37 +00:00
b8c98328e3 更新windows截图 2023-09-08 23:34:01 +08:00
57e6152cbe
!77 增加行号文本框的注释
Merge pull request !77 from Luke/release-v1.1.12
2023-09-08 15:27:17 +00:00
5b8dab62fa 增加行号文本框的注释 2023-09-08 00:36:03 +08:00
格物方能致知
40e17cbe8c
!76 修复 BUG 修复当打开关联文件时多出一行行号的问题
Merge pull request !76 from 格物方能致知/fix-I7ZE89
2023-09-07 07:07:23 +00:00
gewuyou
8da72722f0 🐛 修复 BUG 修复当打开关联文件时多出一行行号的问题 2023-09-07 14:59:48 +08:00
9f80e17558
!75 ♻️ 重构代码 重构插件测试窗口,使代码符合项目风格规范
Merge pull request !75 from 格物方能致知/refactor-I7ZC7T
2023-09-07 05:31:02 +00:00
gewuyou
dd096bf448 ♻️ 重构代码 重构插件测试窗口,使代码符合项目风格规范 2023-09-07 12:53:33 +08:00
a378f6d6f1
!74 feature-I7XGD0 初步实现插件系统
Merge pull request !74 from Luke/release-v1.1.12
2023-09-06 18:39:11 +00:00
0c27ea4323 初步增加插件系统 2023-09-07 02:35:43 +08:00
5cd4cb85c1 初步增加插件系统 2023-09-07 02:27:00 +08:00
731ced20aa Merge remote-tracking branch 'origin/release-v1.1.12' into release-v1.1.12 2023-09-07 00:46:24 +08:00
6059ff20ba 更新jdk和javafx版本到20 2023-09-07 00:45:03 +08:00
3b8e31a7ae
!73 feature-I7XGEL:完善设置功能,并且优化样式
Merge pull request !73 from Luke/release-v1.1.12
2023-09-04 17:45:51 +00:00
850f92ccb4 完善设置页面 2023-09-05 01:44:18 +08:00
635fec362a 完善设置页面 2023-09-05 01:43:21 +08:00
3b15692870
!72 完善设置页面
Merge pull request !72 from Luke/release-v1.1.12
2023-09-03 18:02:14 +00:00
3da865f292 完善设置页面 2023-09-04 02:01:21 +08:00
97c4863a32
!71 ♻️ 重构代码 重构对话框创建,将对话框创建使用建造者模式创建
Merge pull request !71 from 格物方能致知/refactor-17Y4YA
2023-09-03 16:00:45 +00:00
gewuyou
eb6084ca26 ♻️ 重构代码 重构对话框创建,将对话框创建使用建造者模式创建 2023-09-03 23:13:31 +08:00
6035919fb5
!70 ♻️ 重构代码 重构对话框创建与其工具类的初步封装 添加第三方图标库
Merge pull request !70 from 格物方能致知/refactor-I7Y2O0
2023-09-03 04:31:51 +00:00
gewuyou
8cbea747bf ♻️ 重构代码 重构对话框创建与其工具类的初步封装 添加第三方图标库 2023-09-03 12:14:24 +08:00
d772d98f64 增加提示框模型 2023-09-03 01:29:11 +08:00
格物方能致知
f1b8187edd
!69 ♻️ 重构代码
Merge pull request !69 from 格物方能致知/develop
2023-09-02 06:00:12 +00:00
gewuyou
f04e1a4899 ♻️ 重构代码 2023-09-02 13:57:57 +08:00
4f59dda32e
!68 解决冲突
Merge pull request !68 from Luke/release-v1.1.12
2023-09-02 05:35:14 +00:00
bc26c69bb8 解决冲突 2023-09-02 13:33:39 +08:00
3ef7326bbf Merge remote-tracking branch 'origin/release-v1.1.12' into release-v1.1.12
# Conflicts:
#	src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/NewFile.java
#	src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java
#	src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/RenameFile.java
#	src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java
#	src/main/java/org/jcnc/jnotepad/controller/event/handler/tool/SetBtn.java
#	src/main/java/org/jcnc/jnotepad/tool/UiUtil.java
#	src/main/java/org/jcnc/jnotepad/ui/module/LineNumberTextArea.java
2023-09-02 13:28:39 +08:00
3e04aa09da 解决冲突 2023-09-02 13:25:17 +08:00
格物方能致知
6e012e3855
!67 ♻️ 重构代码 移除Ui单例于Ui工具类
Merge pull request !67 from 格物方能致知/develop
2023-09-02 05:21:10 +00:00
gewuyou
c18941c0ef ♻️ 重构代码 2023-09-02 13:19:45 +08:00
71dfbb76d4
!66 feature-I7XXVS:优化项目doc注释和符合规范
Merge pull request !66 from Luke/feature-I7XXVS
2023-09-02 04:48:01 +00:00
ffaa91ca9a Merge remote-tracking branch 'origin/release-v1.1.12' into feature-I7XXVS
# Conflicts:
#	src/main/java/module-info.java
#	src/main/java/org/jcnc/jnotepad/LunchApp.java
#	src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/NewFile.java
#	src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java
#	src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/RenameFile.java
#	src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java
#	src/main/java/org/jcnc/jnotepad/root/RootBorderPane.java
#	src/main/java/org/jcnc/jnotepad/root/bottom/RootBottomSideBarVBox.java
#	src/main/java/org/jcnc/jnotepad/root/center/main/MainBorderPane.java
#	src/main/java/org/jcnc/jnotepad/root/right/RootRightSideBarVBox.java
#	src/main/java/org/jcnc/jnotepad/root/top/RootTopBorderPane.java
#	src/main/java/org/jcnc/jnotepad/tool/SingletonUtil.java
#	src/main/java/org/jcnc/jnotepad/tool/UiUtil.java
#	src/main/java/org/jcnc/jnotepad/ui/module/LineNumberTextArea.java
2023-09-02 12:43:41 +08:00
f7543c2a64 优化项目doc注释和符合规范 2023-09-02 01:08:38 +08:00
格物方能致知
facb9c886e
!64 ♻️ 重构代码 移除Ui单例于Ui工具类
Merge pull request !64 from 格物方能致知/refactor-17XXTP
2023-09-01 16:44:51 +00:00
gewuyou
730d2492d9 格式化代码 2023-09-02 00:10:35 +08:00
gewuyou
03570f5cd1 Merge branch 'release-v1.1.12' of https://gitee.com/jcnc-org/JNotepad into develop 2023-09-02 00:08:08 +08:00
gewuyou
29c3a59b34 ♻️ 重构代码 移除Ui单例于Ui工具类 2023-09-02 00:06:59 +08:00
6a856a7a6d
!63 修改打开重复文件的逻辑
Merge pull request !63 from 格物方能致知/feature-I7XXQ9
2023-09-01 15:47:03 +00:00
gewuyou
2ffefb6c7b 🚩 修改打开重复文件的逻辑 2023-09-01 23:35:53 +08:00
e418d8b239
!61 feat: #I7X2YR 使用gluonfx打包
Merge pull request !61 from songdragon/feature-I7X2YR
2023-08-31 18:31:48 +00:00
7ff5233b9c
!62 fix: #I7XN52优化设置页面UI
Merge pull request !62 from Luke/dev
2023-08-31 18:29:49 +00:00
c5224b9ca7
update .gitee/ISSUE_TEMPLATE/refactor.yml.
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-08-31 18:26:15 +00:00
e7fd2213e7 优化设置页面的样式 2023-09-01 02:24:08 +08:00
105a7fff15 增加自定义标题栏 2023-09-01 02:05:02 +08:00
songdragon
cee299b7c2
!59 ♻️ 重构代码 优化 文件选择对话框的创建逻辑
Merge pull request !59 from 格物方能致知/refactor-I7XMJG
2023-08-31 17:16:07 +00:00
songdragon
83c81ee667 feat: #I7X2YR 同事进行javafx和gluonfx打包 2023-09-01 01:11:46 +08:00
songdragon
8d0bf22ad7 doc: #I7X2YR 增加windows打包步骤说明 2023-09-01 01:02:15 +08:00
4abe8b7a35 把设置按钮放到侧边栏 2023-09-01 00:25:02 +08:00
3e6d984e58
!60 feat: #I7XMM2 增加重构模版;增加版本下拉选项
Merge pull request !60 from songdragon/feature-I7XMM2
2023-08-31 15:11:46 +00:00
格物方能致知
f9a9cb99f8
!2 refactor: #I7XMJG 工厂改为单例模式
Merge pull request !2 from songdragon/refactor-factory
2023-08-31 15:10:30 +00:00
songdragon
d995b9c8af refactor: #I7XMJG 工厂使用单例模式 2023-08-31 22:47:07 +08:00
songdragon
4e18118a24 feat: #I7XMM2 增加重构模版;增加版本下拉选项 2023-08-31 22:38:23 +08:00
songdragon
d883a581f6 feat: 实现gluonfx打包 2023-08-31 22:23:54 +08:00
gewuyou
03c2363b1e ♻️ 重构代码 优化 文件选择对话框的创建逻辑 2023-08-31 22:09:53 +08:00
格物方能致知
3aec1f93cd
!58 refactor-重构UI代码,提高美观度
Merge pull request !58 from Luke/dev
2023-08-31 00:12:10 +00:00
60e4eb3f22 优化底部状态栏 2023-08-31 02:14:46 +08:00
dec9193469 增加侧边栏 2023-08-31 01:56:50 +08:00
3bd945a2e6 重构ui代码 2023-08-31 01:42:05 +08:00
3671354b5d
!56 feat: #I7SOWB 重命名tab时样式调整
Merge pull request !56 from songdragon/feature-I7SOWB
2023-08-30 16:02:44 +00:00
45b26c6de1
!57 fix: #I7XAY8 修复另存时可能失败的问题
Merge pull request !57 from songdragon/fix-I7XAY8
2023-08-30 16:02:35 +00:00
songdragon
757a0e233b refactor: 封装保存为指定文件方法 2023-08-30 22:59:03 +08:00
songdragon
10181fb0d1 Merge branch 'release-v1.1.12' of gitee.com:jcnc-org/JNotepad into fix-I7XAY8 2023-08-30 22:52:50 +08:00
songdragon
6fa60fe02e
!55 修复 BUG 重复重命名文件失败
Merge pull request !55 from 格物方能致知/fix-I7XAU0
2023-08-30 14:52:27 +00:00
songdragon
8853866742 fix: 修复另存时可能保存失败的bug 2023-08-30 22:46:52 +08:00
songdragon
3f7dbd7cd5 feat: 重复点击重命名不会覆盖名称 2023-08-30 22:37:35 +08:00
gewuyou
c78a251084 🐛 修复 BUG 重复重命名文件失败 2023-08-30 22:27:22 +08:00
songdragon
ba5172d78e feat: 修改重命名时tab tilte样式,并获取焦点 2023-08-30 22:23:11 +08:00
格物方能致知
895fae18f4
!54 ️ 优化代码逻辑
Merge pull request !54 from 格物方能致知/develop
2023-08-30 12:58:14 +00:00
gewuyou
3a8b939cbe ️ 优化代码逻辑 2023-08-30 13:38:04 +08:00
gewuyou
4db5107d81 Merge branch 'release-v1.1.12' of https://gitee.com/jcnc-org/JNotepad into develop
# Conflicts:
#	src/main/java/org/jcnc/jnotepad/ui/setStage/SetStage.java
2023-08-30 12:34:24 +08:00
gewuyou
e1a058d891 ️ 优化代码逻辑 2023-08-30 12:31:52 +08:00
c3b78c9ebd
!53 feature-I7X1L7增加设置按钮和增加设置页面,修改UI代码结构
Merge pull request !53 from Luke/I7X1L7
2023-08-30 03:41:11 +00:00
a316425ab0 增加注释 2023-08-30 11:38:05 +08:00
3add6372ab 移除了重复定义的 contentDisplay 变量。 2023-08-30 10:19:10 +08:00
5b5fdaadd7
!52 feature-I7WZJW 增加设置按钮和增加设置页面,修改UI代码结构
Merge pull request !52 from Luke/release-v1.1.12
2023-08-30 02:09:14 +00:00
442c7ebee6 增加设置页面 2023-08-30 05:24:24 +08:00
dc7ead7d8d 增加设置按钮 2023-08-30 04:15:56 +08:00
69922ea846 增加设置按钮 2023-08-30 04:12:49 +08:00
43da503da9
!51 添加重命名功能
Merge pull request !51 from 格物方能致知/feature-I7SOWB
2023-08-29 16:06:52 +00:00
gewuyou
6164698dd7 🚩 添加重命名功能 2023-08-29 23:21:01 +08:00
gewuyou
c1fe25e2bb Merge branch 'master' of https://gitee.com/jcnc-org/JNotepad into develop 2023-08-29 20:13:43 +08:00
8eb8b7bf27
!50 hotfix: #I7WXRO 修复关联文件打开失败问题
Merge pull request !50 from songdragon/hotfix-I7WXRO
2023-08-29 11:10:51 +00:00
songdragon
317e486f85 fix: #I7WXRO 修复关联文件打开失败的问题 2023-08-29 18:53:16 +08:00
eb785eec45
!49 添加使用系统文件选择器时显示应用图标
Merge pull request !49 from 格物方能致知/feature-17WSJY
2023-08-29 06:39:45 +00:00
gewuyou
5cda1ddf87 🚩 添加使用系统文件选择器时显示应用图标 2023-08-29 14:22:54 +08:00
gewuyou
8f4448bac2 💡 添加或修改源代码注释️ 优化代码逻辑 2023-08-28 23:38:08 +08:00
songdragon
422bb59161
!48 feat: #I7WMXH 补充Q&A
Merge pull request !48 from songdragon/feature-I7WMXH
2023-08-28 14:39:10 +00:00
songdragon
a1d6446f06 feat: #I7WMXH 增加Q&A;补充拉取本地分支的特殊情况说明 2023-08-28 22:37:28 +08:00
songdragon
ca73febbf7
!47 fix: 修复错别字
Merge pull request !47 from songdragon/fix-typo
2023-08-28 14:22:06 +00:00
songdragon
27104812da fix: typo 按照->安装 2023-08-28 22:21:26 +08:00
songdragon
1162c1147a
!46 feat: #I7WMUV 增加开发指南说明
Merge pull request !46 from songdragon/feature-I7WMUV
2023-08-28 14:17:34 +00:00
songdragon
c9a361b8ce doc: #I7WMUV 更新PR图 2023-08-28 22:16:58 +08:00
songdragon
f1a423fd5f doc: #I7WMUV 增加开发指南 2023-08-28 22:14:45 +08:00
3747df5272
!43 修复输入框向下滑动的bug
Merge pull request !43 from Luke/Develop
2023-08-27 16:17:16 +00:00
24bb39ee63
!42 修复输入框向下滑动的bug
Merge pull request !42 from Luke/master
2023-08-27 16:16:21 +00:00
7ea8630f2c 修复输入框向下滑动的bug 2023-08-28 00:08:48 +08:00
格物方能致知
c81517b96b
!41 refactor: #I7VLH0 统一配置入口
Merge pull request !41 from songdragon/refactor-I7VLH0
2023-08-27 15:26:27 +00:00
songdragon
53d96ecffc refactor: 保存配置文件时,增加语言刷新 2023-08-27 22:54:55 +08:00
songdragon
787da7f1be refactor: 删除无用的imports 2023-08-27 22:37:35 +08:00
songdragon
718c24fd6c fix: 增加目录自动创建 2023-08-27 22:33:52 +08:00
songdragon
6ff1af20dc refactor: 解耦ResourceBundle和LocalizationController 2023-08-27 22:07:14 +08:00
songdragon
feb364eae0 refactor: 删除重复功能代码 2023-08-27 21:58:44 +08:00
songdragon
50aea7774e refactor: 统一配置入口 2023-08-27 21:49:16 +08:00
songdragon
8ed04be941
!40 refactor: json输出增加为4个空格
Merge pull request !40 from songdragon/refactor-jsonspaces
2023-08-27 11:40:37 +00:00
songdragon
3bc147e4a2 chorn: 增加项目配置 2023-08-27 19:40:06 +08:00
songdragon
13767ad1cc doc: 增加代码规范说明 2023-08-27 19:38:32 +08:00
songdragon
92fd8f3dbd refactor: json缩进增加为4个空格 2023-08-27 18:43:56 +08:00
songdragon
ea594c4048
!39 ️ 优化代码 ️ 清理无效的代码
Merge pull request !39 from 格物方能致知/master
2023-08-27 10:18:15 +00:00
gewuyou
a9dd2e21c5 ️ 优化代码 🗑️ 清理无效的代码 2023-08-27 17:59:31 +08:00
格物方能致知
2eb71bece8
!38 #I7W8LM 国际化和本地化使用i18n
Merge pull request !38 from songdragon/refactor-I7W8LM
2023-08-27 09:12:39 +00:00
songdragon
59c8b8f5ed doc: 增加UIResourceBundle注释 2023-08-27 17:00:34 +08:00
songdragon
6679d6e04d refactor: 移除不使用的代码 2023-08-27 16:54:41 +08:00
songdragon
2f78ab6632 refactor: #I7W8LM 国际化和本地化使用i18n 2023-08-27 16:37:13 +08:00
667222ce91
!37 修复 BUG 当切换语言时,下方状态栏无法切换的bug,️ 优化 切换语言的逻辑
Merge pull request !37 from 格物方能致知/master
2023-08-27 02:02:55 +00:00
gewuyou
ab2030a0e2 🐛 修复 BUG 当切换语言时,下方状态栏无法切换的bug,️ 优化 切换语言的逻辑 2023-08-27 09:41:07 +08:00
songdragon
20bf5e75f4 fix: 修复读取json并修改的问题 2023-08-27 00:58:38 +08:00
815e2b01a8 实现对象化json 2023-08-27 00:46:00 +08:00
格物方能致知
1849f01b0e
!36 refactor: #I7W7AG 使用jackson替代json
Merge pull request !36 from songdragon/refactor-I7W7AG
2023-08-26 16:20:32 +00:00
songdragon
d26827075b refactor: #I7W7AG 使用jackson替代json 2023-08-27 00:14:50 +08:00
bd840fa160
!35 添加并使用线程池管理项目中的异步操作
Merge pull request !35 from 格物方能致知/master
2023-08-26 14:56:15 +00:00
gewuyou
d31772b3d5 🧵 添加并使用线程池管理项目中的异步操作 2023-08-26 22:25:20 +08:00
abb3633172
!34 添加语言切换功能
Merge pull request !34 from 格物方能致知/master
2023-08-26 10:36:02 +00:00
gewuyou
8c589d6133 🚩 添加语言切换功能 2023-08-26 18:18:36 +08:00
c7d36438e1 // todo 切换语言并将语言修改设置回本地,
//  1.只更新json的language为english,没有保存
2023-08-26 15:18:01 +08:00
0a4fb7150d
!33 修改项目文件结构 ⚰️ 删除无用代码 修复 BUG 当无打开文件时打开文件程序抛空指针异常问题
Merge pull request !33 from 格物方能致知/master
2023-08-26 06:45:38 +00:00
gewuyou
7ab6793179 ⚰️ 删除无用代码 🐛 修复 BUG 当无打开文件时打开文件程序抛空指针异常问题 2023-08-26 13:49:35 +08:00
gewuyou
5ad48be1cd 🔨 修改项目文件结构,补上误删除的目录 2023-08-26 12:35:53 +08:00
gewuyou
c68992fd00 Merge branch 'master' of https://gitee.com/jcnc-org/JNotepad 2023-08-26 12:30:12 +08:00
gewuyou
785600c857 🔨 修改项目文件结构 2023-08-26 12:29:53 +08:00
2f17aa5090
!32 添加置顶 功能,修复 BUG ✏️ 修正错别字
Merge pull request !32 from 格物方能致知/master
2023-08-25 16:18:12 +00:00
gewuyou
f5fdb925ea 🐛 修复 BUG 修改配置文件的默认语言后,没有即时刷新 2023-08-26 00:05:30 +08:00
gewuyou
1bd9e17b60 🐛 修复 BUG 当没有配置文件打开软件报错问题,解决合并冲突,✏️ 修正错别字 2023-08-25 23:51:51 +08:00
gewuyou
631c7b82f2 Merge branch 'master' of https://gitee.com/jcnc-org/JNotepad
# Conflicts:
#	src/main/java/org/jcnc/jnotepad/app/config/LoadJnotepadConfig.java
#	src/main/java/org/jcnc/jnotepad/init/Config.java
#	src/main/java/org/jcnc/jnotepad/tool/EncodingDetector.java
2023-08-25 23:10:54 +08:00
gewuyou
eb7eb2552c 添加置顶功能 ️ 优化配置文件读取 2023-08-25 23:05:44 +08:00
a95a65e6d5
!30 fix: #I7VP0I 修复UNKOWN在配置文件不存在的问题
Merge pull request !30 from songdragon/fix-I7VP0I
2023-08-25 09:57:57 +00:00
240abc1e91
!31 feat: #I7VS0O 统一配置文件默认输出目录
Merge pull request !31 from songdragon/feature-I7VS0O
2023-08-25 09:57:33 +00:00
gewuyou
fbc1d020d9 添加置顶功能 2023-08-25 16:18:26 +08:00
songdragon
8c533b7a6f feat: 固定使用user.home/.jnotepad作为输出目录 2023-08-25 00:36:17 +08:00
songdragon
017156b0eb feat: 使用用户目录下.jnotepad作为默认配置目录 2023-08-25 00:25:59 +08:00
gewuyou
3daba03cbb Merge branch 'master' of https://gitee.com/jcnc-org/JNotepad 2023-08-24 23:40:32 +08:00
gewuyou
f108f95426 添加排除文件类型 2023-08-24 23:40:24 +08:00
songdragon
92d0339146 fix: #I7VP0I 修复UNKOWN在配置文件不存在的问题 2023-08-24 23:24:38 +08:00
f950b42450
!29 fix: #I7VP0I 检测文件失败时,使用系统默认编码
Merge pull request !29 from songdragon/fix-I7VP0I
2023-08-24 15:18:09 +00:00
4d942625cb 修复换行bug 2023-08-24 23:16:40 +08:00
597062153a 修复换行bug 2023-08-24 23:16:26 +08:00
songdragon
8a931725bf fix: #I7VP0I 遍历文件编码时,取概率相同,但最先出现的 2023-08-24 23:08:31 +08:00
songdragon
c678fda573 fix: #I7VP0I 检测文件失败时,使用系统默认编码 2023-08-24 22:57:20 +08:00
13382d2cd4 修复EncodingDetector 文本常量 2023-08-24 22:37:19 +08:00
cda1f95b75
!28 fix: #I7VP0I 检测文件编码时尝试所有可能顺序
Merge pull request !28 from songdragon/fix-I7VP0I
2023-08-24 13:38:41 +00:00
songdragon
f7505f6063 fix: #I7VP0I 检测文件编码时尝试所有可能顺序 2023-08-24 18:01:58 +08:00
c4b998100e
!27 添加自定义配置文件功能
Merge pull request !27 from 格物方能致知/master
2023-08-24 09:37:36 +00:00
gewuyou
97d8776a5e 🚩 添加自定义配置文件功能 2023-08-24 17:30:22 +08:00
810ba4c447
!26 ️ 优化:抽取项目中的文本放到常量类
Merge pull request !26 from 格物方能致知/master
2023-08-24 07:55:01 +00:00
gewuyou
ce9280134e ️ 优化:抽取项目中的文本放到常量类 2023-08-24 11:17:19 +08:00
gewuyou
60f4f90f48 Merge branch 'master' of https://gitee.com/gewuyou/JNotepad 2023-08-24 09:23:42 +08:00
gewuyou
c3408b9d3f ️ 优化:优化代码逻辑 2023-08-24 09:23:30 +08:00
076e497171 设置默认为英语 2023-08-24 02:11:20 +08:00
014186b050 增加多语言配置文件 2023-08-24 02:08:44 +08:00
80872b6511
!25 fix: #I7VAM0 修复编码状态栏未更新的问题
Merge pull request !25 from songdragon/feature-I7VAM0
2023-08-23 18:02:59 +00:00
2efb7d33a7 增加多语言配置文件 2023-08-24 02:01:49 +08:00
362e63d781 增加多语言配置文件 2023-08-24 01:49:13 +08:00
songdragon
834d5ac708 fix: 修复切换tab编码没变更的问题 2023-08-24 01:40:40 +08:00
songdragon
8e4c35db29 fix: 修复打开文件时,编码错误的问题 2023-08-24 01:38:42 +08:00
5aea9e96a9
!24 feat: #I7VAM0 实现自动检测文件编码
Merge pull request !24 from songdragon/feature-I7VAM0
2023-08-23 17:25:42 +00:00
songdragon
b1ca13404a refactor: 使用jdk17编译module-info 2023-08-24 01:22:26 +08:00
songdragon
73830bb306 refactor: 删除行尾注释 2023-08-24 01:08:20 +08:00
songdragon
849161d237 refactor: #I7VAM0 解决icu4j link失败的问题 2023-08-24 01:03:03 +08:00
songdragon
2410f6e213 feat: 实现侦测文件内容编码,并使用侦测成功的编码打开 2023-08-24 00:46:51 +08:00
58cd858643
update .gitee/ISSUE_TEMPLATE/bug.yml.
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-08-23 10:24:29 +00:00
27f1593844
update .gitee/ISSUE_TEMPLATE/bug.yml.
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-08-23 10:23:04 +00:00
2e72676dec
update .gitee/ISSUE_TEMPLATE/bug.yml.
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-08-23 10:16:29 +00:00
ec1bb66f1e
update .gitee/ISSUE_TEMPLATE/bug.yml.
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-08-23 10:10:19 +00:00
ee78f2bc60
!23 添加保存按钮,优化项目代码逻辑
Merge pull request !23 from 格物方能致知/master
2023-08-23 09:59:15 +00:00
gewuyou
f0731aa259 🚩 添加:添加保存功能 ️ 优化:优化代码逻辑 2023-08-23 15:30:47 +08:00
songdragon
1cd41017b2
!22 添加依赖:添加并引入Slf4j api 与 logback作为项目日志框架,已替换项目内控制台输出,简单封装了日志工具类
Merge pull request !22 from 格物方能致知/master
2023-08-23 00:53:06 +00:00
gewuyou
2c6c88d62c ✏️ 修正:修复打包后,启动程序日志框架提示缺少javax/naming/NamingException 2023-08-22 16:52:58 +08:00
gewuyou
74ffd70c3a Merge branch 'master' of https://gitee.com/jcnc-org/JNotepad 2023-08-22 16:24:05 +08:00
格物方能致知
b9bb72ac64
!21 fix: #I7UJZL 修复自动换行不生效的问题
Merge pull request !21 from songdragon/fix-I7UJZL
2023-08-22 08:12:00 +00:00
gewuyou
7b9e4f4aef 添加依赖:添加并引入Slf4j api 与 logback作为项目日志框架,已替换项目内控制台输出,简单封装了日志工具类 2023-08-22 15:57:23 +08:00
songdragon
60d4de6832 doc: 增加GlobalConfig注释说明 2023-08-22 13:14:29 +08:00
songdragon
641081b0a5 fix: #I7UJZL 修复自动换行不生效的问题 2023-08-22 13:08:43 +08:00
3c7c35d50b
!20 refactor: #I7UK1H 将菜单、tab提取到单独的类中,作为UI组件
Merge pull request !20 from songdragon/refactor-I7UK1H
2023-08-21 18:29:42 +00:00
songdragon
65f8a50dd9 refactor: 将菜单\菜单项\Tab提取到单独的类中,作为UI组件 2023-08-22 00:09:12 +08:00
09b4f3c64b
!19 重构项目代码,让其更符合规范
Merge pull request !19 from 格物方能致知/master
2023-08-21 12:22:16 +00:00
格物方能致知
4bf14ed939
Revert " 添加依赖:添加并引入Slf4j api 与 logback作为项目日志框架,已替换项目内控制台输出,简单封装了日志工具类"
This reverts commit 79e368dc17da8914f09649c4fe1fd2bcbecc3485.
2023-08-21 12:16:36 +00:00
gewuyou
79e368dc17 添加依赖:添加并引入Slf4j api 与 logback作为项目日志框架,已替换项目内控制台输出,简单封装了日志工具类 2023-08-21 18:29:29 +08:00
gewuyou
b1662b717a ♻️ 重构代码:重构项目代码,让项目更符合规范 2023-08-21 16:38:51 +08:00
f1811216dd
!16 fix: #I7UK2L 修复行号可能错位的问题
Merge pull request !16 from songdragon/fix-I7UK2L
2023-08-20 17:41:21 +00:00
e9ebcfc649 增加配置文件功能 2023-08-21 01:21:24 +08:00
songdragon
d89c39ea0b fix: #I7UK2L 修复行号可能错位的问题 2023-08-21 00:20:02 +08:00
d4d7cdfc68 修改行标像素 2023-08-20 23:40:01 +08:00
200c090887 修改行标像素 2023-08-20 23:33:02 +08:00
6391df41a2
!15 fix: #I7UHOH 修复行号宽度不能自动减小的问题;增加默认最小宽度计算
Merge pull request !15 from songdragon/fix-I7UHOH
2023-08-20 15:21:04 +00:00
songdragon
308fe8db1c fix: #I7UHOH 修复行号宽度不能自动减小的问题;增加默认最小宽度计算 2023-08-20 21:57:15 +08:00
a767c9978d
!14 修复了关闭最后一个页面时,下方数据依旧是最后一个页面的bug
Merge pull request !14 from 格物方能致知/master
2023-08-20 08:32:38 +00:00
gewuyou
08005c73c5 修复:关闭最后一个标签页时下方数据不刷新的问题。 2023-08-20 13:13:46 +08:00
013c47d81e 修改mainTextArea样式 2023-08-20 01:51:49 +08:00
45d9f4b4fc
!13 fix: 读取用户自定义配置文件
Merge pull request !13 from 一个大转盘/master
2023-08-19 17:49:26 +00:00
kb
877976f1d7 fix: 导入用户自定义配置文件 2023-08-20 01:22:37 +08:00
kb
a391ed05e0 fix: 资源文件导入方式 2023-08-20 01:01:07 +08:00
b5afb513db
!11 fix: hutool 打包失败
Merge pull request !11 from 一个大转盘/master
2023-08-19 16:01:54 +00:00
kb
9808d49377 更换解析json库 2023-08-19 22:57:11 +08:00
7138ef02a1 修改行号外观 2023-08-19 22:05:30 +08:00
f5cca69c50 修改截图 2023-08-19 22:01:03 +08:00
af14b6f946 把css放到css包,删除,修改textarea样式 2023-08-19 21:43:54 +08:00
cbcdc120b7
!10 feat: #I7UFOF 隐藏滚动条
Merge pull request !10 from songdragon/feat-I7UFOF
2023-08-19 12:07:06 +00:00
songdragon
87ce23ce12 feat: 行号宽度根据最后一个行号自动调整 2023-08-19 16:04:00 +08:00
songdragon
871da1b9c2 Merge branch 'master' of gitee.com:jcnc-org/JNotepad into feat-I7UFOF 2023-08-19 15:23:22 +08:00
songdragon
7cf9ba2eac feat: 隐藏滚动条 2023-08-19 15:23:15 +08:00
87e48b9130 修复newFile的新建报错 2023-08-19 15:20:47 +08:00
songdragon
2bda1d8097 fix: #I7UFUR 修复使用LineNumberTextArea自动保存和统计失效的问题 2023-08-19 14:53:57 +08:00
020ee7008e
!8 fix: #I7UE5B 修复新建文件后自动保存失效的问题
Merge pull request !8 from songdragon/fix-I7UE5B
2023-08-19 06:35:36 +00:00
songdragon
4506b0c310 merge: 解决冲突 2023-08-19 14:28:27 +08:00
2a3b32503f
!7 fix: #I7UE8N 修复另存文件名固定的问题
Merge pull request !7 from songdragon/fix-I7UE8N
2023-08-19 06:15:46 +00:00
b2718c1932 实现带行标的textarea
(未隐藏滚动条)
2023-08-19 14:12:58 +08:00
9617ee5046
删除文件 .idea 2023-08-19 06:09:44 +00:00
songdragon
aebcdd9646 chron: 忽略.idea 2023-08-19 11:28:44 +08:00
songdragon
b8b649d097 rollback: 回滚.gitignore改动 2023-08-19 11:22:16 +08:00
songdragon
12b59555b6 fix: #I7UE5B 修复新建文件后自动保存失效问题 2023-08-19 11:15:21 +08:00
songdragon
b2676f861f refactor: Controller使用sigleton,避免多次创建对象 2023-08-19 11:11:06 +08:00
songdragon
0216a3bfcb fix: #I7UE8N 修复另存文件名固定的问题 2023-08-19 10:40:02 +08:00
c014a72ace v1.1.10打包修改 2023-08-19 00:57:07 +08:00
a49c31586a
!6 feat: 自定义快捷键
Merge pull request !6 from 一个大转盘/master
2023-08-18 16:43:23 +00:00
kb
2ea22fc5a2 feat: 快捷键 2023-08-18 19:18:30 +08:00
500057e926 删除静态调用 2023-08-17 00:54:48 +08:00
18799d0c2f 删除静态调用 2023-08-17 00:54:16 +08:00
73b6bcf7fc 增加软件截图图片 2023-08-15 13:40:57 +08:00
25767b48fb 增加软件截图图片 2023-08-15 13:39:54 +08:00
5c608d332f 增加软件截图图片 2023-08-15 13:24:48 +08:00
f9cb763f1c 引入亚特兰大fx主题 2023-08-15 13:18:23 +08:00
7344936d92 引入亚特兰大fx主题 2023-08-15 13:17:26 +08:00
6bd259992a 删除无效代码 2023-08-15 12:40:08 +08:00
6acc5024f3
!5 fix: #I7T5XZ 修复自动保存失效的问题
Merge pull request !5 from songdragon/fix-I7T5XZ
2023-08-15 04:22:58 +00:00
songdragon
ea91fcb8bb fix: #I7T5XZ 修复自动保存失效的问题 2023-08-15 10:05:46 +08:00
a9ffa47801
!4 fix: 关联文件打开会出现两个tap
Merge pull request !4 from 一个大转盘/master
2023-08-14 15:39:27 +00:00
kb
0ba9794b8e fix: 关联文件打开会出现两个tap 2023-08-14 22:51:55 +08:00
ebaf3be194 修改main 2023-08-14 22:47:07 +08:00
4dd21a14ce 删除打包工具 2023-08-14 22:38:33 +08:00
df3e901ca1 增加JNotepad.iss 用于打包项目项目为发行版 2023-08-14 22:37:55 +08:00
f0f68e101b 增加build.txt 用于jlink后构建项目 2023-08-14 22:36:40 +08:00
d680623993 增加注释 2023-08-14 13:12:18 +08:00
929ae12fbf 增加注释 2023-08-14 13:04:04 +08:00
704e928bb4 OpenFile.java 增加注释 2023-08-14 12:41:35 +08:00
1524094d6d 增加注释文档 2023-08-14 11:08:34 +08:00
fc490e8ddc 增加注释 2023-08-14 11:02:32 +08:00
ffb36c5dc6 修改readme 2023-08-14 10:59:21 +08:00
d1a976577b 修改readme 2023-08-14 10:58:41 +08:00
ef3f8869fc 修改readme 2023-08-14 10:55:27 +08:00
bdbbc3441e
readme updata
Signed-off-by: Luke <luke.k.xu@hotmail.com>
2023-08-12 08:00:33 +00:00
227 changed files with 13477 additions and 922 deletions

View File

@ -0,0 +1,56 @@
name: Bug 反馈
description: 当你在代码中发现了一个 Bug导致应用崩溃或抛出异常或者有一个组件存在问题或者某些地方看起来不对劲。
title: "[Bug]: "
labels: [ "bug" ]
body:
- type: markdown
attributes:
value: |
感谢对项目的支持与关注。在提出问题之前,请确保你已查看相关开发或使用文档:
- https://gitee.com/jcnc-org/docs/blob/master/zh-cn/doc/doc-jnotepad/doc-jnotepad.md
- type: checkboxes
attributes:
label: 这个问题是否已经存在?
options:
- label: 我已经搜索过现有的问题 (https://gitee.com/organizations/jcnc-org/issues)
required: true
- type: textarea
attributes:
label: 如何复现
description: 请详细告诉我们如何复现你遇到的问题,如涉及代码,可提供一个最小代码示例,并使用反引号```附上它
placeholder: |
操作系统:
复现步骤:
1. ...
2. ...
3. ...
validations:
required: true
- type: textarea
attributes:
label: 预期结果
description: 请告诉我们你预期会发生什么。
validations:
required: true
- type: textarea
attributes:
label: 实际结果
description: 请告诉我们实际发生了什么。
validations:
required: true
- type: textarea
attributes:
label: 截图或视频
description: 如果可以的话,上传任何关于 bug 的截图。
value: |
[在这里上传图片]
- type: dropdown
id: version
attributes:
label: 版本
description: 你当前正在使用我们软件的哪个版本/分支?
options:
- V1.1.14(最新开发版)
- V1.1.13(最新发行版)
validations:
required: true

View File

@ -0,0 +1,5 @@
blank_issues_enabled: true
#contact_links:
# - name: Gitee 帮助中心
# url: https://help.gitee.com/
# about: 提供 Git 使用指南、教程、Gitee.com 平台基本功能使用、介绍和常见问题解答

View File

@ -0,0 +1,43 @@
name: 功能建议
description: 对本项目提出一个功能建议
title: "[功能建议]: "
labels: [ "feature" ]
body:
- type: markdown
attributes:
value: |
感谢提出功能建议,我们将仔细考虑!
- type: textarea
id: related-problem
attributes:
label: 你的功能建议是否和某个问题相关?
description: 清晰并简洁地描述问题是什么,例如,当我...时,我总是感到困扰。
validations:
required: false
- type: textarea
id: desired-solution
attributes:
label: 你希望看到什么解决方案?
description: 清晰并简洁地描述你希望发生的事情。
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: 你考虑过哪些替代方案?
description: 清晰并简洁地描述你考虑过的任何替代解决方案或功能。
validations:
required: false
- type: textarea
id: additional-context
attributes:
label: 你有其他上下文或截图吗?
description: 在此处添加有关功能请求的任何其他上下文或截图。
validations:
required: false
- type: checkboxes
attributes:
label: 意向参与贡献
options:
- label: 我有意向参与具体功能的开发实现并将代码贡献回到上游社区
required: false

View File

@ -0,0 +1,12 @@
name: 重构
description: 对本项目提出一个功能建议
title: "[重构]: "
labels: [ "refactor" ]
body:
- type: textarea
id: related-problem
attributes:
label: 重构目的是什么?
description: 清晰并简洁地描述重构是什么,例如,减少文件选择器重复创建代码。
validations:
required: false

28
.gitignore vendored
View File

@ -2,15 +2,21 @@ target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
test/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
.idea/
*.iws
*.iml
*.ipr
### 此处忽略了json与xml后缀
*.xml
*.json
### 此处排除证书目录
certificate/
### 此处排除项目文件
.jnotepad/
### Eclipse ###
.apt_generated
@ -38,3 +44,15 @@ build/
.DS_Store
/JNotepad/
/src/main/JNotepad.java
/.idea/
!.idea/codeStyles/
!.idea/fileTemplates/
!.idea/encodings.xml
/project.txt
logs/
/ch_language_pack.txt
/en_language_pack.txt
/jnotepadConfig.json
/qodana.yaml
.mvn/
/main.c

8
.idea/.gitignore generated vendored
View File

@ -1,8 +0,0 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -1,24 +0,0 @@
<component name="ArtifactManager">
<artifact type="jar" name="JNotepad:jar">
<output-path>$PROJECT_DIR$/out/artifacts/JNotepad_jar</output-path>
<root id="archive" name="JNotepad.jar">
<element id="module-output" name="JNotepad" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/openjfx/javafx-graphics/17.0.1/javafx-graphics-17.0.1-win.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/openjfx/javafx-fxml/17.0.1/javafx-fxml-17.0.1-win.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/openjfx/javafx-fxml/17.0.1/javafx-fxml-17.0.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/openjfx/javafx-base/17.0.1/javafx-base-17.0.1-win.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/openjfx/javafx-controls/17.0.1/javafx-controls-17.0.1-win.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/openjfx/javafx-base/17.0.1/javafx-base-17.0.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/openjfx/javafx-controls/17.0.1/javafx-controls-17.0.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/openjfx/javafx-graphics/17.0.1/javafx-graphics-17.0.1.jar" path-in-jar="/" />
<element id="library" level="project" name="Maven: org.openjfx:javafx-controls:17.0.1" />
<element id="library" level="project" name="Maven: org.openjfx:javafx-controls:win:17.0.1" />
<element id="library" level="project" name="Maven: org.openjfx:javafx-graphics:17.0.1" />
<element id="library" level="project" name="Maven: org.openjfx:javafx-graphics:win:17.0.1" />
<element id="library" level="project" name="Maven: org.openjfx:javafx-base:17.0.1" />
<element id="library" level="project" name="Maven: org.openjfx:javafx-base:win:17.0.1" />
<element id="library" level="project" name="Maven: org.openjfx:javafx-fxml:17.0.1" />
<element id="library" level="project" name="Maven: org.openjfx:javafx-fxml:win:17.0.1" />
</root>
</artifact>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

6
.idea/encodings.xml generated
View File

@ -1,11 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/11.txt" charset="GBK" />
<file url="file://$PROJECT_DIR$/aaa.txt" charset="GBK" />
<file url="file://$PROJECT_DIR$/init.bat" charset="US-ASCII" />
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/新建文本.txt" charset="GBK" />
</component>
</project>

View File

@ -0,0 +1,7 @@
#foreach($param in $RECORD_COMPONENTS)
* @param $param
#end
#foreach($param in $TYPE_PARAMS)
* @param <$param>
#end
* @author $USER

13
.idea/misc.xml generated
View File

@ -1,13 +0,0 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="openjdk-17.0.2" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

124
.idea/uiDesigner.xml generated
View File

@ -1,124 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

6
.idea/vcs.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

Binary file not shown.

View File

@ -1,2 +0,0 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar

135
README.md
View File

@ -1,79 +1,140 @@
# JNotepad
<p align="center">
<img src="src/main/resources/jcnc/app/svg/icon.svg" alt="JNotepad Icon">
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">JNotepad</h1>
<h4 align="center" style="margin: 30px 0 30px; font-weight: bold;">JavaFX开发插件驱动创造无限可能</h4>
![](https://img.shields.io/badge/Windows-Passing-49%2C198%2C84.svg?style=falt&logo=Windows)
![](https://img.shields.io/badge/Ubuntu-Passing-49%2C198%2C84.svg?style=falt&logo=Ubuntu)
![](https://img.shields.io/badge/MacOS-Passing-49%2C198%2C84.svg?style=falt&logo=Apple)
<p align="center">
<a href='https://gitee.com/jcnc-org/JNotepad/stargazers'><img
src='https://gitee.com/jcnc-org/JNotepad/badge/star.svg?theme=dark' alt='star'>
</a>
<a href='https://gitee.com/jcnc-org/JNotepad/members'><img
src='https://gitee.com/jcnc-org/JNotepad/badge/fork.svg?theme=dark' alt='fork'>
</a>
</p>
<p align="center">
<a href="https://gitee.com/jcnc-org/JNotepad/blob/master/LICENSE">
<img src="https://img.shields.io/badge/%20license-GPL--3.0%20-blue" alt="">
</a>
<a href="https://gitee.com/jcnc-org/JNotepad/blob/master/LICENSE">
<img src="https://img.shields.io/badge/version-v1.1.13-blue" alt="">
</a>
</p>
<p align="center">
<a href="https://gitee.com/jcnc-org/JNotepad/releases">
<img src="https://img.shields.io/badge/Windows-Passing-49%2C198%2C84.svg?style=falt&logo=Windows" alt="">
</a>
<a href="https://gitee.com/jcnc-org/JNotepad/releases">
<img src="https://img.shields.io/badge/Ubuntu-Passing-49%2C198%2C84.svg?style=falt&logo=Ubuntu" alt="">
</a>
<a href="https://gitee.com/jcnc-org/JNotepad/releases">
<img src="https://img.shields.io/badge/MacOS-Passing-49%2C198%2C84.svg?style=falt&logo=Apple" alt="">
</a>
</p>
JNotepad 是一个使用 JavaFX 构建的简单文本编辑器,允许用户创建、打开、编辑和保存文本文件。它支持多个标签,每个标签包含一个文本编辑区域。该编辑器提供基本功能,如创建新文件、打开现有文件、保存文件和使用不同名称保存文件。
[jnotepad-official-plugins]:https://gitee.com/jcnc-org/jnotepad-official-plugins
[jcnc-docs]:https://gitee.com/jcnc-org/docs
| 序号 | 相关仓库 | 链接地址 |
|:---: | :---------------: | :-----------------------------------:|
|1 | JNotepad插件仓库 | [点击访问][jnotepad-official-plugins] |
|2 | JCNC文档仓库 | [点击访问][jcnc-docs] |
JNotepad(Java Notepad)
是一款简约而强大的跨平台文本编辑器旨在提供用户友好的界面和丰富的功能以及插件化使用。无论你是在Linux、Windows还是macOS系统上使用JNotepad都能满足你对文本编辑和查看的需求。
JNotepad使用Java语言编写并基于JavaFX框架开发具有良好的可扩展性和稳定性。
## 功能介绍
- 创建空白文本区域的新文件。
- 打开现有文本文件进行编辑。
- 实时更新和自动保存文件。
- 使用“另存为”功能将文件另存为不同的名称。
- 在状态栏中显示行数、列数和字符计数信息。
- 文本编辑和查看JNotepad提供了完善的文本编辑和查看功能使你能够轻松创建、编辑和浏览各种类型的文本文件。
- 跨平台支持不论你使用哪种操作系统JNotepad都能够无缝地适应并提供一致的用户体验。
- 轻量级设计JNotepad采用简约而现代的设计风格界面清晰简洁使得使用起来非常直观和便捷。
- 基于JavaJNotepad使用Java语言编写并基于JavaFX框架开发具有良好的可扩展性和稳定性。
## 安装教程
1. Windows 平台,可以直接使用我编译好的可执行程序或自己编译
1. Windows 平台,可以直接使用编译的可执行程序或自己编译
[gitee-download]: https://gitee.com/jcnc-org/JNotepad/releases
[java-download]: https://www.oracle.com/cn/java/technologies/downloads/
[qq-url]: http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=zOfwWb1lcle68cbEdJCjSIp3Itx0nEC0&authKey=bOsZFT9OVYZpZQbS6IYO4onBQoeBorF5nanMEi1G%2FgPbzmUkOweXBo9qB0G34R5K&noverify=0&group_code=386279455
[docs-url]: https://gitee.com/jcnc-org/docs
- [下载][gitee-download]
2. Linux/MacOS 平台,查看入门指南
## 入门指南
要使用 JNotepad请按照以下步骤进行:
1. 下载并安装 Java如果尚未安装
- [下载][gitee-download]
2. 克隆或下载 JNotepad 项目。
<pre><code>git clone https://gitee.com/jcnc-org/JNotepad.git</code></pre>
3. 在您偏好的 Java IDE 中打开项目。
## 使用方法
1. 运行 `JNotepad` 类以启动应用程序。
2. 主窗口将显示菜单栏、标签区域和状态栏。
2. 主窗口将显示菜单栏、标签区域和状态栏
3. 使用菜单栏执行各种操作:
- `文件 > 新建`:创建一个带有空白文本区域的新标签。
- `文件 > 打开`:打开现有文本文件进行编辑。
- `文件 > 保存`:将当前活动标签的内容保存到关联文件中。
- `文件 > 另存为`:将当前活动标签的内容保存为新文件。
4. 在每个标签的文本区域中编辑内容。
5. 状态栏将显示有关光标位置和文本统计信息的信息。
- `文件 > 新建`:创建一个带有空白文本区域的新标签。
- `文件 > 打开`:打开现有文本文件进行编辑。
- `文件 > 保存`:将当前活动标签的内容保存到关联文件中。
- `文件 > 另存为`:将当前活动标签的内容保存为新文件。
- `文件 > 重命名`:将当前活动标签的内容重命名。
- `设置 > 自动换行`:打开当前文本自动换行。
- `设置 > 打开配置文件`打开JNotepad的配置文件实现配置快捷键和其他功能。
- `设置 > 窗口置顶`:将程序主仓库置顶。
- `设置 > 语言`:切换语言。
- `插件 > 增加插件`:管理插件系统。
## 依赖项
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>17.0.1</version>
</dependency>
<dependencies>
## 软件运行截图
POM文件中的全部依赖项
| 组ID | 工件ID | 版本 | 功能描述 |
|----------------------------|----------------------------|--------|--------------------------------------------------------------|
| org.kordamp.ikonli | ikonli-javafx | 12.3.1 | 提供JavaFX应用程序中的图标集成。 |
| org.kordamp.ikonli | ikonli-antdesignicons-pack | 12.3.1 | 包含Ant Design图标集的Ikonli图标包。 |
| io.github.mkpaz | atlantafx-base | 2.0.1 | 提供Atlantafx库的基本功能。 |
| org.openjfx | javafx-fxml | 20.0.2 | JavaFX的FXML模块用于构建用户界面。 |
| org.junit.jupiter | junit-jupiter-api | 5.9.2 | 用于JUnit 5测试框架的API。 |
| com.fasterxml.jackson.core | jackson-databind | 2.15.2 | 用于JSON数据的序列化和反序列化。 |
| org.slf4j | slf4j-api | 2.0.7 | 简单日志门面,用于处理日志记录。 |
| ch.qos.logback | logback-core | 1.4.11 | Logback的核心组件用于日志记录。 |
| ch.qos.logback | logback-classic | 1.4.11 | Logback的经典模块提供日志记录功能。 |
| com.ibm.icu | icu4j | 73.2 | ICUInternational Components for Unicode用于处理Unicode字符和文本。 |
## 软件运行截图
- Windows 平台
![Windows](screenshot/windows-1.png)
- MacOS 平台
![MacOS](screenshot/Mac0S-1.png)
### 参与贡献
- MacOS 平台
![MacOS](screenshot/MacOS-1.png)
- Linux 平台
![Linux](screenshot/Linux-1.png)
# 参与贡献
1. Fork 本仓库
1. 加入JCNC社区
1. [加入QQ群:386279455][qq-url]
1. 新建分支
1. 提交代码
1. 新建 Pull Request
2. [阅读JCNC开发者文档][docs-url]
3. 加入QQ群:386279455
4. 联系微信:xuxiaolankaka 加入群聊

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

247
docs/代码规范.md Normal file
View File

@ -0,0 +1,247 @@
# 代码规范
## 1. 排版规则
### 1.1 编码
**规 则IDE的text file encoding设置为UTF-8。**
**规 则IDE中文件的换行符使用Unix格式不要使用windows格式。**
**补 充:**
本项目中`.idea/encodings.xml`已定义src、resource目录为UTF-8编码禁止修改。
### 1.2 页宽
**规 则编辑器列宽应设置为120**
**说 明:**
当一行代码的长度过长时为了阅读方便应该换行展示。但因为每个人的字体设置屏幕大小有所不同因此需要统一列宽为120。
**补 充:**
IDEA默认列宽为120。本项目的.idea/codeStyles目录中应用的即为IDEA默认配置。
### 1.3 换行
**规 则第二行相对第一行缩进4个空格从第三行开始不再缩进而是与第二行保持同级。**
**规 则:运算符、方法调用的点符号与下文一起换行。**
**规 则:在多个参数超长时,逗号后换行。**
**示 例:**
```java
//符合规范
StringBuffer sb=new StringBuffer();
//超过100个字符的时候换行缩进4个空格并且方法钱的点符号一起换行
sb.append("wan").append("fang")...
.append("shu")...
.append("ju")...
.append("gu");
//在逗号后换行
method(arg1,arg2,arg3,...argx,
argx1)
```
### 1.4 缩进
**规 则程序块需要采用缩进风格编写缩进为4个空格。**
**说 明:** 不同的编辑工具会导致Tab字符的宽度不统一在编码时应注意其可能造成的问题。
**补 充:**
本项目的.idea/codeStyles目录中应用的即为IDEA默认配置。
### 1.5 空行
**规则:独立的程序块与变量声明之间加空行分割**
**示 例:**
```java
//不符合规范
if(log.getLevel()<LogConfig.getRecordLevel()){
return;
}
LogWriter writer;
int index;
```
```java
//符合规范
if(log.getLevel()<LogConfig.getRecordLevel()){
return;
}
LogWriter writer;
int index;
```
### 1.6 大括号
**规 则:使用大括号(即使是可选的)**
**说 明:** 大括号与if, else, for, do, while即使只有一条语句或是空也应该把大括号写上。
**示 例:**
```java
//不符合规范的
if(writeToFile)
writeFileThread.interrupt();
```
```java
//符合规范的
if(writeToFile){
writeFileThread.interrupt();
}
```
**规 则对于非空块和块状结构大括号遵循Kernighan和Ritchie风格紧凑风格。**
**说 明:**
* 左大括号前不换行
* 左大括号后换行
* 右大括号前换行
* 如果右大括号是一个语句、函数体或类的终止,则右大括号后换行; 否则不换行。例如如果右大括号后面是else或逗号则不换行
**示 例:**
```java
//不符合规范
if(isOk)
{
someThing();
}
else
{
otherThing();
}
```
```java
//符合规范
if(isOk){
someThing();
}else{
otherThing();
}
```
**补 充:**
Kernighan和Ritchie风格指的是Kernighan和Ritchie的《C Programming
Language》一书中约定的“大括号放在同一行”规则。这种编码风格比较紧凑也是Java官方和Google等诸多主流公司遵循的编码风格。
**建 议:空块可以简洁的写成{},除非它是多块语句的一部分。**
**说 明:** 一个空的块状结构里什么也不包含,大括号可以简洁地写成{},不需要换行。例外:如果它是一个多块语句的一部分(if/else 或
try/catch/finally) ,即使大括号内没内容,右大括号也要换行。
**示 例:**
```java
void doNothing(){}
```
### 1.7 小括号
**规 则:用小括号来限定计算优先级**
**说 明:** 没有理由假设代码的阅读者能够清晰的记住整个Java运算符优先级表。
###2.8 空格
**规 则:函数参数在“,”后需要加空格。**
**规 则:各种双目操作符,比如“=”,“<”等,前后都要加空格。**
**规 则if, while等关键字后面需要有空格。**
**补 充:** 基本上Eclipse的自动格式化功能就能保证这些空格的正确使用。
### 1.8 TODO
**规 则TODO用于任务标记要避免出现无用的TODO标记**
**说 明:** 自动生成代码中如果有TODO标记请根据需要编写注释或删除TODO标记避免产生无用的TODO影响他人跟踪任务。
### 1.9 import
**规 则import不要使用通配符**
**说 明:** 不要出现类似这样的import语句import java.util.*。使用通配符会造成歧义及不可预期的冲突或bug。程序应保持清晰、易懂、无歧义。
**规 则import不要引用不需要的包**
### 1.10 变量
**规 则:每次只声明一个变量**
**说 明:** 不要使用组合声明int a,b;),减少歧义性,增强可读性
**规 则:需要时才声明,并尽快进行初始化**
**规 则使用非C风格的数组声明**
**说 明:** 使用String[] args而非String args[]
**建 议:谨慎的使用公共变量**
**说 明:** 公共变量是增大模块间耦合的原因之一,应减少没必要的公共变量以降低模块间的耦合度。
## 2 命名规范
**规 则:命名应尽可能做到见名知意,力求语义表达清晰完整。**
**规 则:代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。**
**规 则:代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。**
### 2.1 package
**规 则:包名均采用小写**
**建 议:包名一般为一个单词**
**规 则内部包名规则一般为com.部门名称/站点域名.产品名/项目名.模块名通用包放在com.wanfangdata下**
**说 明:** 由于历史遗留原因,部分内部包名格式为:部门名称.产品名/项目名.模块名。原则上,新建的包不要用此命名规则,但暂时也不修改此规则的包名
### 2.2 Class
**规 则:类的命名应该都是名词**
**规 则类名第一个字母要为大写其他每个单词的第一个字母为大写UpperCamelCase**
**规 则:类名要用完整的单词,除非是被公认的单词缩写**
**规 则抽象类命名应以Abstract为开头**
**规 则异常类命名使用Exception结尾**
**规 则测试类命名以它要测试的类名称开始以Test结尾**
**建 议:如果使用了设计模式,建议在类(接口)命名中体现出具体的设计模式**
**建 议枚举类名以Enum后缀结束美剧成员名称需要全部大写单词间用下划线分隔。**
**示 例:**
```java
public class BalanceLimitAccountHandler;
public interface SqlSessionFactory;
public class LoginProxy;
```
### 2.3 Interface
**规 则:接口名的命名应该是名词或形容词**
**规 则接口名应以大写的I开头其他单词第一个字母都要大写UpperCamelCase**
**规 则:接口名要用完整的单词,除非是被公认的单词缩写**
**建 议接口类中的方法和属性不要加任何修饰符好public也不要加。**
### 2.4 方法命名
**规 则:方法名应以动词或惯用短语描述**
**规 则方法名第一个字母都要小写其他每个单词第一个字母都要大写lowerCamelCase**
**建 议:方法名中不要加入对象名字,可能会带来误解。**
**说 明:** 因为对象本身已经包含在调用语句中了
### 2.5 变量
**规 则:应避免用单个字符命名变量**
**规 则:代码中严禁使用拼音与英文混合的方式,优先使用英文而非拼音**
**规 则参数名第一个字母使用小写其他每个单词第一个字母大写lowerCamelCase**
**规 则:常量名应该为名词或名词短语**
**规 则常量名每一个字母都应为大写单词之间用“_”分开。**
**规 则:不允许出现任何魔法值(未经定义的常量)直接出现在代码中。**
**建 议:不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护。**
**规 则long或者Long初始赋值时必须使用大写的L不能是小写的l小写容易跟数字1混淆造成误解。**
**规 则局部变量名第一个字母使用小写其他每个单词第一个字母大写lowerCamelCase。**
**规 则:局部变量名可以更宽松的使用缩写,但除了临时变量和循环变量应避免单字符命名。**
**规 则POJO类中布尔类型的变量都不要加is否则部分框架解析会引起序列化错误。参考阿里java规范**
**说 明:**
定义为基本数据类型Boolean isSuccess的属性它的方法也是isSuccess()RPC框架在反向解析的时候“以为”对应的属性名称是success导致属性获取不到进而抛出异常。
## 注释规范
见目录: .idea/fileTemplates/code
如需增加新的注释模板进入IDEA设置->Editor->File and Code Templates可进行修改。
注意确保schema选择的是project。
![codeTemplate.png](images/codeTemplate.png)

80
docs/开发指南.md Normal file
View File

@ -0,0 +1,80 @@
# 1. 开发流程
基于**AoneFlow**开发流程,具体请阅读:[在阿里,我们如何管理代码分支?](https://developer.aliyun.com/article/573549)
## 1.1 IDEA IDE版
### 步骤一 Fork JCNC/JNotepad或同步JCNC/JNotepad到个人仓库
#### 首次开发进行Fork操作
![输入图片说明](https://foruda.gitee.com/images/1693230738686081312/d1f9178e_341872.png "屏幕截图")
#### 非首次开发,进行同步操作
![输入图片说明](https://foruda.gitee.com/images/1693230711005054075/9d8adb17_341872.png "屏幕截图")
### 步骤二 clone个人仓库或fetch
#### 首次开发clone个人仓库
![输入图片说明](https://foruda.gitee.com/images/1693230809903750175/da0d73b5_341872.png "屏幕截图")
#### 非首次开发执行fetch
![输入图片说明](https://foruda.gitee.com/images/1693231554501661630/308a9783_341872.png "屏幕截图")
### 步骤三 从remote下的master分支创建本地开发分支。
**特殊情况开发依赖release分支已提交内容或是对已提交内容进行修改那么需要从release分支上进行拉取。**
![输入图片说明](https://foruda.gitee.com/images/1693231016998001511/7a6a6f3d_341872.png "屏幕截图")
![输入图片说明](https://foruda.gitee.com/images/1693231347247142683/17ff5fd4_341872.png "屏幕截图")
分支命名规则见1.3节
### 步骤四 推送本地分支到远程
### 步骤五 发起Pull RequestPR
![输入图片说明](https://foruda.gitee.com/images/1693232191273920333/65665291_341872.png "屏幕截图")
**注意目标分支选择预期要发布的release分支**
## 1.2 GIT命令行版本
步骤一、步骤二、步骤五同1.1操作
```shell
# 步骤三
git fetch
git checkout origin/master && git checkout -b feature-demo
# 步骤四
git push origin feature-demo
```
## 1.3 分支命名规则
| issue类别 | 分支名格式 | 示例 |
|------------|------------------|-----------------|
| 功能/优化/文档修改 | feature-issue编号 | feature-I7W9LX |
| bug fix | fix-issue编号 | fix-I7W9LX |
| 代码重构 | refactor-issue编号 | refactor-I7W9LX |
# 2. IDEA插件配置
* 安装Resource Bundle插件
![输入图片说明](https://foruda.gitee.com/images/1693125995274955090/9efa2d4c_341872.png "屏幕截图")
* 安装成功后打开i18n.properties可以看到Resource Bundle tab
![输入图片说明](https://foruda.gitee.com/images/1693126057242554469/10667419_341872.png "屏幕截图")
# Q&A
Q: 本地开发时,主仓库合并了新代码,如何处理?
A: 继续完成本地开发发起PR时再解决冲突。
Q: 解决冲突步骤是什么?
A: 一般按如下步骤。
1. 先同步主仓库
2. 本地仓库进行fetch
3. 本地开发分支merge/pull/rebase更新的release分支

44
libs/README.md Normal file
View File

@ -0,0 +1,44 @@
# 解决jlink error指南
## 现象
jlink时如果出现如下错误参考本文档
```
"automatic module cannot be used with jlink"
```
## 解决方法:
1. 为jar生成module-info.class
```shell
jdeps --ignore-missing-deps --module-path <jar_dir_path> --add-modules <module_name --generate-module-info <out_dir_path> <jar_path>
javac --patch-module <module_name>=<jar_path> <module-info.java>
jar uf <jar_path> -C <module_name> <module-info.class>
```
以本次icu4j为例先将依赖的jar包copy到libs目录然后执行
```shell
jdeps --ignore-missing-deps --module-path libs --add-modules com.ibm.icu --generate-module-info libs/tmpOut libs/icu4j-73.2.jar
javac --patch-module com.ibm.icu=libs/icu4j-73.2.jar libs/tmpOut/com.ibm.icu/module-info.java
jar uf libs/icu4j-73.2.jar -C libs/tmpOut/com.ibm.icu module-info.class
```
2. pom中添加依赖
```xml
<dependency>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>73.2</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/icu4j-73.2.jar</systemPath>
</dependency>
```
## Reference
1. [java_jlink_automatic_module_cannot_be_used_with_jlink](https://tacosteemers.com/articles/java_jlink_automatic_module_cannot_be_used_with_jlink.html)

BIN
libs/commonmark-0.21.0.jar Normal file

Binary file not shown.

BIN
libs/icu4j-73.2.jar Normal file

Binary file not shown.

Binary file not shown.

132
pom.xml
View File

@ -1,25 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.jcnc</groupId>
<artifactId>JNotepad</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0.14-alpha</version>
<name>JNotepad</name>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.9.2</junit.version>
<javafx.version>20.0.2</javafx.version>
</properties>
<dependencies>
<dependency>
<!-- https://mvnrepository.com/artifact/org.fxmisc.richtext/richtextfx -->
<!--JavaFX 的富文本区域-->
<dependency>
<groupId>org.fxmisc.richtext</groupId>
<artifactId>richtextfx</artifactId>
<version>0.11.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/richtextfx-fat-0.11.1.jar</systemPath>
</dependency>
<!-- https://mvnrepository.com/artifact/org.commonmark/commonmark -->
<dependency>
<groupId>org.commonmark</groupId>
<artifactId>commonmark</artifactId>
<version>0.21.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/commonmark-0.21.0.jar</systemPath>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>${javafx.version}</version>
</dependency>
<!--图标库主依赖-->
<dependency>
<groupId>org.kordamp.ikonli</groupId>
<artifactId>ikonli-javafx</artifactId>
<version>12.3.1</version>
</dependency>
<!--蚂蚁图标库-->
<dependency>
<groupId>org.kordamp.ikonli</groupId>
<artifactId>ikonli-antdesignicons-pack</artifactId>
<version>12.3.1</version>
</dependency>
<!--亚特兰大fx主题-->
<dependency>
<groupId>io.github.mkpaz</groupId>
<artifactId>atlantafx-base</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>17.0.1</version>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
@ -27,6 +75,38 @@
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!--Json依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!--log-->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.4.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.11</version>
</dependency>
<!--国际化依赖-->
<dependency>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>73.2</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/icu4j-73.2.jar</systemPath>
</dependency>
</dependencies>
<build>
@ -49,9 +129,8 @@
<!-- Default configuration for running with: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<mainClass>org.jcnc.jnotepad/org.jcnc.jnotepad.LunchApp</mainClass>
<mainClass>org.jcnc.jnotepad/org.jcnc.jnotepad.JnotepadApp</mainClass>
<launcher>JNotepad</launcher>
<jlinkZipName>JNotepad</jlinkZipName>
<jlinkImageName>JNotepad</jlinkImageName>
<noManPages>true</noManPages>
<stripDebug>true</stripDebug>
@ -61,6 +140,41 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.gluonhq</groupId>
<artifactId>gluonfx-maven-plugin</artifactId>
<version>1.0.19</version>
<configuration>
<mainClass>org.jcnc.jnotepad/org.jcnc.jnotepad.JnotepadApp</mainClass>
<reflectionList>
org.jcnc.jnotepad.app.config.UserConfig,org.jcnc.jnotepad.app.config.UserConfig$ShortcutKey
</reflectionList>
<bundlesList>
i18n/i18n
</bundlesList>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

BIN
screenshot/Linux-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

BIN
screenshot/MacOS-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -1,12 +1,53 @@
module org.jcnc.jnotepad {
requires javafx.controls;
// 不知道为什么不加这个日志框架在打包后的程序不起作用会报错
// Exception in thread "JavaFX Application Thread" java.lang.NoClassDefFoundError: javax/naming/NamingException
// 但我打开源代码他们的模块的确有包含这个java.naming这个没懂我干脆自己导入
requires java.naming;
requires atlantafx.base;
requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.annotation;
requires org.slf4j;
requires ch.qos.logback.core;
requires ch.qos.logback.classic;
requires com.ibm.icu;
requires org.kordamp.ikonli.core;
requires org.kordamp.ikonli.javafx;
requires org.kordamp.ikonli.antdesignicons;
requires richtextfx.fat;
requires java.desktop;
requires org.commonmark;
requires javafx.web;
exports org.jcnc.jnotepad;
exports org.jcnc.jnotepad.tool;
exports org.jcnc.jnotepad.Interface;
exports org.jcnc.jnotepad.controller.event.handler;
exports org.jcnc.jnotepad.model.enums;
exports org.jcnc.jnotepad.app.config;
exports org.jcnc.jnotepad.app.i18n;
exports org.jcnc.jnotepad.app.common.constants;
exports org.jcnc.jnotepad.controller.config;
exports org.jcnc.jnotepad.controller.manager;
exports org.jcnc.jnotepad.view.init;
exports org.jcnc.jnotepad.view.manager;
exports org.jcnc.jnotepad.constants;
exports org.jcnc.jnotepad.controller.i18n;
exports org.jcnc.jnotepad.controller.event.handler.toolbar;
exports org.jcnc.jnotepad.controller.event.handler.menuitem;
exports org.jcnc.jnotepad.ui.component.module.interfaces;
opens org.jcnc.jnotepad.app.config;
exports org.jcnc.jnotepad.controller.plugin.interfaces;
exports org.jcnc.jnotepad.ui.views.root.bottom.function;
exports org.jcnc.jnotepad.ui.component.module;
exports org.jcnc.jnotepad.model.entity;
exports org.jcnc.jnotepad.ui.views.root.bottom;
exports org.jcnc.jnotepad.ui.views.root.bottom.status;
exports org.jcnc.jnotepad.api.core.views.sidebar.bottom;
exports org.jcnc.jnotepad.api.core.controller.config;
exports org.jcnc.jnotepad.ui.component.module.base;
exports org.jcnc.jnotepad.ui.component.stage.setting;
exports org.jcnc.jnotepad.ui.component.module.vbox;
exports org.jcnc.jnotepad.ui.component.module.hbox;
exports org.jcnc.jnotepad.ui.component.stage.topmenu.help;
exports org.jcnc.jnotepad.ui.component.stage.topmenu.plugin;
exports org.jcnc.jnotepad.ui.component.module.vbox.components;
exports org.jcnc.jnotepad.ui.views.root.center.main.center.tab;
}

View File

@ -1,43 +0,0 @@
package org.jcnc.jnotepad.Interface;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.TextArea;
import java.io.File;
import java.util.List;
public interface ControllerInterface {
TextArea openAssociatedFileAndCreateTextArea(List<String> rawParameters);
EventHandler<ActionEvent> getLineFeedEventHandler(TextArea textArea);
EventHandler<ActionEvent> getNewFileEventHandler(TextArea textArea);
EventHandler<ActionEvent> getOpenFileEventHandler();
EventHandler<ActionEvent> getSaveFileEventHandler();
EventHandler<ActionEvent> getSaveAsFileEventHandler();
void autoSave(TextArea textArea);
void updateStatusLabel(TextArea textArea);
void openAssociatedFile(String filePath);
void getText(File file);
void upDateEncodingLabel(String text);
int getRow(int caretPosition, String text);
int getColumn(int caretPosition, String text);
void initTabPane();
void saveAsFile();
}

View File

@ -0,0 +1,55 @@
package org.jcnc.jnotepad;
import javafx.application.Application;
import javafx.stage.Stage;
import org.jcnc.jnotepad.app.manager.ApplicationManager;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
/**
* 启动程序类
*
* <p>该类用于启动 JNotepad 记事本应用程序</p>
*
* @author 许轲
*/
public class JnotepadApp extends Application {
private static final ApplicationManager APPLICATION_MANAGER = ApplicationManager.getInstance();
/**
* 应用程序的入口点启动 JavaFX 应用程序
*
* @param args 命令行参数
*/
public static void main(String[] args) {
launch(args);
}
@Override
public void init() {
// 获取当前启动位置a
String currentWorkingDirectory = System.getProperty("user.dir");
LoggerUtil.getLogger(this.getClass()).info("当前启动位置:{}", currentWorkingDirectory);
// 设置参数
APPLICATION_MANAGER.setApplication(this);
}
@Override
public void start(Stage primaryStage) {
APPLICATION_MANAGER.setPrimaryStage(primaryStage);
// 加载应用程序资源
APPLICATION_MANAGER.loadAppResources();
// 加载应用程序缓存
APPLICATION_MANAGER.loadAppCache();
// 初始化应用程序
APPLICATION_MANAGER.initializeApp();
// 初始化默认操作
APPLICATION_MANAGER.executeDefaultAction();
primaryStage.show();
}
@Override
public void stop() {
APPLICATION_MANAGER.operationBeforeStopping();
}
}

View File

@ -1,82 +0,0 @@
package org.jcnc.jnotepad;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import org.jcnc.jnotepad.constants.Constants;
import org.jcnc.jnotepad.controller.manager.Controller;
import org.jcnc.jnotepad.view.manager.ViewManager;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.jcnc.jnotepad.view.init.View.initItem;
import static org.jcnc.jnotepad.view.init.View.initTabPane;
public class LunchApp extends Application {
private static final ExecutorService threadPool = Executors.newCachedThreadPool();
public static boolean isRelevance = true;
Controller controller = new Controller();
@Override
public void start(Stage primaryStage) {
Pane root = new Pane();
double width = Constants.SCREEN_WIDTH;
double length = Constants.SCREEN_LENGTH;
String name = Constants.APP_NAME;
String icon = Constants.APP_ICON;
Scene scene = new Scene(root, width, length);
primaryStage.setTitle(name);
primaryStage.setWidth(width);
primaryStage.setHeight(length);
primaryStage.setScene(scene);
primaryStage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResource(icon)).toString()));
primaryStage.show();
ViewManager viewManager = ViewManager.getInstance(scene);
viewManager.initScreen(scene);
// 初始化菜单项和标签栏
initItem();
initTabPane();
if (isRelevance) {
// 使用线程池加载关联文件并创建文本区域
List<String> rawParameters = getParameters().getRaw();
threadPool.execute(() -> {
TextArea textArea = controller.openAssociatedFileAndCreateTextArea(rawParameters);
Platform.runLater(() -> updateUIWithNewTextArea(textArea));
});
}
}
private void updateUIWithNewTextArea(TextArea textArea) {
Tab tab = new Tab("新建文件 " + (++ViewManager.tabIndex));
tab.setContent(textArea);
ViewManager.tabPane.getTabs().add(tab);
ViewManager.tabPane.getSelectionModel().select(tab);
controller.updateStatusLabel(textArea);
}
@Override
public void stop() {
// 关闭线程池
threadPool.shutdownNow();
}
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,70 @@
package org.jcnc.jnotepad.api.core.component.stage;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
/**
* 抽象窗格舞台
* <p>
* 该类是一个抽象的窗格舞台用于创建自定义的JavaFX窗口
* </p>
*
* @author gewuyou
*/
public abstract class AbstractPaneStage extends BorderPane {
private final Stage stage = new Stage();
/**
* 获取舞台图标
*
* @return 舞台图标
*/
protected abstract Image getStageIcon();
/**
* 获取舞台标题
*
* @return 舞台标题
*/
protected abstract String getStageTitle();
/**
* 获取自定义舞台场景
*
* @return 舞台场景
*/
protected abstract Scene getCustomizationScene();
/**
* 初始化方法
* <p>
* 在此方法中您可以进行与窗口相关的初始化操作
* </p>
*/
protected abstract void initialize();
/**
* 自定义启动方法
*
* @param stage 自定义舞台
*/
public abstract void run(Stage stage);
/**
* 启动方法
* <p>
* 该方法设置窗口的图标标题场景并将窗口设置为模态对话框然后显示窗口
* </p>
*/
public void run() {
stage.getIcons().add(getStageIcon());
stage.setTitle(getStageTitle());
stage.setScene(getCustomizationScene());
// 设置为模态
stage.initModality(Modality.APPLICATION_MODAL);
stage.show();
}
}

View File

@ -0,0 +1,135 @@
package org.jcnc.jnotepad.api.core.controller.config;
import org.jcnc.jnotepad.api.core.controller.interfaces.ConfigController;
import org.jcnc.jnotepad.app.utils.JsonUtil;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.jcnc.jnotepad.app.utils.PopUpUtil;
import org.jcnc.jnotepad.controller.exception.AppException;
import org.slf4j.Logger;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* 抽象基本配置文件控制器类
* <p>
* 该类是基本配置文件控制器的抽象实现提供了加载持久化配置文件以及其他相关方法
* </p>
*
* @param <T> 配置文件类型
* @author gewuyou
*/
public abstract class BaseConfigController<T> implements ConfigController<T> {
protected static final String ROOT_CONFIG_DIR = "config";
protected static final String SYSTEM_CONFIG_DIR = "system";
private final Logger logger = LoggerUtil.getLogger(getClass());
protected T config;
/**
* 获取配置文件Class类
*
* @return 配置文件Class类
*/
protected abstract Class<T> getConfigClass();
/**
* 获取配置文件名称
*
* @return 配置文件名称
*/
protected abstract String getConfigName();
/**
* 获取配置文件文件夹路径
*
* @return 配置文件夹路径
*/
protected abstract String getConfigDir();
/**
* 获取配置文件对象
*
* @return 配置文件对象
*/
public T getConfig() {
return config;
}
/**
* 加载配置文件内容
*/
@Override
public void loadConfig() {
createConfigIfNotExists();
// 存在则加载
try {
logger.info("正在加载配置文件: {}...", getConfigClass());
String configContent = Files.readString(getConfigPath());
config = JsonUtil.OBJECT_MAPPER.readValue(configContent, getConfigClass());
} catch (IOException e) {
logger.error("加载配置文件错误", e);
PopUpUtil.errorAlert("错误", "读写错误", "加载配置文件错误!", null, null);
throw new AppException(e);
}
}
/**
* 配置文件持久化
*/
@Override
public void writeConfig() {
createConfigIfNotExists();
writeConfig(getConfig());
}
/**
* 配置文件持久化
*
* @param config 配置文件对象
*/
@Override
public void writeConfig(T config) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(getConfigPath().toString()))) {
if (config == null) {
config = generateDefaultConfig();
}
writer.write(JsonUtil.toJsonString(config));
} catch (Exception e) {
logger.error("", e);
PopUpUtil.errorAlert("错误", "读写错误", "配置文件读写错误!", null, null);
}
}
/**
* 如果配置文件不存在则创建
*/
@Override
public void createConfigIfNotExists() {
Path configPath = getConfigPath();
if (configPath.toFile().exists()) {
return;
}
File directory = new File(getConfigDir());
if (!directory.exists()) {
directory.mkdirs();
}
writeConfig(null);
}
/**
* 获取配置文件路径
*
* @return 配置文件路径
*/
@Override
public Path getConfigPath() {
return Paths.get(getConfigDir(), getConfigName());
}
}

View File

@ -0,0 +1,69 @@
package org.jcnc.jnotepad.api.core.controller.interfaces;
import java.nio.file.Path;
/**
* 配置文件控制器接口
* <p>
* 该接口定义了配置文件相关的操作包括加载持久化创建和获取配置文件路径等
* </p>
*
* @param <T> 配置文件类型
* @author gewuyou
*/
public interface ConfigController<T> {
/**
* 加载配置文件内容
* <p>
* 从配置文件中加载配置信息
* </p>
*/
void loadConfig();
/**
* 配置文件持久化
* <p>
* 将配置信息持久化到配置文件中
* </p>
*/
void writeConfig();
/**
* 配置文件持久化
* <p>
* 将指定的配置对象持久化到配置文件中
* </p>
*
* @param config 配置文件对象
*/
void writeConfig(T config);
/**
* 如果配置文件不存在则创建
* <p>
* 在需要的情况下创建配置文件如果配置文件已存在则不执行任何操作
* </p>
*/
void createConfigIfNotExists();
/**
* 创建配置文件实体
* <p>
* 生成默认的配置文件实体对象用于后续的序列化操作
* </p>
*
* @return 默认的配置文件实体
* @apiNote 返回默认的配置文件实体用于序列化 JSON 数据
*/
T generateDefaultConfig();
/**
* 获取配置文件路径
* <p>
* 返回配置文件的路径
* </p>
*
* @return 配置文件路径
*/
Path getConfigPath();
}

View File

@ -0,0 +1,118 @@
package org.jcnc.jnotepad.api.core.manager;
import org.jcnc.jnotepad.model.entity.Cache;
import java.util.Map;
/**
* 抽象缓存管理类
*
* <p>
* 该类是缓存管理的抽象基类用于管理不同类型的缓存
* </p>
*
* @author gewuyou
*/
public abstract class AbstractCacheManager {
/**
* 缓存集合
*/
protected Map<String, Cache> caches;
/**
* 获取全局命名空间
*
* @return 全局命名空间
*/
public abstract String getGlobalNamespace();
/**
* 创建缓存类
*
* @param group 缓存组
* @param name 缓存名称
* @param cacheData 缓存数据
* @param expirationTime 过期时间
* @return 缓存类
* @apiNote 这个方法只需通过自定义的缓存管理类调用此方法无需每次指定相同地命名空间
*/
public Cache createCache(String group, String name, Object cacheData, Long expirationTime) {
return new Cache(getGlobalNamespace(), group, name, cacheData, expirationTime);
}
/**
* 获取缓存集合
*
* @return 缓存集合
*/
public Map<String, Cache> getCaches() {
return caches;
}
/**
* 设置缓存集合
*
* @param caches 缓存集合
*/
public void setCaches(Map<String, Cache> caches) {
this.caches = caches;
}
/**
* 添加缓存
*
* @param cache 缓存
*/
public void addCache(Cache cache) {
String cacheKey = cache.getCacheKey();
// 如果集合中已存在该缓存则更新读写时间
if (caches.containsKey(cacheKey)) {
cache.setLastReadOrWriteTime(System.currentTimeMillis());
}
caches.put(cacheKey, cache);
}
/**
* 获取缓存类
*
* @param cacheKey 缓存key
* @return 缓存类
*/
public Cache getCache(String cacheKey) {
if (caches == null || caches.isEmpty()) {
return null;
}
if (caches.containsKey(cacheKey)) {
Cache cache = caches.get(cacheKey);
cache.setLastReadOrWriteTime(System.currentTimeMillis());
return cache;
}
return null;
}
/**
* 获取缓存类
*
* @param group
* @param name 缓存名
* @return 缓存类
*/
public Cache getCache(String group, String name) {
return getCache(Cache.getCacheKey(getGlobalNamespace(), group, name));
}
/**
* 获取缓存数据
*
* @param group
* @param name 缓存名
* @return 缓存类
*/
public Object getCacheData(String group, String name) {
Cache cache = getCache(group, name);
if (cache == null) {
return null;
}
return cache.getCacheData();
}
}

View File

@ -0,0 +1,22 @@
package org.jcnc.jnotepad.api.core.views.manager;
import java.util.List;
/**
* 抽象管理类
*
* @author gewuyou
*/
public abstract class AbstractManager<T> {
/**
* 获取节点列表
*
* @return 节点列表
*/
public abstract List<T> getNodeList();
public void registerNode(T node) {
getNodeList().add(node);
}
}

View File

@ -0,0 +1,46 @@
package org.jcnc.jnotepad.api.core.views.manager.builder;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.Optional;
/**
* 下方状态栏按钮建造者
*
* @author gewuyou
*/
public class BottomStatusBoxButtonBuilder {
private Button button;
private FontIcon fontIcon;
private EventHandler<ActionEvent> eventHandler;
public BottomStatusBoxButtonBuilder() {
}
public BottomStatusBoxButtonBuilder(Button button) {
this.button = button;
}
public BottomStatusBoxButtonBuilder setFontIcon(FontIcon fontIcon) {
this.fontIcon = fontIcon;
return this;
}
public BottomStatusBoxButtonBuilder setEventHandler(EventHandler<ActionEvent> eventHandler) {
this.eventHandler = eventHandler;
return this;
}
public Button build() {
Optional<Button> container = Optional.ofNullable(button);
button = container.orElseGet(Button::new);
button.setGraphic(fontIcon);
button.setOnAction(eventHandler);
return button;
}
}

View File

@ -0,0 +1,74 @@
package org.jcnc.jnotepad.api.core.views.manager.builder;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import java.util.Optional;
/**
* 侧边栏按钮建造者
*
* @author gewuyou
*/
public class SideBarButtonBuilder {
private Button button;
private ImageView imageView;
private EventHandler<ActionEvent> eventHandler;
public Button build() {
Optional<Button> container = Optional.ofNullable(button);
button = container.orElseGet(Button::new);
button.setGraphic(imageView);
button.setOnAction(eventHandler);
return button;
}
public SideBarButtonBuilder setImageView(ImageView imageView) {
this.imageView = imageView;
return this;
}
public SideBarButtonBuilder setButtonEssentialAttribute(Double relativelyPrefWidth, Double relativelyPrefHeight) {
Optional<Double> container = Optional.ofNullable(relativelyPrefHeight);
button.setPrefWidth(imageView.getFitWidth() + container.orElse(20D));
container = Optional.ofNullable(relativelyPrefWidth);
button.setPrefHeight(imageView.getFitHeight() + container.orElse(20D));
return this;
}
/**
* 设置ImageView属性
*
* @param fitWidth 适合宽度
* @param fitHeight 适合高度
* @param preserveRatio 保持比例
* @param scaleX X轴比例
* @param scaleY Y轴比例
* @return 建造者对象
*/
public SideBarButtonBuilder setImageViewEssentialAttribute(Double fitWidth, Double fitHeight, boolean preserveRatio, Double scaleX, Double scaleY) {
Optional<Double> container = Optional.ofNullable(fitWidth);
imageView.setFitWidth(container.orElse(10D));
container = Optional.ofNullable(fitHeight);
imageView.setFitHeight(container.orElse(10D));
imageView.setPreserveRatio(preserveRatio);
container = Optional.ofNullable(scaleX);
imageView.setScaleX(container.orElse(2.5));
container = Optional.ofNullable(scaleY);
imageView.setScaleY(container.orElse(2.5));
return this;
}
public SideBarButtonBuilder setEventHandler(EventHandler<ActionEvent> eventHandler) {
this.eventHandler = eventHandler;
return this;
}
public SideBarButtonBuilder setButton(Button button) {
this.button = button;
return this;
}
}

View File

@ -0,0 +1,50 @@
package org.jcnc.jnotepad.api.core.views.menu;
import javafx.collections.ObservableList;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import org.jcnc.jnotepad.app.i18n.UiResourceBundle;
import org.jcnc.jnotepad.ui.views.root.top.menubar.TopMenuBar;
/**
* 抽象基础菜单类
*
* <p>
* 此抽象类用于创建基础菜单包括菜单项的注册和初始化
* </p>
*
* @author gewuyou
*/
public abstract class AbstractBaseMenu extends AbstractMenu<Menu> {
protected final TopMenuBar topMenuBar = TopMenuBar.getInstance();
/**
* 获取菜单名称
*
* @return 菜单名称
*/
public abstract String getMenuName();
/**
* 获取菜单项
*
* @return 菜单项集合
*/
@Override
protected ObservableList<MenuItem> getItems() {
return getMenu().getItems();
}
/**
* 初始化菜单栏
*/
@Override
public void initMenu() {
registerMenu();
Menu menu = getMenu();
// 菜单名称国际化
UiResourceBundle.bindStringProperty(menu.textProperty(), getMenuName());
// 初始化菜单项
initMenuItems(getMenuItems());
}
}

View File

@ -0,0 +1,119 @@
package org.jcnc.jnotepad.api.core.views.menu;
import javafx.beans.value.ChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.MenuItem;
import javafx.scene.control.RadioMenuItem;
import org.jcnc.jnotepad.app.i18n.UiResourceBundle;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.jcnc.jnotepad.controller.config.UserConfigController;
import org.slf4j.Logger;
import java.util.HashMap;
import java.util.Map;
/**
* 抽象菜单类
*
* @author gewuyou
*/
public abstract class AbstractMenu<T> {
protected Logger logger = LoggerUtil.getLogger(this.getClass());
UserConfigController userConfigController = UserConfigController.getInstance();
/**
* 获取菜单
*
* @return 菜单
*/
public abstract T getMenu();
/**
* 获取菜单项集合
*
* @return 菜单项集合
*/
public abstract Map<String, MenuItem> getMenuItems();
/**
* 注册菜单
*/
protected abstract void registerMenu();
/**
* 初始化菜单
*/
protected abstract void initMenu();
/**
* 获取菜单项
*
* @return 菜单项集合
*/
protected abstract ObservableList<MenuItem> getItems();
/**
* 注册菜单项
*
* @param menuItem 菜单项
* @param menuItemName 菜单项名称
* @param userData 用户数据用来存放必要的数据比如按钮菜单项名称
* @param eventHandler 事件处理器
*/
public void registerMenuItem(MenuItem menuItem, String menuItemName, Object userData, EventHandler<ActionEvent> eventHandler) {
getMenuItems().put(menuItemName, menuItem);
menuItem.setUserData(userData);
menuItem.setOnAction(eventHandler);
}
/**
* 注册检查菜单项
*
* @param checkMenuItem 检查菜单项
* @param menuItemName 菜单项名称
* @param userData 用户数据用来存放必要的数据比如按钮菜单项名称
* @param listener 监听器
*/
public void registerMenuItem(CheckMenuItem checkMenuItem, String menuItemName, Object userData, ChangeListener<Boolean> listener) {
getMenuItems().put(menuItemName, checkMenuItem);
checkMenuItem.setUserData(userData);
checkMenuItem.selectedProperty().addListener(listener);
}
/**
* 注册单选菜单项
*
* @param radioMenuItem 单选菜单项
* @param menuItemName 菜单项名称
* @param userData 用户数据用来存放必要的数据
* @param eventHandler 事件处理器
*/
public void registerRadioMenuItem(Map<String, RadioMenuItem> radioMenuItems, RadioMenuItem radioMenuItem, String menuItemName, Object userData, EventHandler<ActionEvent> eventHandler) {
radioMenuItems.put(menuItemName, radioMenuItem);
radioMenuItem.setUserData(userData);
radioMenuItem.setOnAction(eventHandler);
}
/**
* 初始化菜单项
*
* @param menuItems 菜单项集合
*/
protected void initMenuItems(Map<String, MenuItem> menuItems) {
logger.info("初始化菜单项!");
Map<String, MenuItem> menuItemMap = new HashMap<>(16);
menuItems.forEach((key, value) -> {
UiResourceBundle.bindStringProperty(value.textProperty(), key);
menuItemMap.put((String) value.getUserData(), value);
getItems().add(value);
});
userConfigController.getMenuItems().add(menuItemMap);
userConfigController.initShortcutKeys(menuItemMap);
}
}

View File

@ -0,0 +1,230 @@
package org.jcnc.jnotepad.api.core.views.menu.builder;
import javafx.beans.property.BooleanProperty;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.*;
import org.jcnc.jnotepad.app.i18n.UiResourceBundle;
import org.jcnc.jnotepad.controller.config.UserConfigController;
import java.util.HashMap;
import java.util.Map;
/**
* 抽象菜单建造者类
*
* @author gewuyou
*/
public abstract class AbstractMenuBuilder<B, T> {
/**
* 上下文菜单项
*/
protected final Map<String, MenuItem> menuItems = new HashMap<>();
/**
* Get subclass builder
*
* @return builder
*/
protected abstract B getBuilder();
/**
* 获取菜单
*
* @return 菜单
*/
protected abstract T getMenu();
/**
* Retrieves the items of the menu.
*
* @return an ObservableList of MenuItems
*/
protected abstract ObservableList<MenuItem> getItems();
/**
* 添加菜单项
*
* @param label 菜单项名称
* @param eventHandler 事件
* @return 建造者
*/
public B addMenuItem(String label, EventHandler<ActionEvent> eventHandler) {
MenuItem menuItem = new MenuItem(label);
menuItem.setOnAction(eventHandler);
menuItems.put(label, menuItem);
getItems().add(menuItem);
return getBuilder();
}
/**
* 添加菜单项
*
* @param label 菜单项名称
* @param eventHandler 事件
* @param visible 是否可见
* @return 建造者
*/
public B addMenuItem(String label, EventHandler<ActionEvent> eventHandler, BooleanProperty visible) {
MenuItem menuItem = new MenuItem(label);
menuItem.setOnAction(eventHandler);
menuItem.setVisible(visible.get());
visible.addListener((observable, oldValue, newValue) -> menuItem.setVisible(Boolean.TRUE.equals(newValue)));
menuItems.put(label, menuItem);
getItems().add(menuItem);
return getBuilder();
}
/**
* 添加菜单项
*
* @param label 菜单项名称
* @param eventHandler 事件
* @param visible 是否可见
* @return 建造者
*/
public B addMenuItem(String label, EventHandler<ActionEvent> eventHandler, boolean visible) {
MenuItem menuItem = new MenuItem(label);
menuItem.setOnAction(eventHandler);
menuItem.setVisible(visible);
menuItems.put(label, menuItem);
getItems().add(menuItem);
return getBuilder();
}
/**
* 添加单选菜单项
*
* @param label 菜单项名称
* @param eventHandler 事件
* @return 建造者
*/
public B addRadioMenuItem(String label, EventHandler<ActionEvent> eventHandler) {
RadioMenuItem menuItem = new RadioMenuItem(label);
menuItem.setOnAction(eventHandler);
menuItems.put(label, menuItem);
getItems().add(menuItem);
return getBuilder();
}
/**
* 添加复选菜单项
*
* @param label 菜单项名称
* @param eventHandler 事件
* @return 建造者
*/
public B addCheckMenuItem(String label, EventHandler<ActionEvent> eventHandler) {
CheckMenuItem menuItem = new CheckMenuItem(label);
menuItem.setOnAction(eventHandler);
menuItems.put(label, menuItem);
getItems().add(menuItem);
return getBuilder();
}
public B addCheckMenuItem(CheckMenuItem checkMenuItem, EventHandler<ActionEvent> eventHandler) {
checkMenuItem.setOnAction(eventHandler);
menuItems.put(checkMenuItem.getText(), checkMenuItem);
getItems().add(checkMenuItem);
return getBuilder();
}
/**
* 添加菜单
*
* @param menu 菜单
* @return 建造者
*/
public B addMenu(Menu menu) {
menuItems.put(menu.getText(), menu);
getItems().add(menu);
return getBuilder();
}
/**
* 添加菜单
*
* @param menu 菜单
* @param visible 是否隐藏
* @return 建造者
*/
public B addMenu(Menu menu, BooleanProperty visible) {
menu.setVisible(visible.get());
visible.addListener((observable, oldValue, newValue) -> menu.setVisible(Boolean.TRUE.equals(newValue)));
menuItems.put(menu.getText(), menu);
getItems().add(menu);
return getBuilder();
}
/**
* 添加菜单
*
* @param menu 菜单
* @param visible 是否隐藏
* @return 建造者
*/
public B addMenu(Menu menu, boolean visible) {
menu.setVisible(visible);
menuItems.put(menu.getText(), menu);
getItems().add(menu);
return getBuilder();
}
/**
* 添加分割线
*
* @return 建造者
*/
public B addSeparatorMenuItem() {
getItems().add(new SeparatorMenuItem());
return getBuilder();
}
/**
* 添加分割线
*
* @param visible 是否可见
* @return 建造者
*/
public B addSeparatorMenuItem(BooleanProperty visible) {
SeparatorMenuItem separatorMenuItem = new SeparatorMenuItem();
separatorMenuItem.setVisible(visible.get());
visible.addListener((observable, oldValue, newValue) -> separatorMenuItem.setVisible(Boolean.TRUE.equals(newValue)));
getItems().add(separatorMenuItem);
return getBuilder();
}
/**
* 添加分割线
*
* @param visible 是否可见
* @return 建造者
*/
public B addSeparatorMenuItem(boolean visible) {
SeparatorMenuItem separatorMenuItem = new SeparatorMenuItem();
separatorMenuItem.setVisible(visible);
getItems().add(separatorMenuItem);
return getBuilder();
}
/**
* Build menu
*
* @return menu
*/
public T build() {
UserConfigController userConfigController = UserConfigController.getInstance();
Map<String, MenuItem> menuItemMap = new HashMap<>(16);
menuItems.forEach((key, value) -> {
UiResourceBundle.bindStringProperty(value.textProperty(), key);
menuItemMap.put((String) value.getUserData(), value);
});
userConfigController.getMenuItems().add(menuItemMap);
userConfigController.initShortcutKeys(menuItemMap);
return getMenu();
}
}

View File

@ -0,0 +1,53 @@
package org.jcnc.jnotepad.api.core.views.menu.builder;
import javafx.collections.ObservableList;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
/**
* 上下文菜单建造者类
*
* <p>
* 此类用于构建上下文菜单对象可以添加菜单项单选菜单项复选菜单项以及分割线等
* </p>
*
* @author gewuyou
*/
public class ContextMenuBuilder extends AbstractMenuBuilder<ContextMenuBuilder, ContextMenu> {
private final ContextMenu contextMenu;
public ContextMenuBuilder() {
contextMenu = new ContextMenu();
}
/**
* 获取子类的建造者实例
*
* @return 建造者实例
*/
@Override
protected ContextMenuBuilder getBuilder() {
return this;
}
/**
* 获取菜单
*
* @return 菜单
*/
@Override
protected ContextMenu getMenu() {
return contextMenu;
}
/**
* 获取上下文菜单的菜单项列表
*
* @return 菜单项列表
*/
@Override
protected ObservableList<MenuItem> getItems() {
return contextMenu.getItems();
}
}

View File

@ -0,0 +1,60 @@
package org.jcnc.jnotepad.api.core.views.menu.builder;
import javafx.collections.ObservableList;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
/**
* 菜单建造者类
*
* <p>
* 此类用于构建菜单对象可以添加菜单项单选菜单项复选菜单项以及分割线等
* </p>
*
* @author gewuyou
*/
public class MenuBuilder extends AbstractMenuBuilder<MenuBuilder, Menu> {
private final Menu menu;
/**
* 构造菜单建造者
*
* @param label 菜单的标签
*/
public MenuBuilder(String label) {
menu = new Menu(label);
}
/**
* 获取子类的建造者实例
*
* @return 建造者实例
*/
@Override
protected MenuBuilder getBuilder() {
return this;
}
/**
* 获取菜单
*
* @return 菜单
*/
@Override
protected Menu getMenu() {
return menu;
}
/**
* 获取菜单的菜单项列表
*
* @return 菜单项列表
*/
@Override
protected ObservableList<MenuItem> getItems() {
return menu.getItems();
}
}

View File

@ -0,0 +1,48 @@
package org.jcnc.jnotepad.api.core.views.sidebar.bottom;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import org.jcnc.jnotepad.ui.views.root.bottom.function.FunctionBox;
/**
* 子功能栏抽象类
*
* <p>
* 此抽象类用于构建一个基本的子功能栏包括功能按钮的初始化和添加到功能栏中
* </p>
*
* @author gewuyou
*/
public abstract class AbstractFunctionChildrenBox {
protected final FunctionBox functionBox;
protected final MenuBar menuBar;
protected final Label label = new Label(getFunctionName());
protected Menu menu = new Menu();
/**
* 构造子功能栏抽象类
*/
protected AbstractFunctionChildrenBox() {
functionBox = FunctionBox.getInstance();
menuBar = FunctionBox.getMenuBar();
}
/**
* 初始化子功能栏包括将功能按钮添加到菜单栏中
*/
public void initialize() {
menu.setGraphic(label);
menuBar.getMenus().add(menu);
}
/**
* 获取功能按钮名称
*
* @return 功能按钮名称
*/
protected abstract String getFunctionName();
}

View File

@ -0,0 +1 @@
app 目录存放应用程序配置、应用程序缓存、通用常量和国际化文件。

View File

@ -0,0 +1 @@
common 目录存放应用程序的通用组卷

View File

@ -0,0 +1,69 @@
package org.jcnc.jnotepad.app.common.constants;
import org.jcnc.jnotepad.app.i18n.UiResourceBundle;
import java.util.regex.Pattern;
/**
* 应用常量类用于存放应用程序中的常量值
*
* <p>
* 该类包含了应用程序的一些常用常量如版本号作者软件名称初始宽度和高度Logo地址等
* </p>
*
* <p>
* 还包括了与标签页相关的正则表达式模式以及默认的属性和程序文件目录等常量
* </p>
*
* @author luke
*/
public class AppConstants {
/**
* 版本号
*/
public static final String VERSION = "1.0.14-alpha";
/**
* 作者
*/
public static final String AUTHOR = "JCNC";
/**
* 软件名称
*/
public static final String APP_NAME = "JNotepad";
/**
* 初始宽度
*/
public static final double SCREEN_WIDTH = 1050;
/**
* 初始高度
*/
public static final double SCREEN_LENGTH = 750;
/**
* logo地址
*/
public static final String APP_ICON = "/jcnc/app/images/appIcons/icon.png";
/**
* 默认标签页的正则
*
* <p>
* 用于匹配默认标签页名称的正则表达式格式为"New File"后跟数字
* </p>
*/
public static final Pattern TABNAME_PATTERN = Pattern.compile("^" + Pattern.quote(UiResourceBundle.getContent(TextConstants.NEW_FILE)) + "\\d+$");
/**
* 默认属性
*/
public static final String DEFAULT_PROPERTY = "user.home";
/**
* 程序文件目录
*/
public static final String PROGRAM_FILE_DIRECTORY = ".jnotepad";
/**
* 私有构造函数防止该类被实例化
*/
private AppConstants() {
}
}

View File

@ -0,0 +1,32 @@
package org.jcnc.jnotepad.app.common.constants;
/**
* SplitPane常量类
*
* <p>用于记录SplitPane中子组件的索引</p>
*
* @author cccqyu
*/
public class SplitPaneItemConstants {
/**
* rootSplitPane中的上部分隔栏索引
*/
public static final int ROOT_SPLIT_PANE_TOP_SPLIT_PANE = 0;
/**
* rootSplitPane中的底部指令框索引
*/
public static final int ROOT_SPLIT_PANE_CMD_BOX = 1;
/**
* rootSplitPane中的上部面板的左侧索引
*/
public static final int TOP_SPLIT_PANE_DIRECTORY_SIDEBAR_PANE = 0;
/**
* rootSplitPane中的上部面板的右侧索引
*/
public static final int TOP_SPLIT_PANE_CENTER_TAB_PANE = 1;
}

View File

@ -0,0 +1,264 @@
package org.jcnc.jnotepad.app.common.constants;
/**
* 文本常量类包含多处使用的文本常量
*
* <p>如果只有一个类使用常量请在该类中使用 <code>private static final</code> 声明</p>
*
* @author gewuyou
*/
public class TextConstants {
/**
* 标题文本常量
*/
public static final String TITLE = "title";
/**
* 保存文本常量
*/
public static final String SAVE = "SAVE";
/**
* 文件文本常量
*/
public static final String FILE = "FILE";
/**
* 文件夹
*/
public static final String FOLDER = "FOLDER";
/**
* 构建文本常量
*/
public static final String BUILD = "BUILD";
/**
* 终端文本常量
*/
public static final String TERMINAL = "TERMINAL";
/**
* 运行文本常量
*/
public static final String RUN = "RUN";
/**
* 调试文本常量
*/
public static final String DE_BUG = "DE_BUG";
/**
* 新建文本常量
*/
public static final String NEW = "NEW";
/**
* 打开文本常量
*/
public static final String OPEN = "OPEN";
/**
* 打开目录文本常量
*/
public static final String OPEN_DIRECTORY = "OPEN_DIRECTORY";
/**
* 另存为文本常量
*/
public static final String SAVE_AS = "SAVE_AS";
/**
* 重命名文本常量
*/
public static final String RENAME = "RENAME";
/**
* 设置文本常量
*/
public static final String SET = "SET";
/**
* 帮助文本常量
*/
public static final String HELP = "HELP";
/**
* 自动换行文本常量
*/
public static final String WORD_WRAP = "WORD_WRAP";
/**
* 插件文本常量
*/
public static final String PLUGIN = "PLUGIN";
/**
* 管理插件文本常量
*/
public static final String MANAGER_PLUGIN = "MANAGER_PLUGIN";
/**
* 关于文本常量
*/
public static final String ABOUT = "ABOUT";
/**
* 开发者文本常量
*/
public static final String DEVELOPER = "DEVELOPER";
/**
* 统计文本常量
*/
public static final String STATISTICS = "STATISTICS";
/**
* 打开配置文件文本常量
*/
public static final String OPEN_CONFIGURATION_FILE = "OPEN_CONFIGURATION_FILE";
/**
* 顶部文本常量
*/
public static final String TOP = "TOP";
/**
* 语言文本常量
*/
public static final String LANGUAGE = "LANGUAGE";
/**
* 中文文本常量
*/
public static final String UPPER_CHINESE = "CHINESE";
/**
* 英文文本常量
*/
public static final String UPPER_ENGLISH = "ENGLISH";
/**
* 新建文件文本常量
*/
public static final String NEW_FILE = "NEW_FILE";
/**
* 新建文件夹
*/
public static final String NEW_DIRECTORY = "NEW_DIRECTORY";
/**
* 删除
*/
public static final String DELETE = "DELETE";
/**
* 行文本常量
*/
public static final String ROW = "ROW";
/**
* 列文本常量
*/
public static final String COLUMN = "COLUMN";
/**
* 字数统计文本常量
*/
public static final String WORD_COUNT = "WORD_COUNT";
/**
* 编码文本常量
*/
public static final String ENCODE = "ENCODE";
/**
* 英文小写文本常量
*/
public static final String ENGLISH = "english";
/**
* 中文小写文本常量
*/
public static final String CHINESE = "chinese";
/**
* 关闭
*/
public static final String CLOSE = "CLOSE";
/**
* 关闭其他标签页
*/
public static final String CLOSE_OTHER_TABS = "CLOSE_OTHER_TABS";
/**
* 关闭所有标签页
*/
public static final String CLOSE_ALL_TABS = "CLOSE_ALL_TABS";
/**
* 关闭左侧标签
*/
public static final String CLOSE_LEFT_TABS = "CLOSE_LEFT_TABS";
/**
* 关闭右侧标签
*/
public static final String CLOSE_RIGHT_TABS = "CLOSE_RIGHT_TABS";
/**
* 复制
*/
public static final String COPY = "COPY";
/**
* 粘贴
*/
public static final String PASTE = "PASTE";
/**
* 剪切
*/
public static final String SHEAR = "SHEAR";
/**
* 文件名
*/
public static final String FILE_NAME = "FILE_NAME";
/**
* 文件路径
*/
public static final String FILE_PATH = "FILE_PATH";
/**
* 所在文件夹
*/
public static final String FOLDER_PATH = "FOLDER_PATH";
/**
* 固定标签页
*/
public static final String FIXED_TAB = "FIXED_TAB";
/**
* 只读
*/
public static final String READ_ONLY = "READ_ONLY";
public static final String SEPARATOR = "separator";
/**
* 打开于
*/
public static final String OPEN_ON = "OPEN_ON";
/**
* 资源管理器
*/
public static final String EXPLORER = "EXPLORER";
private TextConstants() {
}
}

View File

@ -0,0 +1,39 @@
package org.jcnc.jnotepad.app.common.manager;
import org.jcnc.jnotepad.api.core.manager.AbstractCacheManager;
/**
* 应用程序缓存管理类
* <p>
* 该类用于管理应用程序中的缓存数据
* </p>
*
* @author gewuyou
*/
public class ApplicationCacheManager extends AbstractCacheManager {
private static final ApplicationCacheManager INSTANCE = new ApplicationCacheManager();
private ApplicationCacheManager() {
}
/**
* 获取 ApplicationCacheManager 的实例
*
* @return ApplicationCacheManager 实例
*/
public static ApplicationCacheManager getInstance() {
return INSTANCE;
}
/**
* 获取全局命名空间
*
* @return 全局命名空间字符串
*/
@Override
public String getGlobalNamespace() {
return "jcnc";
}
}

View File

@ -0,0 +1,91 @@
package org.jcnc.jnotepad.app.common.manager;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.slf4j.Logger;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 线程池管理类
* <p>
* 该类用于管理应用程序中的线程池提供了异步操作的执行环境
* </p>
*
* @author gewuyou
*/
public class ThreadPoolManager {
private static final Logger logger = LoggerUtil.getLogger(ThreadPoolManager.class);
/**
* 核心线程数
*/
private static final int CORE_POOL_SIZE = 2;
/**
* 最大线程数
*/
private static final int MAXIMUM_POOL_SIZE = 4;
/**
* 空闲线程回收时间间隔
*/
private static final Long KEEP_ALIVE_TIME = 3L;
/**
* 空闲线程回收时间间隔单位
*/
private static final TimeUnit TIME_UNIT = TimeUnit.SECONDS;
/**
* 提交任务的队列<br>
* <br>
* ArrayBlockingQueue有界带缓冲阻塞队列<br>
* SynchronousQueue无缓冲阻塞队列<br>
* LinkedBlockingQueue无界带缓冲阻塞队列
*/
private static final BlockingQueue<Runnable> BLOCKING_QUEUE = new LinkedBlockingQueue<>(4);
/**
* 当前运行线程数
*/
private static final AtomicInteger THREAD_COUNT = new AtomicInteger(1);
/**
* 线程工厂
*/
private static final ThreadFactory THREAD_FACTORY = r -> {
Thread thread = new Thread(r);
thread.setName("JNotepad-ThreadPool-Thread-" + THREAD_COUNT.getAndIncrement());
thread.setDaemon(true);
// 设置异常处理器
thread.setUncaughtExceptionHandler((t, e) -> logger.error("线程执行异常!", e));
return thread;
};
/**
* 拒绝处理任务时的策略
*/
private static final RejectedExecutionHandler HANDLER = new ThreadPoolExecutor.AbortPolicy();
/**
* 线程池
*/
private static final ThreadPoolExecutor THREAD_POOL = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, TIME_UNIT, BLOCKING_QUEUE, THREAD_FACTORY, HANDLER);
private ThreadPoolManager() {
}
/**
* 当前运行线程数自减
* <p>
* 当你创建任务时务必在最后执行一次该方法
* </p>
*
* @see ThreadPoolManager
*/
public static void threadContSelfSubtracting() {
THREAD_COUNT.decrementAndGet();
}
/**
* 获取线程池实例
*
* @return 线程池实例
*/
public static ExecutorService getThreadPool() {
return THREAD_POOL;
}
}

View File

@ -0,0 +1,108 @@
package org.jcnc.jnotepad.app.config;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.io.File;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import static org.jcnc.jnotepad.app.common.constants.AppConstants.DEFAULT_PROPERTY;
import static org.jcnc.jnotepad.app.common.constants.AppConstants.PROGRAM_FILE_DIRECTORY;
/**
* 应用程序配置文件
*
* <p>
* 此类用于存储应用程序的配置信息包括程序根路径排除的文件夹和文件等
* </p>
*
* @author gewuyou
*/
public class AppConfig {
/**
* 排除的文件夹
*/
@JsonIgnore
private final Set<File> ignoreFolder;
/**
* 排除的文件
*/
@JsonIgnore
private final Set<File> ignoreFile;
/**
* 程序根路径
*/
private String rootPath;
/**
* 上次的程序根路径
*/
@JsonIgnore
private String lastRootPath;
/**
* 构造应用程序配置对象
*/
public AppConfig() {
ignoreFolder = Set.of(
new File(Paths.get(System.getProperty(DEFAULT_PROPERTY), PROGRAM_FILE_DIRECTORY, "system").toString()),
new File(Paths.get(System.getProperty(DEFAULT_PROPERTY), PROGRAM_FILE_DIRECTORY, "logs").toString())
);
ignoreFile = Collections.emptySet();
}
/**
* 获取程序根路径
*
* @return 程序根路径
*/
public String getRootPath() {
return Optional.ofNullable(rootPath).orElse(System.getProperty(DEFAULT_PROPERTY));
}
/**
* 设置程序根路径
*
* @param rootPath 程序根路径
*/
public void setRootPath(String rootPath) {
this.rootPath = rootPath;
}
/**
* 获取上次的程序根路径
*
* @return 上次的程序根路径
*/
public String getLastRootPath() {
return lastRootPath;
}
/**
* 设置上次的程序根路径
*
* @param lastRootPath 上次的程序根路径
*/
public void setLastRootPath(String lastRootPath) {
this.lastRootPath = lastRootPath;
}
/**
* 获取排除的文件夹集合
*
* @return 排除的文件夹集合
*/
public Set<File> getIgnoreFolder() {
return ignoreFolder;
}
/**
* 获取排除的文件集合
*
* @return 排除的文件集合
*/
public Set<File> getIgnoreFile() {
return ignoreFile;
}
}

View File

@ -0,0 +1,36 @@
package org.jcnc.jnotepad.app.config;
import org.jcnc.jnotepad.model.entity.PluginDescriptor;
import java.util.List;
/**
* 插件配置文件
*
* <p>
* 此类用于存储插件的配置信息包括插件描述符的列表
* </p>
*
* @author gewuyou
*/
public class PluginConfig {
private List<PluginDescriptor> plugins;
/**
* 获取插件描述符列表
*
* @return 插件描述符列表
*/
public List<PluginDescriptor> getPlugins() {
return plugins;
}
/**
* 设置插件描述符列表
*
* @param plugins 插件描述符列表
*/
public void setPlugins(List<PluginDescriptor> plugins) {
this.plugins = plugins;
}
}

View File

@ -0,0 +1,77 @@
package org.jcnc.jnotepad.app.config;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.jcnc.jnotepad.model.entity.ShortcutKey;
import java.util.List;
/**
* 用户配置文件类
*
* <p>
* 此类用于存储用户的配置信息包括语言设置文本自动换行设置和快捷键配置
* </p>
*
* @author luke
*/
public class UserConfig {
private String language;
@JsonIgnore
private boolean textWrap;
private List<ShortcutKey> shortcutKey;
/**
* 获取语言设置
*
* @return 语言设置
*/
public String getLanguage() {
return language;
}
/**
* 设置语言设置
*
* @param language 语言设置
*/
public void setLanguage(String language) {
this.language = language;
}
/**
* 获取快捷键配置列表
*
* @return 快捷键配置列表
*/
public List<ShortcutKey> getShortcutKey() {
return shortcutKey;
}
/**
* 设置快捷键配置列表
*
* @param shortcutKey 快捷键配置列表
*/
public void setShortcutKey(List<ShortcutKey> shortcutKey) {
this.shortcutKey = shortcutKey;
}
/**
* 获取文本自动换行设置
*
* @return 是否启用文本自动换行
*/
public boolean isTextWrap() {
return textWrap;
}
/**
* 设置文本自动换行设置
*
* @param textWrap 是否启用文本自动换行
*/
public void setTextWrap(boolean textWrap) {
this.textWrap = textWrap;
}
}

View File

@ -0,0 +1,136 @@
package org.jcnc.jnotepad.app.i18n;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* UI资源绑定用于加载语言文件
*
* <p>
* 此类用于加载和管理UI资源文件支持国际化和多语言功能可以通过绑定StringProperty和键值对应的内容以及获取当前资源文件的内容
* </p>
*
* <p>
* 该类是一个单例类通过getInstance方法获取实例
* </p>
*
* <p>
* 使用方法示例
* <code>
* UiResourceBundle.bindStringProperty(stringProperty, "key");
* String content = UiResourceBundle.getContent("key");
* </code>
* </p>
*
* @author songdragon
*/
public class UiResourceBundle {
private static final UiResourceBundle INSTANCE = new UiResourceBundle();
/**
* resource目录下的i18n/i18nXXX.properties
*/
private static final String BASENAME = "jcnc/app/i18n/i18n";
/**
* 资源文件的观察者绑定
*/
private final ObjectProperty<ResourceBundle> resources = new SimpleObjectProperty<>();
/**
* 当前语言
*/
private Locale currentLocale;
private UiResourceBundle() {
}
/**
* 获取UiResourceBundle的单例实例
*
* @return UiResourceBundle的单例实例
*/
public static UiResourceBundle getInstance() {
return INSTANCE;
}
/**
* 工具方法绑定StringProperty和Key对应的内容
*
* @param stringProperty 字符串属性
* @param key 键值
*/
public static void bindStringProperty(StringProperty stringProperty, String key) {
if (stringProperty == null) {
return;
}
stringProperty.bind(getInstance().getStringBinding(key));
}
/**
* 获取当前资源中的key值
*
* @param key 资源所对应键
* @return 当前键所对应的值
*/
public static String getContent(String key) {
return INSTANCE.getResources().getString(key);
}
/**
* 获取当前资源文件
*
* @return 资源文件
*/
public ObjectProperty<ResourceBundle> resourcesProperty() {
return resources;
}
public final ResourceBundle getResources() {
return resourcesProperty().get();
}
public final void setResources(ResourceBundle resources) {
resourcesProperty().set(resources);
}
/**
* 重置当前local
*
* @param toLocal 要设置的新的Locale
*/
public final void resetLocal(Locale toLocal) {
if (this.currentLocale == toLocal) {
return;
}
this.currentLocale = toLocal;
ResourceBundle resourceBundle = ResourceBundle.getBundle(BASENAME, currentLocale);
this.setResources(resourceBundle);
}
/**
* 获取key对应的绑定属性内容
*
* @param key key
* @return key对应的内容
*/
public StringBinding getStringBinding(String key) {
return Bindings.createStringBinding(() -> getResources().getString(key), resourcesProperty());
}
/**
* 注册资源变更监听器
*
* @param listener 变更监听器
*/
public void addListener(ChangeListener<? super ResourceBundle> listener) {
this.resources.addListener(listener);
}
}

View File

@ -0,0 +1,328 @@
package org.jcnc.jnotepad.app.manager;
import atlantafx.base.theme.PrimerLight;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.Window;
import org.jcnc.jnotepad.JnotepadApp;
import org.jcnc.jnotepad.app.common.constants.AppConstants;
import org.jcnc.jnotepad.app.common.constants.TextConstants;
import org.jcnc.jnotepad.app.common.manager.ThreadPoolManager;
import org.jcnc.jnotepad.app.config.AppConfig;
import org.jcnc.jnotepad.app.i18n.UiResourceBundle;
import org.jcnc.jnotepad.app.utils.FileUtil;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.jcnc.jnotepad.app.utils.UiUtil;
import org.jcnc.jnotepad.controller.ResourceController;
import org.jcnc.jnotepad.controller.cache.CacheController;
import org.jcnc.jnotepad.controller.config.AppConfigController;
import org.jcnc.jnotepad.controller.config.PluginConfigController;
import org.jcnc.jnotepad.controller.exception.AppException;
import org.jcnc.jnotepad.controller.manager.Controller;
import org.jcnc.jnotepad.controller.plugin.manager.PluginManager;
import org.jcnc.jnotepad.ui.views.manager.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import static org.jcnc.jnotepad.app.common.constants.AppConstants.DEFAULT_PROPERTY;
import static org.jcnc.jnotepad.app.common.constants.AppConstants.PROGRAM_FILE_DIRECTORY;
/**
* 应用程序管理类
*
* <p>
* 此类负责管理应用程序的生命周期和操作它包括初始化应用程序执行默认操作加载缓存加载资源迁移程序根文件夹停止前操作等功能
* </p>
*
* <p>
* 该类是一个单例类通过getInstance方法获取实例
* </p>
*
* @author gewuyou
*/
public class ApplicationManager {
private static final ApplicationManager INSTANCE = new ApplicationManager();
/**
* 线程池
*/
private final ExecutorService threadPool = ThreadPoolManager.getThreadPool();
private Pane root = new Pane();
private Scene scene;
private Stage primaryStage;
private Application application;
private ApplicationManager() {
}
/**
* 获取ApplicationManager的单例实例
*
* @return ApplicationManager的单例实例
*/
public static ApplicationManager getInstance() {
return INSTANCE;
}
/**
* 初始化应用程序
*
* <p>
* 此方法用于初始化应用程序的各个组件包括设置应用程序主题初始化UI组件初始化插件初始化顶部菜单栏初始化侧边工具栏初始化下方状态栏初始化标签页布局等
* </p>
*/
public void initializeApp() {
// 设置应用程序主题
Application.setUserAgentStylesheet(new PrimerLight().getUserAgentStylesheet());
// 初始化scene
initScene();
// 初始化插件
PluginManager.getInstance().initPlugins();
// 初始化顶部菜单栏
TopMenuBarManager.getInstance().initTopMenuBar();
// 初始化侧边工具栏
SidebarToolBarManager.getInstance().initSidebarToolBar();
// 初始化下方状态栏
BottomStatusBoxManager.getInstance().initStatusBox();
// 初始标签页布局组件
CenterTabPaneManager.getInstance().initCenterTabPane();
// 初始化应用布局
initAppLayout();
// 初始化primaryStage
initPrimaryStage();
}
/**
* 执行默认操作
*
* <p>
* 此方法用于执行应用程序的默认操作例如根据参数打开关联文件并创建文本区域加载已打开的文件夹等
* </p>
*/
public void executeDefaultAction() {
// 使用加载关联文件并创建文本区域
List<String> rawParameters = application.getParameters().getRaw();
Controller.getInstance().openAssociatedFileAndCreateTextArea(rawParameters);
// 加载已打开的文件夹
DirectorySidebarManager.getInstance().expandTheOpenFileTree();
}
private void initScene() {
// 初始化scene
double width = AppConstants.SCREEN_WIDTH;
double length = AppConstants.SCREEN_LENGTH;
scene = new Scene(root, width, length);
scene.getStylesheets().add(Objects.requireNonNull(application.getClass().getResource("/jcnc/app/css/styles.css")).toExternalForm());
}
private void initPrimaryStage() {
primaryStage.setScene(scene);
primaryStage.setWidth(scene.getWidth());
primaryStage.setHeight(scene.getHeight());
primaryStage.getIcons().add(UiUtil.getAppIcon());
primaryStage.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (Boolean.TRUE.equals(newValue)) {
CenterTabPaneManager instance = CenterTabPaneManager.getInstance();
instance.checkFileTabStatus(instance.getSelected());
}
});
}
/**
* 加载缓存
*
* <p>
* 此方法用于加载应用程序的缓存
* </p>
*/
public void loadAppCache() {
// 加载缓存
CacheController.getInstance().loadCaches();
}
/**
* 加载资源
*
* <p>
* 此方法用于加载应用程序的资源包括加载资源文件和绑定快捷键
* </p>
*/
public void loadAppResources() {
// 加载资源
ResourceController.getInstance().loadResources();
// 绑定快捷键
UiResourceBundle.bindStringProperty(primaryStage.titleProperty(), TextConstants.TITLE);
}
/**
* 迁移程序根文件夹
*
* <p>
* 此方法用于迁移应用程序的根文件夹将根文件夹从之前的位置迁移到新的位置
* </p>
*/
public void migrateFileRootFolder() {
AppConfig config = AppConfigController.getInstance().getConfig();
String lastRootPath = config.getLastRootPath();
if (lastRootPath == null) {
return;
}
// 获取源文件夹
File sourceFolder = new File(lastRootPath, PROGRAM_FILE_DIRECTORY);
// 获取目标文件夹
File targetFolder = new File(config.getRootPath(), PROGRAM_FILE_DIRECTORY);
// 设置忽略文件夹
Set<File> ignoredFolders = config.getIgnoreFolder();
// 设置忽略文件
Set<File> ignoredFiles = config.getIgnoreFile();
// 移动文件夹
FileUtil.migrateFolder(sourceFolder, targetFolder, ignoredFolders, ignoredFiles);
// 删除.jnotepad
if (!sourceFolder.equals(new File(Paths.get(System.getProperty(DEFAULT_PROPERTY), PROGRAM_FILE_DIRECTORY).toString()))) {
try {
Files.delete(sourceFolder.toPath());
} catch (IOException e) {
throw new AppException(e);
}
}
// 保存新配置
AppConfigController.getInstance().writeConfig();
}
/**
* 停止前操作
*
* <p>
* 在停止应用程序之前执行一系列操作包括刷新插件配置销毁插件保存已打开的文件标签页将缓存写入本地迁移程序根文件夹关闭线程池等
* </p>
*/
public void operationBeforeStopping() {
PluginConfigController pluginConfigController = PluginConfigController.getInstance();
// 刷新插件配置文件
pluginConfigController.getConfig().setPlugins(PluginManager.getInstance().getPluginDescriptors());
pluginConfigController.writeConfig();
// 销毁插件可能申请的资源
PluginManager.getInstance().destroyPlugins();
// 保存已打开的文件标签页
CenterTabPaneManager.getInstance().saveOpenFileTabs();
// 将缓存写入本地
CacheController.getInstance().writeCaches();
// 迁移文件夹
migrateFileRootFolder();
// 关闭线程池
threadPool.shutdownNow();
}
/**
* 获取当前窗口
*
* @return 当前窗口
*/
public Window getWindow() {
return scene.getWindow();
}
/**
* 获取当前窗口的场景
*
* @return 当前窗口的场景
*/
public Scene getScene() {
return scene;
}
public void setScene(Scene scene) {
this.scene = scene;
}
/**
* 加载程序布局
*
* <p>
* 此方法用于加载应用程序的布局包括根布局容器底部根侧边栏垂直布局主界面边界布局顶部边界面板右侧边栏垂直布局根布局等组件
* </p>
*/
public void initAppLayout() {
// 加载根布局容器
RootManager rootManager = RootManager.getInstance(scene);
rootManager.initScreen(scene);
// 初始化底部根侧边栏垂直布局
RootBottomSideBarVerticalBoxManager.getInstance().initSidebarVerticalBox();
// 初始化主界面边界布局
MainBorderPaneManager.getInstance().initMainBorderPane();
// 初始化顶部边界面板
RootTopBorderPaneManager.getInstance().initRootBorderPane();
// 初始化右侧边栏垂直布局
RootRightSideBarVerticalBoxManager.getInstance().initRootRightSideBarVerticalBox();
// 初始化根布局
RootBorderPaneManager.getInstance().initRootBorderPane();
}
/**
* 重启应用程序
*
* <p>
* 此方法用于重启当前的Java应用程序
* </p>
*/
public void restart() {
try {
// 获取当前Java应用程序的命令
String javaCommand = System.getProperty("java.home") + "/bin/java";
String mainClass = JnotepadApp.class.getName();
// 构建新进程来重新启动应用程序
ProcessBuilder builder = new ProcessBuilder(javaCommand, "-cp", System.getProperty("java.class.path"), mainClass);
builder.start();
// 关闭当前应用程序
stop();
} catch (IOException e) {
LoggerUtil.getLogger("正在重启当前应用程序".getClass());
}
}
public Pane getRoot() {
return root;
}
public void setRoot(Pane root) {
this.root = root;
}
public Application getApplication() {
return application;
}
public void setApplication(Application application) {
this.application = application;
}
public Stage getPrimaryStage() {
return primaryStage;
}
public void setPrimaryStage(Stage primaryStage) {
this.primaryStage = primaryStage;
}
/**
* 停止应用程序
*
* <p>
* 此方法用于停止应用程序
* </p>
*/
public void stop() {
Platform.exit();
}
}

View File

@ -0,0 +1,42 @@
package org.jcnc.jnotepad.app.utils;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
/**
* 剪切板工具
*
* @author gewuyou
*/
public class ClipboardUtil {
/**
* 系统剪切板对象
*/
private static final Clipboard CLIPBOARD = Clipboard.getSystemClipboard();
private ClipboardUtil() {
}
/**
* Writes the provided text to the system clipboard.
*
* @param text the text to be written to the clipboard
*/
public static void writeTextToClipboard(String text) {
ClipboardContent content = new ClipboardContent();
content.putString(text);
CLIPBOARD.setContent(content);
}
/**
* Reads text from the clipboard.
*
* @return the text read from the clipboard
*/
public static String readTextFromClipboard() {
String text = CLIPBOARD.getString();
LoggerUtil.getLogger(ClipboardUtil.class).info("剪切板内容:{}", text);
return text;
}
}

View File

@ -0,0 +1,78 @@
package org.jcnc.jnotepad.app.utils;
import com.ibm.icu.text.CharsetDetector;
import com.ibm.icu.text.CharsetMatch;
import org.slf4j.Logger;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.Charset;
/**
* 编码检测工具类
*
* <p>该工具类用于检测文本文件的编码类型</p>
*
* @author 许轲
*/
public class EncodingDetector {
/**
* 编码侦测概率阈值50%
*/
public static final int THRESHOLD_CONFIDENCE = 50;
private static final Logger LOG = LoggerUtil.getLogger(EncodingDetector.class);
private EncodingDetector() {
}
/**
* 检测文本编码
*
* @param file 要检测的文件
* @return 字符串表示的编码如果检测失败则返回 "UNKNOWN"
*/
public static String detectEncoding(File file) {
CharsetDetector charsetDetector = new CharsetDetector();
try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file.getPath()))) {
charsetDetector.setText(inputStream);
CharsetMatch[] matchList = charsetDetector.detectAll();
if (matchList == null || matchList.length == 0) {
return null;
}
CharsetMatch maxConfidence = matchList[0];
if (maxConfidence.getConfidence() < THRESHOLD_CONFIDENCE) {
return null;
}
for (int i = 1; i < matchList.length; i++) {
CharsetMatch match = matchList[i];
LOG.debug("{} : {}", match.getName(), match.getConfidence());
if (match.getConfidence() >= THRESHOLD_CONFIDENCE && match.getConfidence() >= maxConfidence.getConfidence()) {
maxConfidence = match;
} else {
return maxConfidence.getName();
}
}
} catch (Exception e) {
LOG.error("", e);
}
return null;
}
/**
* 检测文本编码
*
* @param file 文件
* @return Charset编码
*/
public static Charset detectEncodingCharset(File file) {
String charset = detectEncoding(file);
try {
// 断言charset != null
assert charset != null;
return Charset.forName(charset);
} catch (Exception e) {
return Charset.defaultCharset();
}
}
}

View File

@ -0,0 +1,434 @@
package org.jcnc.jnotepad.app.utils;
import javafx.scene.Node;
import javafx.scene.image.ImageView;
import org.jcnc.jnotepad.controller.event.handler.menuitem.OpenFile;
import org.jcnc.jnotepad.controller.exception.AppException;
import org.jcnc.jnotepad.model.entity.DirFileModel;
import org.kordamp.ikonli.javafx.FontIcon;
import org.slf4j.Logger;
import java.awt.*;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.*;
import static org.kordamp.ikonli.antdesignicons.AntDesignIconsFilled.*;
/**
* 文件工具
*
* @author gewuyou
*/
public class FileUtil {
private static final MessageDigest MESSAGE_DIGEST_SHA_256;
private static final int BUFFER_SIZE = 8192;
private static final Logger logger = LoggerUtil.getLogger(FileUtil.class);
private static final String WINDOWS = "win";
private static final String MAC = "mac";
private static final String PATH = "path";
static {
try {
MESSAGE_DIGEST_SHA_256 = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new AppException(e);
}
}
private FileUtil() {
}
/**
* 将字节数组转换为String类型哈希值
*
* @param bytes 字节数组
* @return 哈希值
*/
private static String bytes2HashCode(byte[] bytes) {
StringBuilder hashString = new StringBuilder();
for (byte b : bytes) {
hashString.append(String.format("%02x", b));
}
return hashString.toString();
}
/**
* 获取本地文件Sha256哈希值字符串
*
* @param file 本地文件
* @return 本地文件Sha256哈希值
*/
public static String getLocalFileSha256HashString(File file) {
try (
// 获取文件输入流
FileInputStream fileInputStream = new FileInputStream(file);
// 获取字节流通道
FileChannel channel = fileInputStream.getChannel()
) {
// 设置8k缓冲区
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
while (channel.read(buffer) != -1) {
buffer.flip();
MESSAGE_DIGEST_SHA_256.update(buffer);
buffer.clear();
}
} catch (IOException e) {
throw new AppException(e);
}
return bytes2HashCode(MESSAGE_DIGEST_SHA_256.digest());
}
/**
* 获取本地文件Sha256哈希值字符串
*
* @param pathStr 本地文件路径字符串
* @return 本地文件Sha256哈希值
*/
public static String getLocalFileSha256HashString(String pathStr) {
return getLocalFileSha256HashString(new File(pathStr));
}
/**
* 获取本地文件Sha256哈希值字符串
*
* @param path 本地文件路径
* @return 本地文件Sha256哈希值
*/
public static String getLocalFileSha256HashString(Path path) {
return getLocalFileSha256HashString(path.toFile());
}
/**
* 获取文件中的文本内容
*
* @param file 文件对象
* @return 文本内容
*/
public static String getFileText(File file) {
return getFileText(file, EncodingDetector.detectEncodingCharset(file));
}
/**
* 获取文件中的文本内容
*
* @param file 文件对象
* @param encoding 文件编码
* @return 文本内容
*/
public static String getFileText(File file, Charset encoding) {
StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(file, encoding))) {
String line;
while ((line = reader.readLine()) != null) {
if (!stringBuilder.isEmpty()) {
stringBuilder.append("\n");
}
stringBuilder.append(line);
}
} catch (IOException ignored) {
LoggerUtil.getLogger(OpenFile.class).info("已忽视IO异常!");
}
return stringBuilder.toString();
}
/**
* 将文件夹转为DirFileModel
*
* @param file 文件
* @return DirFileModel 存储文件夹与文件关系的实体类
*/
public static DirFileModel getDirFileModel(File file) {
if (!file.exists()) {
return null;
}
DirFileModel dirFileModel = new DirFileModel(
file.getAbsolutePath(),
file.getName(), new ArrayList<>(),
new FontIcon(FOLDER),
new FontIcon(FOLDER_OPEN));
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
if (f.isDirectory()) {
DirFileModel childDirFileModel = getDirFileModel(f);
dirFileModel.getChildFile().add(childDirFileModel);
} else {
// 在此监测文件后缀设置对应的图标
dirFileModel.getChildFile().add(new DirFileModel(
f.getAbsolutePath(), f.getName(), null,
getIconCorrespondingToFileName(f.getName()),
null));
}
}
}
return dirFileModel;
}
/**
* Retrieves a DirFileModel object based on the given dirFileModels map.
*
* @param dirFileModels a map containing the dirFileModels data
* @return the DirFileModel object
*/
public static DirFileModel getDirFileModel(Map<String, Object> dirFileModels) {
if (Objects.isNull(dirFileModels) || dirFileModels.isEmpty()) {
return null;
}
File rootDir = new File((String) dirFileModels.get(PATH));
DirFileModel dirFileModel = new DirFileModel(
rootDir.getAbsolutePath(),
rootDir.getName(), new ArrayList<>(),
new FontIcon(FOLDER),
new FontIcon(FOLDER_OPEN), (Boolean) dirFileModels.get("open"));
Optional<Object> o = Optional.ofNullable(dirFileModels.get("childFile"));
if (o.isEmpty()) {
return null;
}
List<Map<String, Object>> childFile = (List<Map<String, Object>>) o.get();
File[] files = rootDir.listFiles();
if (files == null) {
return null;
}
for (File f : files) {
if (f.isDirectory()) {
Optional<Map<String, Object>> first = childFile
.stream()
.filter(map -> map.get(PATH).equals(f.getAbsolutePath())).findFirst();
DirFileModel childDirFileModel;
if (first.isPresent()) {
childDirFileModel = getDirFileModel(first.get());
} else {
childDirFileModel = getDirFileModel(f);
}
dirFileModel.getChildFile().add(childDirFileModel);
} else {
// 在此监测文件后缀设置对应的图标
dirFileModel.getChildFile().add(new DirFileModel(
f.getAbsolutePath(), f.getName(), null,
getIconCorrespondingToFileName(f.getName()),
null));
}
}
return dirFileModel;
}
/**
* 文件夹迁移
*
* @param sourceFolder 源文件夹
* @param targetFolder 目标文件夹
* @since 2023/10/5 12:18
*/
public static void migrateFolder(File sourceFolder, File targetFolder) {
// 创建目标文件夹
targetFolder.mkdirs();
// 获取源文件夹中的所有文件和文件夹
File[] files = sourceFolder.listFiles();
if (files != null) {
// 遍历源文件夹中的每个文件和文件夹
for (File file : files) {
if (file.isDirectory()) {
// 如果是文件夹递归调用自身进行迁移
migrateFolder(file, new File(targetFolder, file.getName()));
} else {
// 如果是文件将文件复制到目标文件夹中
Path sourceFilePath = file.toPath();
Path targetFilePath = new File(targetFolder, file.getName()).toPath();
try {
Files.copy(sourceFilePath, targetFilePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new AppException(e);
}
}
}
}
}
/**
* 迁移文件夹
*
* @param sourceFolder 源文件夹
* @param targetFolder 目标文件夹
* @param ignoredFolders 忽略的文件夹集合
* @param ignoredFiles 忽略的文件集合
* @since 2023/10/5 13:58
*/
public static void migrateFolder(File sourceFolder, File targetFolder, Set<File> ignoredFolders, Set<File> ignoredFiles) {
// 创建目标文件夹
targetFolder.mkdir();
// 获取源文件夹中的所有文件和文件夹
File[] files = sourceFolder.listFiles();
if (files != null) {
// 遍历源文件夹中的每个文件和文件夹
for (File file : files) {
// 如果是文件夹且不是忽略的文件夹递归调用自身进行迁移
if (file.isDirectory() && !ignoredFolders.contains(file)) {
migrateFolder(targetFolder, ignoredFolders, ignoredFiles, file);
continue;
}
// 如果是文件且不是忽略的文件将文件复制到目标文件夹中
if (!file.isDirectory() && !ignoredFiles.contains(file)) {
migrateFile(targetFolder, file);
}
}
}
}
/**
* 迁移文件
*
* @param targetFolder 目标文件夹
* @param file 文件
*/
public static void migrateFile(File targetFolder, File file) {
Path sourceFilePath = file.toPath();
Path targetFilePath = new File(targetFolder, file.getName()).toPath();
try {
Files.copy(sourceFilePath, targetFilePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new AppException(e);
}
// 删除源文件
try {
Files.delete(file.toPath());
} catch (IOException e) {
throw new AppException(e);
}
}
/**
* 迁移文件夹
*
* @param targetFolder 目标文件夹
* @param ignoredFolders 忽略的文件夹集合
* @param ignoredFiles 忽略的文件集合
* @param file 文件
*/
private static void migrateFolder(File targetFolder, Set<File> ignoredFolders, Set<File> ignoredFiles, File file) {
migrateFolder(file, new File(targetFolder, file.getName()), ignoredFolders, ignoredFiles);
// 调用完毕删除当前目录
try {
Files.deleteIfExists(file.toPath());
} catch (IOException e) {
throw new AppException(e);
}
}
/**
* Opens the file explorer to the specified file or its parent directory.
*
* @param file the file or directory to open in the file explorer
*/
public static void openExplorer(File file) {
try { // 判断传入的是文件还是文件夹
if (file.isDirectory()) {
Desktop.getDesktop().open(file);
}
// 如果是文件则打开所在文件夹
else {
Desktop.getDesktop().open(file.getParentFile());
}
} catch (IOException e) {
throw new AppException(e);
}
}
/**
* Retrieves the icon corresponding to the given file name.
*
* @param fileName the file name
* @return the corresponding icon for the file extension
*/
public static Node getIconCorrespondingToFileName(String fileName) {
// 在此根据文件缀名获取对应的图标
String fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1);
Node orDefault = UiUtil.getIconMap().getOrDefault(fileExtension, FontIcon.of(FILE_UNKNOWN));
if (orDefault instanceof FontIcon fontIcon) {
return new FontIcon(fontIcon.getIconLiteral());
}
if (orDefault instanceof ImageView imageView) {
return new ImageView(imageView.getImage());
}
return orDefault;
}
/**
* Opens a terminal in the specified folder.
*
* @param folder the folder in which to open the terminal
*/
public static void openTerminal(File folder) {
if (!folder.exists()) {
return;
}
if (folder.isFile()) {
folder = folder.getParentFile();
}
String os = System.getProperty("os.name").toLowerCase();
ProcessBuilder processBuilder = getProcessBuilder(folder, os);
try {
processBuilder.start();
} catch (IOException e) {
PopUpUtil.errorAlert("打开失败", "打开于终端失败", "错误原因:" + e.getMessage(), null, null);
}
}
/**
* Returns a ProcessBuilder object based on the provided folder and operating system.
*
* @param folder the folder to set as the working directory for the ProcessBuilder object
* @param os the operating system to determine the appropriate command for the ProcessBuilder object
* @return a ProcessBuilder object with the correct command for the specified operating system
*/
private static ProcessBuilder getProcessBuilder(File folder, String os) {
ProcessBuilder processBuilder;
if (os.contains(WINDOWS)) {
// Windows系统
processBuilder = new ProcessBuilder("cmd.exe", "/c", "start", "cmd.exe", "/k", "cd", folder.getAbsolutePath());
} else if (os.contains(MAC)) {
// macOS系统
processBuilder = new ProcessBuilder("open", "-a", "Terminal", folder.getAbsolutePath());
} else {
// Linux或其他系统
processBuilder = new ProcessBuilder("xdg-open", folder.getAbsolutePath());
}
return processBuilder;
}
/**
* Creates a file at the specified path.
*
* @param path The path to the file to be created.
*/
public static void createFile(Path path) {
try {
Files.createFile(path);
} catch (IOException e) {
logger.error("创建文件失败", e);
}
}
}

View File

@ -0,0 +1,81 @@
package org.jcnc.jnotepad.app.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.jcnc.jnotepad.controller.exception.AppException;
import static com.fasterxml.jackson.core.util.DefaultIndenter.SYS_LF;
/**
* Jackson 解析器的外观类主要提供 ObjectMapper 对象
*
* <p>该类用于封装 Jackson 对象映射工具的配置和操作</p>
*
* @author songdragon
*/
public class JsonUtil {
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
static {
OBJECT_MAPPER.enable(SerializationFeature.INDENT_OUTPUT);
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
DefaultIndenter di = new DefaultIndenter(" ", SYS_LF);
prettyPrinter.indentArraysWith(di);
prettyPrinter.indentObjectsWith(di);
OBJECT_MAPPER.setDefaultPrettyPrinter(prettyPrinter);
}
private JsonUtil() {
}
/**
* 将对象转换为 JSON 字符串
*
* @param o 要转换的对象
* @return 对象的 JSON 表示
* @throws AppException 如果转换失败
*/
public static String toJsonString(Object o) {
try {
return OBJECT_MAPPER.writeValueAsString(o);
} catch (JsonProcessingException e) {
throw new AppException(e);
}
}
/**
* 将json字符串解析成对象
*
* @param json json字符串
* @param clazz 对象类型
* @return 对象
* @throws AppException 如果解析失败
*/
public static <T> T fromJsonString(String json, Class<T> clazz) {
try {
return OBJECT_MAPPER.readValue(json, clazz);
} catch (JsonProcessingException e) {
throw new AppException(e);
}
}
/**
* 将json字符串解析成对象
*
* @param json json字符串
* @param valueTypeRef 类型 引用
* @return 对象
* @throws AppException 如果解析失败
*/
public static <T> T fromJsonString(String json, TypeReference<T> valueTypeRef) {
try {
return OBJECT_MAPPER.readValue(json, valueTypeRef);
} catch (JsonProcessingException e) {
throw new AppException(e);
}
}
}

View File

@ -0,0 +1,29 @@
package org.jcnc.jnotepad.app.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 日志工具
*
* <p>注意如果在JavaFX项目中需要调用日志请使用Platform.runLater()来调用</p>
*
* @author gewuyou
*/
public class LoggerUtil {
private LoggerUtil() {
}
/**
* 获取日志类
*
* @param currentClass 所要记录的类
* @return org.slf4j.Logger 日志对象
*
* <p>传入当前需要记录的类返回记录该类的日志类</p>
* <p>建议一个类调用超过两次这个方法时应当将该日志类变成成员对象而不是多次调用</p>
*/
public static Logger getLogger(Class<?> currentClass) {
return LoggerFactory.getLogger(currentClass);
}
}

View File

@ -0,0 +1,102 @@
package org.jcnc.jnotepad.app.utils;
import atlantafx.base.controls.Notification;
import atlantafx.base.theme.Styles;
import javafx.scene.layout.StackPane;
import org.jcnc.jnotepad.ui.views.manager.RootManager;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.Collections;
/**
* 通知实用程序
*
* @author gewuyou
*/
public class NotificationUtil {
private static final RootManager ROOT_MANAGER = RootManager.getInstance();
private static final StackPane ROOT_STACK_PANE = ROOT_MANAGER.getRootStackPane();
private NotificationUtil() {
}
/**
* Generates a custom notification with the given message and icon, applying the specified styles.
*
* @param message the message to display in the notification
* @param icon the icon to display in the notification
* @param styles additional styles to apply to the notification
*/
public static void customNotification(String message, FontIcon icon, String... styles) {
Notification notification = new Notification(message, icon);
Collections.addAll(notification.getStyleClass(), styles);
RootManager.addNotificationToStackPane(ROOT_STACK_PANE, notification, true);
}
/**
* Displays a success notification with the given message.
*
* @param message the message to be displayed in the notification
*/
public static void successNotification(String message) {
Notification notification = new Notification(message, UiUtil.getSuccessIcon());
setStyleClass(notification, Styles.SUCCESS);
RootManager.addNotificationToStackPane(ROOT_STACK_PANE, notification, true);
}
/**
* Generates an info notification with the given message.
*
* @param message the message to display in the notification
*/
public static void infoNotification(String message) {
Notification notification = new Notification(message, UiUtil.getInfoIcon());
setStyleClass(notification, Styles.ACCENT);
RootManager.addNotificationToStackPane(ROOT_STACK_PANE, notification, true);
}
/**
* Generates an error notification with the given message and displays it on the root stack pane.
*
* @param message the error message to be displayed
*/
public static void errorNotification(String message) {
Notification notification = new Notification(message, UiUtil.getErrorIcon());
setStyleClass(notification, Styles.DANGER);
RootManager.addNotificationToStackPane(ROOT_STACK_PANE, notification, true);
}
/**
* Generates a warning notification with the given message and displays it on the root stack pane.
*
* @param message the warning message to be displayed
*/
public static void warningNotification(String message) {
Notification notification = new Notification(message, UiUtil.getWarningIcon());
setStyleClass(notification, Styles.WARNING);
RootManager.addNotificationToStackPane(ROOT_STACK_PANE, notification, true);
}
/**
* Generates a question notification with the given message and displays it on the root stack pane.
*
* @param message the question message to be displayed
*/
public static void questionNotification(String message) {
Notification notification = new Notification(message, UiUtil.getQuestionIcon());
setStyleClass(notification, Styles.ACCENT);
RootManager.addNotificationToStackPane(ROOT_STACK_PANE, notification, true);
}
/**
* Sets the style class of the given notification.
*
* @param notification The notification object to set the style class for.
* @param styleClass The style class to add to the notification.
*/
private static void setStyleClass(Notification notification, String styleClass) {
notification.getStyleClass().add(Styles.ELEVATED_1);
notification.getStyleClass().add(styleClass);
}
}

View File

@ -0,0 +1,162 @@
package org.jcnc.jnotepad.app.utils;
import org.jcnc.jnotepad.model.enums.DialogType;
import org.jcnc.jnotepad.ui.component.stage.dialog.AppDialogBuilder;
import org.jcnc.jnotepad.ui.component.stage.dialog.interfaces.DialogButtonAction;
/**
* 弹窗工具类
*
* @author gewuyou
*/
public class PopUpUtil {
private PopUpUtil() {
}
/**
* 设置错误弹窗
*
* @param title 弹窗标题
* @param headerText 头文本
* @param message 消息文本
* @param leftBtnAction 左侧按钮点击事件
* @param rightBtnAction 右侧按钮点击事件
* @apiNote
* @since 2023/9/3 11:54
*/
public static void errorAlert(
String title, String headerText, String message,
DialogButtonAction leftBtnAction, DialogButtonAction rightBtnAction) {
getCustomDialog()
.setDialogType(DialogType.ERROR)
.setTitle(title)
.setHeaderText(headerText)
.setCustomText(message)
.setLeftBtnAction(leftBtnAction)
.setRightBtnAction(rightBtnAction)
.build().showAndWait();
}
/**
* 设置信息弹窗
*
* @param title 弹窗标题
* @param headerText 头文本
* @param message 消息文本
* @param leftBtnAction 左侧按钮点击事件
* @param rightBtnAction 右侧按钮点击事件
* @apiNote
* @since 2023/9/3 11:54
*/
public static void infoAlert(
String title, String headerText, String message,
DialogButtonAction leftBtnAction, DialogButtonAction rightBtnAction) {
getCustomDialog()
.setDialogType(DialogType.INFO)
.setTitle(title)
.setHeaderText(headerText)
.setCustomText(message)
.setLeftBtnAction(leftBtnAction)
.setRightBtnAction(rightBtnAction)
.build().showAndWait();
}
/**
* 设置警告弹窗
*
* @param title 弹窗标题
* @param headerText 头文本
* @param message 消息文本
* @param leftBtnAction 左侧按钮点击事件
* @param rightBtnAction 右侧按钮点击事件
* @apiNote
* @since 2023/9/3 11:54
*/
public static void warningAlert(
String title, String headerText, String message,
DialogButtonAction leftBtnAction, DialogButtonAction rightBtnAction) {
getCustomDialog()
.setDialogType(DialogType.WARNING)
.setTitle(title)
.setHeaderText(headerText)
.setCustomText(message)
.setLeftBtnAction(leftBtnAction)
.setRightBtnAction(rightBtnAction)
.build().showAndWait();
}
/**
* 设置疑问弹窗
*
* @param title 弹窗标题
* @param headerText 头文本
* @param message 消息文本
* @param leftBtnAction 左侧按钮点击事件
* @param rightBtnAction 右侧按钮点击事件
* @apiNote
* @since 2023/9/3 11:54
*/
public static void questionAlert(
String title, String headerText, String message,
DialogButtonAction leftBtnAction, DialogButtonAction rightBtnAction) {
getCustomDialog()
.setDialogType(DialogType.QUESTION)
.setTitle(title)
.setHeaderText(headerText)
.setCustomText(message)
.setLeftBtnAction(leftBtnAction)
.setRightBtnAction(rightBtnAction)
.build().showAndWait();
}
/**
* 设置疑问弹窗
*
* @param title 弹窗标题
* @param headerText 头文本
* @param message 消息文本
* @param leftBtnAction 左侧按钮点击事件
* @param rightBtnAction 右侧按钮点击事件
* @apiNote
* @since 2023/9/3 11:54
*/
public static void questionAlert(
String title, String headerText, String message,
DialogButtonAction leftBtnAction, DialogButtonAction rightBtnAction, String leftBtnText, String rightBtnText) {
getCustomDialog()
.setDialogType(DialogType.QUESTION)
.setTitle(title)
.setHeaderText(headerText)
.setCustomText(message)
.setLeftBtnAction(leftBtnAction)
.setRightBtnAction(rightBtnAction)
.setLeftBtnText(leftBtnText)
.setRightBtnText(rightBtnText)
.build().showAndWait();
}
public static void successAlert(
String title, String headerText, String message,
DialogButtonAction leftBtnAction, DialogButtonAction rightBtnAction) {
getCustomDialog()
.setDialogType(DialogType.SUCCESS)
.setTitle(title)
.setHeaderText(headerText)
.setCustomText(message)
.setLeftBtnAction(leftBtnAction)
.setRightBtnAction(rightBtnAction)
.build().showAndWait();
}
/**
* 获取自定义弹窗
*
* @apiNote 使用此方法会返回原始的应用对话框建造者类以实现自定义弹窗
* @since 2023/9/3 11:54
*/
public static AppDialogBuilder getCustomDialog() {
return new AppDialogBuilder();
}
}

View File

@ -0,0 +1,70 @@
package org.jcnc.jnotepad.app.utils;
import org.jcnc.jnotepad.JnotepadApp;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.Objects;
/**
* 资源工具
*
* @author gewuyou
*/
public class ResourceUtil {
public static final String MODULE_DIR = "/jcnc/app/";
private ResourceUtil() {
}
/**
* Retrieves an input stream for the specified resource.
*
* @param resource the path to the resource
* @return the input stream for the resource
*/
public static InputStream getResourceAsStream(String resource) {
String path = resolve(resource);
return Objects.requireNonNull(
JnotepadApp.class.getResourceAsStream(resolve(path)),
"Resource not found: " + path
);
}
/**
* Retrieves the resource with the specified path.
*
* @param resource the path of the resource to retrieve
* @return the URI of the retrieved resource
*/
public static URI getResource(String resource) {
String path = resolve(resource);
URL url = Objects.requireNonNull(JnotepadApp.class.getResource(resolve(path)), "Resource not found: " + path);
return URI.create(url.toExternalForm());
}
/**
* Resolves a resource path by checking if it starts with a "/". If it does,
* the resource path is returned as is. If it doesn't, the resource path is
* concatenated with the module directory path.
*
* @param resource the resource path to be resolved
* @param moduleDir the module directory path
* @return the resolved resource path
*/
public static String resolve(String resource, String moduleDir) {
Objects.requireNonNull(resource);
return resource.startsWith("/") ? resource : moduleDir + resource;
}
/**
* Resolve the given resource using the default module directory.
*
* @param resource the resource to resolve
* @return the resolved resource
*/
public static String resolve(String resource) {
return resolve(resource, MODULE_DIR);
}
}

View File

@ -0,0 +1,338 @@
package org.jcnc.jnotepad.app.utils;
import javafx.scene.control.Tab;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.stage.FileChooser;
import org.jcnc.jnotepad.app.common.constants.AppConstants;
import org.jcnc.jnotepad.app.common.constants.TextConstants;
import org.jcnc.jnotepad.app.common.manager.ApplicationCacheManager;
import org.jcnc.jnotepad.app.i18n.UiResourceBundle;
import org.jcnc.jnotepad.controller.config.UserConfigController;
import org.jcnc.jnotepad.controller.event.handler.menuitem.OpenFile;
import org.jcnc.jnotepad.controller.i18n.LocalizationController;
import org.jcnc.jnotepad.model.entity.Cache;
import org.jcnc.jnotepad.model.enums.CacheExpirationTime;
import org.jcnc.jnotepad.ui.component.module.TextCodeArea;
import org.jcnc.jnotepad.ui.component.stage.dialog.factory.impl.BasicFileChooserFactory;
import org.jcnc.jnotepad.ui.views.manager.BottomStatusBoxManager;
import org.jcnc.jnotepad.ui.views.manager.CenterTabPaneManager;
import org.jcnc.jnotepad.ui.views.root.center.main.center.tab.CenterTab;
import org.jcnc.jnotepad.ui.views.root.center.main.center.tab.CenterTabPane;
import org.slf4j.Logger;
import java.io.File;
import java.nio.charset.Charset;
import java.util.Comparator;
import java.util.List;
import static org.jcnc.jnotepad.app.common.constants.TextConstants.NEW_FILE;
import static org.jcnc.jnotepad.app.utils.FileUtil.getFileText;
import static org.jcnc.jnotepad.controller.config.UserConfigController.CONFIG_NAME;
/**
* 标签页工具
*
* @author gewuyou
*/
public class TabUtil {
private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance();
private static final Logger logger = LoggerUtil.getLogger(TabUtil.class);
private TabUtil() {
}
/**
* 保存文件标签页
*/
public static void saveFile(CenterTab tab) {
if (tab == null) {
return;
}
// 如果打开的是非关联文件则调用另存为方法
if (!tab.getRelevanceProperty()) {
logger.info("当前保存文件为非关联打开文件,调用另存为方法");
saveAsFile(tab);
} else {
logger.info("当前保存文件为关联打开文件,调用自动保存方法");
// 调用tab保存方法
tab.saveSelectedFileTab();
// 如果该文件是配置文件则刷新快捷键
if (CONFIG_NAME.equals(tab.getText())) {
// 重新加载语言包和快捷键
UserConfigController.getInstance().loadConfig();
UserConfigController.getInstance().initAllShortcutKeys();
LocalizationController.initLocal();
logger.info("已刷新语言包!");
logger.info("已刷新快捷键!");
}
}
}
/**
* 另存为
*
* @apiNote 将当前选中的标签页进行另存为弹出窗口式的保存
* @see LoggerUtil
*/
public static void saveAsFile(CenterTab tab) {
if (tab == null) {
return;
}
Cache cache = CACHE_MANAGER.getCache("folder", "saveFile");
File file = BasicFileChooserFactory.getInstance().createFileChooser(
UiResourceBundle.getContent(TextConstants.SAVE_AS),
tab.getText(),
cache == null ? null : new File((String) cache.getCacheData()),
new FileChooser.ExtensionFilter("All types", "*.*"))
.showSaveDialog(UiUtil.getAppWindow());
if (file != null) {
if (cache == null) {
CACHE_MANAGER.addCache(
CACHE_MANAGER.createCache("folder", "saveFile", file.getParent(),
CacheExpirationTime.NEVER_EXPIRES.getValue()));
} else {
cache.setCacheData(file.getParent());
CACHE_MANAGER.addCache(cache);
}
logger.info("正在保存文件: {}", file.getName());
tab.save(file);
// 将保存后的文件设置为关联文件
tab.setRelevanceProperty(true);
// 更新标签页上的文件名
tab.setText(file.getName());
}
}
/**
* 重命名
*/
public static void rename(CenterTab tab) {
if (tab == null || tab.getText().isEmpty()) {
return;
}
// 判断当前是否为关联文件
if (tab.getRelevanceProperty()) {
// 重命名关联文件
handleRenameRelevanceFile(tab);
}
// 如果当前不是关联文件则重命名标签页
else {
handleRenameTab(tab);
}
}
/**
* 重命名标签页
*
* @param tab 标签页组件
*/
private static void handleRenameTab(CenterTab tab) {
// 临时记录标签页名称
String tempName = tab.getText();
TextField textField = new TextField(tempName);
textField.getStyleClass().add("tab-title-editable");
// 清空标签页名称
tab.setText("");
// 监听 Enter 完成编辑
textField.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ENTER) {
String newTabName = textField.getText();
// 检查是否存在相同名称的标签页
if (tabNameExists(newTabName)) {
// 显示弹窗并提示用户更换名称
showDuplicateNameAlert(newTabName);
// 恢复原始名称
tab.setText(tempName);
} else {
tab.setText(newTabName);
// 可选移除 TextField 的图形
tab.setGraphic(null);
// 可选恢复标签页的关闭按钮
tab.setClosable(true);
}
}
});
// 监听失去焦点事件完成编辑
textField.focusedProperty().addListener((observable, oldValue, newValue) -> {
String newTabName = textField.getText();
// 检查是否存在相同名称的标签页
if (tabNameExists(newTabName)) {
// 恢复原始名称
tab.setText(tempName);
}
if (Boolean.FALSE.equals(newValue)) {
tab.setText(newTabName);
// 可选移除 TextField 的图形
tab.setGraphic(null);
// 可选恢复标签页的关闭按钮
tab.setClosable(true);
}
});
tab.setClosable(false);
// 设置 TextField 作为标签页的图形
tab.setGraphic(textField);
// 默认获取焦点并选中所有文字
textField.requestFocus();
textField.selectAll();
}
/**
* 判断是否存在具有相同名称的标签页
*
* @param newTabName 要检查的新标签页名称
* @return 如果存在具有相同名称的标签页则返回 true否则返回 false
*/
private static boolean tabNameExists(String newTabName) {
CenterTabPane tabPane = CenterTabPane.getInstance();
return tabPane.getTabs().stream()
.anyMatch(tab -> tab.getText().equals(newTabName));
}
/**
* 显示警告弹窗提示用户更换重复的名称
*/
private static void showDuplicateNameAlert(String newTabName) {
PopUpUtil.errorAlert(
"重命名错误",
"\" " + newTabName + "\" 和已有标签页名字重复",
"请再次重命名",
null,
null);
}
/**
* 重命名关联文件
*
* @param tab 标签页组件
*/
private static void handleRenameRelevanceFile(CenterTab tab) {
// 获取原始文件对象
File file = (File) tab.getUserData();
// 获取应用窗口并绑定
File newFile = BasicFileChooserFactory.getInstance()
.createFileChooser(
UiResourceBundle.getContent(TextConstants.RENAME),
tab.getText(),
new File(file.getParent()),
new FileChooser.ExtensionFilter("All types", "*.*"))
.showSaveDialog(UiUtil.getAppWindow());
if (newFile != null) {
boolean rename = file.renameTo(newFile);
// 设置文件数据
tab.setUserData(newFile);
if (rename) {
tab.setText(newFile.getName());
logger.info("文件重命名成功");
} else {
logger.debug("文件重命名失败");
}
}
}
/**
* 添加新的文件标签页
*/
public static void addNewFileTab() {
// 创建一个新的文本编辑区
TextCodeArea textArea = new TextCodeArea();
// 创建标签页
CenterTab centerTab = new CenterTab(
generateDefaultName(),
textArea);
// 将Tab页添加到TabPane中
CenterTabPaneManager.getInstance().addNewTab(centerTab);
// 更新编码信息
BottomStatusBoxManager.getInstance().updateEncodingLabel();
}
/**
* Generate the default name for a new tab.
*
* @return The default name for a new tab.
*/
private static String generateDefaultName() {
// 设定初始索引
int index = 1;
StringBuilder tabTitle = new StringBuilder();
String content = UiResourceBundle.getContent(NEW_FILE);
// 获取当前默认创建标签页集合
List<Tab> tabs = CenterTabPane.getInstance()
.getTabs()
.stream()
// 排除不属于默认创建的标签页
.filter(tab -> AppConstants.TABNAME_PATTERN.matcher(tab.getText()).matches())
// 对默认创建的标签页进行排序
.sorted(Comparator.comparing(tab -> {
String tabText = tab.getText();
// 提取数字部分
String numberPart = tabText.substring(content.length());
// 解析为数字
return Integer.parseInt(numberPart);
}))
// 转为List集合
.toList();
// 构建初始标签页名称
tabTitle.append(content).append(index);
for (Tab tab : tabs) {
if (tab.getText().contentEquals(tabTitle)) {
tabTitle.setLength(0);
tabTitle.append(content).append(++index);
} else {
break;
}
}
return tabTitle.toString();
}
/**
* 打开文件到选项卡
*
* @param file 文件对象
*/
public static void openFileToTab(File file) {
// 获取标签页集合
CenterTabPane centerTabPane = CenterTabPane.getInstance();
// 遍历标签页查找匹配的标签页
for (Tab tab : centerTabPane.getTabs()) {
// 获取绑定的文件
File tabFile = (File) tab.getUserData();
if (tabFile == null) {
continue;
}
if (file.getPath().equals((tabFile).getPath())) {
// 找到匹配的标签页设置为选中状态并跳转
centerTabPane.getSelectionModel().select(tab);
return;
}
}
getText(file);
}
/**
* 读取文本文件的内容
*
* @param file 文件对象
*/
public static void getText(File file) {
TextCodeArea textCodeArea = new TextCodeArea();
// 检测文件编码
Charset encoding = EncodingDetector.detectEncodingCharset(file);
String fileText = getFileText(file, encoding);
LoggerUtil.getLogger(OpenFile.class).info("已调用读取文件功能");
textCodeArea.appendText(fileText);
// 设置当前标签页关联本地文件 设置标签页关联文件
CenterTab tab = new CenterTab(file.getName(), textCodeArea, encoding, file, true);
// 设置关联文件最后的修改时间
tab.setLastModifiedTimeOfAssociatedFile(file.lastModified());
CenterTabPaneManager.getInstance().addNewTab(tab);
}
}

View File

@ -0,0 +1,222 @@
package org.jcnc.jnotepad.app.utils;
import atlantafx.base.theme.Styles;
import javafx.scene.Node;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Window;
import org.jcnc.jnotepad.app.common.constants.AppConstants;
import org.jcnc.jnotepad.app.manager.ApplicationManager;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static org.kordamp.ikonli.antdesignicons.AntDesignIconsFilled.*;
/**
* UI工具
*
* <p>封装了项目中需要引入的UI组件</p>
*
* @author gewuyou
*/
public class UiUtil {
/**
* 应用程序图标
*/
private static final Image APP_ICON = new Image(Objects.requireNonNull(UiUtil.class.getResource(AppConstants.APP_ICON)).toString());
/**
* 错误图标
*/
private static final FontIcon ERROR_ICON = FontIcon.of(EXCLAMATION_CIRCLE);
/**
* 信息图标
*/
private static final FontIcon INFO_ICON = FontIcon.of(INFO_CIRCLE);
/**
* 警告图标
*/
private static final FontIcon WARNING_ICON = FontIcon.of(WARNING);
/**
* 问题图标
*/
private static final FontIcon QUESTION_ICON = FontIcon.of(QUESTION_CIRCLE);
private static final FontIcon SUCCESS_ICON = FontIcon.of(CHECK_CIRCLE);
/**
* 图标集合
*/
private static final Map<String, Node> ICON_MAP = new HashMap<>(32);
static {
// 暂时设置颜色
ERROR_ICON.getStyleClass().addAll(Styles.DANGER);
INFO_ICON.getStyleClass().addAll(Styles.ACCENT);
QUESTION_ICON.getStyleClass().addAll(Styles.ACCENT);
WARNING_ICON.getStyleClass().addAll(Styles.WARNING);
SUCCESS_ICON.getStyleClass().addAll(Styles.SUCCESS);
ICON_MAP.put("css", fileIconByPng("css"));
ICON_MAP.put("doc", fileIconByPng("doc"));
ICON_MAP.put("dll", fileIconByPng("dll"));
ICON_MAP.put("exe", fileIconByPng("exe"));
ICON_MAP.put("gif", fileIconByPng("gif"));
ICON_MAP.put("gitignore", fileIconByPng("git"));
ICON_MAP.put("html", fileIconByPng("html"));
ICON_MAP.put("json", fileIconByPng("json"));
ICON_MAP.put("md", fileIconByPng("markdown"));
ICON_MAP.put("pdf", FontIcon.of(FILE_PDF));
ICON_MAP.put("ppt", FontIcon.of(FILE_PPT));
ICON_MAP.put("png", fileIconByPng("png"));
ICON_MAP.put("sql", fileIconByPng("database"));
ICON_MAP.put("svg", fileIconByPng("svg"));
ICON_MAP.put("txt", FontIcon.of(FILE_TEXT));
ICON_MAP.put("xls", FontIcon.of(FILE_EXCEL));
ICON_MAP.put("xml", fileIconByPng("xml"));
// 编程语言
ICON_MAP.put("bat", fileIconByPng("bat"));
ICON_MAP.put("c", fileIconByPng("c"));
ICON_MAP.put("cs", fileIconByPng("csharp"));
ICON_MAP.put("cpp", fileIconByPng("cplusplus"));
ICON_MAP.put("go", fileIconByPng("golang"));
ICON_MAP.put("js", fileIconByPng("js"));
ICON_MAP.put("java", fileIconByPng("java"));
ICON_MAP.put("kt", fileIconByPng("kotlin"));
ICON_MAP.put("lua", fileIconByPng("lua"));
ICON_MAP.put("py", fileIconByPng("python"));
ICON_MAP.put("php", fileIconByPng("php"));
ICON_MAP.put("rb", fileIconByPng("ruby"));
ICON_MAP.put("sh", fileIconByPng("sh"));
}
private UiUtil() {
}
/**
* 获取应用程序图标
*
* @return javafx.scene.image.Image 应用程序图标对象
*/
public static Image getAppIcon() {
return APP_ICON;
}
/**
* Retrieves the information icon.
*
* @return the information icon
*/
public static FontIcon getInfoIcon() {
return INFO_ICON;
}
/**
* Returns the error icon.
*
* @return the error icon
*/
public static FontIcon getErrorIcon() {
return ERROR_ICON;
}
/**
* Retrieves the warning icon.
*
* @return the warning icon
*/
public static FontIcon getWarningIcon() {
return WARNING_ICON;
}
/**
* Retrieves the question icon.
*
* @return the question icon
*/
public static FontIcon getQuestionIcon() {
return QUESTION_ICON;
}
/**
* Retrieves the success icon.
*
* @return the success icon as a FontIcon object
*/
public static FontIcon getSuccessIcon() {
return SUCCESS_ICON;
}
/**
* 获取应用窗口
*
* @return javafx.stage.Window 应用窗口对象
* @apiNote JnotepadApp.getWindow()
*/
public static Window getAppWindow() {
return ApplicationManager.getInstance().getWindow();
}
/**
* Generates an ImageView with the specified module directory, name, and format.
*
* @param moduleDir the directory where the module is located
* @param name the name of the icon
* @param format the format of the icon
* @return the generated ImageView
*/
public static ImageView icon(String moduleDir, String name, String format) {
return icon(moduleDir + name + format);
}
/**
* Generates an ImageView object with the image specified by the given path.
*
* @param path the path to the image file
* @return the ImageView object with the specified image
*/
public static ImageView icon(String path) {
var img = new Image(ResourceUtil.getResourceAsStream(path));
return new ImageView(img);
}
/**
* Generates an ImageView based on a PNG file.
*
* @param moduleDir the directory of the module
* @param name the name of the PNG file
* @return the generated ImageView
*/
public static ImageView iconByPng(String moduleDir, String name) {
return icon(moduleDir + name + ".png");
}
/**
* Generates an ImageView object for a file icon based on the given PNG name.
*
* @param name the name of the PNG file for the file icon
* @return the ImageView object representing the file icon
*/
public static ImageView fileIconByPng(String name) {
return iconByPng("images/fileIcons/", name);
}
/**
* Generates an ImageView object for a file icon based on the given PNG name.
*
* @param name the name of the PNG file for the file icon
* @return the ImageView object representing the file icon
*/
public static ImageView sidebarIconByPng(String name) {
return iconByPng("images/sidebarIcons/", name);
}
public static Map<String, Node> getIconMap() {
return ICON_MAP;
}
}

View File

@ -0,0 +1 @@
util 存放通用的实用工具代码。

View File

@ -1,12 +0,0 @@
package org.jcnc.jnotepad.constants;
/**
* Constants持有所有共享信息的全局变量
*/
public class Constants {
public static final double SCREEN_WIDTH = 800; //宽度
public static final double SCREEN_LENGTH = 600; //高度
public static final String APP_NAME = "JNotepad"; //名字
public static final String APP_ICON = "/img/icon.png"; //logo地址
}

View File

@ -0,0 +1,28 @@
package org.jcnc.jnotepad.controller;
import org.jcnc.jnotepad.controller.i18n.LocalizationController;
import org.jcnc.jnotepad.controller.plugin.PluginLoader;
/**
* 资源控制器
*
* @author gewuyou
*/
public class ResourceController {
private static final ResourceController INSTANCE = new ResourceController();
private ResourceController() {
}
public static ResourceController getInstance() {
return INSTANCE;
}
public void loadResources() {
// 1. 加载语言
LocalizationController.initLocal();
// 2. 加载插件
PluginLoader.getInstance().loadPlugins();
}
}

View File

@ -0,0 +1 @@
controller 存放控制器相关的代码,包括事件处理、异常处理等。

View File

@ -0,0 +1,243 @@
package org.jcnc.jnotepad.controller.cache;
import com.fasterxml.jackson.core.type.TypeReference;
import org.jcnc.jnotepad.app.common.manager.ApplicationCacheManager;
import org.jcnc.jnotepad.app.utils.JsonUtil;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.jcnc.jnotepad.controller.config.AppConfigController;
import org.jcnc.jnotepad.model.entity.Cache;
import org.slf4j.Logger;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* 缓存控制器
*
* @author gewuyou
*/
public class CacheController {
private static final ApplicationCacheManager APPLICATION_CACHE_MANAGER = ApplicationCacheManager.getInstance();
private static final CacheController INSTANCE = new CacheController();
Logger logger = LoggerUtil.getLogger(this.getClass());
private String cacheDir;
private CacheController() {
cacheDir = Paths.get(AppConfigController.getInstance().getConfig().getRootPath(), ".jnotepad", "caches").toString();
}
public static CacheController getInstance() {
return INSTANCE;
}
public void loadCaches() {
// 如果本地没有缓存的话就创建缓存
if (createCachesIfNotExists()) {
return;
}
// 检查并获取缓存根目录
File cacheFileDir = createCacheRootIfNotExist();
// 获取缓存命名空间
String[] namespaces = cacheFileDir.list();
// 如果缓存根目录下为空则创建缓存
if (Objects.requireNonNull(namespaces).length == 0) {
APPLICATION_CACHE_MANAGER.setCaches(new HashMap<>(16));
return;
}
Map<String, Cache> caches = new HashMap<>(16);
setCaches(namespaces, cacheFileDir, caches);
}
/**
* Sets the caches for the given namespaces.
*
* @param namespaces an array of namespace names
* @param cacheFileDir the directory where the cache files are stored
* @param caches a map of caches to be set
*/
private void setCaches(String[] namespaces, File cacheFileDir, Map<String, Cache> caches) {
for (String namespace : namespaces) {
// 获取命名空间对应的文件夹
File namespaceDir = new File(cacheFileDir, namespace);
// 获取缓存组对应的文件名称列表
String[] groupNames = namespaceDir.list();
// 如果命名空间文件夹下没有文件则删除该文件夹
if (cleanEmptyFileOrFolder(namespaceDir)) {
continue;
}
for (String groupName : Objects.requireNonNull(groupNames)) {
// 获取缓存组对应的文件
File groupFile = new File(namespaceDir, groupName);
// 清理空文件
if (cleanEmptyFileOrFolder(groupFile)) {
continue;
}
// 获取缓存
try {
String cacheJson = Files.readString(groupFile.toPath());
// 获取缓存集合
Map<String, Cache> cacheMap = JsonUtil.fromJsonString(cacheJson, new TypeReference<>() {
});
// 设置缓存
cacheMap.forEach((k, v) -> setUpCache(namespace, groupName, k, v, caches));
} catch (IOException e) {
logger.error("读取缓存文件出错!", e);
try {
Files.delete(cacheFileDir.toPath());
} catch (IOException ignore) {
logger.error("删除失败");
}
}
}
// 设置缓存
APPLICATION_CACHE_MANAGER.setCaches(caches);
}
}
/**
* 设置缓存
*
* @param namespace 命名空间
* @param groupName 缓存组
* @param k 缓存名称
* @param v 缓存类
* @param caches 缓存集合
*/
private void setUpCache(String namespace, String groupName, String k, Cache v, Map<String, Cache> caches) {
// 判断缓存是否过期,没有过期才加载进内存
if (v.getLastReadOrWriteTime() + v.getExpirationTime() > System.currentTimeMillis() || v.getExpirationTime() < 0) {
v.setNamespace(namespace);
v.setGroup(groupName);
v.setName(k);
caches.put(v.getCacheKey(), v);
}
}
/**
* 清理空文件或空文件夹并返回结果
*
* @param fileOrFolder 文件或文件夹
* @return 是否清理
*/
private boolean cleanEmptyFileOrFolder(File fileOrFolder) {
try {
if (fileOrFolder.isFile() && fileOrFolder.length() == 0) {
Files.delete(fileOrFolder.toPath());
logger.info("删除缓存文件:{}", fileOrFolder);
return true;
}
if (fileOrFolder.isDirectory() && Objects.requireNonNull(fileOrFolder.list()).length == 0) {
Files.delete(fileOrFolder.toPath());
logger.info("删除缓存文件夹:{}", fileOrFolder);
return true;
}
} catch (IOException e) {
logger.error("清理缓存文件或文件夹出错!", e);
}
return false;
}
/**
* 写缓存(writeCache)
*/
public void writeCaches() {
writeCaches(APPLICATION_CACHE_MANAGER.getCaches());
}
/**
* 写缓存
*
* @param caches 缓存集合
*/
public void writeCaches(Map<String, Cache> caches) {
// 检查并获取缓存根目录
File cacheFileDir = createCacheRootIfNotExist();
Map<File, Map<String, Cache>> fileMap = new HashMap<>(16);
// 生成缓存
caches.forEach((key, value) -> {
// 判断当前命名空间对应目录是否创建
File namespaceDir = new File(cacheFileDir, value.getNamespace());
if (!namespaceDir.exists()) {
namespaceDir.mkdirs();
}
// 判断当前组文件是否创建
File groupFile = new File(namespaceDir, value.getGroup());
if (!groupFile.exists()) {
try {
boolean flag = groupFile.createNewFile();
if (!flag) {
logger.error("创建缓存文件失败:{}", groupFile);
}
} catch (IOException e) {
logger.error("创建缓存文件失败!", e);
}
}
fileMap.computeIfAbsent(groupFile, k -> new HashMap<>(16));
// 设置需要写入的数据
fileMap.get(groupFile).put(value.getName(), value);
});
Set<File> fileSet = fileMap.keySet();
// 清空原来的缓存
fileSet.forEach(file -> {
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
fileOutputStream.write(new byte[0]);
} catch (IOException e) {
logger.error("清空缓存文件失败!", e);
}
});
// 写入缓存
for (Map.Entry<File, Map<String, Cache>> entry : fileMap.entrySet()) {
try (FileWriter writer = new FileWriter(entry.getKey(), true)) {
writer.write(JsonUtil.toJsonString(entry.getValue()));
} catch (IOException e) {
logger.error("写入缓存文件失败!", e);
}
}
}
/**
* 如果不存在则创建缓存根目录
*
* @return 缓存根目录
*/
private File createCacheRootIfNotExist() {
File cacheFileDir = new File(cacheDir);
if (!cacheFileDir.exists()) {
cacheFileDir.mkdirs();
}
return cacheFileDir;
}
/**
* 如果本地没有缓存则创建缓存
*
* @return 是否创建成功
*/
private boolean createCachesIfNotExists() {
File cacheFileDir = createCacheRootIfNotExist();
if (Objects.requireNonNull(cacheFileDir.list()).length == 0) {
APPLICATION_CACHE_MANAGER.setCaches(new HashMap<>(16));
return true;
}
return false;
}
public String getCacheDir() {
return cacheDir;
}
public void setCacheDir(String cacheDir) {
this.cacheDir = cacheDir;
}
}

View File

@ -0,0 +1,77 @@
package org.jcnc.jnotepad.controller.config;
import org.jcnc.jnotepad.api.core.controller.config.BaseConfigController;
import org.jcnc.jnotepad.app.config.AppConfig;
import java.nio.file.Paths;
import static org.jcnc.jnotepad.app.common.constants.AppConstants.DEFAULT_PROPERTY;
import static org.jcnc.jnotepad.app.common.constants.AppConstants.PROGRAM_FILE_DIRECTORY;
/**
* 应用程序配置文件控制器
*
* @author gewuyou
*/
public class AppConfigController extends BaseConfigController<AppConfig> {
/**
* 配置文件名
*/
public static final String CONFIG_NAME = "JNotepadConfig.json";
private static final AppConfigController INSTANCE = new AppConfigController();
private final String configDir;
public AppConfigController() {
configDir = Paths.get(System.getProperty(DEFAULT_PROPERTY), PROGRAM_FILE_DIRECTORY, SYSTEM_CONFIG_DIR).toString();
loadConfig();
}
public static AppConfigController getInstance() {
return INSTANCE;
}
/**
* 获取配置文件Class类
*
* @return 配置文件Class类
*/
@Override
protected Class<AppConfig> getConfigClass() {
return AppConfig.class;
}
/**
* 获取配置文件名称
*
* @return 配置文件名称
*/
@Override
protected String getConfigName() {
return CONFIG_NAME;
}
/**
* 获取配置文件文件夹路径
*
* @return 配置文件夹路径
*/
@Override
protected String getConfigDir() {
return configDir;
}
/**
* 创建配置文件实体
*
* @return 默认的配置文件实体
* @apiNote 返回默认的配置文件实体用于序列化json
*/
@Override
public AppConfig generateDefaultConfig() {
AppConfig config = new AppConfig();
config.setRootPath(System.getProperty(DEFAULT_PROPERTY));
return config;
}
}

View File

@ -0,0 +1,102 @@
package org.jcnc.jnotepad.controller.config;
import org.jcnc.jnotepad.api.core.controller.config.BaseConfigController;
import org.jcnc.jnotepad.app.config.PluginConfig;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import static org.jcnc.jnotepad.app.common.constants.AppConstants.PROGRAM_FILE_DIRECTORY;
/**
* 插件控制器
*
* @author gewuyou
*/
public class PluginConfigController extends BaseConfigController<PluginConfig> {
/**
* 插件配置文件
*/
public static final String CONFIG_NAME = "pluginConfig.json";
private static final PluginConfigController INSTANCE = new PluginConfigController();
private String configDir;
private String pluginsDir;
private PluginConfigController() {
String rootPath = AppConfigController.getInstance().getConfig().getRootPath();
configDir = Paths.get(rootPath, PROGRAM_FILE_DIRECTORY, ROOT_CONFIG_DIR).toString();
setPluginsDir(Paths.get(rootPath, PROGRAM_FILE_DIRECTORY, "plugins").toString());
loadConfig();
}
public static PluginConfigController getInstance() {
return INSTANCE;
}
/**
* 获取配置文件Class类
*
* @return 配置文件Class类
*/
@Override
protected Class<PluginConfig> getConfigClass() {
return PluginConfig.class;
}
/**
* 获取配置文件名称
*
* @return 配置文件名称
*/
@Override
protected String getConfigName() {
return CONFIG_NAME;
}
/**
* 获取配置文件文件夹路径
*
* @return 配置文件夹路径
*/
@Override
protected String getConfigDir() {
return configDir;
}
public void setConfigDir(String configDir) {
this.configDir = configDir;
}
/**
* 创建配置文件实体
*
* @return 默认的配置文件实体
* @apiNote 返回默认的配置文件实体用于序列化json
*/
@Override
public PluginConfig generateDefaultConfig() {
PluginConfig pluginConfig = new PluginConfig();
pluginConfig.setPlugins(new ArrayList<>());
return pluginConfig;
}
/**
* 获取插件路径
*
* @return 插件路径
*/
public Path getPlungsPath() {
return Paths.get(getPluginsDir());
}
public String getPluginsDir() {
return pluginsDir;
}
public void setPluginsDir(String pluginsDir) {
this.pluginsDir = pluginsDir;
}
}

View File

@ -0,0 +1,206 @@
package org.jcnc.jnotepad.controller.config;
import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyCombination;
import org.jcnc.jnotepad.api.core.controller.config.BaseConfigController;
import org.jcnc.jnotepad.app.config.UserConfig;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.jcnc.jnotepad.model.entity.ShortcutKey;
import org.slf4j.Logger;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.jcnc.jnotepad.app.common.constants.AppConstants.PROGRAM_FILE_DIRECTORY;
import static org.jcnc.jnotepad.app.common.constants.TextConstants.CHINESE;
/**
* 应用程序配置控制器
*
* <p>该类负责管理应用程序的配置文件包括加载持久化和更新配置信息等操作</p>
*
* @author songdragon
*/
public class UserConfigController extends BaseConfigController<UserConfig> {
/**
* 配置文件名
*/
public static final String CONFIG_NAME = "userConfig.json";
private static final String CTRL_N = "ctrl+n";
private static final String CTRL_O = "ctrl+o";
private static final String CTRL_S = "ctrl+s";
private static final String CTRL_ALT_S = "ctrl+alt+s";
private static final String ALT_S = "alt+s";
private static final UserConfigController INSTANCE = new UserConfigController();
private final List<Map<String, MenuItem>> menuItems = new ArrayList<>();
Logger logger = LoggerUtil.getLogger(this.getClass());
private String configDir;
private UserConfigController() {
configDir = Paths.get(AppConfigController.getInstance().getConfig().getRootPath(), PROGRAM_FILE_DIRECTORY, ROOT_CONFIG_DIR).toString();
loadConfig();
}
/**
* 获取 UserConfigController 的实例
*
* @return UserConfigController 的实例
*/
public static UserConfigController getInstance() {
return INSTANCE;
}
/**
* 创建 ShortcutKey 对象
*
* @param buttonName 按钮名称
* @param shortcutKeyValue 快捷键值
* @return ShortcutKey 对象
*/
private static ShortcutKey createShortcutKey(String buttonName, String shortcutKeyValue) {
ShortcutKey shortcutKey = new ShortcutKey();
shortcutKey.setButtonName(buttonName);
shortcutKey.setShortcutKeyValue(shortcutKeyValue);
return shortcutKey;
}
/**
* 获取配置文件Class类
*
* @return 配置文件Class类
*/
@Override
protected Class<UserConfig> getConfigClass() {
return UserConfig.class;
}
/**
* 获取配置文件名称
*
* @return 配置文件名称
*/
@Override
protected String getConfigName() {
return CONFIG_NAME;
}
/**
* 获取配置文件文件夹路径
*
* @return 配置文件夹路径
*/
@Override
protected String getConfigDir() {
return configDir;
}
public void setConfigDir(String configDir) {
this.configDir = configDir;
}
/**
* 创建配置文件实体
*
* @return 默认的配置文件实体
* @apiNote 返回默认的配置文件实体用于序列化json
*/
@Override
public UserConfig generateDefaultConfig() {
UserConfig config = new UserConfig();
config.setLanguage(CHINESE);
config.setTextWrap(false);
List<ShortcutKey> shortcutKeys = new ArrayList<>();
shortcutKeys.add(createShortcutKey("newItem", CTRL_N));
shortcutKeys.add(createShortcutKey("openItem", CTRL_O));
shortcutKeys.add(createShortcutKey("saveItem", CTRL_S));
shortcutKeys.add(createShortcutKey("saveAsItem", CTRL_ALT_S));
shortcutKeys.add(createShortcutKey("lineFeedItem", ""));
shortcutKeys.add(createShortcutKey("openConfigItem", ALT_S));
shortcutKeys.add(createShortcutKey("pluginManager", ""));
shortcutKeys.add(createShortcutKey("countItem", ""));
shortcutKeys.add(createShortcutKey("aboutItem", ""));
config.setShortcutKey(shortcutKeys);
return config;
}
/**
* 获取自动换行设置默认自动换行
*
* @return true自动换行false不自动换行
*/
public boolean getAutoLineConfig() {
return getConfig().isTextWrap();
}
public void setAutoLineConfig(boolean isAutoLine) {
getConfig().setTextWrap(isAutoLine);
}
/**
* 更新配置文件中的语言设置
*
* @param language 更新后的语言设置
*/
public void updateLanguage(String language) {
if (getLanguage().equals(language)) {
return;
}
getConfig().setLanguage(language);
writeConfig();
}
/**
* 获取当前的语言设置
*
* @return 语言设置
*/
public String getLanguage() {
return getConfig().getLanguage();
}
/**
* 获取快捷键设置
*
* @return 快捷键设置列表
*/
public List<ShortcutKey> getShortcutKey() {
return getConfig().getShortcutKey();
}
public void initAllShortcutKeys() {
menuItems.forEach(this::initShortcutKeys);
}
/**
* 初始化快捷键
*/
public void initShortcutKeys(Map<String, MenuItem> menuItemMap) {
List<MenuItem> itemsToUnbind = new ArrayList<>();
List<ShortcutKey> shortcutKeyConfigs = getShortcutKey();
for (ShortcutKey shortcutKey : shortcutKeyConfigs) {
// 保证json的key必须和变量名一致
MenuItem menuItem = menuItemMap.get(shortcutKey.getButtonName());
String shortKeyValue = shortcutKey.getShortcutKeyValue();
if ("".equals(shortKeyValue) && menuItem != null) {
itemsToUnbind.add(menuItem);
continue;
}
if (menuItem != null) {
logger.info("功能名称:{}->快捷键:{}", menuItem.getText(), shortKeyValue);
// 动态添加快捷键
menuItem.setAccelerator(KeyCombination.keyCombination(shortKeyValue));
}
}
// 解绑需要解绑的快捷键
itemsToUnbind.forEach(menuItem -> menuItem.setAccelerator(null));
}
public List<Map<String, MenuItem>> getMenuItems() {
return menuItems;
}
}

View File

@ -1,18 +0,0 @@
package org.jcnc.jnotepad.controller.event.handler;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.TextArea;
public class LineFeed implements EventHandler<ActionEvent> {
private final TextArea textArea;
public LineFeed(TextArea textArea) {
this.textArea = textArea;
}
@Override
public void handle(ActionEvent event) {
String text = textArea.getText();
textArea.setText(text + "\n");
}
}

View File

@ -1,25 +0,0 @@
package org.jcnc.jnotepad.controller.event.handler;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Tab;
import javafx.scene.control.TextArea;
import org.jcnc.jnotepad.controller.manager.Controller;
import org.jcnc.jnotepad.view.manager.ViewManager;
import static org.jcnc.jnotepad.view.manager.ViewManager.tabPane;
public class NewFile implements EventHandler<ActionEvent> {
@Override
public void handle(ActionEvent event) {
Controller controller = new Controller();
TextArea textArea = new TextArea(); // 创建新的文本编辑区
Tab tab = new Tab("新建文本 " + ++ViewManager.tabIndex); // 创建新的Tab页
tab.setContent(textArea);
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
controller.updateStatusLabel(textArea);
// 更新编码信息
controller.upDateEncodingLabel(textArea.getText()); // 更新文本编码信息
}
}

View File

@ -1,43 +0,0 @@
package org.jcnc.jnotepad.controller.event.handler;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.TextArea;
import javafx.stage.FileChooser;
import org.jcnc.jnotepad.controller.manager.Controller;
import java.io.File;
import static org.jcnc.jnotepad.view.manager.ViewManager.tabPane;
// 打开文件事件处理器
public class OpenFile implements EventHandler<ActionEvent> {
@Override
public void handle(ActionEvent event) {
Controller controller = new Controller();
FileChooser fileChooser = new FileChooser();
File file = fileChooser.showOpenDialog(null);
if (file != null) {
Task<Void> openFileTask = new Task<>() {
@Override
protected Void call() {
controller.getText(file);
controller.upDateEncodingLabel(((TextArea) tabPane.getSelectionModel().getSelectedItem().getContent()).getText());
return null;
}
};
openFileTask.setOnSucceeded(e -> {
// 在需要时处理成功
});
openFileTask.setOnFailed(e -> {
// 在需要时处理失败
});
Thread thread = new Thread(openFileTask);
thread.start();
}
}
}

View File

@ -1,43 +0,0 @@
package org.jcnc.jnotepad.controller.event.handler;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Tab;
import javafx.scene.control.TextArea;
import javafx.stage.FileChooser;
import org.jcnc.jnotepad.controller.manager.Controller;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import static org.jcnc.jnotepad.view.manager.ViewManager.tabPane;
public class SaveAsFile implements EventHandler<ActionEvent> {
@Override
public void handle(ActionEvent event) {
Controller controller = new Controller();
Tab selectedTab = tabPane.getSelectionModel().getSelectedItem();
if (selectedTab != null) {
FileChooser fileChooser = new FileChooser();
fileChooser.setInitialFileName("新建文本");
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("文本文档", "*.txt"));
File file = fileChooser.showSaveDialog(null);
if (file != null) {
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
TextArea textArea = (TextArea) selectedTab.getContent(); // 获取当前Tab页的文本编辑区
controller.autoSave(textArea);// 自动保存
String text = textArea.getText();
writer.write(text); // 写入文件内容
writer.flush();
writer.close();
selectedTab.setText(file.getName()); // 更新Tab页标签上的文件名
selectedTab.setUserData(file); // 将文件对象保存到Tab页的UserData中
} catch (IOException ignored) {
}
}
}
}
}

View File

@ -1,37 +0,0 @@
package org.jcnc.jnotepad.controller.event.handler;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Tab;
import javafx.scene.control.TextArea;
import org.jcnc.jnotepad.controller.manager.Controller;
import java.io.*;
import java.nio.charset.StandardCharsets;
import static org.jcnc.jnotepad.view.manager.ViewManager.tabPane;
// 保存文件事件处理器
public class SaveFile implements EventHandler<ActionEvent> {
@Override
public void handle(ActionEvent event) {
Controller controller = new Controller();
Tab selectedTab = tabPane.getSelectionModel().getSelectedItem();
if (selectedTab != null) {
File file = (File) selectedTab.getUserData();
if (file == null) {
controller.saveAsFile();
} else {
try {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8));
TextArea textArea = (TextArea) selectedTab.getContent();
String text = textArea.getText();
writer.write(text);
writer.flush();
writer.close();
} catch (IOException ignored) {
}
}
}
}
}

View File

@ -0,0 +1,25 @@
package org.jcnc.jnotepad.controller.event.handler.menuitem;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import static org.jcnc.jnotepad.app.utils.TabUtil.addNewFileTab;
/**
* 新建文件事件的事件处理程序
*
* <p>当用户选择新建文件时将创建一个新的文本编辑区并在Tab页中显示</p>
*
* @author 许轲
*/
public class NewFile implements EventHandler<ActionEvent> {
/**
* 处理新建文件事件
*
* @param event 事件对象
*/
@Override
public void handle(ActionEvent event) {
addNewFileTab();
}
}

View File

@ -0,0 +1,33 @@
package org.jcnc.jnotepad.controller.event.handler.menuitem;
import javafx.event.ActionEvent;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.jcnc.jnotepad.controller.config.UserConfigController;
import java.io.File;
import static org.jcnc.jnotepad.app.utils.TabUtil.openFileToTab;
/**
* 打开配置文件事件处理程序
*
* <p>该事件处理程序用于打开配置文件</p>
*
* @author gewuyou
*/
public class OpenConfig extends OpenFile {
/**
* 处理打开配置文件事件
*
* @param actionEvent 事件对象
*/
@Override
public void handle(ActionEvent actionEvent) {
// 显示文件选择对话框并获取配置文件
File file = UserConfigController.getInstance().getConfigPath().toFile();
LoggerUtil.getLogger(this.getClass()).info("已调用打开配置文件功能, {}", file);
// 创建打开文件的任务并启动线程执行任务
openFileToTab(file);
}
}

View File

@ -0,0 +1,57 @@
package org.jcnc.jnotepad.controller.event.handler.menuitem;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.stage.FileChooser;
import org.jcnc.jnotepad.app.common.constants.TextConstants;
import org.jcnc.jnotepad.app.common.manager.ApplicationCacheManager;
import org.jcnc.jnotepad.app.i18n.UiResourceBundle;
import org.jcnc.jnotepad.app.utils.UiUtil;
import org.jcnc.jnotepad.model.entity.Cache;
import org.jcnc.jnotepad.model.enums.CacheExpirationTime;
import org.jcnc.jnotepad.ui.component.stage.dialog.factory.impl.BasicFileChooserFactory;
import java.io.File;
import static org.jcnc.jnotepad.app.utils.TabUtil.openFileToTab;
/**
* 打开文件的事件处理程序
* <p>
* 当用户选择打开文件时将创建一个新的文本编辑区并在Tab页中显示
*
* @author 许轲
*/
public class OpenFile implements EventHandler<ActionEvent> {
private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance();
/**
* 处理打开文件事件
*
* @param event 事件对象
*/
@Override
public void handle(ActionEvent event) {
// 获取缓存
Cache cache = CACHE_MANAGER.getCache("folder", "openFile");
// 显示文件选择对话框并获取选中的文件
File file = BasicFileChooserFactory.getInstance().createFileChooser(
UiResourceBundle.getContent(TextConstants.OPEN),
null,
cache == null ? null : new File((String) cache.getCacheData()),
new FileChooser.ExtensionFilter("All types", "*.*"))
.showOpenDialog(UiUtil.getAppWindow());
if (file == null) {
return;
}
// 设置缓存
if (cache == null) {
CACHE_MANAGER.addCache(CACHE_MANAGER.createCache("folder", "openFile", file.getParent(), CacheExpirationTime.NEVER_EXPIRES.getValue()));
} else {
cache.setCacheData(file.getParent());
CACHE_MANAGER.addCache(cache);
}
openFileToTab(file);
}
}

View File

@ -0,0 +1,22 @@
package org.jcnc.jnotepad.controller.event.handler.menuitem;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import org.jcnc.jnotepad.ui.views.manager.CenterTabPaneManager;
import static org.jcnc.jnotepad.app.utils.TabUtil.rename;
/**
* 重命名文件事件处理器
* <p>
* 当用户选择重命名文件时如果当前标签页关联文件则重命名关联文件
* 否则重命名标签页
*
* @author gewuyou
*/
public class RenameFile implements EventHandler<ActionEvent> {
@Override
public void handle(ActionEvent actionEvent) {
rename(CenterTabPaneManager.getInstance().getSelected());
}
}

View File

@ -0,0 +1,30 @@
package org.jcnc.jnotepad.controller.event.handler.menuitem;
import javafx.event.ActionEvent;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.jcnc.jnotepad.ui.views.manager.CenterTabPaneManager;
import static org.jcnc.jnotepad.app.utils.TabUtil.saveAsFile;
/**
* 保存文件事件处理器
* <p>
* 当用户选择保存文件时当用户选择另存为文件菜单或按钮时
* 会弹出一个保存文件对话框用户选择保存位置和文件名后
* 将当前文本编辑区的内容保存到指定文件中
* 并更新Tab页上的文件名和UserData
*
* @author 许轲
*/
public class SaveAsFile extends SaveFile {
/**
* 处理保存文件事件
*
* @param event 事件对象
*/
@Override
public void handle(ActionEvent event) {
LoggerUtil.getLogger(SaveAsFile.class).info("已调用另存为功能");
saveAsFile(CenterTabPaneManager.getInstance().getSelected());
}
}

View File

@ -0,0 +1,31 @@
package org.jcnc.jnotepad.controller.event.handler.menuitem;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import org.jcnc.jnotepad.ui.views.manager.CenterTabPaneManager;
import static org.jcnc.jnotepad.app.utils.TabUtil.saveFile;
/**
* 保存文件事件处理程序
* <p>
* 当用户选择保存文件时如果当前标签页是关联文件则自动保存
* 否则调用另存为方法
*
* @author gewuyou
*/
public class SaveFile implements EventHandler<ActionEvent> {
/**
* 处理保存文件事件
*
* @param actionEvent 事件对象
* @apiNote 当用户选择保存文件时如果当前标签页是关联文件则自动保存
* 否则调用另存为方法
*/
@Override
public void handle(ActionEvent actionEvent) {
// 保存当前选中的标签页
saveFile(CenterTabPaneManager.getInstance().getSelected());
}
}

View File

@ -0,0 +1,23 @@
package org.jcnc.jnotepad.controller.event.handler.toolbar;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import org.jcnc.jnotepad.ui.views.manager.DirectorySidebarManager;
/**
* 文件树按钮
*
* <p>文件树按钮事件的事件处理程序</p>
*
* @author cccqyu
*/
public class DirTreeBtn implements EventHandler<ActionEvent> {
private static final DirectorySidebarManager DIRECTORY_SIDEBAR_MANAGER = DirectorySidebarManager.getInstance();
@Override
public void handle(ActionEvent actionEvent) {
DIRECTORY_SIDEBAR_MANAGER.controlShow();
}
}

View File

@ -0,0 +1,71 @@
package org.jcnc.jnotepad.controller.event.handler.toolbar;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import org.jcnc.jnotepad.app.common.constants.TextConstants;
import org.jcnc.jnotepad.app.common.manager.ApplicationCacheManager;
import org.jcnc.jnotepad.app.i18n.UiResourceBundle;
import org.jcnc.jnotepad.app.utils.FileUtil;
import org.jcnc.jnotepad.app.utils.UiUtil;
import org.jcnc.jnotepad.model.entity.Cache;
import org.jcnc.jnotepad.model.entity.DirFileModel;
import org.jcnc.jnotepad.model.enums.CacheExpirationTime;
import org.jcnc.jnotepad.ui.component.stage.dialog.factory.impl.BasicDirectoryChooserFactory;
import org.jcnc.jnotepad.ui.views.manager.DirectorySidebarManager;
import java.io.File;
/**
* 打开文件夹處理器
*
* <p>当用户选择打开文件夹时将创建一个左侧树型结构目录</p>
*
* @author cccqyu
*/
public class OpenDirectory implements EventHandler<ActionEvent> {
public static final String GROUP = "directory";
private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance();
private static final DirectorySidebarManager DIRECTORY_SIDEBAR_MANAGER = DirectorySidebarManager.getInstance();
@Override
public void handle(ActionEvent actionEvent) {
// 获取缓存
Cache cache = CACHE_MANAGER.getCache(GROUP, "openDirectory");
// 显示文件选择对话框并获取选中的文件
File file = BasicDirectoryChooserFactory.getInstance().createDirectoryChooser(
UiResourceBundle.getContent(TextConstants.OPEN),
cache == null ? null : new File((String) cache.getCacheData())
)
.showDialog(UiUtil.getAppWindow());
if (file == null) {
return;
}
// 设置缓存
if (cache == null) {
CACHE_MANAGER.addCache(CACHE_MANAGER.createCache(GROUP, "openDirectory", file.getAbsolutePath(), CacheExpirationTime.NEVER_EXPIRES.getValue()));
} else {
cache.setCacheData(file.getParent());
CACHE_MANAGER.addCache(cache);
}
flushDirSidebar(file);
}
/**
* Flushes the directory sidebar with the given file.
*
* @param file the file to be converted into an entity class
*/
public void flushDirSidebar(File file) {
// 将文件转为实体类
DirFileModel dirFileModel = FileUtil.getDirFileModel(file);
// 打开侧边栏
DIRECTORY_SIDEBAR_MANAGER.controlShow(true);
// 设置文件树功能
DIRECTORY_SIDEBAR_MANAGER.setTreeView(dirFileModel);
// 缓存已打开的文件夹
CACHE_MANAGER.addCache(CACHE_MANAGER.createCache(GROUP, "folderThatWasOpened", dirFileModel, CacheExpirationTime.NEVER_EXPIRES.getValue()));
}
}

View File

@ -0,0 +1,21 @@
package org.jcnc.jnotepad.controller.event.handler.toolbar;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import org.jcnc.jnotepad.ui.views.manager.BuildPanelManager;
/**
* 终端处理器
*
* @author cccqyu
*/
public class RunBtn implements EventHandler<ActionEvent> {
private static final BuildPanelManager BUILD_PANEL_MANAGER = BuildPanelManager.getInstance();
@Override
public void handle(ActionEvent event) {
BUILD_PANEL_MANAGER.controlShow();
}
}

View File

@ -0,0 +1,34 @@
package org.jcnc.jnotepad.controller.event.handler.toolbar;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import org.jcnc.jnotepad.ui.component.stage.setting.SetStage;
/**
* 设置按钮事件的事件处理程序
*
* <p>当用户点击设置按钮时将打开设置窗口</p>
*
* @author 许轲
*/
public class SetBtn implements EventHandler<ActionEvent> {
/**
* 标志变量跟踪Stage是否已创建
*/
private boolean isStageCreated = false;
/**
* 打开设置窗口处理事件
*
* @param event 事件对象
*/
@Override
public void handle(ActionEvent event) {
if (!isStageCreated) {
SetStage.getInstance().openSetStage();
// 设置标志变量为true表示Stage已创建
isStageCreated = true;
}
}
}

View File

@ -0,0 +1,29 @@
package org.jcnc.jnotepad.controller.exception;
/**
* 应用异常类用于处理应用程序中的异常情况
*
* <p>应用异常是一个运行时异常通常用于捕获和处理应用程序中的不可预料的错误和异常情况</p>
*
* @author gewuyou
*/
public class AppException extends RuntimeException {
/**
* 构造一个应用异常对象
*
* @param message 异常消息
*/
public AppException(String message) {
super(message);
}
/**
* 构造一个应用异常对象
*
* @param cause 异常的原因
*/
public AppException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,96 @@
package org.jcnc.jnotepad.controller.i18n;
import org.jcnc.jnotepad.JnotepadApp;
import org.jcnc.jnotepad.app.i18n.UiResourceBundle;
import org.jcnc.jnotepad.controller.config.UserConfigController;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import static org.jcnc.jnotepad.app.common.constants.TextConstants.CHINESE;
import static org.jcnc.jnotepad.app.common.constants.TextConstants.ENGLISH;
/**
* 本地化控制器
*
* <p>该类负责处理应用程序的本地化配置包括语言和区域设置</p>
*
* @author gewuyou
* @see JnotepadApp
*/
public class LocalizationController {
private static final LocalizationController LOCALIZATION_CONFIG = new LocalizationController();
private static final Map<String, Locale> SUPPORT_LOCALES;
private static final Map<Locale, String> SUPPORT_LANGUAGES;
static {
Locale.setDefault(Locale.ENGLISH);
SUPPORT_LOCALES = new LinkedHashMap<>();
SUPPORT_LOCALES.put(CHINESE, Locale.CHINESE);
SUPPORT_LOCALES.put(ENGLISH, Locale.ENGLISH);
SUPPORT_LANGUAGES = new HashMap<>();
SUPPORT_LANGUAGES.put(Locale.CHINESE, CHINESE);
SUPPORT_LANGUAGES.put(Locale.ENGLISH, ENGLISH);
}
private final UserConfigController userConfigController;
private LocalizationController() {
this.userConfigController = UserConfigController.getInstance();
}
/**
* 获取当前语言配置
*
* @return 当前语言的Locale对象
*/
public static Locale getCurrentLocal() {
return Locale.getDefault();
}
/**
* 设置当前语言配置
*
* @param locale 当前语言的Locale对象
*/
public static void setCurrentLocal(Locale locale) {
if (locale != null && locale.equals(getCurrentLocal())) {
// 要更新的语言与当前语言一致则不执行
return;
}
if (locale == null) {
locale = SUPPORT_LOCALES.get(LOCALIZATION_CONFIG.getLanguage());
}
if (locale == null) {
locale = getCurrentLocal();
}
Locale.setDefault(locale);
UiResourceBundle.getInstance().resetLocal(getCurrentLocal());
LOCALIZATION_CONFIG.setLanguage(SUPPORT_LANGUAGES.get(locale));
}
/**
* 初始化语言配置
*/
public static void initLocal() {
setCurrentLocal(null);
}
/**
* 查询当前语言配置
*
* @return appConfig中的当前语言配置
*/
public String getLanguage() {
return userConfigController.getLanguage();
}
private void setLanguage(String language) {
userConfigController.updateLanguage(language);
}
}

View File

@ -1,223 +1,71 @@
package org.jcnc.jnotepad.controller.manager;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Tab;
import javafx.scene.control.TextArea;
import javafx.stage.FileChooser;
import org.jcnc.jnotepad.Interface.ControllerInterface;
import org.jcnc.jnotepad.LunchApp;
import org.jcnc.jnotepad.controller.event.handler.*;
import org.jcnc.jnotepad.app.common.manager.ApplicationCacheManager;
import org.jcnc.jnotepad.ui.component.module.interfaces.ControllerAble;
import java.io.*;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static org.jcnc.jnotepad.view.manager.ViewManager.*;
import static org.jcnc.jnotepad.tool.EncodingDetector.detectEncoding;
import static org.jcnc.jnotepad.app.utils.TabUtil.addNewFileTab;
import static org.jcnc.jnotepad.app.utils.TabUtil.openFileToTab;
public class Controller implements ControllerInterface {
/**
* 控制器类实现 ControllerAble 接口用于管理文本编辑器的各种操作和事件处理
*
* @author 许轲
*/
public class Controller implements ControllerAble<List<String>> {
private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance();
private static final Controller INSTANCE = new Controller();
private Controller() {
}
/**
* 获取 Controller 的唯一实例
*
* @return Controller 的实例
*/
public static Controller getInstance() {
return INSTANCE;
}
/**
* 打开关联文件并创建文本区域
*
* @param rawParameters 原始参数列表
*/
@Override
public TextArea openAssociatedFileAndCreateTextArea(List<String> rawParameters) {
public void openAssociatedFileAndCreateTextArea(List<String> rawParameters) {
// 获取上次打开的页面
Optional<Object> cacheData = Optional.ofNullable(CACHE_MANAGER.getCacheData("tabs", "centerTabs"));
// 判空
List<String> fileTab = (List<String>) cacheData.orElse(Collections.emptyList());
// 打开上次打开的标签页
fileTab.forEach(filePath -> openFileToTab(new File(filePath)));
if (!rawParameters.isEmpty()) {
String filePath = rawParameters.get(0);
openAssociatedFile(filePath);
return;
}
TextArea textArea = createNewTextArea();
configureTextArea(textArea);
return textArea;
}
@Override
public EventHandler<ActionEvent> getLineFeedEventHandler(TextArea textArea) {
return new LineFeed(textArea);
}
@Override
public EventHandler<ActionEvent> getNewFileEventHandler(TextArea textArea) {
return new NewFile();
}
@Override
public EventHandler<ActionEvent> getOpenFileEventHandler() {
return new OpenFile();
}
@Override
public void autoSave(TextArea textArea) {
textArea.textProperty().addListener((observable, oldValue, newValue) -> {
Tab tab = tabPane.getSelectionModel().getSelectedItem();
if (tab != null) {
File file = (File) tab.getUserData();
if (file != null) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
writer.write(newValue);
} catch (IOException ignored) {
}
}
}
});
}
@Override
public EventHandler<ActionEvent> getSaveFileEventHandler() {
return new SaveFile();
}
@Override
public EventHandler<ActionEvent> getSaveAsFileEventHandler() {
return new SaveAsFile();
}
@Override
public void saveAsFile() {
Tab selectedTab = tabPane.getSelectionModel().getSelectedItem();
if (selectedTab != null) {
FileChooser fileChooser = new FileChooser();
fileChooser.setInitialFileName("新建文本");
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("文本文档", "*.txt"));
File file = fileChooser.showSaveDialog(null);
if (file != null) {
saveFile();
}
if (fileTab.isEmpty()) {
addNewFileTab();
}
}
@Override
public void updateStatusLabel(TextArea textArea) {
int caretPosition = textArea.getCaretPosition();
int row = getRow(caretPosition, textArea.getText());
int column = getColumn(caretPosition, textArea.getText());
int length = textArea.getLength();
statusLabel.setText("行: " + row + " \t列: " + column + " \t字数: " + length);
}
/**
* 打开关联文件
*
* @param filePath 文件路径
*/
@Override
public void openAssociatedFile(String filePath) {
File file = new File(filePath);
if (file.exists() && file.isFile()) {
LunchApp.isRelevance = false;
openFile(file);
openFileToTab(file);
}
}
@Override
public void getText(File file) {
TextArea textArea = createNewTextArea();
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
StringBuilder textBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
textBuilder.append(line).append("\n");
}
String text = textBuilder.toString();
Platform.runLater(() -> {
textArea.setText(text);
Tab tab = createNewTab(file.getName(), textArea);
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
updateStatusLabel(textArea);
autoSave(textArea);
});
} catch (IOException ignored) {
}
}
@Override
public void upDateEncodingLabel(String text) {
String encoding = detectEncoding(text);
enCodingLabel.setText("\t编码: " + encoding);
}
@Override
public int getRow(int caretPosition, String text) {
caretPosition = Math.min(caretPosition, text.length());
String substring = text.substring(0, caretPosition);
int count = 0;
for (char c : substring.toCharArray()) {
if (c == '\n') {
count++;
}
}
return count + 1;
}
@Override
public int getColumn(int caretPosition, String text) {
return caretPosition - text.lastIndexOf("\n", caretPosition - 1);
}
@Override
public void initTabPane() {
Controller controller = new Controller();
tabPane.getSelectionModel().selectedItemProperty().addListener((observable, oldTab, newTab) -> {
if (newTab != null) {
// 获取新选定的标签页并关联的文本区域
TextArea textArea = (TextArea) newTab.getContent();
// 更新状态标签
controller.updateStatusLabel(textArea);
// 监听文本光标位置的变化更新状态标签
textArea.caretPositionProperty().addListener((caretObservable, oldPosition, newPosition) -> controller.updateStatusLabel(textArea));
// 更新编码标签
controller.upDateEncodingLabel(textArea.getText());
}
});
}
private void configureTextArea(TextArea textArea) {
textArea.setWrapText(true);
upDateEncodingLabel(textArea.getText());
updateStatusLabel(textArea);
textArea.textProperty().addListener((observable, oldValue, newValue) -> updateStatusLabel(textArea));
autoSave(textArea);
}
private TextArea createNewTextArea() {
return new TextArea();
}
private Tab createNewTab(String tabName, TextArea textArea) {
Tab tab = new Tab(tabName);
tab.setContent(textArea);
tab.setUserData(null);
return tab;
}
private Task<Void> createOpenFileTask(File file) {
TextArea textArea = createNewTextArea();
return new Task<>() {
@Override
protected Void call() {
getText(file);
upDateEncodingLabel(textArea.getText());
return null;
}
};
}
private void openFile(File file) {
Task<Void> openFileTask = createOpenFileTask(file);
Thread thread = new Thread(openFileTask);
thread.start();
}
private void saveFile() {
new SaveFile();
}
}

View File

@ -0,0 +1,285 @@
package org.jcnc.jnotepad.controller.plugin;
import org.jcnc.jnotepad.app.common.manager.ThreadPoolManager;
import org.jcnc.jnotepad.app.utils.JsonUtil;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.jcnc.jnotepad.controller.config.PluginConfigController;
import org.jcnc.jnotepad.controller.exception.AppException;
import org.jcnc.jnotepad.controller.plugin.interfaces.Plugin;
import org.jcnc.jnotepad.controller.plugin.manager.PluginManager;
import org.jcnc.jnotepad.model.entity.PluginDescriptor;
import org.slf4j.Logger;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
/**
* 插件加载类
*
* @author gewuyou
*/
public class PluginLoader {
private static final PluginLoader INSTANCE = new PluginLoader();
private static final ExecutorService THREAD_POOL = ThreadPoolManager.getThreadPool();
Logger logger = LoggerUtil.getLogger(this.getClass());
/**
* 从插件jar包中读取 json 文件到 PluginDescriptor
*
* @param pluginJar jar
*/
public static PluginDescriptor readPlugin(File pluginJar) throws IOException {
InputStream is;
StringBuilder sb;
try (JarFile jarFile = new JarFile(pluginJar)) {
ZipEntry zipEntry = jarFile.getEntry("META-INF/plugin.json");
is = jarFile.getInputStream(zipEntry);
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
String temp;
sb = new StringBuilder();
while ((temp = br.readLine()) != null) {
sb.append(temp);
}
}
}
return JsonUtil.OBJECT_MAPPER.readValue(sb.toString(), PluginDescriptor.class);
}
public static PluginLoader getInstance() {
return INSTANCE;
}
/**
* 检查插件
*
* @param configPluginDescriptors 配置文件插件信息
* @param pluginDescriptor 插件信息类
* @param pluginDescriptors 插件信息集合
* @return boolean 是否检查通过
* @apiNote
* @since 2023/9/16 14:04
*/
private static boolean checkPlugin(List<PluginDescriptor> configPluginDescriptors, PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors) {
// 如果应用程序配置文件中没有该插件则默认禁用
if (pluginDoesNotExist(pluginDescriptor, configPluginDescriptors)) {
disabledByDefault(configPluginDescriptors, pluginDescriptor, pluginDescriptors);
THREAD_POOL.submit(() -> {
PluginConfigController.getInstance().writeConfig();
ThreadPoolManager.threadContSelfSubtracting();
});
return true;
}
// 如果应用程序配置文件中该插件禁用则不加载
for (PluginDescriptor configPluginDescriptor : configPluginDescriptors) {
if (disableDoNotLoad(pluginDescriptor, pluginDescriptors, configPluginDescriptor)) {
return true;
}
}
// 判断该插件是否已经加载
return loaded(pluginDescriptor, pluginDescriptors);
}
/**
* 插件不存在
*
* @param pluginDescriptor 插件描述类
* @param configPluginDescriptors 配置文件插件信息集合
* @return boolean 插件不存在
* @apiNote
* @since 2023/9/19 19:16
*/
private static boolean pluginDoesNotExist(PluginDescriptor pluginDescriptor, List<PluginDescriptor> configPluginDescriptors) {
for (PluginDescriptor configPluginDescriptor : configPluginDescriptors) {
if (configPluginDescriptor.getId().equals(pluginDescriptor.getId())) {
return false;
}
}
return true;
}
private static boolean loaded(PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors) {
Iterator<PluginDescriptor> iterator = pluginDescriptors.iterator();
while (iterator.hasNext()) {
PluginDescriptor plugin = iterator.next();
if (plugin.getId().equals(pluginDescriptor.getId())) {
if (plugin.getVersion().equals(pluginDescriptor.getVersion())) {
return true;
}
// 如果当前插件版本更低则更新
if (plugin.getVersion().compareTo(pluginDescriptor.getVersion()) < 0) {
// 删除当前的插件
iterator.remove();
} else {
throw new AppException("当前加载的插件版本低于本地版本!");
}
}
}
return false;
}
/**
* 如果插件被禁用则不加载
*
* @param pluginDescriptor 插件描述类
* @param pluginDescriptors 插件描述类集合
* @param configPluginDescriptor 配置文件插件信息
* @return boolean
* @apiNote
* @since 2023/9/19 18:45
*/
private static boolean disableDoNotLoad(PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors, PluginDescriptor configPluginDescriptor) {
if (configPluginDescriptor.getId().equals(pluginDescriptor.getId()) && !configPluginDescriptor.isEnabled()) {
pluginDescriptor.setEnabled(false);
pluginDescriptors.add(pluginDescriptor);
return true;
}
return false;
}
/**
* 默认禁用
*
* @param configPluginDescriptors 配置文件插件信息
* @param pluginDescriptor 插件描述类
* @param pluginDescriptors 插件描述类集合
* @apiNote
* @since 2023/9/19 18:48
*/
private static void disabledByDefault(List<PluginDescriptor> configPluginDescriptors, PluginDescriptor pluginDescriptor, List<PluginDescriptor> pluginDescriptors) {
pluginDescriptor.setEnabled(false);
pluginDescriptors.add(pluginDescriptor);
configPluginDescriptors.add(pluginDescriptor);
}
/**
* 加载插件
*
* @param pluginFilePath 插件文件的路径
*/
public void loadPluginByPath(String pluginFilePath) {
File file = new File(pluginFilePath);
loadPluginByFile(file, PluginConfigController.getInstance().getConfig().getPlugins());
}
/**
* 根据文件加载插件
*
* @param pluginJar 插件jar包
* @param configPluginDescriptors 配置文件插件信息集合
* @apiNote
* @since 2023/9/16 14:05
*/
public void loadPluginByFile(File pluginJar, List<PluginDescriptor> configPluginDescriptors) {
PluginManager pluginManager = PluginManager.getInstance();
Map<String, List<String>> categories = pluginManager.getLoadedPluginsByCategory();
List<PluginDescriptor> pluginDescriptors = pluginManager.getPluginDescriptors();
if (pluginJar.exists() && pluginJar.isFile()) {
try {
PluginDescriptor pluginDescriptor = readPlugin(pluginJar);
// 检查插件状态
if (checkPlugin(configPluginDescriptors, pluginDescriptor, pluginDescriptors)) {
return;
}
pluginDescriptor.setEnabled(true);
pluginDescriptors.add(pluginDescriptor);
// 创建URLClassLoader以加载Jar文件中的类
Class<?> pluginClass = loaderJarFileClass(pluginJar, pluginDescriptor);
if (pluginClass == null) {
return;
}
Plugin plugin;
plugin = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
pluginDescriptor.setPlugin(plugin);
categories.computeIfAbsent(pluginDescriptor.getCategory(), k -> new ArrayList<>()).add(pluginDescriptor.getName());
} catch (IOException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new AppException(e);
} catch (ClassNotFoundException e) {
logger.error("无法找到对应的插件类!", e);
} catch (NoSuchMethodException e) {
logger.error("插件类中没有找到指定方法!", e);
}
} else {
logger.info("PluginDescriptor file not found");
}
}
/**
* 加载类中的class文件并返回插件主类
*
* @param pluginJar 插件jar包
* @param pluginDescriptor 插件描述
* @return java.lang.Class<?> 插件主类
* @apiNote
* @since 2023/9/19 14:00
*/
private Class<?> loaderJarFileClass(File pluginJar, PluginDescriptor pluginDescriptor) throws IOException, ClassNotFoundException {
Class<?> pluginClass;
try (
URLClassLoader classLoader = new URLClassLoader(new URL[]{pluginJar.toURI().toURL()});
JarFile jar = new JarFile(pluginJar)
) {
// 加载插件所需的依赖类
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
String className = entry.getName().replace("/", ".").replace(".class", "");
if (!pluginDescriptor.getMainClass().equals(className) && !"module-info".equals(className)) {
classLoader.loadClass(className);
}
}
}
pluginClass = classLoader.loadClass(pluginDescriptor.getMainClass());
}
logger.info("已加载插件:{}", pluginDescriptor.getName());
return pluginClass;
}
/**
* 装载插件
*
* @since 2023/9/15 21:39
*/
public void loadPlugins() {
// 扫描并装载插件
scanLoadPlugins(PluginConfigController.getInstance().getPlungsPath());
}
/**
* 扫描插件
*
* @param pluginsPath 插件路径
* @apiNote 扫描所有插件更新配置文件中的插件信息
* @since 2023/9/16 0:21
*/
private void scanLoadPlugins(Path pluginsPath) {
if (!Files.isDirectory(pluginsPath)) {
try {
Files.createDirectory(pluginsPath);
} catch (IOException e) {
throw new AppException("这不是一个有效的路径!");
}
}
// 获取插件加载器
PluginLoader pluginLoader = PluginLoader.getInstance();
try (Stream<Path> pathStream = Files.walk(pluginsPath)) {
pathStream.filter(path -> path.toString().endsWith(".jar")).forEach(path -> pluginLoader.loadPluginByPath(path.toString()));
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,118 @@
package org.jcnc.jnotepad.controller.plugin;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.jcnc.jnotepad.app.utils.PopUpUtil;
import org.jcnc.jnotepad.app.utils.UiUtil;
import org.jcnc.jnotepad.controller.plugin.manager.PluginManager;
import org.jcnc.jnotepad.ui.component.stage.dialog.factory.impl.BasicFileChooserFactory;
import org.slf4j.Logger;
import java.io.File;
import java.util.List;
import java.util.Map;
/**
* 插件管理界面
* <p>
* 用于演示插件加载和执行的界面
*
* @author luke gewuyou
*/
public class PluginManagerInterface {
private static final PluginManagerInterface INSTANCE = new PluginManagerInterface();
Logger logger = LoggerUtil.getLogger(this.getClass());
public static PluginManagerInterface getInstance() {
return INSTANCE;
}
/**
* 启动插件演示界面
*
* @param primaryStage JavaFX的主舞台
*/
public void start(Stage primaryStage) {
PluginManager pluginManager = PluginManager.getInstance();
FileChooser fileChooser = BasicFileChooserFactory.getInstance().createFileChooser(
"选择插件",
null,
null,
new FileChooser.ExtensionFilter("JAR Files", "*.jar")
);
Button loadButton = createLoadButton(primaryStage, fileChooser, pluginManager);
Button executeButton = new Button("执行插件");
executeButton.setOnAction(event -> pluginManager.executePlugins());
VBox root = new VBox(10, loadButton, executeButton);
Scene scene = new Scene(root, 300, 200);
primaryStage.getIcons().add(UiUtil.getAppIcon());
primaryStage.setScene(scene);
primaryStage.setTitle("插件演示");
primaryStage.show();
}
/**
* 显示已加载插件的信息
*
* @param primaryStage JavaFX的主舞台
* @param pluginManager 插件管理器
*/
private void displayPluginInfo(Stage primaryStage, PluginManager pluginManager) {
Map<String, List<String>> loadedPluginsByCategory = pluginManager.getLoadedPluginsByCategory();
VBox infoBox = new VBox();
loadedPluginsByCategory.forEach((key, pluginNames) -> {
Label categoryLabel = new Label("类别: " + key);
VBox categoryInfoBox = new VBox();
for (String pluginName : pluginNames) {
Label pluginLabel = new Label("插件名称: " + pluginName);
categoryInfoBox.getChildren().add(pluginLabel);
}
infoBox.getChildren().addAll(categoryLabel, categoryInfoBox);
});
Scene infoScene = new Scene(infoBox, 400, 300);
Stage infoStage = new Stage();
infoStage.setScene(infoScene);
infoStage.setTitle("已加载的插件");
infoStage.initOwner(primaryStage);
infoStage.show();
}
/**
* 创建加载插件的按钮
*
* @param primaryStage JavaFX的主舞台
* @param fileChooser 文件选择器
* @param pluginManager 插件管理器
* @return 加载插件的按钮
*/
private Button createLoadButton(Stage primaryStage, FileChooser fileChooser, PluginManager pluginManager) {
Button loadButton = new Button("加载插件");
loadButton.setOnAction(event -> {
try {
File selectedFile = fileChooser.showOpenDialog(primaryStage);
if (selectedFile != null) {
String pluginFilePath = selectedFile.getAbsolutePath();
PluginLoader.getInstance().loadPluginByPath(pluginFilePath);
// 更新插件信息显示
displayPluginInfo(primaryStage, pluginManager);
} else {
PopUpUtil.infoAlert(null, null, "未找到插件!", null, null);
logger.info("未找到插件!");
}
} catch (Exception e) {
logger.error("加载插件失败!", e);
}
});
return loadButton;
}
}

View File

@ -0,0 +1 @@
plugin 存放插件相关的代码,包括插件接口和管理器。

View File

@ -0,0 +1,27 @@
package org.jcnc.jnotepad.controller.plugin.interfaces;
/**
* 插件接口
* <p>
* 描述插件的基本功能
*
* @author luke gewuyou
*/
public interface Plugin {
/**
* 初始化插件
*/
void initialize();
/**
* 执行插件的逻辑
*/
void execute();
/**
* 销毁资源
*/
void destroyed();
}

View File

@ -0,0 +1,222 @@
package org.jcnc.jnotepad.controller.plugin.manager;
import org.jcnc.jnotepad.app.common.manager.ThreadPoolManager;
import org.jcnc.jnotepad.app.manager.ApplicationManager;
import org.jcnc.jnotepad.app.utils.LoggerUtil;
import org.jcnc.jnotepad.app.utils.PopUpUtil;
import org.jcnc.jnotepad.controller.config.PluginConfigController;
import org.jcnc.jnotepad.model.entity.PluginDescriptor;
import org.slf4j.Logger;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import static org.jcnc.jnotepad.controller.plugin.PluginLoader.readPlugin;
/**
* 插件管理器
* <p>
* 该类用于管理插件的加载和执行
* 插件可以通过加载外部JAR文件中的类来扩展应用程序的功能
*
* @author luke
*/
public class PluginManager {
private static final PluginManager INSTANCE = new PluginManager();
/**
* 插件类别
*/
private final Map<String, List<String>> categories = new HashMap<>();
Logger logger = LoggerUtil.getLogger(this.getClass());
/**
* 插件信息
*/
private List<PluginDescriptor> pluginDescriptors = new ArrayList<>();
/**
* 插件信息临时集合
*/
private List<PluginDescriptor> temporaryPluginDescriptors;
private PluginManager() {
}
public static PluginManager getInstance() {
return INSTANCE;
}
/**
* 初始化插件临时集合
*/
public void initializeTemporaryPluginDescriptors() {
temporaryPluginDescriptors = new ArrayList<>(pluginDescriptors.size());
pluginDescriptors.forEach(pluginDescriptor -> temporaryPluginDescriptors.add(new PluginDescriptor(pluginDescriptor)));
}
/**
* 卸载插件
*
* @param pluginDescriptor 插件信息类
* @since 2023/9/11 12:28
*/
public void unloadPlugin(PluginDescriptor pluginDescriptor) {
// 删除集合中的插件信息
ThreadPoolManager.getThreadPool().submit(() -> {
// 移除插件管理类中的插件描述类
pluginDescriptors.remove(pluginDescriptor);
// 移除插件配置文件类中的插件描述类
PluginConfigController instance = PluginConfigController.getInstance();
instance.getConfig().getPlugins().remove(pluginDescriptor);
// 刷新配置
instance.writeConfig();
// 删除本地插件jar包
Path plungsPath = instance.getPlungsPath();
try (Stream<Path> pathStream = Files.walk(plungsPath)) {
pathStream.filter(path -> path.toString().endsWith(".jar")).forEach(path -> {
try {
File pluginJar = new File(path.toString());
PluginDescriptor temp = readPlugin(pluginJar);
if (temp.getId().equals(pluginDescriptor.getId())) {
Files.delete(pluginJar.toPath());
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
});
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
ThreadPoolManager.threadContSelfSubtracting();
});
}
/**
* 禁用插件
*
* @param pluginDescriptor 需要禁用的某个插件的插件类
* @apiNote
* @since 2023/9/11 12:34
*/
public void disablePlugIn(PluginDescriptor pluginDescriptor) {
pluginDescriptor.setEnabled(false);
}
/**
* 初始化所有启用的插件
*/
public void initPlugins() {
for (PluginDescriptor pluginDescriptor : pluginDescriptors) {
if (pluginDescriptor.isEnabled()) {
pluginDescriptor.getPlugin().initialize();
}
}
}
/**
* 执行插件
*
* @param pluginDescriptor 需要执行的插件的信息类
* @apiNote
* @since 2023/9/16 14:58
*/
public void executePlugin(PluginDescriptor pluginDescriptor) {
pluginDescriptor.getPlugin().execute();
}
/**
* 执行加载的插件
*
* @deprecated 待删除
*/
public void executePlugins() {
for (PluginDescriptor pluginDescriptor : pluginDescriptors) {
if (pluginDescriptor.isEnabled()) {
pluginDescriptor.getPlugin().execute();
}
}
}
/**
* 销毁插件可能申请的资源
*/
public void destroyPlugins() {
for (PluginDescriptor pluginDescriptor : pluginDescriptors) {
if (pluginDescriptor.isEnabled() && pluginDescriptor.getPlugin() != null) {
pluginDescriptor.getPlugin().destroyed();
}
}
}
/**
* 获取按类别分类的已加载插件
*
* @return 插件类别映射
*/
public Map<String, List<String>> getLoadedPluginsByCategory() {
return categories;
}
public List<PluginDescriptor> getPluginDescriptors() {
return pluginDescriptors;
}
public List<PluginDescriptor> getTemporaryPluginDescriptors() {
return temporaryPluginDescriptors;
}
/**
* 启用插件
*
* @param pluginDescriptor 插件信息类
*/
public void enablePlugIn(PluginDescriptor pluginDescriptor) {
pluginDescriptor.setEnabled(true);
}
/**
* 保存插件设置并退出
*/
public void saveAndExitSettings() {
settingsChange();
clearTemporarySettings();
}
/**
* 设置更改
*/
private void settingsChange() {
boolean equals = temporaryPluginDescriptors.equals(pluginDescriptors);
if (!equals) {
pluginDescriptors = temporaryPluginDescriptors;
PopUpUtil.questionAlert("更改", "程序与插件更新", "请重启程序以应用插件中的更改!",
appDialog -> {
appDialog.close();
// 执行重启操作
ApplicationManager.getInstance().restart();
}, null, "重启", "以后再说");
}
}
/**
* 保存插件设置但不退出
*/
public void saveNotExitSettings() {
settingsChange();
}
/**
* 清除插件临时设置
*/
public void clearTemporarySettings() {
temporaryPluginDescriptors = null;
}
}

View File

@ -0,0 +1 @@
model 存放模型相关的代码,包括实体类和枚举。

View File

@ -0,0 +1,123 @@
package org.jcnc.jnotepad.model.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* 缓存类
*
* @author gewuyou
*/
public class Cache {
/**
* 命名空间
*/
@JsonIgnore
private String namespace;
/**
*
*/
@JsonIgnore
private String group;
/**
* 缓存名称
*/
@JsonIgnore
private String name;
/**
* 缓存数据
*/
private Object cacheData;
/**
* 过期时间<br/>如果过期时间为负数则永不过期
*/
private Long expirationTime;
/**
* 上次读或写时间
*/
private Long lastReadOrWriteTime;
public Cache() {
}
public Cache(String namespace, String group, String name, Object cacheData, Long expirationTime) {
this.namespace = namespace;
this.group = group;
this.name = name;
this.cacheData = cacheData;
this.expirationTime = expirationTime;
this.lastReadOrWriteTime = System.currentTimeMillis();
}
/**
* 生成缓存key
*
* @param namespace 命名空间
* @param group
* @param name 缓存名称
* @return 缓存key
*/
public static String getCacheKey(String namespace, String group, String name) {
return namespace + "." + group + "." + name;
}
/**
* 获取缓存key
*
* @return key
*/
@JsonIgnore
public String getCacheKey() {
return getCacheKey(namespace, group, name);
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object getCacheData() {
return cacheData;
}
public void setCacheData(Object cacheData) {
this.cacheData = cacheData;
}
public Long getExpirationTime() {
return expirationTime;
}
public void setExpirationTime(Long expirationTime) {
this.expirationTime = expirationTime;
}
public Long getLastReadOrWriteTime() {
return lastReadOrWriteTime;
}
public void setLastReadOrWriteTime(Long lastReadOrWriteTime) {
this.lastReadOrWriteTime = lastReadOrWriteTime;
}
}

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