From 35551de78b5c854c1e1acf10ce5f6d57028a76c2 Mon Sep 17 00:00:00 2001 From: gewuyou <1063891901@qq.com> Date: Sat, 30 Sep 2023 20:38:48 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=9A=A9=20=E5=AE=9E=E7=8E=B0=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E9=80=BB=E8=BE=91=E7=BC=93=E5=AD=98=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 +- src/main/java/module-info.java | 1 - src/main/java/org/jcnc/jnotepad/LunchApp.java | 2 + .../app/manager/ApplicationManager.java | 27 ++- .../common/manager/AbstractCacheManager.java | 41 +++- .../manager/ApplicationCacheManager.java | 4 +- .../controller/cache/CacheController.java | 182 ++++++++++++++++-- .../event/handler/menubar/OpenFile.java | 71 +++---- .../event/handler/menubar/RenameFile.java | 1 + .../event/handler/menubar/SaveFile.java | 13 +- .../controller/manager/Controller.java | 14 +- .../org/jcnc/jnotepad/model/entity/Cache.java | 28 ++- .../model/enums/CacheExpirationTime.java | 44 +++++ .../jcnc/jnotepad/plugin/PluginLoader.java | 6 +- .../plugin/manager/PluginManager.java | 2 +- .../ui/pluginstage/PluginManagementPane.java | 15 +- .../java/org/jcnc/jnotepad/util/JsonUtil.java | 35 +++- .../views/manager/CenterTabPaneManager.java | 23 +++ .../center/main/center/tab/CenterTab.java | 1 + .../center/main/center/tab/CenterTabPane.java | 1 - 20 files changed, 418 insertions(+), 99 deletions(-) create mode 100644 src/main/java/org/jcnc/jnotepad/model/enums/CacheExpirationTime.java diff --git a/.gitignore b/.gitignore index 568c8be..4ebcc43 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,10 @@ test/ ### 此处忽略了json与xml后缀 *.xml *.json + +### 此处排除证书目录 +certificate/ + ### Eclipse ### .apt_generated .classpath @@ -47,4 +51,4 @@ logs/ /en_language_pack.txt /jnotepadConfig.json /qodana.yaml -.mvn/ \ No newline at end of file +.mvn/ diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 92394cf..ac75d3f 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -20,7 +20,6 @@ module org.jcnc.jnotepad { requires org.commonmark; requires javafx.web; - exports org.jcnc.jnotepad; exports org.jcnc.jnotepad.model.enums; exports org.jcnc.jnotepad.app.config; diff --git a/src/main/java/org/jcnc/jnotepad/LunchApp.java b/src/main/java/org/jcnc/jnotepad/LunchApp.java index 896d77f..4a48cf2 100644 --- a/src/main/java/org/jcnc/jnotepad/LunchApp.java +++ b/src/main/java/org/jcnc/jnotepad/LunchApp.java @@ -31,6 +31,8 @@ public class LunchApp extends Application { APPLICATION_MANAGER.setPrimaryStage(primaryStage); // 加载应用程序资源 APPLICATION_MANAGER.loadAppResources(); + // 加载应用程序缓存 + APPLICATION_MANAGER.loadAppCache(); // 初始化应用程序 APPLICATION_MANAGER.initializeApp(); // 初始化默认操作 diff --git a/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java b/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java index af42fb0..cda55ad 100644 --- a/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java +++ b/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java @@ -12,6 +12,8 @@ import org.jcnc.jnotepad.common.constants.AppConstants; import org.jcnc.jnotepad.common.constants.TextConstants; import org.jcnc.jnotepad.common.manager.ThreadPoolManager; 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.manager.Controller; import org.jcnc.jnotepad.plugin.manager.PluginManager; @@ -76,7 +78,7 @@ public class ApplicationManager { } public void initializeDefaultAction() { - // 使用线程池加载关联文件并创建文本区域 + // 使用加载关联文件并创建文本区域 List rawParameters = application.getParameters().getRaw(); Controller.getInstance().openAssociatedFileAndCreateTextArea(rawParameters); } @@ -96,11 +98,16 @@ public class ApplicationManager { primaryStage.getIcons().add(UiUtil.getAppIcon()); } + /** + * 加载缓存 + */ + public void loadAppCache() { + // 加载缓存 + CacheController.getInstance().loadCaches(); + } + /** * 加载资源 - * - * @apiNote - * @since 2023/9/20 18:29 */ public void loadAppResources() { // 加载资源 @@ -115,12 +122,18 @@ public class ApplicationManager { * @apiNote 在停止程序之前会执行此操作 */ public void stopApp() { - PluginConfigController instance = PluginConfigController.getInstance(); + PluginConfigController pluginConfigController = PluginConfigController.getInstance(); // 刷新插件配置文件 - instance.getConfig().setPlugins(PluginManager.getInstance().getPluginDescriptors()); - instance.writeConfig(); + pluginConfigController.getConfig().setPlugins(PluginManager.getInstance().getPluginDescriptors()); + pluginConfigController.writeConfig(); + // 保存配置文件 + AppConfigController.getInstance().writeConfig(); // 销毁插件可能申请的资源 PluginManager.getInstance().destroyPlugins(); + // 保存已打开的文件标签页 + CenterTabPaneManager.getInstance().saveOpenFileTabs(); + // 将缓存写入本地 + CacheController.getInstance().writeCaches(); // 关闭线程池 threadPool.shutdownNow(); } diff --git a/src/main/java/org/jcnc/jnotepad/common/manager/AbstractCacheManager.java b/src/main/java/org/jcnc/jnotepad/common/manager/AbstractCacheManager.java index 233d7ed..9a77a0b 100644 --- a/src/main/java/org/jcnc/jnotepad/common/manager/AbstractCacheManager.java +++ b/src/main/java/org/jcnc/jnotepad/common/manager/AbstractCacheManager.java @@ -13,7 +13,7 @@ public abstract class AbstractCacheManager { /** * 缓存集合 */ - protected Map> caches; + protected Map caches; /** * 获取全局命名空间 @@ -22,27 +22,41 @@ public abstract class AbstractCacheManager { */ 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 abstract Map> getCaches(); + public abstract Map getCaches(); /** * 设置缓存集合 * * @param caches 缓存集合 */ - public abstract void setCaches(Map> caches); + public abstract void setCaches(Map caches); /** * 添加缓存 * * @param cache 缓存 */ - public void addCache(Cache cache) { + public void addCache(Cache cache) { String cacheKey = cache.getCacheKey(); // 如果集合中已存在该缓存,则更新读写时间 if (caches.containsKey(cacheKey)) { @@ -57,18 +71,29 @@ public abstract class AbstractCacheManager { * @param cacheKey 缓存key * @return 缓存类 */ - public Cache getCache(String cacheKey) { - if (caches.isEmpty()) { + public Cache getCache(String cacheKey) { + if (caches == null || caches.isEmpty()) { return null; } if (caches.containsKey(cacheKey)) { - Cache cache = caches.get(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)); + } + /** * 获取缓存数据 * @@ -76,7 +101,7 @@ public abstract class AbstractCacheManager { * @return 缓存类 */ public Object getCacheData(String cacheKey) { - Cache cache = getCache(cacheKey); + Cache cache = getCache(cacheKey); if (cache == null) { return null; } diff --git a/src/main/java/org/jcnc/jnotepad/common/manager/ApplicationCacheManager.java b/src/main/java/org/jcnc/jnotepad/common/manager/ApplicationCacheManager.java index 67670ee..65161db 100644 --- a/src/main/java/org/jcnc/jnotepad/common/manager/ApplicationCacheManager.java +++ b/src/main/java/org/jcnc/jnotepad/common/manager/ApplicationCacheManager.java @@ -22,12 +22,12 @@ public class ApplicationCacheManager extends AbstractCacheManager { } @Override - public Map> getCaches() { + public Map getCaches() { return caches; } @Override - public void setCaches(Map> caches) { + public void setCaches(Map caches) { this.caches = caches; } diff --git a/src/main/java/org/jcnc/jnotepad/controller/cache/CacheController.java b/src/main/java/org/jcnc/jnotepad/controller/cache/CacheController.java index f04d8a1..3cbd792 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/cache/CacheController.java +++ b/src/main/java/org/jcnc/jnotepad/controller/cache/CacheController.java @@ -1,12 +1,23 @@ package org.jcnc.jnotepad.controller.cache; +import com.fasterxml.jackson.core.type.TypeReference; +import org.jcnc.jnotepad.common.manager.ApplicationCacheManager; import org.jcnc.jnotepad.exception.AppException; import org.jcnc.jnotepad.model.entity.Cache; +import org.jcnc.jnotepad.util.JsonUtil; +import org.jcnc.jnotepad.util.LogUtil; +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; import static org.jcnc.jnotepad.common.constants.AppConstants.DEFAULT_PROPERTY; @@ -17,28 +28,131 @@ import static org.jcnc.jnotepad.common.constants.AppConstants.DEFAULT_PROPERTY; */ public class CacheController { + private static final ApplicationCacheManager APPLICATION_CACHE_MANAGER = ApplicationCacheManager.getInstance(); + Logger logger = LogUtil.getLogger(this.getClass()); + private static final CacheController INSTANCE = new CacheController(); private String cacheDir; private CacheController() { cacheDir = Paths.get(System.getProperty(DEFAULT_PROPERTY), ".jnotepad", "caches").toString(); - loadCaches(); } public static CacheController getInstance() { return INSTANCE; } - private void loadCaches() { + 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 caches = new HashMap<>(16); + 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 cacheMap = JsonUtil.fromJsonString(cacheJson, new TypeReference<>() { + }); + // 设置缓存 + cacheMap.forEach((k, v) -> setUpCache(namespace, groupName, k, v, caches)); + } catch (IOException e) { + logger.error("读取缓存文件出错!", e); + } + } + // 设置缓存 + APPLICATION_CACHE_MANAGER.setCaches(caches); + } } - private void writeCaches(Map> caches) { - File cacheFileDir = new File(cacheDir); - if (!cacheFileDir.exists()) { - cacheFileDir.mkdirs(); + /** + * 设置缓存 + * + * @param namespace 命名空间 + * @param groupName 缓存组 + * @param k 缓存名称 + * @param v 缓存类 + * @param caches 缓存集合 + */ + private void setUpCache(String namespace, String groupName, String k, Cache v, Map 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) { + throw new AppException(e); + } + return false; + } + + /** + * 写缓存(writeCache) + */ + public void writeCaches() { + writeCaches(APPLICATION_CACHE_MANAGER.getCaches()); + } + + /** + * 写缓存 + * + * @param caches 缓存集合 + */ + public void writeCaches(Map caches) { + // 检查并获取缓存根目录 + File cacheFileDir = createCacheRootIfNotExist(); + Map> fileMap = new HashMap<>(16); // 生成缓存 caches.forEach((key, value) -> { // 判断当前命名空间对应目录是否创建 @@ -58,17 +172,61 @@ public class CacheController { throw new AppException(e); } } - + fileMap.computeIfAbsent(groupFile, k -> new HashMap<>(16)); + // 设置需要写入的数据 + fileMap.get(groupFile).put(value.getName(), value); }); - - // + Set fileSet = fileMap.keySet(); + // 清空原来的缓存 + fileSet.forEach(file -> { + try (FileOutputStream fileOutputStream = new FileOutputStream(file)) { + fileOutputStream.write(new byte[0]); + } catch (IOException e) { + throw new AppException(e); + } + }); + // 写入缓存 + for (Map.Entry> entry : fileMap.entrySet()) { + try (FileWriter writer = new FileWriter(entry.getKey(), true)) { + writer.write(JsonUtil.toJsonString(entry.getValue())); + } catch (IOException e) { + throw new AppException(e); + } + } } /** - * 如果缓存 + * 如果不存在,则创建缓存根目录 + * + * @return 缓存根目录 */ - private void createCachesIfNotExists() { - + 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; + } } diff --git a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java index 3f4fbae..9e25d9e 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java +++ b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java @@ -1,14 +1,14 @@ package org.jcnc.jnotepad.controller.event.handler.menubar; -import javafx.application.Platform; -import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.control.Tab; import javafx.stage.FileChooser; import org.jcnc.jnotepad.app.i18n.UiResourceBundle; import org.jcnc.jnotepad.common.constants.TextConstants; -import org.jcnc.jnotepad.common.manager.ThreadPoolManager; +import org.jcnc.jnotepad.common.manager.ApplicationCacheManager; +import org.jcnc.jnotepad.model.entity.Cache; +import org.jcnc.jnotepad.model.enums.CacheExpirationTime; import org.jcnc.jnotepad.ui.dialog.factory.impl.BasicFileChooserFactory; import org.jcnc.jnotepad.ui.module.LineNumberTextArea; import org.jcnc.jnotepad.util.EncodingDetector; @@ -24,8 +24,6 @@ import java.io.FileReader; import java.io.IOException; import java.nio.charset.Charset; -import static org.jcnc.jnotepad.common.manager.ThreadPoolManager.threadContSelfSubtracting; - /** * 打开文件的事件处理程序。 *

