|
What this is
Other links
The source code/* * 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 org.netbeans.editor.GlyphGutter; import javax.swing.plaf.TextUI; import org.netbeans.editor.FontMetricsCache; /** * 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 int STYLE_CNT = 4; 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; /** Listener to changes in settings */ private PropertyChangeListener settingsListener; /** 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) || SettingsNames.PRINT_LINE_NUMBER_VISIBLE.equals(settingName) ) { lineNumberVisibleSetting = SettingsUtil.getBoolean(kitClass, (component != null) ? SettingsNames.LINE_NUMBER_VISIBLE : SettingsNames.PRINT_LINE_NUMBER_VISIBLE, (component != null) ? SettingsDefaults.defaultLineNumberVisible : SettingsDefaults.defaultPrintLineNumberVisible ); 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; } lineHeightCorrection = ((Float)value).floatValue(); } 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 |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.