diff options
Diffstat (limited to 'libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java')
-rw-r--r-- | libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java | 635 |
1 files changed, 635 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java new file mode 100644 index 00000000000..dd0828e466a --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java @@ -0,0 +1,635 @@ +/* BasicTextUI.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.plaf.basic; + +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.InputMap; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.TextUI; +import javax.swing.plaf.UIResource; +import javax.swing.text.BadLocationException; +import javax.swing.text.Caret; +import javax.swing.text.DefaultCaret; +import javax.swing.text.DefaultEditorKit; +import javax.swing.text.DefaultHighlighter; +import javax.swing.text.Document; +import javax.swing.text.EditorKit; +import javax.swing.text.Element; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; +import javax.swing.text.Keymap; +import javax.swing.text.PlainView; +import javax.swing.text.Position; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; + + +public abstract class BasicTextUI extends TextUI + implements ViewFactory +{ + public static class BasicCaret extends DefaultCaret + implements UIResource + { + public BasicCaret() + { + } + } + + public static class BasicHighlighter extends DefaultHighlighter + implements UIResource + { + public BasicHighlighter() + { + } + } + + private class RootView extends View + { + private View view; + + public RootView() + { + super(null); + } + + // View methods. + + public ViewFactory getViewFactory() + { + // FIXME: Handle EditorKit somehow. + return BasicTextUI.this; + } + + public void setView(View v) + { + if (view != null) + view.setParent(null); + + if (v != null) + v.setParent(null); + + view = v; + } + + public Container getContainer() + { + return textComponent; + } + + public float getPreferredSpan(int axis) + { + if (view != null) + return view.getPreferredSpan(axis); + + return Integer.MAX_VALUE; + } + + public void paint(Graphics g, Shape s) + { + if (view != null) + view.paint(g, s); + } + + public Shape modelToView(int position, Shape a, Position.Bias bias) + throws BadLocationException + { + if (view == null) + return null; + + return ((PlainView) view).modelToView(position, a, bias).getBounds(); + } + + /** + * Notification about text insertions. These are forwarded to the + * real root view. + * + * @param ev the DocumentEvent describing the change + * @param shape the current allocation of the view's display + * @param vf the ViewFactory to use for creating new Views + */ + public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) + { + view.insertUpdate(ev, shape, vf); + } + + /** + * Notification about text removals. These are forwarded to the + * real root view. + * + * @param ev the DocumentEvent describing the change + * @param shape the current allocation of the view's display + * @param vf the ViewFactory to use for creating new Views + */ + public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) + { + view.removeUpdate(ev, shape, vf); + } + + /** + * Notification about text changes. These are forwarded to the + * real root view. + * + * @param ev the DocumentEvent describing the change + * @param shape the current allocation of the view's display + * @param vf the ViewFactory to use for creating new Views + */ + public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) + { + view.changedUpdate(ev, shape, vf); + } + } + + class UpdateHandler implements PropertyChangeListener + { + public void propertyChange(PropertyChangeEvent event) + { + if (event.getPropertyName().equals("document")) + { + // Document changed. + modelChanged(); + } + } + } + + /** + * Listens for changes on the underlying model and forwards notifications + * to the View. This also updates the caret position of the text component. + * + * TODO: Maybe this should somehow be handled through EditorKits + */ + class DocumentHandler implements DocumentListener + { + /** + * Notification about a document change event. + * + * @param ev the DocumentEvent describing the change + */ + public void changedUpdate(DocumentEvent ev) + { + Dimension size = textComponent.getSize(); + rootView.changedUpdate(ev, new Rectangle(0, 0, size.width, size.height), + BasicTextUI.this); + } + + /** + * Notification about a document insert event. + * + * @param ev the DocumentEvent describing the insertion + */ + public void insertUpdate(DocumentEvent ev) + { + Dimension size = textComponent.getSize(); + rootView.insertUpdate(ev, new Rectangle(0, 0, size.width, size.height), + BasicTextUI.this); + int caretPos = textComponent.getCaretPosition(); + if (caretPos >= ev.getOffset()) + textComponent.setCaretPosition(caretPos + ev.getLength()); + } + + /** + * Notification about a document removal event. + * + * @param ev the DocumentEvent describing the removal + */ + public void removeUpdate(DocumentEvent ev) + { + Dimension size = textComponent.getSize(); + rootView.removeUpdate(ev, new Rectangle(0, 0, size.width, size.height), + BasicTextUI.this); + int caretPos = textComponent.getCaretPosition(); + if (caretPos >= ev.getOffset()) + textComponent.setCaretPosition(ev.getOffset()); + } + } + + static EditorKit kit = new DefaultEditorKit(); + + RootView rootView = new RootView(); + JTextComponent textComponent; + UpdateHandler updateHandler = new UpdateHandler(); + + /** The DocumentEvent handler. */ + DocumentHandler documentHandler = new DocumentHandler(); + + public BasicTextUI() + { + } + + protected Caret createCaret() + { + return new BasicCaret(); + } + + protected Highlighter createHighlighter() + { + return new BasicHighlighter(); + } + + protected final JTextComponent getComponent() + { + return textComponent; + } + + public void installUI(final JComponent c) + { + super.installUI(c); + c.setOpaque(true); + + textComponent = (JTextComponent) c; + + Document doc = textComponent.getDocument(); + if (doc == null) + { + doc = getEditorKit(textComponent).createDefaultDocument(); + textComponent.setDocument(doc); + } + + textComponent.addPropertyChangeListener(updateHandler); + modelChanged(); + + installDefaults(); + installListeners(); + installKeyboardActions(); + } + + protected void installDefaults() + { + Caret caret = textComponent.getCaret(); + if (caret == null) + { + caret = createCaret(); + textComponent.setCaret(caret); + } + + Highlighter highlighter = textComponent.getHighlighter(); + if (highlighter == null) + textComponent.setHighlighter(createHighlighter()); + + String prefix = getPropertyPrefix(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + textComponent.setBackground(defaults.getColor(prefix + ".background")); + textComponent.setForeground(defaults.getColor(prefix + ".foreground")); + textComponent.setMargin(defaults.getInsets(prefix + ".margin")); + textComponent.setBorder(defaults.getBorder(prefix + ".border")); + textComponent.setFont(defaults.getFont(prefix + ".font")); + + caret.setBlinkRate(defaults.getInt(prefix + ".caretBlinkRate")); + } + + private FocusListener focuslistener = new FocusListener() { + public void focusGained(FocusEvent e) + { + textComponent.repaint(); + } + public void focusLost(FocusEvent e) + { + textComponent.repaint(); + } + }; + + protected void installListeners() + { + textComponent.addFocusListener(focuslistener); + installDocumentListeners(); + } + + /** + * Installs the document listeners on the textComponent's model. + */ + private void installDocumentListeners() + { + Document doc = textComponent.getDocument(); + if (doc != null) + doc.addDocumentListener(documentHandler); + } + + /** + * Returns the name of the keymap for this type of TextUI. + * + * This is implemented so that the classname of this TextUI + * without the package prefix is returned. This way subclasses + * don't have to override this method. + * + * @return the name of the keymap for this TextUI + */ + protected String getKeymapName() + { + String fullClassName = getClass().getName(); + int index = fullClassName.lastIndexOf('.'); + String className = fullClassName.substring(index + 1); + return className; + } + + protected Keymap createKeymap() + { + String prefix = getPropertyPrefix(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + JTextComponent.KeyBinding[] bindings = + (JTextComponent.KeyBinding[]) defaults.get(prefix + ".keyBindings"); + if (bindings == null) + { + bindings = new JTextComponent.KeyBinding[0]; + defaults.put(prefix + ".keyBindings", bindings); + } + + Keymap km = JTextComponent.addKeymap(getKeymapName(), + JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP)); + JTextComponent.loadKeymap(km, bindings, textComponent.getActions()); + return km; + } + + protected void installKeyboardActions() + { + // load any bindings for the older Keymap interface + Keymap km = JTextComponent.getKeymap(getKeymapName()); + if (km == null) + km = createKeymap(); + textComponent.setKeymap(km); + + // load any bindings for the newer InputMap / ActionMap interface + SwingUtilities.replaceUIInputMap(textComponent, + JComponent.WHEN_FOCUSED, + getInputMap(JComponent.WHEN_FOCUSED)); + SwingUtilities.replaceUIActionMap(textComponent, getActionMap()); + } + + InputMap getInputMap(int condition) + { + String prefix = getPropertyPrefix(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + switch (condition) + { + case JComponent.WHEN_IN_FOCUSED_WINDOW: + // FIXME: is this the right string? nobody seems to use it. + return (InputMap) defaults.get(prefix + ".windowInputMap"); + case JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT: + return (InputMap) defaults.get(prefix + ".ancestorInputMap"); + default: + case JComponent.WHEN_FOCUSED: + return (InputMap) defaults.get(prefix + ".focusInputMap"); + } + } + + ActionMap getActionMap() + { + String prefix = getPropertyPrefix(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + ActionMap am = (ActionMap) defaults.get(prefix + ".actionMap"); + if (am == null) + { + am = createActionMap(); + defaults.put(prefix + ".actionMap", am); + } + return am; + } + + ActionMap createActionMap() + { + Action[] actions = textComponent.getActions(); + ActionMap am = new ActionMapUIResource(); + for (int i = 0; i < actions.length; ++i) + { + String name = (String) actions[i].getValue(Action.NAME); + if (name != null) + am.put(name, actions[i]); + } + return am; + } + + public void uninstallUI(final JComponent component) + { + super.uninstallUI(component); + rootView.setView(null); + + textComponent.removePropertyChangeListener(updateHandler); + + uninstallDefaults(); + uninstallListeners(); + uninstallKeyboardActions(); + + textComponent = null; + } + + protected void uninstallDefaults() + { + // Do nothing here. + } + + protected void uninstallListeners() + { + textComponent.removeFocusListener(focuslistener); + } + + protected void uninstallKeyboardActions() + { + // Do nothing here. + } + + protected abstract String getPropertyPrefix(); + + public Dimension getPreferredSize(JComponent c) + { + View v = getRootView(textComponent); + + float w = v.getPreferredSpan(View.X_AXIS); + float h = v.getPreferredSpan(View.Y_AXIS); + + return new Dimension((int) w, (int) h); + } + + /** + * Returns the maximum size for text components that use this UI. + * + * This returns (Integer.MAX_VALUE, Integer.MAX_VALUE). + * + * @return the maximum size for text components that use this UI + */ + public Dimension getMaximumSize(JComponent c) + { + // Sun's implementation returns Integer.MAX_VALUE here, so do we. + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + public final void paint(Graphics g, JComponent c) + { + paintSafely(g); + } + + protected void paintSafely(Graphics g) + { + Caret caret = textComponent.getCaret(); + Highlighter highlighter = textComponent.getHighlighter(); + + if (textComponent.isOpaque()) + paintBackground(g); + + if (highlighter != null + && textComponent.getSelectionStart() != textComponent.getSelectionEnd()) + highlighter.paint(g); + + rootView.paint(g, getVisibleEditorRect()); + + if (caret != null && textComponent.hasFocus()) + caret.paint(g); + } + + protected void paintBackground(Graphics g) + { + g.setColor(textComponent.getBackground()); + g.fillRect(0, 0, textComponent.getWidth(), textComponent.getHeight()); + } + + public void damageRange(JTextComponent t, int p0, int p1) + { + damageRange(t, p0, p1, null, null); + } + + public void damageRange(JTextComponent t, int p0, int p1, + Position.Bias firstBias, Position.Bias secondBias) + { + } + + public EditorKit getEditorKit(JTextComponent t) + { + return kit; + } + + public int getNextVisualPositionFrom(JTextComponent t, int pos, + Position.Bias b, int direction, + Position.Bias[] biasRet) + throws BadLocationException + { + return 0; + } + + public View getRootView(JTextComponent t) + { + return rootView; + } + + public Rectangle modelToView(JTextComponent t, int pos) + throws BadLocationException + { + return modelToView(t, pos, Position.Bias.Forward); + } + + public Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias) + throws BadLocationException + { + return rootView.modelToView(pos, getVisibleEditorRect(), bias).getBounds(); + } + + public int viewToModel(JTextComponent t, Point pt) + { + return viewToModel(t, pt, null); + } + + public int viewToModel(JTextComponent t, Point pt, Position.Bias[] biasReturn) + { + return 0; + } + + public View create(Element elem) + { + // Subclasses have to implement this to get this functionality. + return null; + } + + public View create(Element elem, int p0, int p1) + { + // Subclasses have to implement this to get this functionality. + return null; + } + + protected Rectangle getVisibleEditorRect() + { + int width = textComponent.getWidth(); + int height = textComponent.getHeight(); + + if (width <= 0 || height <= 0) + return null; + + Insets insets = textComponent.getInsets(); + return new Rectangle(insets.left, insets.top, + width - insets.left + insets.right, + height - insets.top + insets.bottom); + } + + protected final void setView(View view) + { + rootView.setView(view); + view.setParent(rootView); + } + + protected void modelChanged() + { + if (textComponent == null || rootView == null) + return; + ViewFactory factory = rootView.getViewFactory(); + if (factory == null) + return; + Document doc = textComponent.getDocument(); + if (doc == null) + return; + installDocumentListeners(); + Element elem = doc.getDefaultRootElement(); + if (elem == null) + return; + setView(factory.create(elem)); + } +} |