jEdit 源码解析之:纯文本编辑器 JEditTextArea 初探

1. jEdit 简介

jEdit 是一款 Java 写的面向开发者的多功能文本编辑器,它遵守 GPL 2.0,既可以下载下来作为文本编辑器工具独立运行,也可以作为插件安装到其他应用,还可以作为第三方工具包直接为其他应用所用 (对此 jEdit 官网提供了详尽的 API http://www.jedit.org/api/index.html)。
jEdit 官网 www.jedit.org,源码下载地址 https://sourceforge.net/projects/jedit/,最新版本为 5.5.0。

2. JEditTextArea 简介

2.1. JEditTextArea 进化史

jEdit 2.0 以后对包结构进行了重构,JEditTextArea 被转移至 org.gjt.sp.jedit.textarea 包下,并且不再提供之前版本的源代码。jEdit 4.3 版本对 JEditTextArea 进行了重构,提炼出来一个父类 TextArea,后者继承自 javax.swing.JPanel (javax.swing.JComponent 的子类)。本文讨论的 JEditTextArea 是重构前的版本,即 1.x 版。虽然官方已不再提供 jEdit 1.x 版本的源码,但一些网站仍然保留有该时期的源码供参考,如 docjar 提供的 JEditTextArea 1.2 版本的源码:http://www.docjar.com/html/api/org/syntax/jedit/JEditTextArea.java.html

2.2. JEditTextArea 工作机制

org.syntax.jedit.JEditTextArea 继承自 javax.swing.JComponent,所以我们可以在 swing 应用中对它进行注入。swing 是基于事件处理的,所以 JEditTextArea 内部定义了大量事件处理接口,所以我们可以很轻松地通过一系列的 swing 事件对 JEditTextArea 进行掌控和应用,还可以对其行为进行改写或扩展。

2.3. JEditTextArea 内部结构

  • 存储已输入文本内容的 SyntaxDocument 的 document 实例
  • 右键弹出菜单 JPopupMenu 的 popup 实例
  • 选中文本的起始位置 selectionStart
  • 选中文本的起始行 selectionStartLine
  • 选中文本的结束位置 selectionEnd
  • 选中文本的结束行 selectionEndLine
  • 文本是否可以编辑标识 editable
  • 输入操作处理器 InputHandler 的实例 inputHandler
  • 画图工具 TextAreaPainter 的实例 painter
  • 文本监听处理器 DocumentHandler 的实例 documentHandler
    所有的对象都是 protected 的,另外 JEditTextArea 本身不是 final 的,也就是说 jEdit 允许我们通过继承的办法随意对 JEditTextArea 进行修改或扩展。

2.4. JEditTextArea 的处理器

2.4.1. JEditTextArea 内部类自定义处理器

  • 自定义文档事件处理器 DocumentHandler
  • 自定义鼠标拖曳处理器 DragHandler
  • 自定义焦点处理器 FocusHandler
  • 自定义鼠标事件处理器 MouseHandler

注意以上定义都是默认权限的,也就是说只有自己和同包类可以调用,不允许外部调用甚至继承者使用。
这说明以上事件处理器和 JEditTextArea 完美适配,用户不需要再去有所改动。但如果我们想改动也并非不可能,因为上述关键属性都定义为 protected 嘛,我们可以通过继承拿到这些属性,然后把其自定义处理器移除,加入自己改动后的处理器,类似于这样:

        // remove DragHandler defined in JEditTextArea
        MouseMotionListener[] superMouseMotionListeners = super.painter.getMouseMotionListeners();
        if (null != superMouseMotionListeners && superMouseMotionListeners.length > 0) {
            for (int i = 0;i < superMouseMotionListeners.length;i ++) {
                MouseMotionListener superInstance = superMouseMotionListeners[i];
                if (superInstance.getClass().getName().contains("JEditTextArea$")) {
                    super.painter.removeMouseMotionListener(superInstance);
                }
            }
        }
        // add enhanced DragHandler
        super.painter.addMouseMotionListener(new MyDragHandler());

MyDragHandler 为改动后的处理器。

2.4.2. jEdit 内定义的处理器

  • 键盘输入处理器 org.syntax.jedit.InputHandler

上述那么多事件处理器,每一个事件的连续性如一个鼠标拖曳动作可能会一股脑产生几十个拖曳事件,JEditTextArea 里的方法都没有同步锁,那么如何保证事件处理的一致性和最终结果的正确性?
jw-0606-swingworker1-100156726-orig.gif
如上图所示,Swing 在这里使用了著名的发布订阅模式,对于单个控件而言能够保证事件的先进先出以及处理完再处理下一个,进而确保不会有脏数据——即使控件本身没有做安全同步,开发者只需专注事件处理本身的数据处理即可。

发布了273 篇原创文章 · 获赞 1324 · 访问量 649万+

猜你喜欢

转载自blog.csdn.net/defonds/article/details/93653857
今日推荐