@@ -34,6 +32,8 @@ import static org.jcnc.jnotepad.common.manager.ThreadPoolManager.threadContSelfS * @author 许轲 */ public class OpenFile implements EventHandler { + private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance(); + /** * 处理打开文件事件。 * @@ -41,42 +41,28 @@ public class OpenFile implements EventHandler { */ @Override public void handle(ActionEvent event) { + // 获取缓存 + Cache cache = CACHE_MANAGER.getCache("folder", "openFile"); // 显示文件选择对话框,并获取选中的文件 File file = BasicFileChooserFactory.getInstance().createFileChooser( UiResourceBundle.getContent(TextConstants.OPEN), null, - 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); + } openFile(file); } - /** - * 创建打开文件的任务。 - * - * @param file 文件对象 - * @return 打开文件的任务 - */ - public Task createOpenFileTask(File file) { - Task openFileTask = new Task<>() { - @Override - protected Void call() { - getText(file); - return null; - } - - }; - // 设置任务成功完成时的处理逻辑 - openFileTask.setOnSucceeded(e -> threadContSelfSubtracting()); - - // 设置任务失败时的处理逻辑 - openFileTask.setOnFailed(e -> threadContSelfSubtracting()); - return openFileTask; - } - /** * 打开文件。 * @@ -98,7 +84,7 @@ public class OpenFile implements EventHandler { return; } } - ThreadPoolManager.getThreadPool().submit(createOpenFileTask(file)); + getText(file); } /** @@ -110,25 +96,24 @@ public class OpenFile implements EventHandler { LineNumberTextArea textArea = createNewTextArea(); // 检测文件编码 Charset encoding = EncodingDetector.detectEncodingCharset(file); + StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new FileReader(file, encoding))) { - StringBuilder textBuilder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { - if (!textBuilder.isEmpty()) { - textBuilder.append("\n"); + if (!stringBuilder.isEmpty()) { + stringBuilder.append("\n"); } - textBuilder.append(line); + stringBuilder.append(line); } - String text = textBuilder.toString(); + String text = stringBuilder.toString(); LogUtil.getLogger(this.getClass()).info("已调用读取文件功能"); - Platform.runLater(() -> { - textArea.appendText(text); - CenterTab tab = createNewTab(file.getName(), textArea, encoding); - // 设置当前标签页关联本地文件 - tab.setRelevance(true); - tab.setUserData(file); - CenterTabPaneManager.getInstance().addNewTab(tab); - }); + + textArea.appendText(text); + CenterTab tab = createNewTab(file.getName(), textArea, encoding); + // 设置当前标签页关联本地文件 + tab.setRelevance(true); + tab.setUserData(file); + CenterTabPaneManager.getInstance().addNewTab(tab); } catch (IOException ignored) { LogUtil.getLogger(this.getClass()).info("已忽视IO异常!"); } diff --git a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/RenameFile.java b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/RenameFile.java index 57bf5e5..9b4999b 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/RenameFile.java +++ b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/RenameFile.java @@ -139,6 +139,7 @@ public class RenameFile implements EventHandler { private void handleRenameRelevanceFile(CenterTab centerTab) { // 获取原始文件对象 File file = (File) centerTab.getUserData(); + // 获取应用窗口并绑定 File newFile = BasicFileChooserFactory.getInstance() .createFileChooser( diff --git a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java index ef71642..54f893c 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java +++ b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java @@ -5,8 +5,11 @@ import javafx.event.EventHandler; import javafx.stage.FileChooser; import org.jcnc.jnotepad.app.i18n.UiResourceBundle; import org.jcnc.jnotepad.common.constants.TextConstants; +import org.jcnc.jnotepad.common.manager.ApplicationCacheManager; import org.jcnc.jnotepad.controller.config.AppConfigController; 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.dialog.factory.impl.BasicFileChooserFactory; import org.jcnc.jnotepad.util.LogUtil; import org.jcnc.jnotepad.util.UiUtil; @@ -28,6 +31,7 @@ import static org.jcnc.jnotepad.controller.config.AppConfigController.CONFIG_NAM * @author gewuyou */ public class SaveFile implements EventHandler { + private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance(); Logger logger = LogUtil.getLogger(this.getClass()); /** @@ -76,13 +80,20 @@ public class SaveFile implements EventHandler { if (selectedTab == null) { return; } + Cache cache = CACHE_MANAGER.getCache("folder", "saveFile"); File file = BasicFileChooserFactory.getInstance().createFileChooser( UiResourceBundle.getContent(TextConstants.SAVE_AS), selectedTab.getText(), - null, + 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); + } LogUtil.getLogger(currentClass).info("正在保存文件: {}", file.getName()); selectedTab.save(file); // 将保存后的文件设置为关联文件 diff --git a/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java b/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java index 0fe6a08..4a8b755 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java +++ b/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java @@ -1,8 +1,10 @@ package org.jcnc.jnotepad.controller.manager; import org.jcnc.jnotepad.common.interfaces.ControllerAble; +import org.jcnc.jnotepad.common.manager.ApplicationCacheManager; import org.jcnc.jnotepad.controller.event.handler.menubar.NewFile; import org.jcnc.jnotepad.controller.event.handler.menubar.OpenFile; +import org.jcnc.jnotepad.model.entity.Cache; import java.io.File; import java.util.List; @@ -13,6 +15,7 @@ import java.util.List; * @author 许轲 */ public class Controller implements ControllerAble { + private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance(); private static final Controller INSTANCE = new Controller(); @@ -35,10 +38,19 @@ public class Controller implements ControllerAble { */ @Override public void openAssociatedFileAndCreateTextArea(List rawParameters) { + // 获取上次打开的页面 + Cache cache = CACHE_MANAGER.getCache("tabs", "centerTabs"); + List fileTab = (List) cache.getCacheData(); +// fileTab.forEach(filePath -> new OpenFile().openFile(new File(filePath))); + for (String filePath : fileTab) { + new OpenFile().openFile(new File(filePath)); + } if (!rawParameters.isEmpty()) { String filePath = rawParameters.get(0); openAssociatedFile(filePath); - } else { + return; + } + if (fileTab.isEmpty()) { new NewFile().addNewFileTab(); } } diff --git a/src/main/java/org/jcnc/jnotepad/model/entity/Cache.java b/src/main/java/org/jcnc/jnotepad/model/entity/Cache.java index 4a52f13..8826144 100644 --- a/src/main/java/org/jcnc/jnotepad/model/entity/Cache.java +++ b/src/main/java/org/jcnc/jnotepad/model/entity/Cache.java @@ -8,7 +8,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; * * @author gewuyou */ -public class Cache { +public class Cache { /** * 命名空间 */ @@ -22,12 +22,13 @@ public class Cache { /** * 缓存名称 */ + @JsonIgnore private String name; /** * 缓存数据 */ - private T cacheData; + private Object cacheData; /** * 过期时间
如果过期时间为负数则永不过期 */ @@ -41,7 +42,7 @@ public class Cache { } - public Cache(String namespace, String group, String name, T cacheData, Long expirationTime) { + public Cache(String namespace, String group, String name, Object cacheData, Long expirationTime) { this.namespace = namespace; this.group = group; this.name = name; @@ -51,12 +52,25 @@ public class Cache { } /** - * 根据缓存key + * 生成缓存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 this.getNamespace() + "." + this.getGroup() + "." + this.getName(); + return getCacheKey(namespace, group, name); } public String getNamespace() { @@ -83,11 +97,11 @@ public class Cache { this.name = name; } - public T getCacheData() { + public Object getCacheData() { return cacheData; } - public void setCacheData(T cacheData) { + public void setCacheData(Object cacheData) { this.cacheData = cacheData; } diff --git a/src/main/java/org/jcnc/jnotepad/model/enums/CacheExpirationTime.java b/src/main/java/org/jcnc/jnotepad/model/enums/CacheExpirationTime.java new file mode 100644 index 0000000..9686842 --- /dev/null +++ b/src/main/java/org/jcnc/jnotepad/model/enums/CacheExpirationTime.java @@ -0,0 +1,44 @@ +package org.jcnc.jnotepad.model.enums; + +/** + * 缓存过期时间枚举 + * + * @author gewuyou + */ +public enum CacheExpirationTime { + /** + * 一小时 + */ + ONE_HOUR(60 * 60 * 1000L), + /** + * 一天 + */ + ONE_DAY(24 * ONE_HOUR.value), + /** + * 一周 + */ + ONE_WEEK(7 * ONE_DAY.value), + /** + * 一月 + */ + ONE_MONTH(30 * ONE_DAY.value), + /** + * 一年 + */ + ONE_YEAR(365 * ONE_DAY.value), + + /** + * 永不过期 + */ + NEVER_EXPIRES(-1L); + private final Long value; + + CacheExpirationTime(Long value) { + this.value = value; + } + + public Long getValue() { + return value; + } + +} diff --git a/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java b/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java index 681b8d0..79c9e8e 100644 --- a/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java +++ b/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java @@ -99,7 +99,7 @@ public class PluginLoader { */ private static boolean pluginDoesNotExist(PluginDescriptor pluginDescriptor, List configPluginDescriptors) { for (PluginDescriptor configPluginDescriptor : configPluginDescriptors) { - if ((configPluginDescriptor.getName() + configPluginDescriptor.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor())) { + if (configPluginDescriptor.getId().equals(pluginDescriptor.getId())) { return false; } } @@ -110,7 +110,7 @@ public class PluginLoader { Iterator iterator = pluginDescriptors.iterator(); while (iterator.hasNext()) { PluginDescriptor plugin = iterator.next(); - if ((plugin.getName() + plugin.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor())) { + if (plugin.getId().equals(pluginDescriptor.getId())) { if (plugin.getVersion().equals(pluginDescriptor.getVersion())) { return true; } @@ -137,7 +137,7 @@ public class PluginLoader { * @since 2023/9/19 18:45 */ private static boolean disableDoNotLoad(PluginDescriptor pluginDescriptor, List pluginDescriptors, PluginDescriptor configPluginDescriptor) { - if ((configPluginDescriptor.getName() + configPluginDescriptor.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor()) && !configPluginDescriptor.isEnabled()) { + if (configPluginDescriptor.getId().equals(pluginDescriptor.getId()) && !configPluginDescriptor.isEnabled()) { pluginDescriptor.setEnabled(false); pluginDescriptors.add(pluginDescriptor); return true; diff --git a/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java b/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java index adc8e8e..1d41a39 100644 --- a/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java +++ b/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java @@ -85,7 +85,7 @@ public class PluginManager { try { File pluginJar = new File(path.toString()); PluginDescriptor temp = readPlugin(pluginJar); - if ((temp.getName() + temp.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor())) { + if (temp.getId().equals(pluginDescriptor.getId())) { Files.delete(pluginJar.toPath()); } } catch (IOException e) { diff --git a/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java b/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java index 63beb77..90826bf 100644 --- a/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java +++ b/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java @@ -26,8 +26,6 @@ import org.commonmark.parser.Parser; import org.commonmark.renderer.html.HtmlRenderer; import org.jcnc.jnotepad.model.entity.PluginDescriptor; import org.jcnc.jnotepad.plugin.manager.PluginManager; -import org.jcnc.jnotepad.ui.dialog.AppDialog; -import org.jcnc.jnotepad.ui.dialog.interfaces.DialogButtonAction; import org.jcnc.jnotepad.ui.module.CustomSetButton; import org.jcnc.jnotepad.util.LogUtil; import org.jcnc.jnotepad.util.PopUpUtil; @@ -272,14 +270,11 @@ public class PluginManagementPane extends BorderPane { BooleanProperty booleanProperty = toggleSwitch.selectedProperty(); state.textProperty().bind(Bindings.when(booleanProperty).then("禁用").otherwise("启用")); - uninstallItem.setOnAction(event -> PopUpUtil.warningAlert("卸载", "确定要卸载" + pluginDescriptor.getName() + "吗?", "此操作无法撤销!", new DialogButtonAction() { - @Override - public void handleAction(AppDialog dialog) { - pluginManager.unloadPlugin(pluginDescriptor); - state.setDisable(true); - toggleSwitch.setDisable(true); - dialog.close(); - } + uninstallItem.setOnAction(event -> PopUpUtil.warningAlert("卸载", "确定要卸载" + pluginDescriptor.getName() + "吗?", "此操作无法撤销!", dialog -> { + pluginManager.unloadPlugin(pluginDescriptor); + state.setDisable(true); + toggleSwitch.setDisable(true); + dialog.close(); }, null)); state.getStyleClass().addAll(Styles.ACCENT); state.setPrefWidth(80); diff --git a/src/main/java/org/jcnc/jnotepad/util/JsonUtil.java b/src/main/java/org/jcnc/jnotepad/util/JsonUtil.java index 6e29f38..a83f6ee 100644 --- a/src/main/java/org/jcnc/jnotepad/util/JsonUtil.java +++ b/src/main/java/org/jcnc/jnotepad/util/JsonUtil.java @@ -1,6 +1,7 @@ package org.jcnc.jnotepad.util; 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; @@ -35,7 +36,7 @@ public class JsonUtil { * 将对象转换为 JSON 字符串。 * * @param o 要转换的对象 - * @return 对象的 JSON 表示,如果转换失败则抛出 AppException 异常 + * @return 对象的 JSON 表示 * @throws AppException 如果转换失败 */ public static String toJsonString(Object o) { @@ -45,4 +46,36 @@ public class JsonUtil { throw new AppException(e); } } + + /** + * 将json字符串解析成对象 + * + * @param json json字符串 + * @param clazz 对象类型 + * @return 对象 + * @throws AppException 如果解析失败 + */ + public static T fromJsonString(String json, Class 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 fromJsonString(String json, TypeReference valueTypeRef) { + try { + return OBJECT_MAPPER.readValue(json, valueTypeRef); + } catch (JsonProcessingException e) { + throw new AppException(e); + } + } } diff --git a/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java b/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java index 45ba703..e13b436 100644 --- a/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java +++ b/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java @@ -1,16 +1,25 @@ package org.jcnc.jnotepad.views.manager; +import javafx.collections.ObservableList; +import javafx.scene.control.Tab; +import org.jcnc.jnotepad.common.manager.ApplicationCacheManager; import org.jcnc.jnotepad.controller.config.AppConfigController; +import org.jcnc.jnotepad.model.enums.CacheExpirationTime; import org.jcnc.jnotepad.views.root.center.main.center.tab.CenterTab; import org.jcnc.jnotepad.views.root.center.main.center.tab.CenterTabPane; import org.jcnc.jnotepad.views.root.top.menu.TopMenuBar; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + /** * 中心标签页窗格管理类 * * @author gewuyou */ public class CenterTabPaneManager { + private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance(); private static final CenterTabPaneManager INSTANCE = new CenterTabPaneManager(); private final CenterTabPane centerTabPane = CenterTabPane.getInstance(); @@ -88,4 +97,18 @@ public class CenterTabPaneManager { selectedTab.setAutoLine(AppConfigController.getInstance().getAutoLineConfig()); bottomStatusBoxManager.updateWhenTabSelected(); } + + public void saveOpenFileTabs() { + // 获取当前所有标签页 + ObservableList tabs = centerTabPane.getTabs(); + List filePaths = new ArrayList<>(); + // 缓存当前打开关联的文件 + tabs.forEach(tab -> { + File file = (File) tab.getUserData(); + if (file != null) { + filePaths.add(file.getPath()); + } + }); + CACHE_MANAGER.addCache(CACHE_MANAGER.createCache("tabs", "centerTabs", filePaths, CacheExpirationTime.NEVER_EXPIRES.getValue())); + } } diff --git a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java index bc4e4b3..c65df7e 100644 --- a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java +++ b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java @@ -91,4 +91,5 @@ public class CenterTab extends Tab { this.setUserData(file); save(); } + } diff --git a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTabPane.java b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTabPane.java index 8a774b3..b242380 100644 --- a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTabPane.java +++ b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTabPane.java @@ -8,7 +8,6 @@ import javafx.scene.control.TabPane; * @author songdragon */ public class CenterTabPane extends TabPane { - private static final CenterTabPane INSTANCE = new CenterTabPane(); private CenterTabPane() { From 9711e7b2f1a39c7326dcacd35425d770c8bc414e Mon Sep 17 00:00:00 2001 From: gewuyou <1063891901@qq.com> Date: Sat, 30 Sep 2023 22:05:29 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=90=9B=20=E4=BF=AE=E5=A4=8D=20BUG=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF=E7=9A=84=E7=9B=91=E5=90=AC?= =?UTF-8?q?=E5=99=A8=E8=AE=BE=E7=BD=AE=E6=96=B9=E6=B3=95=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=9A=84=E6=96=87=E4=BB=B6=E6=95=B0=E6=8D=AE=E8=A6=86=E7=9B=96?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/handler/menubar/SaveFile.java | 2 +- .../controller/manager/Controller.java | 6 +- .../ui/module/LineNumberTextArea.java | 64 ------------------- .../views/manager/CenterTabPaneManager.java | 3 + .../center/main/center/tab/CenterTab.java | 56 ++++++++++++++-- 5 files changed, 57 insertions(+), 74 deletions(-) diff --git a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java index 54f893c..c649919 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java +++ b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java @@ -55,7 +55,7 @@ public class SaveFile implements EventHandler { } else { logger.info("当前保存文件为关联打开文件,调用自动保存方法"); // 调用tab保存方法 - selectedTab.save(); + selectedTab.saveSelectedFileTab(); // 如果该文件是配置文件,则刷新快捷键 if (CONFIG_NAME.equals(selectedTab.getText())) { // 重新加载语言包和快捷键 diff --git a/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java b/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java index 4a8b755..a8a74ad 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java +++ b/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java @@ -41,10 +41,8 @@ public class Controller implements ControllerAble { // 获取上次打开的页面 Cache cache = CACHE_MANAGER.getCache("tabs", "centerTabs"); List fileTab = (List) cache.getCacheData(); -// fileTab.forEach(filePath -> new OpenFile().openFile(new File(filePath))); - for (String filePath : fileTab) { - new OpenFile().openFile(new File(filePath)); - } + fileTab.forEach(filePath -> new OpenFile().openFile(new File(filePath))); + if (!rawParameters.isEmpty()) { String filePath = rawParameters.get(0); openAssociatedFile(filePath); diff --git a/src/main/java/org/jcnc/jnotepad/ui/module/LineNumberTextArea.java b/src/main/java/org/jcnc/jnotepad/ui/module/LineNumberTextArea.java index 9f533d5..dfd5905 100644 --- a/src/main/java/org/jcnc/jnotepad/ui/module/LineNumberTextArea.java +++ b/src/main/java/org/jcnc/jnotepad/ui/module/LineNumberTextArea.java @@ -12,18 +12,9 @@ import org.fxmisc.richtext.LineNumberFactory; import org.fxmisc.richtext.model.Paragraph; import org.fxmisc.richtext.model.StyleSpans; import org.fxmisc.richtext.model.StyleSpansBuilder; -import org.jcnc.jnotepad.util.LogUtil; -import org.jcnc.jnotepad.views.manager.BottomStatusBoxManager; -import org.jcnc.jnotepad.views.manager.CenterTabPaneManager; -import org.jcnc.jnotepad.views.root.center.main.center.tab.CenterTab; import org.reactfx.Subscription; import org.reactfx.collection.ListModification; -import org.slf4j.Logger; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; import java.time.Duration; import java.util.Collection; import java.util.Collections; @@ -84,11 +75,6 @@ public class LineNumberTextArea extends CodeArea { + "|(?" + COMMENT_PATTERN + ")" ); - /** - * 用于记录日志的静态Logger对象 - */ - private static final Logger logger = LogUtil.getLogger(LineNumberTextArea.class); - /** * 构造函数 *

@@ -128,10 +114,7 @@ public class LineNumberTextArea extends CodeArea { } } }); - - initListeners(); this.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/css/java_code_styles.css")).toString()); - } @@ -243,51 +226,4 @@ public class LineNumberTextArea extends CodeArea { System.out.println(((CodeArea) getOwnerNode()).getText()); } } - - /** - * 初始化监听器方法 - */ - private void initListeners() { - // 监听主要文本区域的文本变化 - this.textProperty().addListener((observable, oldValue, newValue) -> { - BottomStatusBoxManager.getInstance().updateWordCountStatusLabel(); - save(); - }); - } - - /** - * 保存方法 - */ - public void save() { - // 获取当前选定的中央标签页(CenterTab对象) - CenterTab tab = CenterTabPaneManager.getInstance().getSelected(); - - // 如果没有选定标签页,返回,不执行保存操作 - if (tab == null) { - return; - } - - // 从标签页的用户数据中获取文件对象 - File file = (File) tab.getUserData(); - - // 获取主文本区域中的文本内容 - String newValue = this.getText(); - - // 如果文件对象为空,记录警告信息并返回,不执行保存操作 - if (file == null) { - logger.warn("Tab上没有关联文件信息"); - return; - } - - // 尝试使用BufferedWriter写入文件内容 - try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, tab.getCharset()))) { - // 将新的文本内容写入文件 - writer.write(newValue); - // 记录保存操作的日志信息 - LogUtil.getLogger(this.getClass()).info("正在自动保存---"); - } catch (IOException ignored) { - // 如果发生IO异常,记录忽视的日志信息,但不中断程序执行 - LogUtil.getLogger(this.getClass()).info("已忽视IO异常!"); - } - } } diff --git a/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java b/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java index e13b436..c8b48a3 100644 --- a/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java +++ b/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java @@ -98,6 +98,9 @@ public class CenterTabPaneManager { bottomStatusBoxManager.updateWhenTabSelected(); } + /** + * 保存当前所有打开的文件标签页 + */ public void saveOpenFileTabs() { // 获取当前所有标签页 ObservableList tabs = centerTabPane.getTabs(); diff --git a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java index c65df7e..b095ea8 100644 --- a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java +++ b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java @@ -4,8 +4,15 @@ import javafx.scene.control.Tab; import org.fxmisc.flowless.VirtualizedScrollPane; import org.jcnc.jnotepad.controller.config.AppConfigController; import org.jcnc.jnotepad.ui.module.LineNumberTextArea; +import org.jcnc.jnotepad.util.LogUtil; +import org.jcnc.jnotepad.views.manager.BottomStatusBoxManager; +import org.jcnc.jnotepad.views.manager.CenterTabPaneManager; +import org.slf4j.Logger; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.nio.charset.Charset; /** @@ -15,7 +22,7 @@ import java.nio.charset.Charset; * @author songdragon */ public class CenterTab extends Tab { - + Logger logger = LogUtil.getLogger(this.getClass()); private final LineNumberTextArea lineNumberTextArea; /** * 默认关闭自动换行 @@ -38,6 +45,7 @@ public class CenterTab extends Tab { public CenterTab(String tabTitle, LineNumberTextArea textArea, Charset charset) { super(tabTitle); lineNumberTextArea = textArea; + initTextAreaListeners(); this.setContent(new VirtualizedScrollPane<>(lineNumberTextArea)); setAutoLine(AppConfigController.getInstance().getAutoLineConfig()); this.charset = charset; @@ -73,12 +81,50 @@ public class CenterTab extends Tab { } /** - * 保存文件内容 + * 保存选中的文件标签页 */ - public void save() { - this.lineNumberTextArea.save(); + public void saveSelectedFileTab() { + // 获取当前选定的中央标签页(CenterTab对象) + CenterTab tab = CenterTabPaneManager.getInstance().getSelected(); + + // 如果没有选定标签页,返回,不执行保存操作 + if (tab == null) { + return; + } + + // 从标签页的用户数据中获取文件对象 + File file = (File) tab.getUserData(); + // 获取主文本区域中的文本内容 + String newValue = tab.getLineNumberTextArea().getText(); + + // 如果文件对象为空,记录警告信息并返回,不执行保存操作 + if (file == null) { + logger.warn("Tab上没有关联文件信息"); + return; + } + + // 尝试使用BufferedWriter写入文件内容 + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, tab.getCharset()))) { + // 将新的文本内容写入文件 + writer.write(newValue); + // 记录保存操作的日志信息 + LogUtil.getLogger(this.getClass()).info("正在自动保存---"); + } catch (IOException ignored) { + // 如果发生IO异常,记录忽视的日志信息,但不中断程序执行 + LogUtil.getLogger(this.getClass()).info("已忽视IO异常!"); + } } + /** + * 初始化监听器方法 + */ + private void initTextAreaListeners() { + // 监听主要文本区域的文本变化 + lineNumberTextArea.textProperty().addListener((observable, oldValue, newValue) -> { + BottomStatusBoxManager.getInstance().updateWordCountStatusLabel(); + saveSelectedFileTab(); + }); + } /** * 保存为指定文件 * @@ -89,7 +135,7 @@ public class CenterTab extends Tab { return; } this.setUserData(file); - save(); + saveSelectedFileTab(); } }