All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openide.awt.HtmlRendererImpl Maven / Gradle / Ivy

There is a newer version: RELEASE240
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.openide.awt;


import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;

import java.beans.PropertyChangeListener;
import java.beans.VetoableChangeListener;


import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.AncestorListener;


/**
 * Html renderer component implementation.  The actual painting is done by HtmlLabelUI, which uses
 * HtmlRenderer.renderString().  What this class does:   Provide some methods for resetting its state
 * between uses (see HtmlRenderer.createLabel() for why), overrides for a bunch of things for performance
 * reasons, and some conversions to handle the case that the lightweight html renderer is disabled
 * (-J-Dnb.useSwingHtmlRendering=true), to convert our minor extensions to html syntax to standard
 * syntax for the swing renderer.
 * 

* Mainly this class provides an implementation of the various cell renderer interfaces which * HtmlRenderer.Renderer aggregates, and the convenience methods it provides. * * @author Tim Boudreau * @since 4.30 * */ class HtmlRendererImpl extends JLabel implements HtmlRenderer.Renderer { private static final Rectangle bounds = new Rectangle(); private static final boolean swingRendering = Boolean.getBoolean("nb.useSwingHtmlRendering"); //NOI18N private final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0); enum Type {UNKNOWN, TREE, LIST, TABLE} //For experimentation - holding the graphics object may be the source of some //strange painting problems on Apple private static boolean noCacheGraphics = Boolean.getBoolean("nb.renderer.nocache"); //NOI18N private ScratchGraphics cachedScratchGraphics = null; private boolean centered = false; private boolean parentFocused = false; private Boolean html = null; private int indent = 0; private Border border = null; private boolean selected = false; private boolean leadSelection = false; private Dimension prefSize = null; private Type type = Type.UNKNOWN; private int renderStyle = HtmlRenderer.STYLE_CLIP; private boolean enabled = true; private final boolean cellRenderer; HtmlRendererImpl(boolean cellRenderer) { this.cellRenderer = cellRenderer; } Type type() { return type; } /** Restore the renderer to a pristine state */ public void reset() { assert SwingUtilities.isEventDispatchThread(); parentFocused = false; setCentered(false); html = null; indent = 0; border = null; setIcon(null); setOpaque(false); selected = false; leadSelection = false; prefSize = null; type = Type.UNKNOWN; renderStyle = HtmlRenderer.STYLE_CLIP; setFont(UIManager.getFont("controlFont")); //NOI18N setIconTextGap(3); setEnabled(true); border = null; //Defensively ensure the insets haven't been messed with EMPTY_INSETS.top = 0; EMPTY_INSETS.left = 0; EMPTY_INSETS.right = 0; EMPTY_INSETS.bottom = 0; } public Component getTableCellRendererComponent( JTable table, Object value, boolean selected, boolean leadSelection, int row, int column ) { reset(); configureFrom(value, table, selected, leadSelection); type = Type.TABLE; if (swingRendering && selected) { setBackground(table.getSelectionBackground()); setForeground(table.getSelectionForeground()); setOpaque(true); } return this; } public Component getTreeCellRendererComponent( JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean leadSelection ) { reset(); configureFrom(value, tree, selected, leadSelection); type = Type.TREE; if (swingRendering && selected) { if (HtmlLabelUI.isGTK()) { setBackground(HtmlLabelUI.getBackgroundFor(this)); setForeground(HtmlLabelUI.getForegroundFor(this)); } setOpaque(true); } return this; } public Component getListCellRendererComponent( JList list, Object value, int index, boolean selected, boolean leadSelection ) { reset(); configureFrom(value, list, selected, leadSelection); type = Type.LIST; if (swingRendering && selected) { setBackground(list.getSelectionBackground()); setForeground(list.getSelectionForeground()); setOpaque(true); } // ##93658: In GTK we have to paint borders in combo boxes if (HtmlLabelUI.isGTK()) { if (index == -1) { Color borderC = UIManager.getColor("controlShadow"); //NOI18N borderC = borderC == null ? Color.GRAY : borderC; setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(borderC), BorderFactory.createEmptyBorder(3, 2, 3, 2))); } else { setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); } } return this; } /** Generic code to set properties appropriately from any of the renderer * fetching methods */ private void configureFrom(Object value, JComponent target, boolean selected, boolean leadSelection) { setText((value == null) ? "" : value.toString()); setSelected(selected); if (selected) { setParentFocused(checkFocused(target)); } else { setParentFocused(false); } setEnabled(target.isEnabled()); setLeadSelection(leadSelection); setFont(target.getFont()); } private boolean checkFocused(JComponent c) { Component focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); boolean result = c == focused; if (!result) { result = c.isAncestorOf(focused); } return result; } public @Override void addNotify() { if (swingRendering || !cellRenderer) { super.addNotify(); } } public @Override void removeNotify() { if (swingRendering || !cellRenderer) { super.removeNotify(); } } public void setSelected(boolean val) { selected = val; } public void setParentFocused(boolean val) { parentFocused = val; } public void setLeadSelection(boolean val) { leadSelection = val; } public void setCentered(boolean val) { centered = val; if (val) { setIconTextGap(5); } if (swingRendering) { if (val) { setVerticalTextPosition(JLabel.BOTTOM); setHorizontalAlignment(JLabel.CENTER); setHorizontalTextPosition(JLabel.CENTER); } else { setVerticalTextPosition(JLabel.CENTER); setHorizontalAlignment(JLabel.LEADING); setHorizontalTextPosition(JLabel.TRAILING); } } } public void setIndent(int pixels) { this.indent = pixels; if (!cellRenderer || swingRendering) { invalidate(); } } public void setHtml(boolean val) { Boolean wasHtml = html; String txt = getText(); html = val ? Boolean.TRUE : Boolean.FALSE; if ((html != wasHtml) || (swingRendering || !cellRenderer)) { //Ensure label UI gets updated and builds its little document tree... firePropertyChange("text", txt, getText()); //NOI18N } } public void setRenderStyle(int style) { renderStyle = style; } int getRenderStyle() { return renderStyle; } boolean isLeadSelection() { return leadSelection; } boolean isCentered() { return centered; } boolean isParentFocused() { return parentFocused; } boolean isHtml() { Boolean isHtml = html; if (isHtml == null) { String s = getText(); isHtml = checkHtml(s); html = isHtml; } return isHtml.booleanValue(); } private Boolean checkHtml(String s) { Boolean result; if (s == null) { result = Boolean.FALSE; } else if (s.startsWith(""; //NOI18N } return s; } /** * Converts extended UI manager color tags into legal html in the case that we're using swing rendering * * @param s string to convert if it has questionable font tags * @return The converted string */ private static String ensureLegalFontColorTags(String s) { String check = s.toUpperCase(); int start = 0; int fidx = check.indexOf("', start); //NOI18N start = tagEnd + 1; if (tagEnd == -1) { break; } if (cidx != -1) { if (cidx < tagEnd) { //we have a font color tag int eidx = check.indexOf('=', cidx); //NOI18N if (eidx != -1) { int bangIdx = check.indexOf('!', eidx); //NOI18N if ((bangIdx != -1) && (bangIdx < tagEnd)) { int colorStart = bangIdx + 1; int colorEnd = tagEnd; for (int i = colorStart; i < tagEnd; i++) { char c = s.charAt(i); if (!Character.isLetter(c)) { colorEnd = i; break; } } if (sb == null) { sb = new StringBuffer(s); } String colorString = s.substring(colorStart, colorEnd); String converted = convertToStandardColor(colorString); sb.replace(bangIdx, colorEnd, converted); s = sb.toString(); check = s.toUpperCase(); } } } } fidx = check.indexOf("not use the * internal HTML renderer is in effect, this will fire changes normally */ protected @Override final void firePropertyChange(String name, Object old, Object nue) { if (swingRendering || !cellRenderer) { if ("text".equals(name) && isHtml()) { //NOI18N //Force in the HTML tags so the UI will set up swing HTML rendering appropriately nue = getText(); } super.firePropertyChange(name, old, nue); } } public @Override Border getBorder() { Border result; if ((indent != 0) && (swingRendering || !cellRenderer)) { result = BorderFactory.createEmptyBorder(0, indent, 0, 0); } else { result = border; } return result; } public @Override void setBorder(Border b) { Border old = border; border = b; if (swingRendering || !cellRenderer) { firePropertyChange("border", old, b); } } public @Override Insets getInsets() { return getInsets(null); } public @Override Insets getInsets(Insets insets) { Insets result; //Call getBorder(), not just read the field - if swingRendering, the border will be constructed, and the //insets are what will make the indent property work; HtmlLabelUI doesn't need this, it just reads the //insets property, but BasicLabelUI and its ilk do Border b = getBorder(); if (b == null) { // If being used externally, defensively return a new instance result = cellRenderer ? EMPTY_INSETS : new Insets(0, 0, 0, 0); } else { //workaround for open jdk bug, see issue #192388 try { result = b.getBorderInsets(this); } catch( NullPointerException e ) { Logger.getLogger(HtmlRendererImpl.class.getName()).log(Level.FINE, null, e); result = cellRenderer ? EMPTY_INSETS : new Insets(0, 0, 0, 0); } } if( null != insets ) { insets.set( result.top, result.left, result.bottom, result.right); return insets; } return result; } public @Override void setEnabled(boolean b) { //OptimizeIt shows about 12Ms overhead calling back to Component.enable(), so avoid it if possible enabled = b; if (swingRendering || !cellRenderer) { super.setEnabled(b); } } public @Override boolean isEnabled() { return enabled; } public @Override void updateUI() { if (swingRendering) { super.updateUI(); } else { setUI(HtmlLabelUI.createUI(this)); } } /** Overridden to produce a graphics object even when isDisplayable() is * false, so that calls to getPreferredSize() will return accurate * dimensions (presuming the font and text are set correctly) even when * not onscreen. */ public @Override Graphics getGraphics() { Graphics result = null; if (isDisplayable()) { result = super.getGraphics(); } if (result == null) { result = scratchGraphics(); } return result; } private static final class ScratchGraphics { private final GraphicsConfiguration configuration; private Reference graphics = new SoftReference<>(null); public ScratchGraphics(GraphicsConfiguration configuration) { if (configuration == null) { throw new NullPointerException(); } this.configuration = configuration; } public boolean isConfigurationCompatible(GraphicsConfiguration other) { return configuration.getColorModel().equals(other.getColorModel()) && configuration.getDefaultTransform().equals(other.getDefaultTransform()); } public Graphics2D getGraphics() { Graphics2D result = graphics.get(); if (result == null) { /* Equivalent to configuration.createCompatibleImage(int, int), just to show that only the ColorModel field of the GraphicsConfiguration is really relevant here. */ ColorModel model = configuration.getColorModel(); WritableRaster raster = model.createCompatibleWritableRaster(1, 1); BufferedImage img = new BufferedImage(model, raster, model.isAlphaPremultiplied(), null); result = img.createGraphics(); this.graphics = new SoftReference<>(result); } // Restore state on every call, just in case a client modified it for some reason. result.setClip(null); /* NETBEANS-2543: Apply the scaling HiDPI transform. This affects font measurements, via FontRenderContext.getTransform(). */ result.setTransform(configuration.getDefaultTransform()); return result; } } /** Fetch a scratch graphics object for calculating preferred sizes while * offscreen */ private final Graphics2D scratchGraphics() { GraphicsConfiguration gc = getGraphicsConfiguration(); if (gc == null) { gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); } ScratchGraphics scratchGraphics = cachedScratchGraphics; if (scratchGraphics != null && scratchGraphics.isConfigurationCompatible(gc)) { return scratchGraphics.getGraphics(); } scratchGraphics = new ScratchGraphics(gc); if (!noCacheGraphics) { cachedScratchGraphics = scratchGraphics; } return scratchGraphics.getGraphics(); } public @Override void setBounds(int x, int y, int w, int h) { reshape(x, y, w, h); } @Deprecated public @Override void reshape(int x, int y, int w, int h) { if (swingRendering || !cellRenderer) { super.reshape(x, y, w, h); } bounds.setBounds(x, y, w, h); } public @Override int getWidth() { return bounds.width; } public @Override int getHeight() { return bounds.height; } public @Override Point getLocation() { return bounds.getLocation(); } /** Overridden to do nothing for performance reasons */ public @Override void validate() { if (!cellRenderer) { super.validate(); } } public @Override void setText(String text) { if (!cellRenderer) { prefSize = null; } super.setText(text); } /** Overridden to do nothing for performance reasons */ public @Override void repaint(long tm, int x, int y, int w, int h) { if (!cellRenderer) { super.repaint(tm, x, y, w, h); } } /** Overridden to do nothing for performance reasons */ public @Override void repaint() { if (!cellRenderer) { super.repaint(); } } /** Overridden to do nothing for performance reasons */ public @Override void invalidate() { if (!cellRenderer) { super.invalidate(); } } /** Overridden to do nothing for performance reasons */ public @Override void revalidate() { if (!cellRenderer) { super.revalidate(); } } /** Overridden to do nothing for performance reasons */ public @Override void addAncestorListener(AncestorListener l) { if (swingRendering || !cellRenderer) { super.addAncestorListener(l); } } /** Overridden to do nothing for performance reasons */ public @Override void addComponentListener(ComponentListener l) { if (swingRendering || !cellRenderer) { super.addComponentListener(l); } } /** Overridden to do nothing for performance reasons */ public @Override void addContainerListener(ContainerListener l) { if (swingRendering || !cellRenderer) { super.addContainerListener(l); } } /** Overridden to do nothing for performance reasons */ public @Override void addHierarchyListener(HierarchyListener l) { if (swingRendering || !cellRenderer) { super.addHierarchyListener(l); } } /** Overridden to do nothing for performance reasons */ public @Override void addHierarchyBoundsListener(HierarchyBoundsListener l) { if (swingRendering || !cellRenderer) { super.addHierarchyBoundsListener(l); } } /** Overridden to do nothing for performance reasons */ public @Override void addInputMethodListener(InputMethodListener l) { if (swingRendering || !cellRenderer) { super.addInputMethodListener(l); } } /** Overridden to do nothing for performance reasons */ public @Override void addFocusListener(FocusListener fl) { if (swingRendering || !cellRenderer) { super.addFocusListener(fl); } } /** Overridden to do nothing for performance reasons */ public @Override void addMouseListener(MouseListener ml) { if (swingRendering || !cellRenderer) { super.addMouseListener(ml); } } /** Overridden to do nothing for performance reasons */ public @Override void addMouseWheelListener(MouseWheelListener ml) { if (swingRendering || !cellRenderer) { super.addMouseWheelListener(ml); } } /** Overridden to do nothing for performance reasons */ public @Override void addMouseMotionListener(MouseMotionListener ml) { if (swingRendering || !cellRenderer) { super.addMouseMotionListener(ml); } } /** Overridden to do nothing for performance reasons */ public @Override void addVetoableChangeListener(VetoableChangeListener vl) { if (swingRendering || !cellRenderer) { super.addVetoableChangeListener(vl); } } /** Overridden to do nothing for performance reasons, unless using standard swing rendering */ public @Override void addPropertyChangeListener(String s, PropertyChangeListener l) { if (swingRendering || !cellRenderer) { super.addPropertyChangeListener(s, l); } } public @Override void addPropertyChangeListener(PropertyChangeListener l) { if (swingRendering || !cellRenderer) { super.addPropertyChangeListener(l); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy