![JAR search and dependency download from the Maven repository](/logo.png)
org.netbeans.editor.BaseTextUI Maven / Gradle / Ivy
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.util.List;
import java.util.Iterator;
import javax.swing.text.*;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import javax.swing.plaf.TextUI;
import javax.swing.plaf.ComponentUI;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.SwingUtilities;
import javax.swing.Action;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicTextUI;
import org.netbeans.editor.SettingsChangeListener;
import org.netbeans.editor.view.spi.LockView;
/**
* Text UI implementation
*
* @author Miloslav Metelka, Martin Roskanin
* @version 1.00
*/
public class BaseTextUI extends BasicTextUI
implements PropertyChangeListener, DocumentListener, SettingsChangeListener {
/** Extended UI */
private EditorUI editorUI;
private boolean foldingEnabled;
private boolean needsRefresh = false;
/** ID of the component in registry */
int componentID = -1;
private AbstractDocument lastDocument;
/** Instance of the GetFocusedComponentAction */
private static final GetFocusedComponentAction gfcAction
= new GetFocusedComponentAction();
public BaseTextUI() {
}
protected String getPropertyPrefix() {
return "EditorPane"; //NOI18N
}
public static JTextComponent getFocusedComponent() {
return gfcAction.getFocusedComponent2();
}
protected boolean isRootViewReplaceNecessary() {
boolean replaceNecessary = false;
Document doc = getComponent().getDocument();
if (doc != lastDocument) {
replaceNecessary = true;
}
return replaceNecessary;
}
protected void rootViewReplaceNotify() {
// update the newly used document
lastDocument = (AbstractDocument)getComponent().getDocument();
}
/** Called when the model of component is changed */
protected void modelChanged() {
JTextComponent component = getComponent();
// [TODO] assert (component != null);
Document doc = component.getDocument();
if (doc != null && !(doc instanceof AbstractDocument)) {
// This UI works with AbstractDocument document instances only
return; // return silently
}
AbstractDocument adoc = (AbstractDocument)doc;
// Possibly rebuild fold hierarchy prior to rebuilding views.
// Views have optimization in fold hierarchy rebuild listening
// so that the actual views rebuild is only done once.
// Readlock on both last and current docs.
/* if (doc != lastDocument) {
if (lastDocument != null) {
lastDocument.readLock();
}
try {
if (adoc != null) {
adoc.readLock();
}
try {
FoldHierarchySpi.get(component).rebuild();
} finally {
if (adoc != null) {
adoc.readUnlock();
}
}
} finally {
if (lastDocument != null) {
lastDocument.readUnlock();
}
}
}
*/
if (doc != null) {
ViewFactory f = getRootView(component).getViewFactory();
BaseKit kit = (BaseKit)getEditorKit(component);
component.removeAll();
if (isRootViewReplaceNecessary()) {
rootViewReplaceNotify();
Element elem = doc.getDefaultRootElement();
View v = f.create(elem);
setView(v);
}
component.revalidate();
// Execute actions related to document installaction into the component
Settings.KitAndValue[] kv = Settings.getValueHierarchy(kit.getClass(),
SettingsNames.DOC_INSTALL_ACTION_NAME_LIST);
for (int i = kv.length - 1; i >= 0; i--) {
List actList = (List)kv[i].value;
actList = kit.translateActionNameList(actList); // translate names to actions
if (actList != null) {
for (Iterator iter = actList.iterator(); iter.hasNext();) {
Action a = (Action)iter.next();
a.actionPerformed(new ActionEvent(component,
ActionEvent.ACTION_PERFORMED, "")); // NOI18N
}
}
}
}
}
/* XXX - workaround bugfix of issue #45487 and #45678
* The hack can be removed if JDK bug
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5067948
* will be fixed.
*/
protected void installKeyboardActions() {
String mapName = getPropertyPrefix() + ".actionMap"; //NOI18N
// XXX - workaround bugfix of issue #45487
// Because the ActionMap is cached in method BasicTextUI.getActionMap()
// the property 'mapName' is set to null to force new actionMap creation
UIManager.getLookAndFeelDefaults().put(mapName, null);
UIManager.getDefaults().put(mapName, null); //#45678
super.installKeyboardActions();
}
/** Installs the UI for a component. */
public void installUI(JComponent c) {
super.installUI(c);
if (!(c instanceof JTextComponent)) {
return;
}
JTextComponent component = getComponent();
// set margin
Object value = Settings.getValue(Utilities.getKitClass(component), SettingsNames.MARGIN);
Insets margin = (value instanceof Insets) ? (Insets)value : null;
component.setMargin(margin);
getEditorUI().installUI(component);
Boolean foldingEnabledBoolean = (Boolean)Settings.getValue(Utilities.getKitClass(component), SettingsNames.CODE_FOLDING_ENABLE);
foldingEnabled = foldingEnabledBoolean.booleanValue();
component.putClientProperty(SettingsNames.CODE_FOLDING_ENABLE, foldingEnabledBoolean);
Settings.addSettingsChangeListener(this);
// attach to the model and component
//component.addPropertyChangeListener(this); already done in super class
if (component.getClientProperty(UIWatcher.class) == null) {
component.addPropertyChangeListener(new UIWatcher(this.getClass()));
component.putClientProperty(UIWatcher.class, UIWatcher.class);
}
BaseKit kit = (BaseKit)getEditorKit(component);
ViewFactory vf = kit.getViewFactory();
// Create and attach caret
Caret caret = kit.createCaret();
component.setCaretColor(Color.black); // will be changed by settings later
component.setCaret(caret);
// assign blink rate
int br = SettingsUtil.getInteger(Utilities.getKitClass(component), SettingsNames.CARET_BLINK_RATE,
SettingsDefaults.defaultCaretBlinkRate.intValue());
caret.setBlinkRate(br);
// Create document
/* BaseDocument doc = Utilities.getDocument(component);
if (doc != null) {
modelChanged(null, doc);
}
*/
SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
Registry.addComponent(component);
Registry.activate(component);
component.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
}
/** Deinstalls the UI for a component */
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
Settings.removeSettingsChangeListener(this);
//c.removePropertyChangeListener(this);
if (c instanceof JTextComponent){
JTextComponent comp = (JTextComponent)c;
BaseDocument doc = Utilities.getDocument(comp);
if (doc != null) {
doc.removeDocumentListener(this);
}
comp.setKeymap(null);
comp.setCaret(null);
getEditorUI().uninstallUI(comp);
Registry.removeComponent(comp);
}
// Clear the editorUI so it will be recreated according to the kit
// of the component for which the installUI() is called
editorUI = null;
}
public int getYFromPos(int pos) throws BadLocationException {
Rectangle ret = modelToView(getComponent(), pos);
return (ret == null) ? 0 : ret.y;
/* BaseDocument doc = Utilities.getDocument(getComponent());
if (doc!=null){
int ret = (Utilities.getLineOffset(doc, pos)) * getEditorUI().getLineHeight();
return ret;
}
return 0;
*/
}
public int getPosFromY(int y) throws BadLocationException {
return viewToModel(getComponent(), 0, y);
}
public int getBaseX(int y) {
return getEditorUI().getTextMargin().left;
}
public int viewToModel(JTextComponent c, int x, int y) {
return viewToModel(c, new Point(x, y));
}
/** Next visually represented model location where caret can be placed.
* This version works without placing read lock on the document.
*/
public int getNextVisualPositionFrom(JTextComponent t, int pos,
Position.Bias b, int direction, Position.Bias[] biasRet)
throws BadLocationException{
if (biasRet == null) {
biasRet = new Position.Bias[1];
biasRet[0] = Position.Bias.Forward;
}
return super.getNextVisualPositionFrom(t, pos, b, direction, biasRet);
}
/** Fetches the EditorKit for the UI.
*
* @return the component capabilities
*/
public EditorKit getEditorKit(JTextComponent c) {
JEditorPane pane = (JEditorPane)getComponent();
return (pane==null) ? null : pane.getEditorKit();
}
/** Get extended UI. This is called from views to get correct extended UI. */
public EditorUI getEditorUI() {
if (editorUI == null) {
BaseKit kit = (BaseKit)getEditorKit(getComponent());
editorUI = kit.createEditorUI();
}
return editorUI;
}
/**
* This method gets called when a bound property is changed.
* We are looking for document changes on the component.
*/
public void propertyChange(PropertyChangeEvent evt) {
String propName = evt.getPropertyName();
if ("document".equals(propName)) { // NOI18N
BaseDocument oldDoc = (evt.getOldValue() instanceof BaseDocument)
? (BaseDocument)evt.getOldValue() : null;
if (oldDoc != null) {
oldDoc.removeDocumentListener(this);
}
BaseDocument newDoc = (evt.getNewValue() instanceof BaseDocument)
? (BaseDocument)evt.getNewValue() : null;
if (newDoc != null) {
newDoc.addDocumentListener(this);
Registry.activate(newDoc); // Activate the new document
}
} else if ("ancestor".equals(propName)) { // NOI18N
JTextComponent comp = (JTextComponent)evt.getSource();
if (comp.isDisplayable() && editorUI != null && editorUI.hasExtComponent()) {
// #41209: In case extComponent was retrieved set the ancestorOverride
// to true and expect that the editor kit that installed
// this UI will be deinstalled explicitly.
if (!Boolean.TRUE.equals(comp.getClientProperty("ancestorOverride"))) { // NOI18N
comp.putClientProperty("ancestorOverride", Boolean.TRUE); // NOI18N
}
}
}
}
/** Insert to document notification. */
public void insertUpdate(DocumentEvent evt) {
try {
BaseDocumentEvent bevt = (BaseDocumentEvent)evt;
EditorUI editorUI = getEditorUI();
int y = getYFromPos(evt.getOffset());
int lineHeight = editorUI.getLineHeight();
int syntaxY = getYFromPos(bevt.getSyntaxUpdateOffset());
// !!! patch for case when DocMarksOp.eolMark is at the end of document
if (bevt.getSyntaxUpdateOffset() == evt.getDocument().getLength()) {
syntaxY += lineHeight;
}
if (getComponent().isShowing()) {
editorUI.repaint(y, Math.max(lineHeight, syntaxY - y));
}
} catch (BadLocationException ex) {
Utilities.annotateLoggable(ex);
}
}
/** Remove from document notification. */
public void removeUpdate(DocumentEvent evt) {
try {
BaseDocumentEvent bevt = (BaseDocumentEvent)evt;
EditorUI editorUI = getEditorUI();
int y = getYFromPos(evt.getOffset());
int lineHeight = editorUI.getLineHeight();
int syntaxY = getYFromPos(bevt.getSyntaxUpdateOffset());
// !!! patch for case when DocMarksOp.eolMark is at the end of document
if (bevt.getSyntaxUpdateOffset() == evt.getDocument().getLength()) {
syntaxY += lineHeight;
}
if (getComponent().isShowing()) {
editorUI.repaint(y, Math.max(lineHeight, syntaxY - y));
}
} catch (BadLocationException ex) {
Utilities.annotateLoggable(ex);
}
}
/** The change in document notification.
*
* @param evt The change notification from the currently associated document.
*/
public void changedUpdate(DocumentEvent evt) {
if (evt instanceof BaseDocumentEvent) {
BaseDocumentEvent bdevt = (BaseDocumentEvent)evt;
BaseDocument doc = (BaseDocument)bdevt.getDocument();
String layerName = bdevt.getDrawLayerName();
if (layerName != null) {
getEditorUI().addLayer(doc.findLayer(layerName),
bdevt.getDrawLayerVisibility());
}else{ //temp
try {
JTextComponent comp = getComponent();
if (comp!=null && comp.isShowing()) {
getEditorUI().repaintBlock(evt.getOffset(), evt.getOffset() + evt.getLength());
}
} catch (BadLocationException ex) {
Utilities.annotateLoggable(ex);
}
}
}
}
/** Creates a view for an element.
*
* @param elem the element
* @return the newly created view or null
*/
public View create(Element elem) {
String kind = elem.getName();
/*
if (foldingEnabled){
Element parent = elem.getParentElement();
if (parent!=null){
int index = parent.getElementIndex(elem.getStartOffset());
if (index >=3 && index <=6){
return new CollapsedView(parent.getElement(3), parent.getElement(6));
}
}
}
*/
if (kind != null) {
if (kind.equals(AbstractDocument.ContentElementName)) {
return new LabelView(elem);
} else if (kind.equals(AbstractDocument.ParagraphElementName)) {
// System.out.println("creating DrawEngineLineView for elem=" + elem);
return new DrawEngineLineView(elem);//.createFragment(elem.getStartOffset()+10,elem.getStartOffset()+30);
} else if (kind.equals(AbstractDocument.SectionElementName)) {
// return new LockView(new EditorUIBoxView(elem, View.Y_AXIS));
// System.out.println("creating DrawEngineDocView for elem=" + elem);
// return new DrawEngineDocView(getComponent()); // EditorUIBoxView(elem, View.Y_AXIS);
return new LockView(new DrawEngineDocView(elem)); // EditorUIBoxView(elem, View.Y_AXIS);
} else if (kind.equals(StyleConstants.ComponentElementName)) {
return new ComponentView(elem);
} else if (kind.equals(StyleConstants.IconElementName)) {
return new IconView(elem);
}
}
// default to text display
return new DrawEngineLineView(elem);
}
/** Creates a view for an element.
* @param elem the element
* @param p0 the starting offset >= 0
* @param p1 the ending offset >= p0
* @return the view
*/
public View create(Element elem, int p0, int p1) {
return null;
}
/** Specifies that some preference has changed. */
public void preferenceChanged(boolean width, boolean height) {
modelChanged();
}
/** Update height of the views */
void updateHeight() {
}
public void invalidateStartY() {
// no longer available
}
public void settingsChange(SettingsChangeEvent evt) {
JTextComponent component = getComponent();
if (component == null) return;
if (evt == null || Utilities.getKitClass(component) != evt.getKitClass()) return;
if (SettingsNames.CODE_FOLDING_ENABLE.equals(evt.getSettingName())){
Boolean foldingEnabledBoolean =(Boolean)Settings.getValue(evt.getKitClass(), SettingsNames.CODE_FOLDING_ENABLE);
foldingEnabled = foldingEnabledBoolean.booleanValue();
component.putClientProperty(SettingsNames.CODE_FOLDING_ENABLE, foldingEnabledBoolean);
needsRefresh = true;
refresh();
}
}
boolean isFoldingEnabled() {
return foldingEnabled;
}
protected void refresh(){
if (getComponent().isShowing() && needsRefresh){
modelChanged();
needsRefresh = false;
}
}
private static class GetFocusedComponentAction extends TextAction {
private GetFocusedComponentAction() {
super("get-focused-component"); // NOI18N
}
public void actionPerformed(ActionEvent evt) {
}
JTextComponent getFocusedComponent2() {
return super.getFocusedComponent();
}
}
class EditorUIBoxView extends BoxView{
public EditorUIBoxView(Element elem, int axis){
super(elem, axis);
}
private DrawEngine.PreinitializedDrawEngine getPreinitializedDrawEngine(Graphics g){
DrawEngine.PreinitializedDrawEngine drawEngine = null;
Rectangle clip = g.getClipBounds();
if (clip.height <= 0 || clip.width < 0) {
return null;
}
int clipY = clip.y;
int clipHeight = clip.height;
int paintY = Math.max(clipY, 0); // relative start of area to paint
BaseDocument doc = (BaseDocument)getEditorUI().getDocument();
try {
int startPos = getPosFromY(paintY);
int pos = getPosFromY(clipY + clipHeight - 1);
int endPos = Utilities.getRowEnd(doc, pos);
int startOffset = startPos;
int endOffset = endPos;
int y = getYFromPos(startOffset);
if (endOffset > startOffset){
drawEngine = DrawEngine.getDrawEngine().getDrawEngine(
this,
new DrawGraphics.GraphicsDG(g),
getEditorUI(),
startOffset,
endOffset,
getBaseX(y),
y,
Integer.MAX_VALUE
);
}
}catch(BadLocationException ble){
ble.printStackTrace();
}
return drawEngine;
}
public void paint(Graphics g, Shape allocation) {
super.paint(g, allocation);
DrawEngine.PreinitializedDrawEngine pde = null;
JTextComponent component = getComponent();
try{
pde = getPreinitializedDrawEngine(g);
// share the instance of PreinitializedDrawEngine among children views
if (component!=null){
component.putClientProperty(DrawEngine.PreinitializedDrawEngine.class, pde); //NOI18N
}
getEditorUI().paint(g);
}finally{
// release created preinitialized instance
if (pde!=null) pde.release();
if (component!=null){
component.putClientProperty(DrawEngine.PreinitializedDrawEngine.class, null);
}
}
}
}
/** Class that returns back BaseTextUI after its change
* by changing look-and-feel.
*/
static class UIWatcher implements PropertyChangeListener {
private Class uiClass;
UIWatcher(Class uiClass) {
this.uiClass = uiClass;
}
public void propertyChange(PropertyChangeEvent evt) {
Object newValue = evt.getNewValue();
if ("UI".equals(evt.getPropertyName())
&& (newValue != null) && !(newValue instanceof BaseTextUI)
) {
JTextComponent c = (JTextComponent)evt.getSource();
EditorKit kit = ((TextUI)newValue).getEditorKit(c);
if (kit instanceof BaseKit) {
// BaseKit but not BaseTextUI -> restore BaseTextUI
try {
c.setUI((BaseTextUI)uiClass.newInstance());
} catch (InstantiationException e) {
} catch (IllegalAccessException e) {
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy