![JAR search and dependency download from the Maven repository](/logo.png)
org.netbeans.editor.EditorUI 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-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.util.Hashtable;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeSupport;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JViewport;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import javax.swing.text.JTextComponent;
import javax.swing.text.Caret;
import javax.swing.text.BadLocationException;
import javax.swing.text.View;
import javax.swing.plaf.TextUI;
/**
* Editor UI for the component. All the additional UI features
* like advanced scrolling, info about fonts, abbreviations,
* keyword matching are based on this class.
*
* @author Miloslav Metelka
* @version 1.00
*/
public class EditorUI implements ChangeListener, PropertyChangeListener, SettingsChangeListener {
public static final String OVERWRITE_MODE_PROPERTY = "overwriteMode"; // NOI18N
public static final String COMPONENT_PROPERTY = "component"; // NOI18N
/** Default scrolling type is used for the standard
* setDot() call. If the area is on the screen, it
* jumps to it, otherwise it centers the requested area
* vertically in the middle of the window and it uses
* smallest covering on the right side.
*/
public static final int SCROLL_DEFAULT = 0;
/** Scrolling type used for regular caret moves.
* The scrollJump is used when the caret requests area outside the screen.
*/
public static final int SCROLL_MOVE = 1;
/** Scrolling type where the smallest covering
* for the requested rectangle is used. It's useful
* for going to the end of the line for example.
*/
public static final int SCROLL_SMALLEST = 2;
/** Scrolling type for find operations, that can
* request additional configurable area in each
* direction, so the context around is visible too.
*/
public static final int SCROLL_FIND = 3;
private static final Insets NULL_INSETS = new Insets(0, 0, 0, 0);
private static final Insets DEFAULT_INSETS = new Insets(0, SettingsDefaults.defaultTextLeftMarginWidth.intValue(), 0, 0);
private static final Dimension NULL_DIMENSION = new Dimension(0, 0);
/** Default margin on the left and right side of the line number */
public static final Insets defaultLineNumberMargin = new Insets(0, 3, 0, 3);
private static final boolean debugUpdateLineHeight
= Boolean.getBoolean("netbeans.debug.editor.updateLineHeight");
/** Map holding the coloring maps for the different languages.
* It helps to minimize the amount of the coloring maps
* and also save the time necessary for their creation.
*/
private static final HashMap sharedColoringMaps = new HashMap(57);
private static final SettingsChangeListener clearingListener
= new SettingsChangeListener() {
public void settingsChange(SettingsChangeEvent evt) {
// Fired when the Settings are locked
sharedColoringMaps.clear();
}
};
static {
Settings.addSettingsChangeListener( clearingListener );
}
/** Component this extended UI is related to. */
private JTextComponent component;
private JComponent extComponent;
private JToolBar toolBarComponent;
/** Property change support for firing property changes */
PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
/** Document for the case ext ui is constructed without the component */
private BaseDocument printDoc;
/** Draw layer chain */
private DrawLayerList drawLayerList = new DrawLayerList();
/** Map holding the [name, coloring] pairs */
private Map coloringMap;
/** Character (or better line) height. Particular view can use a different
* character height however most views will probably use this one.
*/
private int lineHeight = 1; // prevent possible division by zero
private float lineHeightCorrection = 1.0f;
/** Ascent of the line which is maximum ascent of all the fonts used. */
private int lineAscent;
/** Width of the space in the default coloring's font */
int defaultSpaceWidth = 1;
/** Flag to initialize fonts */
private boolean fontsInited;
/** First paint after preferenceChanged after fonts were inited. */
private boolean fontsInitedPreferenceChanged;
/** Should the search words be colored? */
boolean highlightSearch;
/** Enable displaying line numbers. Both this flag and lineNumberVisibleSetting
* must be true to have the line numbers visible in the window. This flag is false
* by default. It's turned on automatically if the getExtComponent is called.
*/
boolean lineNumberEnabled;
/** This flag corresponds to the LINE_NUMBER_VISIBLE setting. */
boolean lineNumberVisibleSetting;
/** Whether to show line numbers or not. This flag is obtained using bitwise AND
* operation on lineNumberEnabled flag and lineNumberVisibleSetting flag.
*/
boolean lineNumberVisible;
/** Line number total width with indentation. It includes left and right
* line-number margins and lineNumberDigitWidth * lineNumberMaxDigitCount.
*/
int lineNumberWidth;
/** Width of one digit used for line numbering. It's based
* on the information from the line coloring.
*/
int lineNumberDigitWidth;
/** Current maximum count of digits in line number */
int lineNumberMaxDigitCount;
/** This is the size of the editor as component while the real size
* of the lines edited can be lower. The reason why to use this
* virtual size is that each resizing of the component means
* revalidating and therefore repainting of the whole component.
*/
Rectangle virtualSize = new Rectangle();
// /** This is the increment by which the size of the component
// * is increased.
// */
// Rectangle virtualSizeIncrement = new Rectangle(); !!!
/** Margin between the line-number bar and the text. */
int textLeftMarginWidth;
/** This is the full margin around the text. The left margin
* is an addition of component's margin and lineNumberWidth
* and textLeftMarginWidth.
*/
Insets textMargin = DEFAULT_INSETS;
/** How much columns/lines to add when the scroll is performed
* so that the component is not scrolled so often.
* Negative number means portion of the extent width/height
*/
Insets scrollJumpInsets;
/** How much columns/lines to add when the scroll is performed
* so that the component is not scrolled so often.
* Negative number means portion of the extent width/height
*/
Insets scrollFindInsets;
/** Flag saying whether either the width or height in virtualSize
* were updated.
*/
boolean virtualSizeUpdated;
/** EditorUI properties */
Hashtable props = new Hashtable(11);
boolean textLimitLineVisible;
Color textLimitLineColor;
int textLimitWidth;
private Rectangle lastExtentBounds = new Rectangle();
private Dimension componentSizeIncrement = new Dimension();
private Abbrev abbrev;
private WordMatch wordMatch;
private Object componentLock;
/** Status bar */
StatusBar statusBar;
private FocusAdapter focusL;
Map renderingHints;
/** Glyph gutter used for drawing of annotation glyph icons. */
private GlyphGutter glyphGutter = null;
/** The line numbers can be shown in glyph gutter and therefore it is necessary
* to disable drawing of lines here. During the printing on the the other hand, line
* numbers must be visible. */
private boolean disableLineNumbers = true;
/** Left right corner of the JScrollPane */
private JPanel glyphCorner;
/** Whether printing coloring map or component coloring map should be used. */
private boolean usePrintColoringMap;
public static final String LINE_HEIGHT_CHANGED_PROP = "line-height-changed-prop"; //NOI18N
/** init paste action #39678 */
private static boolean isPasteActionInited = false;
/** Construct extended UI for the use with a text component */
public EditorUI() {
this.usePrintColoringMap = false;
Settings.addSettingsChangeListener(this);
focusL = new FocusAdapter() {
public void focusGained(FocusEvent evt) {
Registry.activate(getComponent());
/* Fix of #25475 - copyAction's enabled flag
* must be updated on focus change
*/
stateChanged(null);
if (component!=null){
BaseTextUI ui = (BaseTextUI)component.getUI();
if (ui!=null) ui.refresh();
}
}
};
}
/** Construct extended UI for printing the given document */
public EditorUI(BaseDocument printDoc) {
this(printDoc, true, true);
}
/**
* Construct extended UI for printing the given document
* and specify which set of colors should be used.
*
* @param printDoc document that should be printed.
* @param usePrintColoringMap use printing coloring settings instead
* of the regular ones.
* @param lineNumberEnabled if set to false the line numbers will not be printed.
* If set to true the visibility of line numbers depends on lineNumberVisibleSetting.
*/
public EditorUI(BaseDocument printDoc, boolean usePrintColoringMap, boolean lineNumberEnabled) {
this.printDoc = printDoc;
this.usePrintColoringMap = usePrintColoringMap;
settingsChange(null);
setLineNumberEnabled(lineNumberEnabled);
updateLineNumberWidth(0);
drawLayerList.add(printDoc.getDrawLayerList());
// the fix of #37363
drawLayerList.remove(DrawLayerFactory.GUARDED_LAYER_NAME);
drawLayerList.remove(DrawLayerFactory.HIGHLIGHT_SEARCH_LAYER_NAME);
drawLayerList.remove(DrawLayerFactory.INC_SEARCH_LAYER_NAME);
}
/** Gets the coloring map that can be shared by the components
* with the same kit. Only the component coloring map is provided.
*/
protected static Map getSharedColoringMap(Class kitClass) {
synchronized (Settings.class) { // must sync like this against dedloks
Map cm = (Map)sharedColoringMaps.get(kitClass);
if (cm == null) {
cm = SettingsUtil.getColoringMap(kitClass, false, true);
// Test if there's a default coloring
if (cm.get(SettingsNames.DEFAULT_COLORING) == null) {
cm.put(SettingsNames.DEFAULT_COLORING, SettingsDefaults.defaultColoring);
}
sharedColoringMaps.put(kitClass, cm);
}
return cm;
}
}
/** Called when the BaseTextUI is being installed
* into the component.
*/
protected void installUI(JTextComponent c) {
synchronized (getComponentLock()) {
this.component = c;
putProperty(COMPONENT_PROPERTY, c);
// listen on component
component.addPropertyChangeListener(this);
component.addFocusListener(focusL);
// listen on caret
Caret caret = component.getCaret();
if (caret != null) {
caret.addChangeListener(this);
}
BaseDocument doc = getDocument();
if (doc != null) {
modelChanged(null, doc);
}
}
// Make sure all the things depending on non-null component will be updated
settingsChange(null);
// fix for issue #16352
getDefaultColoring().apply(component);
}
/** Called when the BaseTextUI is being uninstalled
* from the component.
*/
protected void uninstallUI(JTextComponent c) {
synchronized (getComponentLock()) {
// fix for issue 12996
if (component != null) {
// stop listening on caret
Caret caret = component.getCaret();
if (caret != null) {
caret.removeChangeListener(this);
}
// stop listening on component
component.removePropertyChangeListener(this);
component.removeFocusListener(focusL);
}
BaseDocument doc = getDocument();
if (doc != null) {
modelChanged(doc, null);
}
component = null;
putProperty(COMPONENT_PROPERTY, null);
// Clear the font-metrics cache
FontMetricsCache.clear();
}
}
/** Get the lock assuring the component will not be changed
* by installUI() or uninstallUI().
* It's useful for the classes that want to listen for the
* component change in EditorUI.
*/
public Object getComponentLock() {
if (componentLock == null) {
componentLock = new ComponentLock();
}
return componentLock;
}
static class ComponentLock {};
public void addPropertyChangeListener(PropertyChangeListener l) {
propertyChangeSupport.addPropertyChangeListener(l);
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener l) {
propertyChangeSupport.addPropertyChangeListener(propertyName, l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
propertyChangeSupport.removePropertyChangeListener(l);
}
public void removePropertyChangeListener(String propertyName, PropertyChangeListener l) {
propertyChangeSupport.removePropertyChangeListener(propertyName, l);
}
protected final void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
public void settingsChange(SettingsChangeEvent evt) {
if (component != null) {
if (Utilities.getKit(component) == null) {
return; // prevent problems if not garbage collected and settings changed
}
}
Class kitClass = getKitClass();
String settingName = (evt != null) ? evt.getSettingName() : null;
if (settingName == null || SettingsNames.LINE_NUMBER_VISIBLE.equals(settingName)) {
lineNumberVisibleSetting = SettingsUtil.getBoolean(kitClass,
SettingsNames.LINE_NUMBER_VISIBLE,
SettingsDefaults.defaultLineNumberVisible);
lineNumberVisible = lineNumberEnabled && lineNumberVisibleSetting;
// if this is printing, the drawing of original line numbers must be enabled
if (component == null)
disableLineNumbers = false;
if (disableLineNumbers)
lineNumberVisible = false;
}
BaseDocument doc = getDocument();
if (doc != null) {
if (settingName == null || SettingsNames.TEXT_LEFT_MARGIN_WIDTH.equals(settingName)) {
textLeftMarginWidth = SettingsUtil.getInteger(kitClass,
SettingsNames.TEXT_LEFT_MARGIN_WIDTH,
SettingsDefaults.defaultTextLeftMarginWidth);
}
if (settingName == null || SettingsNames.LINE_HEIGHT_CORRECTION.equals(settingName)) {
Object value = Settings.getValue(kitClass, SettingsNames.LINE_HEIGHT_CORRECTION);
if (!(value instanceof Float) || ((Float)value).floatValue() < 0) {
value = SettingsDefaults.defaultLineHeightCorrection;
}
float newLineHeightCorrection = ((Float)value).floatValue();
if (newLineHeightCorrection != lineHeightCorrection){
lineHeightCorrection = newLineHeightCorrection;
updateLineHeight(null);
}
}
if (settingName == null || SettingsNames.TEXT_LIMIT_LINE_VISIBLE.equals(settingName)) {
textLimitLineVisible = SettingsUtil.getBoolean(kitClass,
SettingsNames.TEXT_LIMIT_LINE_VISIBLE, SettingsDefaults.defaultTextLimitLineVisible);
}
if (settingName == null || SettingsNames.TEXT_LIMIT_LINE_COLOR.equals(settingName)) {
Object value = Settings.getValue(kitClass, SettingsNames.TEXT_LIMIT_LINE_COLOR);
textLimitLineColor = (value instanceof Color) ? (Color)value
: SettingsDefaults.defaultTextLimitLineColor;
}
if (settingName == null || SettingsNames.TEXT_LIMIT_WIDTH.equals(settingName)) {
textLimitWidth = SettingsUtil.getPositiveInteger(kitClass,
SettingsNames.TEXT_LIMIT_WIDTH, SettingsDefaults.defaultTextLimitWidth);
}
// component only properties
if (component != null) {
if (settingName == null || SettingsNames.SCROLL_JUMP_INSETS.equals(settingName)) {
Object value = Settings.getValue(kitClass, SettingsNames.SCROLL_JUMP_INSETS);
scrollJumpInsets = (value instanceof Insets) ? (Insets)value : NULL_INSETS;
}
if (settingName == null || SettingsNames.SCROLL_FIND_INSETS.equals(settingName)) {
Object value = Settings.getValue(kitClass, SettingsNames.SCROLL_FIND_INSETS);
scrollFindInsets = (value instanceof Insets) ? (Insets)value : NULL_INSETS;
}
if (settingName == null || SettingsNames.COMPONENT_SIZE_INCREMENT.equals(settingName)) {
Object value = Settings.getValue(kitClass, SettingsNames.COMPONENT_SIZE_INCREMENT);
componentSizeIncrement = (value instanceof Dimension) ? (Dimension)value : NULL_DIMENSION;
}
if (settingName == null || SettingsNames.RENDERING_HINTS.equals(settingName)) {
Object value = Settings.getValue(kitClass, SettingsNames.RENDERING_HINTS);
renderingHints = (value instanceof Map) ? (Map)value : null;
}
if (settingName == null || SettingsNames.CARET_COLOR_INSERT_MODE.equals(settingName)
|| SettingsNames.CARET_COLOR_OVERWRITE_MODE.equals(settingName)
) {
Boolean b = (Boolean)getProperty(OVERWRITE_MODE_PROPERTY);
Color caretColor;
if (b == null || !b.booleanValue()) {
Object value = Settings.getValue(kitClass, SettingsNames.CARET_COLOR_INSERT_MODE);
caretColor = (value instanceof Color) ? (Color)value
: SettingsDefaults.defaultCaretColorInsertMode;
} else {
Object value = Settings.getValue(kitClass, SettingsNames.CARET_COLOR_OVERWRITE_MODE);
caretColor = (value instanceof Color) ? (Color)value
: SettingsDefaults.defaultCaretColorOvwerwriteMode;
}
if (caretColor != null) {
component.setCaretColor(caretColor);
}
}
// fix for issues 13842, 14003
if (SwingUtilities.isEventDispatchThread()) {
component.setKeymap(Utilities.getKit(component).getKeymap());
BaseTextUI ui = (BaseTextUI)component.getUI();
ui.updateHeight();
component.repaint();
} else {
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
JTextComponent c = component;
if (c != null) {
BaseKit kit = Utilities.getKit(c);
if (kit != null) {
c.setKeymap(kit.getKeymap());
BaseTextUI ui = (BaseTextUI)c.getUI();
if (ui != null) {
ui.updateHeight();
c.repaint();
}
}
}
}
}
);
}
}
}
coloringMap = null; // reset coloring map so it's lazily rebuilt
/* make sure there's no pending preferenceChanged() request
* because if it would be then the fontsInited = false
* would have no effect.
*/
fontsInitedPreferenceChanged = false;
fontsInited = false;
}
public void stateChanged(ChangeEvent evt) {
SwingUtilities.invokeLater(
new Runnable() {
/** @return true if the document supports guarded sections
* and when either the caret is in guarded block
* or when selection spans any guarded block(s).
*/
private boolean isCaretGuarded(){
JTextComponent c = component;
BaseDocument bdoc = getDocument();
boolean inGuardedBlock = false;
if (bdoc instanceof GuardedDocument){
GuardedDocument gdoc = (GuardedDocument)bdoc;
boolean selectionSpansGuardedSection = false;
for (int i=c.getSelectionStart(); i viewWidth) {
r.x = viewWidth - r.width;
if (r.x < 0) {
r.x = 0;
r.width = viewWidth;
}
return scrollRectToVisibleImpl(r, scrollPolicy, bounds); // recall
}
// r must be within virtualSize's height
if (r.y +r.height > viewHeight) {
r.y = viewHeight - r.height;
if (r.y < 0) {
r.y = 0;
r.height = viewHeight;
}
return scrollRectToVisibleImpl(r, scrollPolicy, bounds);
}
// if r extends bounds dimension it must be corrected now
if (r.width > bounds.width || r.height > bounds.height) {
Rectangle caretRect = new Rectangle((Rectangle)component.getCaret());
if (caretRect.x >= r.x
&& caretRect.x + caretRect.width <= r.x + r.width
&& caretRect.y >= r.y
&& caretRect.y + caretRect.height <= r.y + r.height
) { // caret inside requested rect
// move scroll rect for best caret visibility
int overX = r.width - bounds.width;
int overY = r.height - bounds.height;
if (overX > 0) {
r.x -= overX * (caretRect.x - r.x) / r.width;
}
if (overY > 0) {
r.y -= overY * (caretRect.y - r.y) / r.height;
}
}
r.height = bounds.height;
r.width = bounds.width; // could be different algorithm
return scrollRectToVisibleImpl(r, scrollPolicy, bounds);
}
int newX = bounds.x;
int newY = bounds.y;
boolean move = false;
// now the scroll rect is within bounds of the component
// and can have size of the extent at maximum
if (r.x < bounds.x) {
move = true;
switch (scrollPolicy) {
case SCROLL_MOVE:
newX = (scrollJumpInsets.left < 0)
? (bounds.width * (-scrollJumpInsets.left) / 100)
: scrollJumpInsets.left * defaultSpaceWidth;
newX = Math.min(newX, bounds.x + bounds.width - (r.x + r.width));
newX = Math.max(r.x - newX, 0); // new bounds.x
break;
case SCROLL_DEFAULT:
case SCROLL_SMALLEST:
default:
newX = r.x;
break;
}
updateVirtualWidth(newX + bounds.width);
} else if (r.x + r.width > bounds.x + bounds.width) {
move = true;
switch (scrollPolicy) {
case SCROLL_SMALLEST:
newX = r.x + r.width - bounds.width;
break;
default:
newX = (scrollJumpInsets.right < 0)
? (bounds.width * (-scrollJumpInsets.right) / 100 )
: scrollJumpInsets.right * defaultSpaceWidth;
newX = Math.min(newX, bounds.width - r.width);
newX = (r.x + r.width) + newX - bounds.width;
break;
}
updateVirtualWidth(newX + bounds.width);
}
if (r.y < bounds.y) {
move = true;
switch (scrollPolicy) {
case SCROLL_MOVE:
newY = r.y;
newY -= (scrollJumpInsets.top < 0)
? (bounds.height * (-scrollJumpInsets.top) / 100 )
: scrollJumpInsets.top * lineHeight;
break;
case SCROLL_SMALLEST:
newY = r.y;
break;
case SCROLL_DEFAULT:
default:
newY = r.y - (bounds.height - r.height) / 2; // center
break;
}
newY = Math.max(newY, 0);
} else if (r.y + r.height > bounds.y + bounds.height) {
move = true;
switch (scrollPolicy) {
case SCROLL_MOVE:
newY = (r.y + r.height) - bounds.height;
newY += (scrollJumpInsets.bottom < 0)
? (bounds.height * (-scrollJumpInsets.bottom) / 100 )
: scrollJumpInsets.bottom * lineHeight;
break;
case SCROLL_SMALLEST:
newY = (r.y + r.height) - bounds.height;
break;
case SCROLL_DEFAULT:
default:
newY = r.y - (bounds.height - r.height) / 2; // center
break;
}
newY = Math.max(newY, 0);
}
if (move) {
setExtentPosition(newX, newY);
}
return move;
}
void setExtentPosition(int x, int y) {
JViewport port = getParentViewport();
if (port != null) {
Point p = new Point(Math.max(x, 0), Math.max(y, 0));
port.setViewPosition(p);
}
}
public void adjustWindow(int caretPercentFromWindowTop) {
final Rectangle bounds = getExtentBounds();
if (component != null && (component.getCaret() instanceof Rectangle)) {
Rectangle caretRect = (Rectangle)component.getCaret();
bounds.y = caretRect.y - (caretPercentFromWindowTop * bounds.height) / 100
+ (caretPercentFromWindowTop * lineHeight) / 100;
Utilities.runInEventDispatchThread(
new Runnable() {
public void run() {
scrollRectToVisible(bounds, SCROLL_SMALLEST);
}
}
);
}
}
/** Set the dot according to the currently visible screen window.
* #param percentFromWindowTop percentage giving the distance of the caret
* from the top of the currently visible window.
*/
public void adjustCaret(int percentFromWindowTop) {
JTextComponent c = component;
if (c != null) {
Rectangle bounds = getExtentBounds();
bounds.y += (percentFromWindowTop * bounds.height) / 100
- (percentFromWindowTop * lineHeight) / 100;
try {
int offset = ((BaseTextUI)c.getUI()).getPosFromY(bounds.y);
if (offset >= 0) {
caretSetDot(offset, null, SCROLL_SMALLEST);
}
} catch (BadLocationException e) {
}
}
}
/** Set the position of the caret and scroll the extent if necessary.
* @param offset position where the caret should be placed
* @param scrollRect rectangle that should become visible. It can be null
* when no scrolling should be done.
* @param scrollPolicy policy to be used when scrolling.
* @deprecated
*/
public void caretSetDot(int offset, Rectangle scrollRect, int scrollPolicy) {
if (component != null) {
Caret caret = component.getCaret();
if (caret instanceof BaseCaret) {
((BaseCaret)caret).setDot(offset, scrollRect, scrollPolicy);
} else {
caret.setDot(offset);
}
}
}
/** Set the position of the caret and scroll the extent if necessary.
* @param offset position where the caret should be placed
* @param scrollRect rectangle that should become visible. It can be null
* when no scrolling should be done.
* @param scrollPolicy policy to be used when scrolling.
* @deprecated
*/
public void caretMoveDot(int offset, Rectangle scrollRect, int scrollPolicy) {
if (component != null) {
Caret caret = component.getCaret();
if (caret instanceof BaseCaret) {
((BaseCaret)caret).moveDot(offset, scrollRect, scrollPolicy);
} else {
caret.moveDot(offset);
}
}
}
/** This method is called by textui to do the paint.
* It is forwarded either to paint through the image
* and then copy the image area to the screen or to
* paint directly to this graphics. The real work occurs
* in draw-engine.
*/
protected void paint(Graphics g) {
if (component != null) { // component must be installed
if (fontsInitedPreferenceChanged) {
fontsInitedPreferenceChanged = false;
fontsInited = true;
getExtentBounds(lastExtentBounds);
}
if (!fontsInited && g != null) {
update(g);
}
// ((BaseTextUI)component.getUI()).paintRegion(g);
}
}
/** Returns the line number margin */
public Insets getLineNumberMargin() {
return defaultLineNumberMargin;
}
private int computeLineNumberDigitWidth(Graphics g){
// Handle line number fonts and widths
Coloring dc = getDefaultColoring();
Coloring lnc = (Coloring)getColoringMap().get(SettingsNames.LINE_NUMBER_COLORING);
if (lnc != null) {
Font lnFont = lnc.getFont();
if (lnFont == null) {
lnFont = dc.getFont();
}
if (component == null) return lineNumberDigitWidth;
FontMetrics lnFM = (g == null) ?
FontMetricsCache.getFontMetrics(lnFont, component) : g.getFontMetrics(lnFont);
if (lnFM == null) return lineNumberDigitWidth;
int maxWidth = 1;
char[] digit = new char[1]; // will be used for '0' - '9'
for (int i = 0; i <= 9; i++) {
digit[0] = (char)('0' + i);
maxWidth = Math.max(maxWidth, lnFM.charsWidth(digit, 0, 1));
}
return maxWidth;
}
return lineNumberDigitWidth;
}
/** Returns width of the one digit */
public int getLineNumberDigitWidth() {
return (lineNumberDigitWidth == 0) ? computeLineNumberDigitWidth(null) : lineNumberDigitWidth;
}
/** Is glyph gutter created and visible for the document or not */
public boolean isGlyphGutterVisible() {
return glyphGutter != null;
}
public final GlyphGutter getGlyphGutter() {
return glyphGutter;
}
protected void updateScrollPaneCornerColor() {
Coloring lineColoring = (Coloring)getColoringMap().get(SettingsNames.LINE_NUMBER_COLORING);
Coloring defaultColoring = (Coloring)getDefaultColoring();
Color backgroundColor;
if (lineColoring.getBackColor() != null)
backgroundColor = lineColoring.getBackColor();
else
backgroundColor = defaultColoring.getBackColor();
glyphCorner.setBackground(backgroundColor);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy