This commit is contained in:
gewuyou 2023-08-24 09:23:42 +08:00
commit 60f4f90f48
25 changed files with 472 additions and 447 deletions

View File

@ -7,12 +7,12 @@ body:
attributes: attributes:
value: | value: |
感谢对项目的支持与关注。在提出问题之前,请确保你已查看相关开发或使用文档: 感谢对项目的支持与关注。在提出问题之前,请确保你已查看相关开发或使用文档:
- https://... - https://gitee.com/jcnc-org/docs
- type: checkboxes - type: checkboxes
attributes: attributes:
label: 这个问题是否已经存在? label: 这个问题是否已经存在?
options: options:
- label: 我已经搜索过现有的问题 (https://gitee.com/../../issues) - label: 我已经搜索过现有的问题 (https://gitee.com/organizations/jcnc-org/issues)
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
@ -48,7 +48,7 @@ body:
label: 版本 label: 版本
description: 你当前正在使用我们软件的哪个版本/分支? description: 你当前正在使用我们软件的哪个版本/分支?
options: options:
- 1.0.2 (默认) - (默认)
- 1.0.3 (最新) - (最新)
validations: validations:
required: true required: true

3
.gitignore vendored
View File

@ -37,3 +37,6 @@ build/
/src/main/JNotepad.java /src/main/JNotepad.java
/.idea/ /.idea/
/project.txt /project.txt
logs/
/ch_language_pack.txt
/en_language_pack.txt

43
libs/README.md Normal file
View File

@ -0,0 +1,43 @@
# 解决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/icu4j-73.2.jar Normal file

Binary file not shown.

View File

@ -55,6 +55,13 @@
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
<version>1.4.11</version> <version>1.4.11</version>
</dependency> </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> </dependencies>
<build> <build>

View File

@ -9,6 +9,8 @@ module org.jcnc.jnotepad {
requires org.slf4j; requires org.slf4j;
requires ch.qos.logback.core; requires ch.qos.logback.core;
requires ch.qos.logback.classic; requires ch.qos.logback.classic;
requires com.ibm.icu;
exports org.jcnc.jnotepad; exports org.jcnc.jnotepad;
exports org.jcnc.jnotepad.tool; exports org.jcnc.jnotepad.tool;
exports org.jcnc.jnotepad.Interface; exports org.jcnc.jnotepad.Interface;

View File

@ -1,12 +0,0 @@
package org.jcnc.jnotepad.Interface;
import java.util.Properties;
/**
* @author 许轲
*/
public interface ConfigInterface {
void showErrorAlert();
Properties readPropertiesFromFile();
void initializePropertiesFile();
}

View File

@ -1,7 +1,5 @@
package org.jcnc.jnotepad.Interface; package org.jcnc.jnotepad.Interface;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import org.jcnc.jnotepad.ui.LineNumberTextArea; import org.jcnc.jnotepad.ui.LineNumberTextArea;
import java.io.File; import java.io.File;
@ -21,43 +19,6 @@ public interface ControllerInterface {
*/ */
LineNumberTextArea openAssociatedFileAndCreateTextArea(List<String> rawParameters); LineNumberTextArea openAssociatedFileAndCreateTextArea(List<String> rawParameters);
/**
* 获取新建文件处理事件处理程序
*
* @param textArea 文本区域
* @return 新建文件处理事件处理程序
*/
EventHandler<ActionEvent> getNewFileEventHandler(LineNumberTextArea textArea);
/**
* 获取打开文件处理事件处理程序
*
* @return 打开文件处理事件处理程序
*/
EventHandler<ActionEvent> getOpenFileEventHandler();
/**
* 获取另存为文件处理事件处理程序
*
* @return 另存为文件处理事件处理程序
*/
EventHandler<ActionEvent> getSaveAsFileEventHandler();
/**
* 自动保存
*
* @param textArea 文本区域
*/
void autoSave(LineNumberTextArea textArea);
/**
* 更新状态标签
*
* @param textArea 文本区域
*/
void updateStatusLabel(LineNumberTextArea textArea);
/** /**
* 打开关联文件 * 打开关联文件
* *
@ -72,36 +33,6 @@ public interface ControllerInterface {
*/ */
LineNumberTextArea getText(File file); LineNumberTextArea getText(File file);
/**
* 更新编码标签
*
* @param text 编码标签文本
*/
void upDateEncodingLabel(String text);
/**
* 获取光标所在行号
*
* @param caretPosition 光标位置
* @param text 文本内容
* @return 行号
*/
int getRow(int caretPosition, String text);
/**
* 获取光标所在列号
*
* @param caretPosition 光标位置
* @param text 文本内容
* @return 列号
*/
int getColumn(int caretPosition, String text);
/**
* 初始化 TabPane
*/
void initTabPane();
/** /**
* 更新UI和标签页 * 更新UI和标签页
* *

View File

@ -53,22 +53,12 @@ public class LunchApp extends Application {
double length = AppConstants.SCREEN_LENGTH; double length = AppConstants.SCREEN_LENGTH;
String icon = AppConstants.APP_ICON; String icon = AppConstants.APP_ICON;
scene = new Scene(root, width, length); scene = new Scene(root, width, length);
Application.setUserAgentStylesheet(new PrimerLight().getUserAgentStylesheet()); Application.setUserAgentStylesheet(new PrimerLight().getUserAgentStylesheet());
scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/css/styles.css")).toExternalForm()); scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/css/styles.css")).toExternalForm());
primaryStage.setTitle(title);
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);
// 初始化菜单项和标签栏
view.initTabPane();
view.initShortcutKey();
// 使用线程池加载关联文件并创建文本区域 // 使用线程池加载关联文件并创建文本区域
List<String> rawParameters = getParameters().getRaw(); List<String> rawParameters = getParameters().getRaw();
THREAD_POOL.execute(() -> { THREAD_POOL.execute(() -> {
@ -77,6 +67,18 @@ public class LunchApp extends Application {
Platform.runLater(() -> controller.updateUiWithNewTextArea(textArea)); Platform.runLater(() -> controller.updateUiWithNewTextArea(textArea));
} }
}); });
primaryStage.setTitle(title);
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);
// 初始化快捷键
view.initShortcutKey();
} }
@Override @Override

View File

@ -29,6 +29,7 @@ public class AppConstants {
/** /**
* 配置文件名 * 配置文件名
*/ */
public static final String PROPERTY_FILE_NAME = "project.txt"; public static final String CH_LANGUAGE_PACK_NAME = "ch_language_pack.txt";
public static final String EN_LANGUAGE_PACK_NAME = "en_language_pack.txt";
} }

View File

@ -1,32 +0,0 @@
package org.jcnc.jnotepad.controller.event.handler;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import org.jcnc.jnotepad.app.config.GlobalConfig;
/**
* 换行事件处理针对当前选中tab进行格式化<br/>
* 配置变更时<br/>
* 1. 更新内存全局配置<br/>
* 2. 对当前tab生效配置每次tab切换根据全局配置设置进行变更<br/>
* <p>
* 用于在文本区域中插入一个换行符
* @see GlobalConfig
*
* @deprecated 事件处理将使用item的listener实现
*
* @author 许轲
*/
@Deprecated
public class LineFeed implements EventHandler<ActionEvent> {
/**
* 处理事件的方法将一个换行符插入到文本区域的末尾
*
* @param event 触发的事件对象
*/
@Override
public void handle(ActionEvent event) {
//
}
}

View File

@ -2,12 +2,15 @@ package org.jcnc.jnotepad.controller.event.handler;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import org.jcnc.jnotepad.controller.manager.Controller; import org.jcnc.jnotepad.init.Config;
import org.jcnc.jnotepad.ui.LineNumberTextArea; import org.jcnc.jnotepad.ui.LineNumberTextArea;
import org.jcnc.jnotepad.ui.status.JNotepadStatusBox;
import org.jcnc.jnotepad.ui.tab.JNotepadTab; import org.jcnc.jnotepad.ui.tab.JNotepadTab;
import org.jcnc.jnotepad.ui.tab.JNotepadTabPane; import org.jcnc.jnotepad.ui.tab.JNotepadTabPane;
import org.jcnc.jnotepad.view.manager.ViewManager; import org.jcnc.jnotepad.view.manager.ViewManager;
import java.util.Properties;
/** /**
* 新建文件事件的事件处理程序 * 新建文件事件的事件处理程序
@ -17,36 +20,30 @@ import org.jcnc.jnotepad.view.manager.ViewManager;
* @author 许轲 * @author 许轲
*/ */
public class NewFile implements EventHandler<ActionEvent> { public class NewFile implements EventHandler<ActionEvent> {
Config config = new Config();
Properties properties = config.readPropertiesFromFile();
String NEW_FILE = properties.getProperty("NEW_FILE");
/** /**
*
* 处理新建文件事件 * 处理新建文件事件
* *
* @param event 事件对象 * @param event 事件对象
*/ */
@Override @Override
public void handle(ActionEvent event) { public void handle(ActionEvent event) {
// 获取控制器
Controller controller = Controller.getInstance();
// 创建一个新的文本编辑区 // 创建一个新的文本编辑区
LineNumberTextArea textArea = new LineNumberTextArea(); LineNumberTextArea textArea = new LineNumberTextArea();
textArea.setStyle(
"-fx-border-color:white ;-fx-background-color:white;"
);
// TODO: refactor统一TextArea新建绑定监听器入口 // TODO: refactor统一TextArea新建绑定监听器入口
// 增加autoSave监听器绑定
controller.autoSave(textArea);
ViewManager viewManager = ViewManager.getInstance(); ViewManager viewManager = ViewManager.getInstance();
// 将Tab页添加到TabPane中 // 将Tab页添加到TabPane中
JNotepadTabPane.getInstance().addNewTab(new JNotepadTab("新建文本 " JNotepadTabPane.getInstance().addNewTab(new JNotepadTab(NEW_FILE
+ viewManager.selfIncreaseAndGetTabIndex(), + viewManager.selfIncreaseAndGetTabIndex(),
textArea)); textArea));
// 更新状态标签
controller.updateStatusLabel(textArea);
// 更新编码信息 // 更新编码信息
controller.upDateEncodingLabel(textArea.getMainTextArea().getText()); JNotepadStatusBox.getInstance().updateEncodingLabel();
} }
} }

View File

@ -5,8 +5,6 @@ import javafx.event.ActionEvent;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import org.jcnc.jnotepad.controller.manager.Controller; import org.jcnc.jnotepad.controller.manager.Controller;
import org.jcnc.jnotepad.ui.LineNumberTextArea;
import org.jcnc.jnotepad.ui.tab.JNotepadTabPane;
import java.io.File; import java.io.File;
@ -15,6 +13,7 @@ import java.io.File;
* 打开文件的事件处理程序 * 打开文件的事件处理程序
* <p> * <p>
* 当用户选择打开文件时将创建一个新的文本编辑区并在Tab页中显示 * 当用户选择打开文件时将创建一个新的文本编辑区并在Tab页中显示
*
* @author 许轲 * @author 许轲
*/ */
public class OpenFile implements EventHandler<ActionEvent> { public class OpenFile implements EventHandler<ActionEvent> {
@ -38,8 +37,6 @@ public class OpenFile implements EventHandler<ActionEvent> {
protected Void call() { protected Void call() {
// 调用控制器的getText方法读取文件内容 // 调用控制器的getText方法读取文件内容
controller.getText(file); controller.getText(file);
// 更新编码标签
controller.upDateEncodingLabel(((LineNumberTextArea) JNotepadTabPane.getInstance().getSelected().getContent()).getMainTextArea().getText());
return null; return null;
} }
}; };

View File

@ -2,10 +2,9 @@ package org.jcnc.jnotepad.controller.event.handler;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.scene.control.Tab;
import org.jcnc.jnotepad.controller.manager.Controller;
import org.jcnc.jnotepad.tool.LogUtil; import org.jcnc.jnotepad.tool.LogUtil;
import org.jcnc.jnotepad.ui.LineNumberTextArea; import org.jcnc.jnotepad.ui.LineNumberTextArea;
import org.jcnc.jnotepad.ui.tab.JNotepadTab;
import org.jcnc.jnotepad.ui.tab.JNotepadTabPane; import org.jcnc.jnotepad.ui.tab.JNotepadTabPane;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -28,7 +27,7 @@ public class SaveFile implements EventHandler<ActionEvent> {
@Override @Override
public void handle(ActionEvent actionEvent) { public void handle(ActionEvent actionEvent) {
// 获取当前tab页 // 获取当前tab页
Tab selectedTab = JNotepadTabPane.getInstance().getSelected(); JNotepadTab selectedTab = JNotepadTabPane.getInstance().getSelected();
if (selectedTab == null) { if (selectedTab == null) {
return; return;
} }
@ -40,10 +39,8 @@ public class SaveFile implements EventHandler<ActionEvent> {
saveTab(this.getClass()); saveTab(this.getClass());
} else { } else {
logger.info("当前保存文件为关联打开文件,调用自动保存方法"); logger.info("当前保存文件为关联打开文件,调用自动保存方法");
// 打开的是关联文件 // 调用tab保存
Controller controller = Controller.getInstance(); selectedTab.save();
// 自动保存
controller.autoSave(textArea);
} }
} }
} }

View File

@ -2,13 +2,8 @@ package org.jcnc.jnotepad.controller.manager;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Tab;
import org.jcnc.jnotepad.Interface.ControllerInterface; import org.jcnc.jnotepad.Interface.ControllerInterface;
import org.jcnc.jnotepad.controller.event.handler.NewFile; import org.jcnc.jnotepad.init.Config;
import org.jcnc.jnotepad.controller.event.handler.OpenFile;
import org.jcnc.jnotepad.controller.event.handler.SaveAsFile;
import org.jcnc.jnotepad.tool.EncodingDetector; import org.jcnc.jnotepad.tool.EncodingDetector;
import org.jcnc.jnotepad.tool.LogUtil; import org.jcnc.jnotepad.tool.LogUtil;
import org.jcnc.jnotepad.ui.LineNumberTextArea; import org.jcnc.jnotepad.ui.LineNumberTextArea;
@ -16,9 +11,13 @@ import org.jcnc.jnotepad.ui.tab.JNotepadTab;
import org.jcnc.jnotepad.ui.tab.JNotepadTabPane; import org.jcnc.jnotepad.ui.tab.JNotepadTabPane;
import org.jcnc.jnotepad.view.manager.ViewManager; import org.jcnc.jnotepad.view.manager.ViewManager;
import java.io.*; import java.io.BufferedReader;
import java.util.ArrayList; import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List; import java.util.List;
import java.util.Properties;
/** /**
* 控制器类实现ControllerInterface接口用于管理文本编辑器的各种操作和事件处理 * 控制器类实现ControllerInterface接口用于管理文本编辑器的各种操作和事件处理
@ -27,6 +26,11 @@ import java.util.List;
* @author 许轲 * @author 许轲
*/ */
public class Controller implements ControllerInterface { public class Controller implements ControllerInterface {
Config config = new Config();
Properties properties = config.readPropertiesFromFile();
String NEW_FILE = properties.getProperty("NEW_FILE");
private static final Controller INSTANCE = new Controller(); private static final Controller INSTANCE = new Controller();
private Controller() { private Controller() {
@ -57,74 +61,6 @@ public class Controller implements ControllerInterface {
} }
} }
/**
* 获取新建文件事件处理程序
*
* @param textArea 文本区域
* @return 新建文件事件处理程序
*/
@Override
public EventHandler<ActionEvent> getNewFileEventHandler(LineNumberTextArea textArea) {
return new NewFile();
}
/**
* 获取打开文件事件处理程序
*
* @return 打开文件事件处理程序
*/
@Override
public EventHandler<ActionEvent> getOpenFileEventHandler() {
return new OpenFile();
}
/**
* 自动保存文本内容
*
* @param textArea 文本区域
*/
@Override
public void autoSave(LineNumberTextArea textArea) {
textArea.getMainTextArea().textProperty().addListener((observable, oldValue, newValue) -> {
Tab tab = JNotepadTabPane.getInstance().getSelected();
if (tab != null) {
File file = (File) tab.getUserData();
if (file != null) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
writer.write(newValue);
LogUtil.getLogger(this.getClass()).info("正在自动保存---");
} catch (IOException ignored) {
LogUtil.getLogger(this.getClass()).info("已忽视IO异常!");
}
}
}
});
}
/**
* 获取另存为文件事件处理程序
*
* @return 另存为文件事件处理程序
*/
@Override
public EventHandler<ActionEvent> getSaveAsFileEventHandler() {
return new SaveAsFile();
}
/**
* 更新状态标签
*
* @param textArea 文本区域
*/
@Override
public void updateStatusLabel(LineNumberTextArea textArea) {
int caretPosition = textArea.getMainTextArea().getCaretPosition();
int row = getRow(caretPosition, textArea.getMainTextArea().getText());
int column = getColumn(caretPosition, textArea.getMainTextArea().getText());
int length = textArea.getMainTextArea().getLength();
ViewManager.getInstance().getStatusLabel().setText("行: " + row + " \t列: " + column + " \t字数: " + length);
}
/** /**
* 打开关联文件 * 打开关联文件
* *
@ -148,7 +84,9 @@ public class Controller implements ControllerInterface {
LineNumberTextArea textArea = createNewTextArea(); LineNumberTextArea textArea = createNewTextArea();
// 设置当前标签页关联本地文件 // 设置当前标签页关联本地文件
textArea.setRelevance(true); textArea.setRelevance(true);
try (BufferedReader reader = new BufferedReader(new FileReader(file))) { // 检测文件编码
Charset encoding = EncodingDetector.detectEncodingCharset(file);
try (BufferedReader reader = new BufferedReader(new FileReader(file, encoding))) {
StringBuilder textBuilder = new StringBuilder(); StringBuilder textBuilder = new StringBuilder();
String line; String line;
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
@ -158,11 +96,9 @@ public class Controller implements ControllerInterface {
Platform.runLater(() -> { Platform.runLater(() -> {
textArea.getMainTextArea().setText(text); textArea.getMainTextArea().setText(text);
JNotepadTab tab = createNewTab(file.getName(), textArea); JNotepadTab tab = createNewTab(file.getName(), textArea, encoding);
tab.setUserData(file); tab.setUserData(file);
JNotepadTabPane.getInstance().addNewTab(tab); JNotepadTabPane.getInstance().addNewTab(tab);
updateStatusLabel(textArea);
autoSave(textArea);
}); });
} catch (IOException ignored) { } catch (IOException ignored) {
@ -171,74 +107,6 @@ public class Controller implements ControllerInterface {
return textArea; return textArea;
} }
/**
* 更新编码标签
*
* @param text 文本内容
*/
@Override
public void upDateEncodingLabel(String text) {
String encoding = EncodingDetector.detectEncoding(text);
ViewManager.getInstance().getEnCodingLabel().setText("\t编码: " + encoding);
}
/**
* 获取光标所在行号
*
* @param caretPosition 光标位置
* @param text 文本内容
* @return 光标所在行号
*/
@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;
}
/**
* 获取光标所在列号
*
* @param caretPosition 光标位置
* @param text 文本内容
* @return 光标所在列号
*/
@Override
public int getColumn(int caretPosition, String text) {
return caretPosition - text.lastIndexOf("\n", caretPosition - 1);
}
/**
* 初始化标签面板
*/
@Override
public void initTabPane() {
JNotepadTabPane.getInstance().getSelectionModel().selectedItemProperty().addListener((observable, oldTab, newTab) -> {
LineNumberTextArea textArea;
if (newTab != null) {
// 获取新选定的标签页并关联的文本区域
textArea = (LineNumberTextArea) newTab.getContent();
} else {
// 刷新状态
textArea = openAssociatedFileAndCreateTextArea(new ArrayList<>());
}
// 更新状态标签
INSTANCE.updateStatusLabel(textArea);
// 监听文本光标位置的变化更新状态标签
textArea.getMainTextArea().caretPositionProperty().addListener((caretObservable, oldPosition, newPosition) -> INSTANCE.updateStatusLabel(textArea));
// 更新编码标签
INSTANCE.upDateEncodingLabel(textArea.getMainTextArea().getText());
});
}
/** /**
* 更新UI和标签页 * 更新UI和标签页
* *
@ -249,9 +117,8 @@ public class Controller implements ControllerInterface {
@Override @Override
public void updateUiWithNewTextArea(LineNumberTextArea textArea) { public void updateUiWithNewTextArea(LineNumberTextArea textArea) {
ViewManager viewManager = ViewManager.getInstance(); ViewManager viewManager = ViewManager.getInstance();
String tabTitle = "新建文件 " + viewManager.selfIncreaseAndGetTabIndex(); String tabTitle = NEW_FILE + viewManager.selfIncreaseAndGetTabIndex();
JNotepadTabPane.getInstance().addNewTab(new JNotepadTab(tabTitle, textArea)); JNotepadTabPane.getInstance().addNewTab(new JNotepadTab(tabTitle, textArea));
updateStatusLabel(textArea);
} }
@ -262,12 +129,6 @@ public class Controller implements ControllerInterface {
*/ */
private void configureTextArea(LineNumberTextArea textArea) { private void configureTextArea(LineNumberTextArea textArea) {
textArea.getMainTextArea().setWrapText(true); textArea.getMainTextArea().setWrapText(true);
upDateEncodingLabel(textArea.getMainTextArea().getText());
updateStatusLabel(textArea);
textArea.textProperty().addListener((observable, oldValue, newValue) -> updateStatusLabel(textArea));
autoSave(textArea);
} }
/** /**
@ -276,12 +137,7 @@ public class Controller implements ControllerInterface {
* @return 新的文本区域 * @return 新的文本区域
*/ */
private LineNumberTextArea createNewTextArea() { private LineNumberTextArea createNewTextArea() {
LineNumberTextArea textArea = new LineNumberTextArea(); return new LineNumberTextArea();
textArea.setStyle(
"-fx-border-color:white;" +
"-fx-background-color:white"
);
return textArea;
} }
/** /**
@ -291,8 +147,8 @@ public class Controller implements ControllerInterface {
* @param textArea 文本区域 * @param textArea 文本区域
* @return 新的标签页 * @return 新的标签页
*/ */
private JNotepadTab createNewTab(String tabName, LineNumberTextArea textArea) { private JNotepadTab createNewTab(String tabName, LineNumberTextArea textArea, Charset charset) {
return new JNotepadTab(tabName, textArea); return new JNotepadTab(tabName, textArea, charset);
} }
/** /**
@ -305,7 +161,7 @@ public class Controller implements ControllerInterface {
return new Task<>() { return new Task<>() {
@Override @Override
protected Void call() { protected Void call() {
upDateEncodingLabel(getText(file).getMainTextArea().getText()); getText(file);
return null; return null;
} }
}; };

View File

@ -1,43 +1,106 @@
package org.jcnc.jnotepad.init; package org.jcnc.jnotepad.init;
import javafx.scene.control.Alert;
import org.jcnc.jnotepad.Interface.ConfigInterface;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Properties; import java.util.Properties;
import static org.jcnc.jnotepad.constants.AppConstants.PROPERTY_FILE_NAME; import static org.jcnc.jnotepad.constants.AppConstants.CH_LANGUAGE_PACK_NAME;
import static org.jcnc.jnotepad.constants.AppConstants.EN_LANGUAGE_PACK_NAME;
/** /**
* @author 许轲 * @author 许轲
* 该类负责配置文件的读取和初始化操作
*/ */
public class Config implements ConfigInterface { public class Config {
String LANGUAGE_PACK_NAME;
/**
* 从文件中读取属性配置
*
* @return 包含从文件中读取的属性的 Properties 对象
*/
public Properties readPropertiesFromFile() { public Properties readPropertiesFromFile() {
Properties properties = new Properties(); Properties properties = new Properties();
try (InputStream inputStream = new FileInputStream(PROPERTY_FILE_NAME)) {
properties.load(inputStream); //设置语言包
LANGUAGE_PACK_NAME = EN_LANGUAGE_PACK_NAME;
try (InputStream inputStream = new FileInputStream(LANGUAGE_PACK_NAME)) {
InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); // 使用 UTF-8 编码
properties.load(reader);
} catch (IOException e) { } catch (IOException e) {
// 如果读取出错则调用初始化方法
initializePropertiesFile(); initializePropertiesFile();
} }
return properties; return properties;
} }
/**
* 初始化属性配置文件
* 如果属性文件不存在将创建一个新的属性文件并设置默认属性
*/
public void initializePropertiesFile() { public void initializePropertiesFile() {
Properties defaultProperties = new Properties(); Properties chLanguagePack = getCHLanguagePack();
defaultProperties.setProperty("title", "JNotepad");
try (OutputStream outputStream = new FileOutputStream(PROPERTY_FILE_NAME)) { Properties enLanguagePack = getENLanguagePack();
defaultProperties.store(outputStream, "JNotepad Properties");
} catch (IOException e) { try (OutputStream outputStream = new FileOutputStream(CH_LANGUAGE_PACK_NAME)) {
showErrorAlert(); OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); // 使用 UTF-8 编码
chLanguagePack.store(writer, "JNotepad ch_language_pack");
} catch (IOException ignored) {
}
try (OutputStream outputStream = new FileOutputStream(EN_LANGUAGE_PACK_NAME)) {
OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); // 使用 UTF-8 编码
enLanguagePack.store(writer, "JNotepad en_language_pack");
} catch (IOException ignored) {
} }
} }
public void showErrorAlert() { private static Properties getCHLanguagePack() {
Alert alert = new Alert(Alert.AlertType.ERROR); Properties properties = new Properties();
alert.setTitle("错误");
alert.setHeaderText("文件读写错误"); properties.setProperty("TITLE", "JNotepad"); // 设置默认属性
alert.setContentText("文件读写错误"); properties.setProperty("NEW_FILE", "新建文件");
alert.showAndWait(); properties.setProperty("SAVA", "保存");
properties.setProperty("FILE", "文件");
properties.setProperty("NEW", "新建");
properties.setProperty("OPEN", "打开");
properties.setProperty("SAVA_AS", "另存为");
properties.setProperty("SET", "设置");
properties.setProperty("WORD_WRAP", "自动换行");
properties.setProperty("PLUGIN", "插件");
properties.setProperty("ADD_PLUGIN", "增加插件");
properties.setProperty("STATISTICS", "统计字数");
properties.setProperty("ROW", "行数");
properties.setProperty("COLUMN", "列数");
properties.setProperty("WORD_COUNT", "字数");
properties.setProperty("ENCODE", "编码");
return properties;
} }
private static Properties getENLanguagePack() {
Properties properties = new Properties();
properties.setProperty("TITLE", "JNotepad");
properties.setProperty("NEW_FILE", "New File");
properties.setProperty("SAVA", "Save");
properties.setProperty("FILE", "File");
properties.setProperty("NEW", "New");
properties.setProperty("OPEN", "Open");
properties.setProperty("SAVA_AS", "Save As");
properties.setProperty("SET", "Settings");
properties.setProperty("WORD_WRAP", "Word Wrap");
properties.setProperty("PLUGIN", "Plugins");
properties.setProperty("ADD_PLUGIN", "Add Plugin");
properties.setProperty("STATISTICS", "Word Count");
properties.setProperty("ROW", "Row");
properties.setProperty("COLUMN", "Column");
properties.setProperty("WORD_COUNT", "Word Count");
properties.setProperty("ENCODE", "Encoding");
return properties;
}
} }

View File

@ -1,11 +1,21 @@
package org.jcnc.jnotepad.tool; package org.jcnc.jnotepad.tool;
import com.ibm.icu.text.CharsetDetector;
import com.ibm.icu.text.CharsetMatch;
import javafx.application.Platform; import javafx.application.Platform;
import org.jcnc.jnotepad.ui.LineNumberTextArea; import org.jcnc.jnotepad.ui.LineNumberTextArea;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/** /**
* 编码检测工具类 * 编码检测工具类
@ -13,67 +23,46 @@ import java.nio.charset.StandardCharsets;
* @author 许轲 * @author 许轲
*/ */
public class EncodingDetector { public class EncodingDetector {
private EncodingDetector() {
}
/** private static final Logger LOG = LoggerFactory.getLogger(EncodingDetector.class);
* 检测 TextArea 中的文本编码 public static final String UNKNOWN = "UNKNOWN";
*
* @param textArea 文本区域 private EncodingDetector() {
* @return 字符串表示的编码如果检测失败则返回 "UNKNOWN"
*/
public static String detectEncoding(LineNumberTextArea textArea) {
String text = textArea.getMainTextArea().getText();
return detectEncoding(text);
} }
/** /**
* 检测文本编码 * 检测文本编码
* *
* @param text 要检测的文本 * @param file 要检测的文件
* @return 字符串表示的编码如果检测失败则返回 "UNKNOWN" * @return 字符串表示的编码如果检测失败则返回 "UNKNOWN"
*/ */
public static String detectEncoding(String text) { public static String detectEncoding(File file) {
// 尝试常见的编码 CharsetDetector charsetDetector = new CharsetDetector();
for (Charset charset : commonCharsets()) { try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file.getPath()))) {
if (isValidEncoding(text, charset)) { charsetDetector.setText(inputStream);
Platform.runLater(() -> LogUtil.getLogger(EncodingDetector.class).info("编码监测结果:{}", isValidEncoding(text, charset))); CharsetMatch match = charsetDetector.detect();
return charset.name(); LOG.debug(match.getName() + " " + match.getConfidence());
if (match.getConfidence() > 50) {
return match.getName();
} }
} catch (Exception e) {
LOG.error("", e);
} }
return "UNKNOWN"; return UNKNOWN;
} }
/** /**
* 获取常见的字符集数组 * 检测文本编码
* *
* @return 常见字符集数组 * @param file 文件
* @return Charset编码
*/ */
private static Charset[] commonCharsets() { public static Charset detectEncodingCharset(File file) {
return new Charset[]{ String charset = detectEncoding(file);
StandardCharsets.UTF_8, if (charset.equals(UNKNOWN)) {
StandardCharsets.UTF_16, return StandardCharsets.UTF_8;
StandardCharsets.UTF_16LE, }
StandardCharsets.UTF_16BE, return Charset.forName(charset);
StandardCharsets.ISO_8859_1
};
} }
/**
* 检查指定编码是否能够正确解码文本
*
* @param text 要检测的文本
* @param encoding 要尝试的编码
* @return 如果指定编码能够正确解码文本则返回 true否则返回 false
*/
private static boolean isValidEncoding(String text, Charset encoding) {
// 尝试使用指定编码解码
byte[] bytes = text.getBytes(encoding);
String decoded = new String(bytes, encoding);
// 解码后的文本相同表示编码有效
return text.equals(decoded);
}
} }

View File

@ -3,6 +3,7 @@ package org.jcnc.jnotepad.tool;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import org.jcnc.jnotepad.ui.LineNumberTextArea; import org.jcnc.jnotepad.ui.LineNumberTextArea;
import org.jcnc.jnotepad.ui.tab.JNotepadTab;
import org.jcnc.jnotepad.ui.tab.JNotepadTabPane; import org.jcnc.jnotepad.ui.tab.JNotepadTabPane;
import java.io.*; import java.io.*;
@ -49,7 +50,7 @@ public class FileUtil {
* @see LogUtil * @see LogUtil
*/ */
public static void saveTab(Class<?> currentClass) { public static void saveTab(Class<?> currentClass) {
Tab selectedTab = JNotepadTabPane.getInstance().getSelected(); JNotepadTab selectedTab = JNotepadTabPane.getInstance().getSelected();
if (selectedTab != null) { if (selectedTab != null) {
// 创建一个文件窗口 // 创建一个文件窗口
FileChooser fileChooser = new FileChooser(); FileChooser fileChooser = new FileChooser();
@ -59,24 +60,13 @@ public class FileUtil {
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("文本文档", "*.txt")); fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("文本文档", "*.txt"));
File file = fileChooser.showSaveDialog(null); File file = fileChooser.showSaveDialog(null);
if (file != null) { if (file != null) {
try ( selectedTab.save();
BufferedWriter writer = new BufferedWriter(new FileWriter(file)) // 将保存后的文件设置为已关联
) { selectedTab.getLineNumberTextArea().setRelevance(true);
// 获取当前Tab页的文本编辑区 // 更新Tab页标签上的文件名
LineNumberTextArea textArea = (LineNumberTextArea) selectedTab.getContent(); selectedTab.setText(file.getName());
// 将保存后的文件设置为已关联 // 将文件对象保存到Tab页的UserData中
textArea.setRelevance(true); selectedTab.setUserData(file);
String text = textArea.getMainTextArea().getText();
// 写入文件内容
writer.write(text);
writer.flush();
// 更新Tab页标签上的文件名
selectedTab.setText(file.getName());
// 将文件对象保存到Tab页的UserData中
selectedTab.setUserData(file);
} catch (IOException ignored) {
LogUtil.getLogger(currentClass).info("已忽视IO异常!");
}
} }
} }
} }

View File

@ -1,8 +1,18 @@
package org.jcnc.jnotepad.ui; package org.jcnc.jnotepad.ui;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.scene.control.Tab;
import javafx.scene.control.TextArea; import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import org.jcnc.jnotepad.tool.LogUtil;
import org.jcnc.jnotepad.ui.status.JNotepadStatusBox;
import org.jcnc.jnotepad.ui.tab.JNotepadTab;
import org.jcnc.jnotepad.ui.tab.JNotepadTabPane;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/** /**
* @author 许轲 * @author 许轲
@ -28,16 +38,50 @@ public class LineNumberTextArea extends BorderPane {
// 设定自定义样式 // 设定自定义样式
lineNumberArea.getStyleClass().add("text-line-number"); lineNumberArea.getStyleClass().add("text-line-number");
mainTextArea.getStyleClass().add("main-text-area"); mainTextArea.getStyleClass().add("main-text-area");
lineNumberArea.textProperty().addListener((observable, oldValue, newValue) -> updateLineNumberWidth()); this.setStyle(
mainTextArea.textProperty().addListener((observable, oldValue, newValue) -> updateLineNumberArea()); "-fx-border-color:white;" +
"-fx-background-color:white"
);
setCenter(mainTextArea);
setLeft(lineNumberArea);
initListeners();
}
private void initListeners() {
// 当主要文本区域的垂直滚动位置发生变化时使行号文本区域的滚动位置保持一致 // 当主要文本区域的垂直滚动位置发生变化时使行号文本区域的滚动位置保持一致
mainTextArea.scrollTopProperty().addListener((observable, oldValue, newValue) -> lineNumberArea.setScrollTop(mainTextArea.getScrollTop())); mainTextArea.scrollTopProperty().addListener((observable, oldValue, newValue) -> lineNumberArea.setScrollTop(mainTextArea.getScrollTop()));
// 当行号文本区域的垂直滚动位置发生变化时使主要文本区域的滚动位置保持一致 // 当行号文本区域的垂直滚动位置发生变化时使主要文本区域的滚动位置保持一致
lineNumberArea.scrollTopProperty().addListener((observable, oldValue, newValue) -> mainTextArea.setScrollTop(lineNumberArea.getScrollTop())); lineNumberArea.scrollTopProperty().addListener((observable, oldValue, newValue) -> mainTextArea.setScrollTop(lineNumberArea.getScrollTop()));
setCenter(mainTextArea); lineNumberArea.textProperty().addListener((observable, oldValue, newValue) -> updateLineNumberWidth());
setLeft(lineNumberArea);
this.mainTextArea.caretPositionProperty().addListener((caretObservable, oldPosition, newPosition) -> JNotepadStatusBox.getInstance().updateWordCountStatusLabel());
this.textProperty().addListener((observable, oldValue, newValue) -> {
// 更新行号
updateLineNumberArea();
// 更新状态栏
JNotepadStatusBox.getInstance().updateWordCountStatusLabel();
// 自动保存
save();
});
}
/**
* 以原文件编码格式写回文件
*/
public void save() {
JNotepadTab tab = JNotepadTabPane.getInstance().getSelected();
File file = (File) tab.getUserData();
String newValue = this.mainTextArea.getText();
if (file != null) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, tab.getCharset()))) {
writer.write(newValue);
LogUtil.getLogger(this.getClass()).info("正在自动保存---");
} catch (IOException ignored) {
LogUtil.getLogger(this.getClass()).info("已忽视IO异常!");
}
}
} }
public boolean isRelevance() { public boolean isRelevance() {
@ -91,5 +135,6 @@ public class LineNumberTextArea extends BorderPane {
public TextArea getMainTextArea() { public TextArea getMainTextArea() {
return mainTextArea; return mainTextArea;
} }
} }

View File

@ -6,11 +6,13 @@ import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem; import javafx.scene.control.MenuItem;
import org.jcnc.jnotepad.app.config.GlobalConfig; import org.jcnc.jnotepad.app.config.GlobalConfig;
import org.jcnc.jnotepad.controller.event.handler.*; import org.jcnc.jnotepad.controller.event.handler.*;
import org.jcnc.jnotepad.init.Config;
import org.jcnc.jnotepad.ui.tab.JNotepadTab; import org.jcnc.jnotepad.ui.tab.JNotepadTab;
import org.jcnc.jnotepad.ui.tab.JNotepadTabPane; import org.jcnc.jnotepad.ui.tab.JNotepadTabPane;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties;
/** /**
* 封装菜单栏组件 * 封装菜单栏组件
@ -19,6 +21,24 @@ import java.util.Map;
*/ */
public class JNotepadMenuBar extends MenuBar { public class JNotepadMenuBar extends MenuBar {
Config config = new Config();
Properties properties = config.readPropertiesFromFile();
String SAVA = properties.getProperty("SAVA");
String FILE = properties.getProperty("FILE");
String NEW = properties.getProperty("NEW");
String OPEN = properties.getProperty("OPEN");
String SAVA_AS = properties.getProperty("SAVA_AS");
String SET = properties.getProperty("SET");
String WORD_WRAP = properties.getProperty("WORD_WRAP");
String PLUGIN = properties.getProperty("PLUGIN");
String ADD_PLUGIN = properties.getProperty("ADD_PLUGIN");
String STATISTICS = properties.getProperty("STATISTICS");
private static final JNotepadMenuBar MENU_BAR = new JNotepadMenuBar(); private static final JNotepadMenuBar MENU_BAR = new JNotepadMenuBar();
private JNotepadMenuBar() { private JNotepadMenuBar() {
@ -90,18 +110,18 @@ public class JNotepadMenuBar extends MenuBar {
*/ */
private void initFileMenu() { private void initFileMenu() {
// 文件菜单 // 文件菜单
fileMenu = new Menu("文件"); fileMenu = new Menu(FILE);
newItem = new MenuItem("新建"); newItem = new MenuItem(NEW);
itemMap.put("newItem", newItem); itemMap.put("newItem", newItem);
openItem = new MenuItem("打开"); openItem = new MenuItem(OPEN);
itemMap.put("openItem", openItem); itemMap.put("openItem", openItem);
saveItem = new MenuItem("保存"); saveItem = new MenuItem(SAVA);
itemMap.put("saveItem", saveItem); itemMap.put("saveItem", saveItem);
saveAsItem = new MenuItem("另存为"); saveAsItem = new MenuItem(SAVA_AS);
itemMap.put("saveAsItem", saveAsItem); itemMap.put("saveAsItem", saveAsItem);
fileMenu.getItems().addAll(newItem, openItem, saveItem, saveAsItem); fileMenu.getItems().addAll(newItem, openItem, saveItem, saveAsItem);
@ -112,9 +132,9 @@ public class JNotepadMenuBar extends MenuBar {
*/ */
private void initSettingMenu() { private void initSettingMenu() {
// 设置菜单 // 设置菜单
setMenu = new Menu("设置"); setMenu = new Menu(SET);
lineFeedItem = new CheckMenuItem("自动换行"); lineFeedItem = new CheckMenuItem(WORD_WRAP);
itemMap.put("lineFeedItem", lineFeedItem); itemMap.put("lineFeedItem", lineFeedItem);
lineFeedItem.selectedProperty().set(true); lineFeedItem.selectedProperty().set(true);
@ -126,11 +146,11 @@ public class JNotepadMenuBar extends MenuBar {
*/ */
private void initPluginMenu() { private void initPluginMenu() {
// 插件菜单 // 插件菜单
pluginMenu = new Menu("插件"); pluginMenu = new Menu(PLUGIN);
addItem = new MenuItem("增加插件"); addItem = new MenuItem(ADD_PLUGIN);
itemMap.put("addItem", addItem); itemMap.put("addItem", addItem);
countItem = new MenuItem("统计字数"); countItem = new MenuItem(STATISTICS);
itemMap.put("countItem", countItem); itemMap.put("countItem", countItem);
pluginMenu.getItems().addAll(addItem, countItem); pluginMenu.getItems().addAll(addItem, countItem);
@ -148,7 +168,6 @@ public class JNotepadMenuBar extends MenuBar {
openItem.setOnAction(new OpenFile()); openItem.setOnAction(new OpenFile());
saveItem.setOnAction(new SaveFile()); saveItem.setOnAction(new SaveFile());
saveAsItem.setOnAction(new SaveAsFile()); saveAsItem.setOnAction(new SaveAsFile());
lineFeedItem.setOnAction(new LineFeed());
lineFeedItem.selectedProperty().addListener((observableValue, before, after) -> { lineFeedItem.selectedProperty().addListener((observableValue, before, after) -> {
// 1. 更新全局配置 // 1. 更新全局配置
GlobalConfig.getConfig().setAutoLineConfig(after); GlobalConfig.getConfig().setAutoLineConfig(after);

View File

@ -0,0 +1,128 @@
package org.jcnc.jnotepad.ui.status;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import org.jcnc.jnotepad.init.Config;
import org.jcnc.jnotepad.ui.tab.JNotepadTabPane;
import java.nio.charset.Charset;
import java.util.Properties;
/**
* 状态栏组件封装
* 1. 文字统计
* 2. 编码
*
* @author songdragon
*/
public class JNotepadStatusBox extends HBox {
Config config = new Config();
Properties properties = config.readPropertiesFromFile();
String ROW = properties.getProperty("ROW");
String COLUMN = properties.getProperty("COLUMN");
String WORD_COUNT = properties.getProperty("WORD_COUNT");
String ENCODE = properties.getProperty("ENCODE");
private static final JNotepadStatusBox STATUS_BOX = new JNotepadStatusBox();
/**
* 字数统计及光标
*/
private final Label statusLabel;
/**
* 显示文本编码
*/
private final Label enCodingLabel;
private JNotepadStatusBox() {
// 创建状态栏
statusLabel = new Label(ROW + "1 \t" + COLUMN + "1 \t" + WORD_COUNT + "0 ");
// 创建新的标签以显示编码信息
enCodingLabel = new Label();
this.getChildren().add(statusLabel);
this.getChildren().add(enCodingLabel);
this.getProperties().put("borderpane-margin", new Insets(5, 10, 5, 10));
}
public static JNotepadStatusBox getInstance() {
return STATUS_BOX;
}
public void updateEncodingLabel() {
updateEncodingLabel(null);
}
/**
* 更新编码展示
*
* @param encoding 文件编码
*/
public void updateEncodingLabel(String encoding) {
if (encoding == null) {
encoding = Charset.defaultCharset().name();
}
this.enCodingLabel.setText("\t" + ENCODE + ": " + encoding);
}
/**
* 更新字数统计
*/
public void updateWordCountStatusLabel() {
TextArea textArea = JNotepadTabPane.getInstance().getSelected().getLineNumberTextArea().getMainTextArea();
int caretPosition = textArea.getCaretPosition();
int row = getRow(caretPosition, textArea.getText());
int column = getColumn(caretPosition, textArea.getText());
int length = textArea.getLength();
this.statusLabel.setText(ROW + ": " + row + " \t" + COLUMN + ": " + column + " \t" + WORD_COUNT + ": " + length);
}
/**
* Tab选中时更新状态栏
* 1. 状态栏更新当前选中tab的数字统计
* 2. 状态栏更新当前选中tab的字符编码
*/
public void updateWhenTabSelected() {
updateWordCountStatusLabel();
updateEncodingLabel(JNotepadTabPane.getInstance().getSelected().getCharset().name());
}
/**
* 获取光标所在行号
*
* @param caretPosition 光标位置
* @param text 文本内容
* @return 光标所在行号
*/
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;
}
/**
* 获取光标所在列号
*
* @param caretPosition 光标位置
* @param text 文本内容
* @return 光标所在列号
*/
public int getColumn(int caretPosition, String text) {
return caretPosition - text.lastIndexOf("\n", caretPosition - 1);
}
}

View File

@ -4,6 +4,8 @@ import javafx.scene.control.Tab;
import org.jcnc.jnotepad.app.config.GlobalConfig; import org.jcnc.jnotepad.app.config.GlobalConfig;
import org.jcnc.jnotepad.ui.LineNumberTextArea; import org.jcnc.jnotepad.ui.LineNumberTextArea;
import java.nio.charset.Charset;
/** /**
* 封装标签页组件增加属于标签页的属性例如自动换行开关 * 封装标签页组件增加属于标签页的属性例如自动换行开关
* 每个Tab关联一个LineNumberTextArea * 每个Tab关联一个LineNumberTextArea
@ -17,16 +19,22 @@ public class JNotepadTab extends Tab {
*/ */
private boolean autoLine = false; private boolean autoLine = false;
private final LineNumberTextArea lineNumberTextArea; private final LineNumberTextArea lineNumberTextArea;
private Charset charset = Charset.defaultCharset();
public JNotepadTab(String tabTitle) { public JNotepadTab(String tabTitle) {
this(tabTitle, new LineNumberTextArea()); this(tabTitle, new LineNumberTextArea());
} }
public JNotepadTab(String tabTitle, LineNumberTextArea textArea) { public JNotepadTab(String tabTitle, LineNumberTextArea textArea) {
this(tabTitle, textArea, Charset.defaultCharset());
}
public JNotepadTab(String tabTitle, LineNumberTextArea textArea, Charset charset) {
super(tabTitle); super(tabTitle);
lineNumberTextArea = textArea; lineNumberTextArea = textArea;
this.setContent(lineNumberTextArea); this.setContent(lineNumberTextArea);
setAutoLine(GlobalConfig.getConfig().getAutoLineConfig()); setAutoLine(GlobalConfig.getConfig().getAutoLineConfig());
this.charset = charset;
} }
public boolean isAutoLine() { public boolean isAutoLine() {
@ -41,4 +49,16 @@ public class JNotepadTab extends Tab {
public LineNumberTextArea getLineNumberTextArea() { public LineNumberTextArea getLineNumberTextArea() {
return lineNumberTextArea; return lineNumberTextArea;
} }
public Charset getCharset() {
return charset;
}
public void setCharset(Charset charset) {
this.charset = charset;
}
public void save() {
this.lineNumberTextArea.save();
}
} }

View File

@ -3,6 +3,7 @@ package org.jcnc.jnotepad.ui.tab;
import javafx.scene.control.TabPane; import javafx.scene.control.TabPane;
import org.jcnc.jnotepad.app.config.GlobalConfig; import org.jcnc.jnotepad.app.config.GlobalConfig;
import org.jcnc.jnotepad.ui.menu.JNotepadMenuBar; import org.jcnc.jnotepad.ui.menu.JNotepadMenuBar;
import org.jcnc.jnotepad.ui.status.JNotepadStatusBox;
/** /**
* 标签页布局组件封装 * 标签页布局组件封装
@ -25,8 +26,11 @@ public class JNotepadTabPane extends TabPane {
this.getSelectionModel().selectedItemProperty().addListener( this.getSelectionModel().selectedItemProperty().addListener(
(ov, from, to) -> { (ov, from, to) -> {
if (to != null) { if (to != null) {
// 更新菜单栏中与tab相关设置
JNotepadMenuBar.getMenuBar().updateMenuStatusBySelectedTab(); JNotepadMenuBar.getMenuBar().updateMenuStatusBySelectedTab();
} }
// 更新状态标签
JNotepadStatusBox.getInstance().updateWhenTabSelected();
} }
); );
} }
@ -65,5 +69,6 @@ public class JNotepadTabPane extends TabPane {
public void fireTabSelected() { public void fireTabSelected() {
JNotepadTab selectedTab = getSelected(); JNotepadTab selectedTab = getSelected();
selectedTab.setAutoLine(GlobalConfig.getConfig().getAutoLineConfig()); selectedTab.setAutoLine(GlobalConfig.getConfig().getAutoLineConfig());
JNotepadStatusBox.getInstance().updateWhenTabSelected();
} }
} }

View File

@ -1,6 +1,5 @@
package org.jcnc.jnotepad.view.init; package org.jcnc.jnotepad.view.init;
import org.jcnc.jnotepad.controller.manager.Controller;
import org.jcnc.jnotepad.controller.manager.ShortcutKey; import org.jcnc.jnotepad.controller.manager.ShortcutKey;
@ -9,10 +8,6 @@ import org.jcnc.jnotepad.controller.manager.ShortcutKey;
*/ */
public class View { public class View {
public void initTabPane() {
Controller.getInstance().initTabPane();
}
// 初始化快捷键 // 初始化快捷键
public void initShortcutKey() { public void initShortcutKey() {
new ShortcutKey().createShortcutKeyByConfig(); new ShortcutKey().createShortcutKeyByConfig();

View File

@ -7,6 +7,7 @@ import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import org.jcnc.jnotepad.exception.AppException; import org.jcnc.jnotepad.exception.AppException;
import org.jcnc.jnotepad.ui.menu.JNotepadMenuBar; import org.jcnc.jnotepad.ui.menu.JNotepadMenuBar;
import org.jcnc.jnotepad.ui.status.JNotepadStatusBox;
import org.jcnc.jnotepad.ui.tab.JNotepadTabPane; import org.jcnc.jnotepad.ui.tab.JNotepadTabPane;
@ -17,11 +18,6 @@ import org.jcnc.jnotepad.ui.tab.JNotepadTabPane;
*/ */
public class ViewManager { public class ViewManager {
/**
* 显示文本编码
*/
private Label enCodingLabel; // 显示文本编码
private int tabIndex = 0; private int tabIndex = 0;
private Boolean line = true; private Boolean line = true;
@ -30,19 +26,13 @@ public class ViewManager {
// 主界面布局 // 主界面布局
private BorderPane root; // 主布局 private BorderPane root; // 主布局
// 状态栏
private Label statusLabel;
private static ViewManager instance = null; private static ViewManager instance = null;
public Label getEnCodingLabel() {
return enCodingLabel;
}
/** /**
* 自增并获取标签页索引 * 自增并获取标签页索引
* *
*
* @return int 标签页索引 * @return int 标签页索引
* @apiNote ++tabIndex * @apiNote ++tabIndex
*/ */
@ -59,10 +49,6 @@ public class ViewManager {
return root; return root;
} }
public Label getStatusLabel() {
return statusLabel;
}
/** /**
* 获取ViewManager的实例如果实例不存在则创建一个新实例 * 获取ViewManager的实例如果实例不存在则创建一个新实例
@ -109,14 +95,7 @@ public class ViewManager {
// 创建标签页和文本编辑区域 // 创建标签页和文本编辑区域
root.setCenter(JNotepadTabPane.getInstance()); root.setCenter(JNotepadTabPane.getInstance());
root.setBottom(JNotepadStatusBox.getInstance());
// 创建状态栏
statusLabel = new Label("行数1 \t列数1 \t字数0 ");
enCodingLabel = new Label(); // 创建新的标签以显示编码信息
HBox statusBox = new HBox(statusLabel, enCodingLabel); // 使用HBox放置状态标签和编码标签
root.setBottom(statusBox);
BorderPane.setMargin(statusBox, new Insets(5, 10, 5, 10));
scene.setRoot(root); scene.setRoot(root);
} }