📄 jxeditorpane.java
字号:
/* * $Id: JXEditorPane.java,v 1.17 2005/10/14 15:37:49 kleopatra Exp $ * * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */package org.jdesktop.swingx;import java.awt.Component;import java.awt.datatransfer.Clipboard;import java.awt.datatransfer.DataFlavor;import java.awt.datatransfer.Transferable;import java.awt.event.ActionEvent;import java.awt.event.ItemEvent;import java.awt.event.ItemListener;import java.beans.PropertyChangeEvent;import java.beans.PropertyChangeListener;import java.io.IOException;import java.io.Reader;import java.io.StringReader;import java.net.URL;import java.util.HashMap;import java.util.Map;import java.util.Vector;import java.util.regex.MatchResult;import java.util.regex.Matcher;import java.util.regex.Pattern;import javax.swing.ActionMap;import javax.swing.DefaultComboBoxModel;import javax.swing.DefaultListCellRenderer;import javax.swing.JComboBox;import javax.swing.JComponent;import javax.swing.JEditorPane;import javax.swing.JList;import javax.swing.KeyStroke;import javax.swing.SwingUtilities;import javax.swing.event.CaretEvent;import javax.swing.event.CaretListener;import javax.swing.event.UndoableEditEvent;import javax.swing.event.UndoableEditListener;import javax.swing.text.AttributeSet;import javax.swing.text.BadLocationException;import javax.swing.text.Document;import javax.swing.text.EditorKit;import javax.swing.text.Element;import javax.swing.text.MutableAttributeSet;import javax.swing.text.Segment;import javax.swing.text.SimpleAttributeSet;import javax.swing.text.StyleConstants;import javax.swing.text.StyledDocument;import javax.swing.text.StyledEditorKit;import javax.swing.text.html.HTML;import javax.swing.text.html.HTMLDocument;import javax.swing.text.html.HTMLEditorKit;import javax.swing.undo.CannotRedoException;import javax.swing.undo.CannotUndoException;import javax.swing.undo.UndoManager;import org.jdesktop.swingx.action.ActionManager;import org.jdesktop.swingx.action.Targetable;/** * An extended editor pane which has the following features built in: * <ul> * <li>Text search * <li>undo/redo * <li>simple html/plain text editing * </ul> * * @author Mark Davidson */public class JXEditorPane extends JEditorPane implements /*Searchable, */Targetable {// private Matcher matcher; private UndoableEditListener undoHandler; private UndoManager undoManager; private CaretListener caretHandler; private JComboBox selector; // The ids of supported actions. Perhaps this should be public. private final static String ACTION_FIND = "find"; private final static String ACTION_UNDO = "undo"; private final static String ACTION_REDO = "redo"; /* * These next 3 actions are part of a *HACK* to get cut/copy/paste * support working in the same way as find, undo and redo. in JTextComponent * the cut/copy/paste actions are _not_ added to the ActionMap. Instead, * a default "transfer handler" system is used, apparently to get the text * onto the system clipboard. * Since there aren't any CUT/COPY/PASTE actions in the JTextComponent's action * map, they cannot be referenced by the action framework the same way that * find/undo/redo are. So, I added the actions here. The really hacky part * is that by defining an Action to go along with the cut/copy/paste keys, * I loose the default handling in the cut/copy/paste routines. So, I have * to remove cut/copy/paste from the action map, call the appropriate * method (cut, copy, or paste) and then add the action back into the * map. Yuck! */ private final static String ACTION_CUT = "cut"; private final static String ACTION_COPY = "copy"; private final static String ACTION_PASTE = "paste"; private TargetableSupport targetSupport = new TargetableSupport(this); private Searchable searchable; public JXEditorPane() { init(); } public JXEditorPane(String url) throws IOException { super(url); init(); } public JXEditorPane(String type, String text) { super(type, text); init(); } public JXEditorPane(URL initialPage) throws IOException { super(initialPage); init(); } private void init() { setEditorKitForContentType("text/html", new SloppyHTMLEditorKit()); addPropertyChangeListener(new PropertyHandler()); getDocument().addUndoableEditListener(getUndoableEditListener()); initActions(); } private class PropertyHandler implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { String name = evt.getPropertyName(); if (name.equals("document")) { Document doc = (Document)evt.getOldValue(); if (doc != null) { doc.removeUndoableEditListener(getUndoableEditListener()); } doc = (Document)evt.getNewValue(); if (doc != null) { doc.addUndoableEditListener(getUndoableEditListener()); } } } } // pp for testing CaretListener getCaretListener() { return caretHandler; } // pp for testing UndoableEditListener getUndoableEditListener() { if (undoHandler == null) { undoHandler = new UndoHandler(); undoManager = new UndoManager(); } return undoHandler; } /** * Overidden to perform document initialization based on type. */ public void setEditorKit(EditorKit kit) { super.setEditorKit(kit); if (kit instanceof StyledEditorKit) { if (caretHandler == null) { caretHandler = new CaretHandler(); } addCaretListener(caretHandler); } } /** * Register the actions that this class can handle. */ protected void initActions() { ActionMap map = getActionMap(); map.put(ACTION_FIND, new Actions(ACTION_FIND)); map.put(ACTION_UNDO, new Actions(ACTION_UNDO)); map.put(ACTION_REDO, new Actions(ACTION_REDO)); map.put(ACTION_CUT, new Actions(ACTION_CUT)); map.put(ACTION_COPY, new Actions(ACTION_COPY)); map.put(ACTION_PASTE, new Actions(ACTION_PASTE)); // this should be handled by the LF! KeyStroke findStroke = KeyStroke.getKeyStroke("control F"); getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(findStroke, "find"); } // undo/redo implementation private class UndoHandler implements UndoableEditListener { public void undoableEditHappened(UndoableEditEvent evt) { undoManager.addEdit(evt.getEdit()); updateActionState(); } } /** * Updates the state of the actions in response to an undo/redo operation. */ private void updateActionState() { // Update the state of the undo and redo actions Runnable doEnabled = new Runnable() { public void run() { ActionManager manager = ActionManager.getInstance(); manager.setEnabled(ACTION_UNDO, undoManager.canUndo()); manager.setEnabled(ACTION_REDO, undoManager.canRedo()); } }; SwingUtilities.invokeLater(doEnabled); } /** * A small class which dispatches actions. * TODO: Is there a way that we can make this static? */ private class Actions extends UIAction { Actions(String name) { super(name); } public void actionPerformed(ActionEvent evt) { String name = getName(); if (ACTION_FIND.equals(name)) { find(); } else if (ACTION_UNDO.equals(name)) { try { undoManager.undo(); } catch (CannotUndoException ex) { ex.printStackTrace(); } updateActionState(); } else if (ACTION_REDO.equals(name)) { try { undoManager.redo(); } catch (CannotRedoException ex) { ex.printStackTrace(); } updateActionState(); } else if (ACTION_CUT.equals(name)) { ActionMap map = getActionMap(); map.remove(ACTION_CUT); cut(); map.put(ACTION_CUT, this); } else if (ACTION_COPY.equals(name)) { ActionMap map = getActionMap(); map.remove(ACTION_COPY); copy(); map.put(ACTION_COPY, this); } else if (ACTION_PASTE.equals(name)) { ActionMap map = getActionMap(); map.remove(ACTION_PASTE); paste(); map.put(ACTION_PASTE, this); } else { System.out.println("ActionHandled: " + name); } } } /** * Retrieves a component which will be used as the paragraph selector. * This can be placed in the toolbar. * <p> * Note: This is only valid for the HTMLEditorKit */ public JComboBox getParagraphSelector() { if (selector == null) { selector = new ParagraphSelector(); } return selector; } /** * A control which should be placed in the toolbar to enable * paragraph selection. */ private class ParagraphSelector extends JComboBox implements ItemListener { private Map itemMap; public ParagraphSelector() { // The item map is for rendering itemMap = new HashMap(); itemMap.put(HTML.Tag.P, "Paragraph"); itemMap.put(HTML.Tag.H1, "Heading 1"); itemMap.put(HTML.Tag.H2, "Heading 2"); itemMap.put(HTML.Tag.H3, "Heading 3"); itemMap.put(HTML.Tag.H4, "Heading 4"); itemMap.put(HTML.Tag.H5, "Heading 5"); itemMap.put(HTML.Tag.H6, "Heading 6"); itemMap.put(HTML.Tag.PRE, "Preformatted"); // The list of items Vector items = new Vector(); items.addElement(HTML.Tag.P); items.addElement(HTML.Tag.H1); items.addElement(HTML.Tag.H2); items.addElement(HTML.Tag.H3); items.addElement(HTML.Tag.H4); items.addElement(HTML.Tag.H5); items.addElement(HTML.Tag.H6); items.addElement(HTML.Tag.PRE); setModel(new DefaultComboBoxModel(items)); setRenderer(new ParagraphRenderer()); addItemListener(this); setFocusable(false); } public void itemStateChanged(ItemEvent evt) { if (evt.getStateChange() == ItemEvent.SELECTED) { applyTag((HTML.Tag)evt.getItem()); } } private class ParagraphRenderer extends DefaultListCellRenderer { public ParagraphRenderer() { setOpaque(true); } public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -