diff --git a/modules/ui/src/com/alee/extended/label/AbstractStyledTextContent.java b/modules/ui/src/com/alee/extended/label/AbstractStyledTextContent.java index 29e2857ca..a9321d19f 100644 --- a/modules/ui/src/com/alee/extended/label/AbstractStyledTextContent.java +++ b/modules/ui/src/com/alee/extended/label/AbstractStyledTextContent.java @@ -17,6 +17,7 @@ package com.alee.extended.label; +import com.alee.painter.decoration.DecorationException; import com.alee.painter.decoration.IDecoration; import com.alee.painter.decoration.content.AbstractTextContent; import com.alee.utils.*; @@ -45,6 +46,7 @@ public abstract class AbstractStyledTextContent rows = layout ( c, d, bounds ); @@ -243,17 +248,24 @@ protected void paintText ( final Graphics2D g2d, final Rectangle bounds, final E th += row.height + rg; } + // Adjusting vertical position according to alignment if ( th < bounds.height ) { switch ( va ) { + case TOP: + break; + case CENTER: - y += ( bounds.height - th ) / 2; + y += Math.ceil ( ( bounds.height - th ) / 2.0 ); break; case BOTTOM: y += bounds.height - th; break; + + default: + throw new DecorationException ( "Incorrect vertical alignment provided: " + va ); } } } @@ -498,38 +510,37 @@ else if ( row.isEmpty () ) protected void paintRow ( final E c, final D d, final Graphics2D g2d, final Rectangle bounds, final int textX, final int textY, final StyledTextRow row, final boolean isLast ) { - int horizontalAlignment = getHorizontalAlignment ( c, d ); - final boolean ltr = c.getComponentOrientation ().isLeftToRight (); - if ( ( horizontalAlignment == SwingConstants.TRAILING && !ltr ) || ( horizontalAlignment == SwingConstants.LEADING && ltr ) ) - { - horizontalAlignment = SwingConstants.LEFT; - } - else if ( ( horizontalAlignment == SwingConstants.LEADING && !ltr ) || ( horizontalAlignment == SwingConstants.TRAILING && ltr ) ) - { - horizontalAlignment = SwingConstants.RIGHT; - } + // Painting settings + final Font font = c.getFont (); + final int defaultFontSize = font.getSize (); + final FontMetrics fm = c.getFontMetrics ( font ); + final TextWrap wt = getWrapType ( c, d ); + final int ha = getAdjustedHorizontalAlignment ( c, d ); + // Calculating text X coordinate int x = textX; - if ( bounds.width > row.width ) { - switch ( horizontalAlignment ) + switch ( ha ) { + case LEFT: + break; + case CENTER: - x += ( bounds.width - row.width ) / 2; + x += Math.floor ( ( bounds.width - row.width ) / 2.0 ); break; + case RIGHT: x += bounds.width - row.width; + break; + + default: + throw new DecorationException ( "Incorrect horizontal alignment provided: " + ha ); } } - final Font font = c.getFont (); - final int defaultFontSize = font.getSize (); - final FontMetrics fm = c.getFontMetrics ( font ); - final TextWrap wt = getWrapType ( c, d ); - final boolean truncated = isTruncated ( c, d ); + // Painting styled text fragments int charDisplayed = 0; - for ( final TextRange textRange : row.fragments ) { final StyleRange style = textRange.getStyleRange (); @@ -550,20 +561,28 @@ else if ( ( horizontalAlignment == SwingConstants.LEADING && !ltr ) || ( horizon final int strWidth = cfm.stringWidth ( s ); // Checking mnemonic - int mneIndex = -1; + int mnemonicIndex = -1; if ( row.mnemonic >= 0 && row.mnemonic < charDisplayed + s.length () ) { - mneIndex = row.mnemonic - charDisplayed; + mnemonicIndex = row.mnemonic - charDisplayed; } - // Checking trim needs - final int availableWidth = bounds.width + bounds.x - x; - if ( truncated && availableWidth < strWidth && - ( wt == TextWrap.none || wt == TextWrap.word || isLast ) ) + // Checking whether or not text should be truncated + final boolean truncated; + if ( isTruncate ( c, d ) ) { - // Clip string - s = SwingUtilities.layoutCompoundLabel ( cfm, s, null, 0, horizontalAlignment, 0, 0, - new Rectangle ( x, y, availableWidth, bounds.height ), new Rectangle (), new Rectangle (), 0 ); + final int availableWidth = bounds.width + bounds.x - x; + truncated = availableWidth < strWidth && ( wt == TextWrap.none || wt == TextWrap.word || isLast ); + if ( truncated ) + { + // Clip string + s = SwingUtilities.layoutCompoundLabel ( cfm, s, null, 0, ha, 0, 0, + new Rectangle ( x, y, availableWidth, bounds.height ), new Rectangle (), new Rectangle (), 0 ); + } + } + else + { + truncated = false; } // Starting of actual painting @@ -590,7 +609,14 @@ else if ( style.isSubscript () ) final boolean useStyleForeground = style != null && !isIgnoreColorSettings ( c, d ) && style.getForeground () != null; final Color textColor = useStyleForeground ? style.getForeground () : getColor ( c, d ); g2d.setPaint ( textColor ); - paintStyledTextFragment ( c, d, g2d, s, x, y, mneIndex, cfm, style, strWidth ); + paintStyledTextFragment ( c, d, g2d, s, x, y, mnemonicIndex, cfm, style, strWidth ); + + // Stop on truncated part + // Otherwise we might end up having two truncated parts + if ( truncated ) + { + break; + } x += strWidth; charDisplayed += s.length (); diff --git a/modules/ui/src/com/alee/extended/label/SimpleStyledTextContent.java b/modules/ui/src/com/alee/extended/label/SimpleStyledTextContent.java index 78284b3d7..c224c34a5 100644 --- a/modules/ui/src/com/alee/extended/label/SimpleStyledTextContent.java +++ b/modules/ui/src/com/alee/extended/label/SimpleStyledTextContent.java @@ -18,6 +18,7 @@ package com.alee.extended.label; import com.alee.painter.decoration.IDecoration; +import com.alee.painter.decoration.content.ContentPropertyListener; import com.alee.utils.SwingUtils; import com.thoughtworks.xstream.annotations.XStreamAsAttribute; @@ -37,7 +38,7 @@ * @see How to use WebStyledLabel */ -@SuppressWarnings ( "UnusedParameters" ) +@SuppressWarnings ("UnusedParameters") public abstract class SimpleStyledTextContent, I extends SimpleStyledTextContent> extends AbstractStyledTextContent { @@ -63,6 +64,11 @@ public abstract class SimpleStyledTextContent contentListener; + @Override public void activate ( final E c, final D d ) { @@ -71,11 +77,17 @@ public void activate ( final E c, final D d ) // Performing default actions super.activate ( c, d ); + + // Installing content property listener + installContentPropertyListener ( c, d ); } @Override public void deactivate ( final E c, final D d ) { + // Uninstalling content property listener + uninstallContentPropertyListener ( c, d ); + // Performing default actions super.deactivate ( c, d ); @@ -114,6 +126,55 @@ protected void destroyContentCache ( final E c, final D d ) styleRanges = null; } + /** + * Installs content property listener. + * + * @param c painted component + * @param d painted decoration state + */ + protected void installContentPropertyListener ( final E c, final D d ) + { + // Adding text change listener + final String property = getStyledTextProperty (); + if ( property != null ) + { + contentListener = new ContentPropertyListener ( c, d ) + { + @Override + public void propertyChange ( final E c, final D d, final String property, final Object oldValue, final Object newValue ) + { + updateContentCache ( c, d ); + } + }; + c.addPropertyChangeListener ( property, contentListener ); + } + } + + /** + * Uninstalls content property listener. + * + * @param c painted component + * @param d painted decoration state + */ + protected void uninstallContentPropertyListener ( final E c, final D d ) + { + // Removing text change listener + final String property = getStyledTextProperty (); + if ( property != null ) + { + c.removePropertyChangeListener ( property, contentListener ); + contentListener = null; + } + } + + /** + * Returns name of the property that contains styled text. + * It is used for registering content property listener that updates style caches. + * + * @return name of the property that contains styled text + */ + protected abstract String getStyledTextProperty (); + /** * Performs style caches update. * diff --git a/modules/ui/src/com/alee/extended/label/StyleRanges.java b/modules/ui/src/com/alee/extended/label/StyleRanges.java index f44889888..165e3be63 100644 --- a/modules/ui/src/com/alee/extended/label/StyleRanges.java +++ b/modules/ui/src/com/alee/extended/label/StyleRanges.java @@ -86,59 +86,66 @@ public List getStyleRanges () protected StyleRanges parseStyledText () { // Parse only if it is needed and it wasn't already completed - if ( !TextUtils.isEmpty ( styledText ) && plainText == null ) + if ( styleRanges == null ) { styleRanges = new ArrayList (); - int begin = nextUnescaped ( styledText, "{", 0 ); - if ( begin != -1 ) + if ( !TextUtils.isEmpty ( styledText ) ) { - plainText = ""; - String trimmedText = styledText; - while ( begin != -1 ) + int begin = nextUnescaped ( styledText, "{", 0 ); + if ( begin != -1 ) { - final int end = nextUnescaped ( trimmedText, "}", begin + 1 ); - if ( end != -1 ) + plainText = ""; + String trimmedText = styledText; + while ( begin != -1 ) { - // Clipping statement - final String statement = trimmedText.substring ( begin + 1, end ); - if ( statement.equals ( "br" ) ) + final int end = nextUnescaped ( trimmedText, "}", begin + 1 ); + if ( end != -1 ) { - // Adding linebreak and proceeding - plainText += trimmedText.substring ( 0, begin ) + "\n"; - } - else - { - // Parsing possible style syntax - final TextRange range = parseStatement ( plainText.length () + begin, statement ); - if ( range != null && range.getStyleRange () != null ) + // Clipping statement + final String statement = trimmedText.substring ( begin + 1, end ); + if ( statement.equals ( "br" ) ) { - // Adding text and style range - plainText += trimmedText.substring ( 0, begin ) + range.getText (); - styleRanges.add ( range.getStyleRange () ); + // Adding linebreak and proceeding + plainText += trimmedText.substring ( 0, begin ) + "\n"; } else { - // Adding plain text - plainText += trimmedText.substring ( 0, begin ) + statement; + // Parsing possible style syntax + final TextRange range = parseStatement ( plainText.length () + begin, statement ); + if ( range != null && range.getStyleRange () != null ) + { + // Adding text and style range + plainText += trimmedText.substring ( 0, begin ) + range.getText (); + styleRanges.add ( range.getStyleRange () ); + } + else + { + // Adding plain text + plainText += trimmedText.substring ( 0, begin ) + statement; + } } - } - // Continue to next - trimmedText = trimmedText.substring ( end + 1 ); - begin = nextUnescaped ( trimmedText, "{", 0 ); - } - else - { - // Something wrong with the syntax - // Abort parsing and add the rest as plain text - break; + // Continue to next + trimmedText = trimmedText.substring ( end + 1 ); + begin = nextUnescaped ( trimmedText, "{", 0 ); + } + else + { + // Something wrong with the syntax + // Abort parsing and add the rest as plain text + break; + } } + plainText += trimmedText; + } + else + { + plainText = styledText; } - plainText += trimmedText; } else { - plainText = styledText; + plainText = null; } } return this; diff --git a/modules/ui/src/com/alee/extended/label/StyledLabelTextContent.java b/modules/ui/src/com/alee/extended/label/StyledLabelTextContent.java index 2c36d899d..a438a1764 100644 --- a/modules/ui/src/com/alee/extended/label/StyledLabelTextContent.java +++ b/modules/ui/src/com/alee/extended/label/StyledLabelTextContent.java @@ -21,8 +21,6 @@ import com.alee.painter.decoration.content.ContentPropertyListener; import com.thoughtworks.xstream.annotations.XStreamAlias; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.View; import java.util.List; /** @@ -92,18 +90,6 @@ protected int getMaximumRows ( final E c, final D d ) return c.getMaximumRows (); } - @Override - protected boolean isHtmlText ( final E c, final D d ) - { - return c.getClientProperty ( BasicHTML.propertyKey ) != null; - } - - @Override - protected View getHtml ( final E c, final D d ) - { - return ( View ) c.getClientProperty ( BasicHTML.propertyKey ); - } - @Override protected String getText ( final E c, final D d ) { diff --git a/modules/ui/src/com/alee/laf/button/ButtonTextContent.java b/modules/ui/src/com/alee/laf/button/ButtonTextContent.java index 44b739879..5296f6844 100644 --- a/modules/ui/src/com/alee/laf/button/ButtonTextContent.java +++ b/modules/ui/src/com/alee/laf/button/ButtonTextContent.java @@ -22,8 +22,6 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import javax.swing.*; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.View; /** * Button text content implementation. @@ -38,18 +36,6 @@ public class ButtonTextContent, I extends ButtonTextContent> extends AbstractTextContent { - @Override - protected boolean isHtmlText ( final E c, final D d ) - { - return c.getClientProperty ( BasicHTML.propertyKey ) != null; - } - - @Override - protected View getHtml ( final E c, final D d ) - { - return ( View ) c.getClientProperty ( BasicHTML.propertyKey ); - } - @Override protected String getText ( final E c, final D d ) { diff --git a/modules/ui/src/com/alee/laf/button/StyledButtonTextContent.java b/modules/ui/src/com/alee/laf/button/StyledButtonTextContent.java index 5909533b5..e0c81ff13 100644 --- a/modules/ui/src/com/alee/laf/button/StyledButtonTextContent.java +++ b/modules/ui/src/com/alee/laf/button/StyledButtonTextContent.java @@ -17,15 +17,12 @@ package com.alee.laf.button; +import com.alee.extended.label.SimpleStyledTextContent; import com.alee.laf.WebLookAndFeel; import com.alee.painter.decoration.IDecoration; -import com.alee.painter.decoration.content.ContentPropertyListener; -import com.alee.extended.label.SimpleStyledTextContent; import com.thoughtworks.xstream.annotations.XStreamAlias; import javax.swing.*; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.View; /** * Abstract button styled text content implementation. @@ -40,50 +37,10 @@ public class StyledButtonTextContent, I extends StyledButtonTextContent> extends SimpleStyledTextContent { - /** - * Component property change listener. - */ - protected transient ContentPropertyListener listener; - - @Override - public void activate ( final E c, final D d ) - { - // Performing default actions - super.activate ( c, d ); - - // Adding text change listener - listener = new ContentPropertyListener ( c, d ) - { - @Override - public void propertyChange ( final E c, final D d, final String property, final Object oldValue, final Object newValue ) - { - updateContentCache ( c, d ); - } - }; - c.addPropertyChangeListener ( WebLookAndFeel.TEXT_PROPERTY, listener ); - } - - @Override - public void deactivate ( final E c, final D d ) - { - // Removing text change listener - c.removePropertyChangeListener ( WebLookAndFeel.TEXT_PROPERTY, listener ); - listener = null; - - // Performing default actions - super.deactivate ( c, d ); - } - - @Override - protected boolean isHtmlText ( final E c, final D d ) - { - return c.getClientProperty ( BasicHTML.propertyKey ) != null; - } - @Override - protected View getHtml ( final E c, final D d ) + protected String getStyledTextProperty () { - return ( View ) c.getClientProperty ( BasicHTML.propertyKey ); + return WebLookAndFeel.TEXT_PROPERTY; } @Override diff --git a/modules/ui/src/com/alee/laf/label/LabelTextContent.java b/modules/ui/src/com/alee/laf/label/LabelTextContent.java index 145e6115b..1e0999907 100644 --- a/modules/ui/src/com/alee/laf/label/LabelTextContent.java +++ b/modules/ui/src/com/alee/laf/label/LabelTextContent.java @@ -22,8 +22,6 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import javax.swing.*; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.View; /** * Label text content implementation. @@ -38,18 +36,6 @@ public class LabelTextContent, I extends LabelTextContent> extends AbstractTextContent { - @Override - protected boolean isHtmlText ( final E c, final D d ) - { - return c.getClientProperty ( BasicHTML.propertyKey ) != null; - } - - @Override - protected View getHtml ( final E c, final D d ) - { - return ( View ) c.getClientProperty ( BasicHTML.propertyKey ); - } - @Override protected String getText ( final E c, final D d ) { diff --git a/modules/ui/src/com/alee/laf/menu/AcceleratorTextContent.java b/modules/ui/src/com/alee/laf/menu/AcceleratorTextContent.java index d640cfe41..0ac26edfe 100644 --- a/modules/ui/src/com/alee/laf/menu/AcceleratorTextContent.java +++ b/modules/ui/src/com/alee/laf/menu/AcceleratorTextContent.java @@ -23,7 +23,6 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import javax.swing.*; -import javax.swing.text.View; /** * Menu item accelerator text content implementation. @@ -44,18 +43,6 @@ public String getId () return id != null ? id : "accelerator"; } - @Override - protected boolean isHtmlText ( final E c, final D d ) - { - return false; - } - - @Override - protected View getHtml ( final E c, final D d ) - { - return null; - } - @Override protected String getText ( final E c, final D d ) { diff --git a/modules/ui/src/com/alee/laf/progressbar/ProgressBarText.java b/modules/ui/src/com/alee/laf/progressbar/ProgressBarText.java index fab9304ed..8d4d9b938 100644 --- a/modules/ui/src/com/alee/laf/progressbar/ProgressBarText.java +++ b/modules/ui/src/com/alee/laf/progressbar/ProgressBarText.java @@ -22,7 +22,6 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import javax.swing.*; -import javax.swing.text.View; /** * Progress bar text content implementation. @@ -43,18 +42,6 @@ public boolean isEmpty ( final E c, final D d ) return !c.isStringPainted () || super.isEmpty ( c, d ); } - @Override - protected boolean isHtmlText ( final E c, final D d ) - { - return false; - } - - @Override - protected View getHtml ( final E c, final D d ) - { - return null; - } - @Override protected String getText ( final E c, final D d ) { diff --git a/modules/ui/src/com/alee/laf/separator/SeparatorContent.java b/modules/ui/src/com/alee/laf/separator/SeparatorContent.java index ab558732c..a44fa3046 100644 --- a/modules/ui/src/com/alee/laf/separator/SeparatorContent.java +++ b/modules/ui/src/com/alee/laf/separator/SeparatorContent.java @@ -51,7 +51,7 @@ public boolean isEmpty ( final E c, final D d ) @Override protected void paintContent ( final Graphics2D g2d, final Rectangle bounds, final E c, final D d ) { - lines.paint ( g2d, bounds, c.getOrientation (), c.getComponentOrientation ().isLeftToRight () ); + lines.paint ( g2d, bounds, c.getOrientation (), isLeftToRight ( c, d ) ); } @Override diff --git a/modules/ui/src/com/alee/laf/tooltip/StyledToolTipTextContent.java b/modules/ui/src/com/alee/laf/tooltip/StyledToolTipTextContent.java index 749365d72..f74849358 100644 --- a/modules/ui/src/com/alee/laf/tooltip/StyledToolTipTextContent.java +++ b/modules/ui/src/com/alee/laf/tooltip/StyledToolTipTextContent.java @@ -17,15 +17,12 @@ package com.alee.laf.tooltip; +import com.alee.extended.label.SimpleStyledTextContent; import com.alee.laf.WebLookAndFeel; import com.alee.painter.decoration.IDecoration; -import com.alee.painter.decoration.content.ContentPropertyListener; -import com.alee.extended.label.SimpleStyledTextContent; import com.thoughtworks.xstream.annotations.XStreamAlias; import javax.swing.*; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.View; /** * Abstract tooltip styled text content implementation. @@ -36,54 +33,14 @@ * @author Mikle Garin */ -@XStreamAlias ( "StyledToolTipText" ) +@XStreamAlias ("StyledToolTipText") public class StyledToolTipTextContent, I extends StyledToolTipTextContent> extends SimpleStyledTextContent { - /** - * Component property change listener. - */ - protected transient ContentPropertyListener listener; - - @Override - public void activate ( final E c, final D d ) - { - // Performing default actions - super.activate ( c, d ); - - // Adding text change listener - listener = new ContentPropertyListener ( c, d ) - { - @Override - public void propertyChange ( final E c, final D d, final String property, final Object oldValue, final Object newValue ) - { - updateContentCache ( c, d ); - } - }; - c.addPropertyChangeListener ( WebLookAndFeel.TIP_TEXT_PROPERTY, listener ); - } - - @Override - public void deactivate ( final E c, final D d ) - { - // Removing text change listener - c.removePropertyChangeListener ( WebLookAndFeel.TIP_TEXT_PROPERTY, listener ); - listener = null; - - // Performing default actions - super.deactivate ( c, d ); - } - - @Override - protected boolean isHtmlText ( final E c, final D d ) - { - return c.getClientProperty ( BasicHTML.propertyKey ) != null; - } - @Override - protected View getHtml ( final E c, final D d ) + protected String getStyledTextProperty () { - return ( View ) c.getClientProperty ( BasicHTML.propertyKey ); + return WebLookAndFeel.TIP_TEXT_PROPERTY; } @Override diff --git a/modules/ui/src/com/alee/laf/tooltip/ToolTipTextContent.java b/modules/ui/src/com/alee/laf/tooltip/ToolTipTextContent.java index 317371900..339006572 100644 --- a/modules/ui/src/com/alee/laf/tooltip/ToolTipTextContent.java +++ b/modules/ui/src/com/alee/laf/tooltip/ToolTipTextContent.java @@ -22,8 +22,6 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import javax.swing.*; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.text.View; /** * Tooltip text content implementation. @@ -38,18 +36,6 @@ public class ToolTipTextContent, I extends ToolTipTextContent> extends AbstractTextContent { - @Override - protected boolean isHtmlText ( final E c, final D d ) - { - return c.getClientProperty ( BasicHTML.propertyKey ) != null; - } - - @Override - protected View getHtml ( final E c, final D d ) - { - return ( View ) c.getClientProperty ( BasicHTML.propertyKey ); - } - @Override protected String getText ( final E c, final D d ) { diff --git a/modules/ui/src/com/alee/laf/tooltip/WToolTipUI.java b/modules/ui/src/com/alee/laf/tooltip/WToolTipUI.java new file mode 100644 index 000000000..f50fb1cdf --- /dev/null +++ b/modules/ui/src/com/alee/laf/tooltip/WToolTipUI.java @@ -0,0 +1,30 @@ +/* + * This file is part of WebLookAndFeel library. + * + * WebLookAndFeel library 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 3 of the License, or + * (at your option) any later version. + * + * WebLookAndFeel library 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 WebLookAndFeel library. If not, see . + */ + +package com.alee.laf.tooltip; + +import javax.swing.plaf.ToolTipUI; + +/** + * Pluggable look and feel interface for {@link com.alee.laf.tooltip.WebToolTip} component. + * + * @author Mikle Garin + */ + +public abstract class WToolTipUI extends ToolTipUI +{ +} \ No newline at end of file diff --git a/modules/ui/src/com/alee/laf/tooltip/WebToolTip.java b/modules/ui/src/com/alee/laf/tooltip/WebToolTip.java index 20be1d427..f61c3d208 100644 --- a/modules/ui/src/com/alee/laf/tooltip/WebToolTip.java +++ b/modules/ui/src/com/alee/laf/tooltip/WebToolTip.java @@ -213,10 +213,31 @@ public void setPadding ( final Insets padding ) PaddingMethodsImpl.setPadding ( this, padding ); } + /** + * Returns the look and feel (L&F) object that renders this component. + * + * @return the {@link WToolTipUI} object that renders this component + */ + @Override + public WToolTipUI getUI () + { + return ( WToolTipUI ) super.getUI (); + } + + /** + * Sets the L&F object that renders this component. + * + * @param ui {@link WToolTipUI} + */ + public void setUI ( final WToolTipUI ui ) + { + super.setUI ( ui ); + } + @Override public void updateUI () { - if ( getUI () == null || !( getUI () instanceof WebToolTipUI ) ) + if ( getUI () == null || !( getUI () instanceof WToolTipUI ) ) { try { diff --git a/modules/ui/src/com/alee/laf/tooltip/WebToolTipUI.java b/modules/ui/src/com/alee/laf/tooltip/WebToolTipUI.java index 7fe9c2052..c9109073d 100644 --- a/modules/ui/src/com/alee/laf/tooltip/WebToolTipUI.java +++ b/modules/ui/src/com/alee/laf/tooltip/WebToolTipUI.java @@ -17,20 +17,16 @@ package com.alee.laf.tooltip; -import com.alee.laf.WebLookAndFeel; import com.alee.managers.style.*; import com.alee.painter.DefaultPainter; import com.alee.painter.Painter; import com.alee.painter.PainterSupport; +import com.alee.utils.SwingUtils; import com.alee.utils.swing.DataRunnable; import javax.swing.*; import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.basic.BasicHTML; -import javax.swing.plaf.basic.BasicToolTipUI; import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; /** * Custom UI for {@link JToolTip} component. @@ -38,19 +34,14 @@ * @author Mikle Garin */ -public class WebToolTipUI extends BasicToolTipUI implements ShapeSupport, MarginSupport, PaddingSupport +public class WebToolTipUI extends WToolTipUI implements ShapeSupport, MarginSupport, PaddingSupport { /** * Component painter. */ - @DefaultPainter (ToolTipPainter.class) + @DefaultPainter ( ToolTipPainter.class ) protected IToolTipPainter painter; - /** - * Base listeners. - */ - protected PropertyChangeListener propertyChangeListener; - /** * Runtime variables. */ @@ -79,38 +70,16 @@ public static ComponentUI createUI ( final JComponent c ) @Override public void installUI ( final JComponent c ) { - super.installUI ( c ); - // Saving tooltip to local variable tooltip = c; + // Installing default component settings + installDefaults ( c ); + // Applying skin StyleManager.installSkin ( tooltip ); } - @Override - protected void installListeners ( final JComponent c ) - { - propertyChangeListener = new PropertyChangeListener () - { - @Override - public void propertyChange ( final PropertyChangeEvent e ) - { - final String name = e.getPropertyName (); - if ( name.equals ( WebLookAndFeel.TIP_TEXT_PROPERTY ) || name.equals ( WebLookAndFeel.FONT_PROPERTY ) || - name.equals ( WebLookAndFeel.FOREGROUND_PROPERTY ) ) - { - // Remove the old html view client property if one existed - // Install a new one if the text installed into the JLabel is html source - final JToolTip tip = ( JToolTip ) e.getSource (); - final String text = tip.getTipText (); - BasicHTML.updateRenderer ( tip, text ); - } - } - }; - c.addPropertyChangeListener ( propertyChangeListener ); - } - /** * Uninstalls UI from the specified component. * @@ -122,17 +91,34 @@ public void uninstallUI ( final JComponent c ) // Uninstalling applied skin StyleManager.uninstallSkin ( tooltip ); + // Uninstalling default component settings + uninstallDefaults ( c ); + // Cleaning up reference this.tooltip = null; + } - // Uninstalling UI - super.uninstallUI ( c ); + /** + * Installs default component settings. + * + * @param c component for this UI + */ + protected void installDefaults ( final JComponent c ) + { + if ( SwingUtils.isUIResource ( c.getFont () ) ) + { + c.setFont ( UIManager.getFont ( "ToolTip.font" ) ); + } } - @Override - protected void uninstallListeners ( final JComponent c ) + /** + * Uninstalls default component settings. + * + * @param c component for this UI + */ + protected void uninstallDefaults ( final JComponent c ) { - c.removePropertyChangeListener ( propertyChangeListener ); + LookAndFeel.uninstallBorder ( c ); } @Override diff --git a/modules/ui/src/com/alee/managers/style/Skin.java b/modules/ui/src/com/alee/managers/style/Skin.java index 6a3065cc3..973ac4f4b 100644 --- a/modules/ui/src/com/alee/managers/style/Skin.java +++ b/modules/ui/src/com/alee/managers/style/Skin.java @@ -34,6 +34,10 @@ * Then you can use {@link StyleManager#setDefaultSkin(Class)} or {@link StyleManager#setDefaultSkin(String)} methods to provide it. * You can also initialize WebLaF directly using your skin through one of {@link com.alee.laf.WebLookAndFeel} static install methods. * + * Existing full skin implementations are: + * {@link com.alee.skin.web.WebSkin} + * {@link com.alee.skin.dark.DarkSkin} + * * @author Mikle Garin * @see How to use StyleManager * @see com.alee.managers.style.StyleManager diff --git a/modules/ui/src/com/alee/painter/decoration/AbstractDecorationPainter.java b/modules/ui/src/com/alee/painter/decoration/AbstractDecorationPainter.java index 675f97313..1c58bdba2 100644 --- a/modules/ui/src/com/alee/painter/decoration/AbstractDecorationPainter.java +++ b/modules/ui/src/com/alee/painter/decoration/AbstractDecorationPainter.java @@ -767,6 +767,7 @@ else if ( decorations.size () == 1 ) // Filling decoration states with all current states // This is required so that decoration correctly stores all states, not just ones it uses + // todo Probably this is pointless now as it may store wrong states, not exactly ones it is displayed for decoration.updateStates ( CollectionUtils.copy ( states ) ); } diff --git a/modules/ui/src/com/alee/painter/decoration/content/AbstractContent.java b/modules/ui/src/com/alee/painter/decoration/content/AbstractContent.java index 7adc9a351..25770eec3 100644 --- a/modules/ui/src/com/alee/painter/decoration/content/AbstractContent.java +++ b/modules/ui/src/com/alee/painter/decoration/content/AbstractContent.java @@ -39,6 +39,7 @@ * @author Alexandr Zernov */ +@SuppressWarnings ( "UnusedParameters" ) public abstract class AbstractContent, I extends AbstractContent> implements IContent { @@ -134,7 +135,7 @@ protected Insets getPadding ( final E c, final D d ) { if ( padding != null ) { - final boolean ltr = c.getComponentOrientation ().isLeftToRight (); + final boolean ltr = isLeftToRight ( c, d ); return new Insets ( padding.top, ltr ? padding.left : padding.right, padding.bottom, ltr ? padding.right : padding.left ); } else @@ -181,7 +182,7 @@ public void paint ( final Graphics2D g2d, final Rectangle bounds, final E c, fin if ( bounds.width > 0 && bounds.height > 0 && !isEmpty ( c, d ) ) { // Proper content clipping -// final Shape os = GraphicsUtils.intersectClip ( g2d, bounds ); + final Shape os = GraphicsUtils.intersectClip ( g2d, bounds ); // Content opacity final float opacity = getOpacity ( c, d ); @@ -232,7 +233,7 @@ public void paint ( final Graphics2D g2d, final Rectangle bounds, final E c, fin GraphicsUtils.restoreComposite ( g2d, oc ); // Restoring clip area -// GraphicsUtils.restoreClip ( g2d, os ); + GraphicsUtils.restoreClip ( g2d, os ); } } @@ -285,7 +286,19 @@ public Dimension getPreferredSize ( final E c, final D d, final Dimension availa protected Rotation getActualRotation ( final E c, final D d ) { final Rotation tr = getRotation ( c, d ); - return c.getComponentOrientation ().isLeftToRight () ? tr : tr.rightToLeft (); + return isLeftToRight ( c, d ) ? tr : tr.rightToLeft (); + } + + /** + * Returns whether or not component has LTR orientation. + * + * @param c painted component + * @param d painted decoration state + * @return {@code true} if component has LTR orientation, {@code false} if it has RTL orientation + */ + protected boolean isLeftToRight ( final E c, final D d ) + { + return c.getComponentOrientation ().isLeftToRight (); } @Override diff --git a/modules/ui/src/com/alee/painter/decoration/content/AbstractTextContent.java b/modules/ui/src/com/alee/painter/decoration/content/AbstractTextContent.java index fcd5ef5b0..ba1851f2d 100644 --- a/modules/ui/src/com/alee/painter/decoration/content/AbstractTextContent.java +++ b/modules/ui/src/com/alee/painter/decoration/content/AbstractTextContent.java @@ -18,15 +18,13 @@ package com.alee.painter.decoration.content; import com.alee.managers.style.StyleException; +import com.alee.painter.decoration.DecorationException; import com.alee.painter.decoration.IDecoration; -import com.alee.utils.ColorUtils; -import com.alee.utils.GraphicsUtils; -import com.alee.utils.SwingUtils; -import com.alee.utils.TextUtils; +import com.alee.utils.*; +import com.alee.utils.swing.BasicHTML; import com.thoughtworks.xstream.annotations.XStreamAsAttribute; import javax.swing.*; -import javax.swing.plaf.UIResource; import javax.swing.text.View; import java.awt.*; import java.util.Map; @@ -41,7 +39,7 @@ * @author Alexandr Zernov */ -@SuppressWarnings ("UnusedParameters") +@SuppressWarnings ( "UnusedParameters" ) public abstract class AbstractTextContent, I extends AbstractTextContent> extends AbstractContent implements SwingConstants { @@ -93,6 +91,26 @@ public abstract class AbstractTextContent= 0 && mnemonicIndex < text.length () ) { - final FontMetrics fm = g2d.getFontMetrics (); + final FontMetrics fm = getFontMetrics ( c, d ); g2d.fillRect ( textX + fm.stringWidth ( text.substring ( 0, mnemonicIndex ) ), textY + fm.getDescent () - 1, fm.charWidth ( text.charAt ( mnemonicIndex ) ), 1 ); } @@ -538,7 +674,7 @@ protected Dimension getContentPreferredSize ( final E c, final D d, final Dimens protected Dimension getPreferredTextSize ( final E c, final D d, final Dimension available ) { final String text = getText ( c, d ); - final FontMetrics fm = c.getFontMetrics ( c.getFont () ); + final FontMetrics fm = getFontMetrics ( c, d ); return new Dimension ( SwingUtils.stringWidth ( fm, text ), fm.getHeight () ); } diff --git a/modules/ui/src/com/alee/skin/dark/resources/button.xml b/modules/ui/src/com/alee/skin/dark/resources/button.xml index 9aea2cd24..19ffb6b50 100644 --- a/modules/ui/src/com/alee/skin/dark/resources/button.xml +++ b/modules/ui/src/com/alee/skin/dark/resources/button.xml @@ -44,7 +44,6 @@ - diff --git a/modules/ui/src/com/alee/skin/dark/resources/checkbox.xml b/modules/ui/src/com/alee/skin/dark/resources/checkbox.xml index 6716c71d1..331b58da4 100644 --- a/modules/ui/src/com/alee/skin/dark/resources/checkbox.xml +++ b/modules/ui/src/com/alee/skin/dark/resources/checkbox.xml @@ -60,7 +60,6 @@ - diff --git a/modules/ui/src/com/alee/skin/dark/resources/radiobutton.xml b/modules/ui/src/com/alee/skin/dark/resources/radiobutton.xml index c8d88c803..5e5a79c86 100644 --- a/modules/ui/src/com/alee/skin/dark/resources/radiobutton.xml +++ b/modules/ui/src/com/alee/skin/dark/resources/radiobutton.xml @@ -60,7 +60,6 @@ - diff --git a/modules/ui/src/com/alee/skin/dark/resources/splitbutton.xml b/modules/ui/src/com/alee/skin/dark/resources/splitbutton.xml index 3e4554406..4b81cb24a 100644 --- a/modules/ui/src/com/alee/skin/dark/resources/splitbutton.xml +++ b/modules/ui/src/com/alee/skin/dark/resources/splitbutton.xml @@ -48,7 +48,6 @@ - diff --git a/modules/ui/src/com/alee/skin/dark/resources/togglebutton.xml b/modules/ui/src/com/alee/skin/dark/resources/togglebutton.xml index 7040370f0..6a70a04ed 100644 --- a/modules/ui/src/com/alee/skin/dark/resources/togglebutton.xml +++ b/modules/ui/src/com/alee/skin/dark/resources/togglebutton.xml @@ -49,7 +49,6 @@ - diff --git a/modules/ui/src/com/alee/skin/dark/resources/tooltip.xml b/modules/ui/src/com/alee/skin/dark/resources/tooltip.xml index 29c28155d..1a6faf796 100644 --- a/modules/ui/src/com/alee/skin/dark/resources/tooltip.xml +++ b/modules/ui/src/com/alee/skin/dark/resources/tooltip.xml @@ -9,8 +9,8 @@ - - + + @@ -21,7 +21,7 @@ - + diff --git a/modules/ui/src/com/alee/skin/dark/resources/tristatecheckbox.xml b/modules/ui/src/com/alee/skin/dark/resources/tristatecheckbox.xml index ae30bda39..88213840d 100644 --- a/modules/ui/src/com/alee/skin/dark/resources/tristatecheckbox.xml +++ b/modules/ui/src/com/alee/skin/dark/resources/tristatecheckbox.xml @@ -66,7 +66,6 @@ - diff --git a/modules/ui/src/com/alee/skin/web/resources/button.xml b/modules/ui/src/com/alee/skin/web/resources/button.xml index d33d69a4c..0d11f98b6 100644 --- a/modules/ui/src/com/alee/skin/web/resources/button.xml +++ b/modules/ui/src/com/alee/skin/web/resources/button.xml @@ -44,7 +44,6 @@ - diff --git a/modules/ui/src/com/alee/skin/web/resources/checkbox.xml b/modules/ui/src/com/alee/skin/web/resources/checkbox.xml index 08bc3d76c..a3662f450 100644 --- a/modules/ui/src/com/alee/skin/web/resources/checkbox.xml +++ b/modules/ui/src/com/alee/skin/web/resources/checkbox.xml @@ -56,7 +56,6 @@ - diff --git a/modules/ui/src/com/alee/skin/web/resources/radiobutton.xml b/modules/ui/src/com/alee/skin/web/resources/radiobutton.xml index fd026e734..1ce99eefe 100644 --- a/modules/ui/src/com/alee/skin/web/resources/radiobutton.xml +++ b/modules/ui/src/com/alee/skin/web/resources/radiobutton.xml @@ -56,7 +56,6 @@ - diff --git a/modules/ui/src/com/alee/skin/web/resources/splitbutton.xml b/modules/ui/src/com/alee/skin/web/resources/splitbutton.xml index d0cddaf4f..70a0438e2 100644 --- a/modules/ui/src/com/alee/skin/web/resources/splitbutton.xml +++ b/modules/ui/src/com/alee/skin/web/resources/splitbutton.xml @@ -48,7 +48,6 @@ - diff --git a/modules/ui/src/com/alee/skin/web/resources/togglebutton.xml b/modules/ui/src/com/alee/skin/web/resources/togglebutton.xml index 67906fefc..2b975d623 100644 --- a/modules/ui/src/com/alee/skin/web/resources/togglebutton.xml +++ b/modules/ui/src/com/alee/skin/web/resources/togglebutton.xml @@ -49,7 +49,6 @@ - diff --git a/modules/ui/src/com/alee/skin/web/resources/tristatecheckbox.xml b/modules/ui/src/com/alee/skin/web/resources/tristatecheckbox.xml index 16bb1d98d..123337be0 100644 --- a/modules/ui/src/com/alee/skin/web/resources/tristatecheckbox.xml +++ b/modules/ui/src/com/alee/skin/web/resources/tristatecheckbox.xml @@ -62,7 +62,6 @@ - diff --git a/modules/ui/src/com/alee/utils/SwingUtils.java b/modules/ui/src/com/alee/utils/SwingUtils.java index 6df2ae445..ca6914959 100644 --- a/modules/ui/src/com/alee/utils/SwingUtils.java +++ b/modules/ui/src/com/alee/utils/SwingUtils.java @@ -32,7 +32,6 @@ import javax.swing.*; import javax.swing.FocusManager; -import javax.swing.border.Border; import javax.swing.event.AncestorListener; import javax.swing.plaf.UIResource; import javax.swing.table.DefaultTableColumnModel; @@ -189,14 +188,14 @@ public static boolean isPreserveBorders ( final JComponent component ) } /** - * Returns whether or not specified border is a UI resource. + * Returns whether or not specified value is a UI resource. * - * @param border border to process - * @return true if specified border is a UI resource, false otherwise + * @param value value {@link Object} to process + * @return {@code true} if specified value is a UI resource, {@code false} otherwise */ - public static boolean isUIResource ( final Border border ) + public static boolean isUIResource ( final Object value ) { - return border == null || border instanceof UIResource; + return value == null || value instanceof UIResource; } /** diff --git a/modules/ui/src/com/alee/utils/swing/BasicHTML.java b/modules/ui/src/com/alee/utils/swing/BasicHTML.java new file mode 100644 index 000000000..77b517ee7 --- /dev/null +++ b/modules/ui/src/com/alee/utils/swing/BasicHTML.java @@ -0,0 +1,706 @@ +/* + * This file is part of WebLookAndFeel library. + * + * WebLookAndFeel library 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 3 of the License, or + * (at your option) any later version. + * + * WebLookAndFeel library 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 WebLookAndFeel library. If not, see . + */ + +package com.alee.utils.swing; + +import javax.swing.*; +import javax.swing.text.*; +import javax.swing.text.html.*; +import java.awt.*; +import java.io.Reader; +import java.io.StringReader; +import java.net.URL; + +/** + * Support for providing HTML views for the swing components. This translates a simple HTML string to a {@link javax.swing.text.View} + * implementation that can render the HTML and provide the necessary layout semantics. + * + * This class is a copy-paste of {@link javax.swing.plaf.basic.BasicHTML} which has some unwanted settings like foreground hardcoded so + * this class acts as a replacement for all WebLaF components that could contain HTML in their text. + * + * @author Timothy Prinzing + * @version %I% %G% + * @since 1.3 + */ + +public final class BasicHTML +{ + /** + * If this client property of a {@link JComponent} is set to {@link Boolean#TRUE} its content is never treated as HTML. + * This is a copy of {@link javax.swing.plaf.basic.BasicHTML#htmlDisable} field which is private. + */ + private static final String htmlDisable = "html.disable"; + + /** + * Check the given string to see if it should trigger the HTML rendering logic in a non-text component that supports HTML rendering. + * + * @param c component that will be displaying HTML content + * @param text text possibly containing HTML + * @return {@code true} if provided string should trigger the HTML rendering logic, {@code false} otherwise + */ + public static boolean isHTMLString ( final JComponent c, final String text ) + { + final Boolean disabled = ( Boolean ) c.getClientProperty ( htmlDisable ); + if ( disabled != Boolean.TRUE ) + { + if ( text != null && ( text.length () >= 6 ) && ( text.charAt ( 0 ) == '<' ) && ( text.charAt ( 5 ) == '>' ) ) + { + final String tag = text.substring ( 1, 5 ); + return tag.equalsIgnoreCase ( javax.swing.plaf.basic.BasicHTML.propertyKey ); + } + } + return false; + } + + /** + * Returns HTML renderer for the given component and string of HTML. + * + * @param c component to create HTML View for + * @param html HTML content + * @param defaultFont text font + * @param foreground text foreground color + * @return HTML renderer for the given component and string of HTML + */ + public static View createHTMLView ( final JComponent c, final String html, final Font defaultFont, final Color foreground ) + { + final BasicEditorKit kit = getFactory (); + final Document doc = kit.createDefaultDocument ( defaultFont, foreground ); + final Object base = c.getClientProperty ( javax.swing.plaf.basic.BasicHTML.documentBaseKey ); + if ( base instanceof URL ) + { + ( ( HTMLDocument ) doc ).setBase ( ( URL ) base ); + } + final Reader r = new StringReader ( html ); + try + { + kit.read ( r, doc, 0 ); + } + catch ( final Throwable e ) + { + // Ignored + } + final ViewFactory f = kit.getViewFactory (); + final View hview = f.create ( doc.getDefaultRootElement () ); + return new Renderer ( f, hview ); + } + + /** + * Returns the baseline for the HTML renderer. + * + * @param view the View to get the baseline for + * @param w the width to get the baseline for + * @param h the height to get the baseline for + * @return baseline or a value < 0 indicating there is no reasonable baseline + * @throws IllegalArgumentException if width or height is < 0 + * @see java.awt.FontMetrics + * @see javax.swing.JComponent#getBaseline(int, int) + * @since 1.6 + */ + public static int getHTMLBaseline ( final View view, final int w, final int h ) + { + if ( w < 0 || h < 0 ) + { + throw new IllegalArgumentException ( "Width and height must be >= 0" ); + } + if ( view instanceof Renderer ) + { + return getBaseline ( view.getView ( 0 ), w, h ); + } + return -1; + } + + /** + * Gets the baseline for the specified component. + * This digs out the View client property, and if non-null the baseline is calculated from it. + * Otherwise the baseline is the value {@code y + ascent}. + */ + private static int getBaseline ( final JComponent c, final int y, final int ascent, + final int w, final int h ) + { + final View view = ( View ) c.getClientProperty ( javax.swing.plaf.basic.BasicHTML.propertyKey ); + if ( view != null ) + { + final int baseline = getHTMLBaseline ( view, w, h ); + if ( baseline < 0 ) + { + return baseline; + } + return y + baseline; + } + return y + ascent; + } + + /** + * Gets the baseline for the specified View. + */ + private static int getBaseline ( final View view, final int w, final int h ) + { + if ( hasParagraph ( view ) ) + { + view.setSize ( w, h ); + return getBaseline ( view, new Rectangle ( 0, 0, w, h ) ); + } + return -1; + } + + private static int getBaseline ( final View view, Shape bounds ) + { + if ( view.getViewCount () == 0 ) + { + return -1; + } + final AttributeSet attributes = view.getElement ().getAttributes (); + Object name = null; + if ( attributes != null ) + { + name = attributes.getAttribute ( StyleConstants.NameAttribute ); + } + int index = 0; + if ( name == HTML.Tag.HTML && view.getViewCount () > 1 ) + { + // For HTML on widgets the header is not visible, skip it. + index++; + } + bounds = view.getChildAllocation ( index, bounds ); + if ( bounds == null ) + { + return -1; + } + final View child = view.getView ( index ); + if ( view instanceof javax.swing.text.ParagraphView ) + { + final Rectangle rect; + if ( bounds instanceof Rectangle ) + { + rect = ( Rectangle ) bounds; + } + else + { + rect = bounds.getBounds (); + } + return rect.y + ( int ) ( rect.height * + child.getAlignment ( View.Y_AXIS ) ); + } + return getBaseline ( child, bounds ); + } + + private static boolean hasParagraph ( final View view ) + { + if ( view instanceof javax.swing.text.ParagraphView ) + { + return true; + } + if ( view.getViewCount () == 0 ) + { + return false; + } + final AttributeSet attributes = view.getElement ().getAttributes (); + Object name = null; + if ( attributes != null ) + { + name = attributes.getAttribute ( StyleConstants.NameAttribute ); + } + int index = 0; + if ( name == HTML.Tag.HTML && view.getViewCount () > 1 ) + { + // For HTML on widgets the header is not visible, skip it. + index = 1; + } + return hasParagraph ( view.getView ( index ) ); + } + + private static BasicEditorKit getFactory () + { + if ( basicHTMLFactory == null ) + { + basicHTMLViewFactory = new BasicHTMLViewFactory (); + basicHTMLFactory = new BasicEditorKit (); + } + return basicHTMLFactory; + } + + /** + * The source of the HTML renderers + */ + private static BasicEditorKit basicHTMLFactory; + + /** + * Creates the Views that visually represent the model. + */ + private static ViewFactory basicHTMLViewFactory; + + /** + * Overrides to the default stylesheet. Should consider + * just creating a completely fresh stylesheet. + */ + private static final String styleChanges = + "p { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0 }" + + "body { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0 }"; + + /** + * The views produced for the ComponentUI implementations aren't + * going to be edited and don't need full HTML support. This kit + * alters the HTMLEditorKit to try and trim things down a bit. + * It does the following: + *
    + *
  • It doesn't produce Views for things like comments, + * head, title, unknown tags, etc. + *
  • It installs a different set of css settings from the default + * provided by HTMLEditorKit. + *
+ */ + private static class BasicEditorKit extends HTMLEditorKit + { + /** + * Shared base style for all documents created by us use. + */ + private static StyleSheet defaultStyles; + + /** + * Overriden to return our own slimmed down style sheet. + */ + public StyleSheet getStyleSheet () + { + if ( defaultStyles == null ) + { + defaultStyles = new StyleSheet (); + final StringReader r = new StringReader ( styleChanges ); + try + { + defaultStyles.loadRules ( r, null ); + } + catch ( final Throwable e ) + { + // don't want to die in static initialization... + // just display things wrong. + } + r.close (); + defaultStyles.addStyleSheet ( super.getStyleSheet () ); + } + return defaultStyles; + } + + /** + * Sets the async policy to flush everything in one chunk, and + * to not display unknown tags. + */ + public Document createDefaultDocument ( final Font defaultFont, final Color foreground ) + { + final StyleSheet styles = getStyleSheet (); + final StyleSheet ss = new StyleSheet (); + ss.addStyleSheet ( styles ); + final BasicDocument doc = new BasicDocument ( ss, defaultFont, foreground ); + doc.setAsynchronousLoadPriority ( Integer.MAX_VALUE ); + doc.setPreservesUnknownTags ( false ); + return doc; + } + + /** + * Returns the ViewFactory that is used to make sure the Views don't + * load in the background. + */ + public ViewFactory getViewFactory () + { + return basicHTMLViewFactory; + } + } + + /** + * BasicHTMLViewFactory extends HTMLFactory to force images to be loaded + * synchronously. + */ + private static class BasicHTMLViewFactory extends HTMLEditorKit.HTMLFactory + { + public View create ( final Element elem ) + { + final View view = super.create ( elem ); + + if ( view instanceof ImageView ) + { + ( ( ImageView ) view ).setLoadsSynchronously ( true ); + } + return view; + } + } + + /** + * The subclass of HTMLDocument that is used as the model. getForeground + * is overridden to return the foreground property from the Component this + * was created for. + */ + private static class BasicDocument extends HTMLDocument + { + /** + * The host, that is where we are rendering. + */ + // private JComponent host; + public BasicDocument ( final StyleSheet s, final Font defaultFont, final Color foreground ) + { + super ( s ); + setPreservesUnknownTags ( false ); + setFontAndColor ( defaultFont, foreground ); + } + + /** + * Sets the default font and default color. These are set by + * adding a rule for the body that specifies the font and color. + * This allows the HTML to override these should it wish to have + * a custom font or color. + */ + private void setFontAndColor ( final Font font, final Color fg ) + { + getStyleSheet ().addRule ( displayPropertiesToCSS ( font, fg ) ); + } + + private String displayPropertiesToCSS ( final Font font, final Color fg ) + { + final StringBuffer rule = new StringBuffer ( "body {" ); + if ( font != null ) + { + rule.append ( " font-family: " ); + rule.append ( font.getFamily () ); + rule.append ( " ; " ); + rule.append ( " font-size: " ); + rule.append ( font.getSize () ); + rule.append ( "pt ;" ); + if ( font.isBold () ) + { + rule.append ( " font-weight: 700 ; " ); + } + if ( font.isItalic () ) + { + rule.append ( " font-style: italic ; " ); + } + } + if ( fg != null ) + { + rule.append ( " color: #" ); + if ( fg.getRed () < 16 ) + { + rule.append ( '0' ); + } + rule.append ( Integer.toHexString ( fg.getRed () ) ); + if ( fg.getGreen () < 16 ) + { + rule.append ( '0' ); + } + rule.append ( Integer.toHexString ( fg.getGreen () ) ); + if ( fg.getBlue () < 16 ) + { + rule.append ( '0' ); + } + rule.append ( Integer.toHexString ( fg.getBlue () ) ); + rule.append ( " ; " ); + } + rule.append ( " }" ); + return rule.toString (); + } + } + + /** + * Root text view that acts as an HTML renderer. + */ + private static class Renderer extends View + { + private final View view; + private final ViewFactory factory; + + private int width; + + public Renderer ( final ViewFactory f, final View v ) + { + super ( null ); + factory = f; + view = v; + view.setParent ( this ); + // initially layout to the preferred size + setSize ( view.getPreferredSpan ( X_AXIS ), view.getPreferredSpan ( Y_AXIS ) ); + } + + /** + * Fetches the attributes to use when rendering. At the root + * level there are no attributes. If an attribute is resolved + * up the view hierarchy this is the end of the line. + */ + public AttributeSet getAttributes () + { + return null; + } + + /** + * Determines the preferred span for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the span the view would like to be rendered into. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + */ + public float getPreferredSpan ( final int axis ) + { + if ( axis == X_AXIS ) + { + // width currently laid out to + return width; + } + return view.getPreferredSpan ( axis ); + } + + /** + * Determines the minimum span for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the span the view would like to be rendered into. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + */ + public float getMinimumSpan ( final int axis ) + { + return view.getMinimumSpan ( axis ); + } + + /** + * Determines the maximum span for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the span the view would like to be rendered into. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + */ + public float getMaximumSpan ( final int axis ) + { + return Integer.MAX_VALUE; + } + + /** + * Specifies that a preference has changed. + * Child views can call this on the parent to indicate that + * the preference has changed. The root view routes this to + * invalidate on the hosting component. + *

+ * This can be called on a different thread from the + * event dispatching thread and is basically unsafe to + * propagate into the component. To make this safe, + * the operation is transferred over to the event dispatching + * thread for completion. It is a design goal that all view + * methods be safe to call without concern for concurrency, + * and this behavior helps make that true. + * + * @param child the child view + * @param width true if the width preference has changed + * @param height true if the height preference has changed + */ + public void preferenceChanged ( final View child, final boolean width, final boolean height ) + { + // + } + + /** + * Determines the desired alignment for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @return the desired alignment, where 0.0 indicates the origin + * and 1.0 the full span away from the origin + */ + public float getAlignment ( final int axis ) + { + return view.getAlignment ( axis ); + } + + /** + * Renders the view. + * + * @param g the graphics context + * @param allocation the region to render into + */ + public void paint ( final Graphics g, final Shape allocation ) + { + final Rectangle alloc = allocation.getBounds (); + view.setSize ( alloc.width, alloc.height ); + view.paint ( g, allocation ); + } + + /** + * Sets the view parent. + * + * @param parent the parent view + */ + public void setParent ( final View parent ) + { + throw new Error ( "Can't set parent on root view" ); + } + + /** + * Returns the number of views in this view. Since + * this view simply wraps the root of the view hierarchy + * it has exactly one child. + * + * @return the number of views + * @see #getView + */ + public int getViewCount () + { + return 1; + } + + /** + * Gets the n-th view in this container. + * + * @param n the number of the view to get + * @return the view + */ + public View getView ( final int n ) + { + return view; + } + + /** + * Provides a mapping from the document model coordinate space + * to the coordinate space of the view mapped to it. + * + * @param pos the position to convert + * @param a the allocated region to render into + * @return the bounding box of the given position + */ + public Shape modelToView ( final int pos, final Shape a, final Position.Bias b ) throws BadLocationException + { + return view.modelToView ( pos, a, b ); + } + + /** + * Provides a mapping from the document model coordinate space + * to the coordinate space of the view mapped to it. + * + * @param p0 the position to convert >= 0 + * @param b0 the bias toward the previous character or the + * next character represented by p0, in case the + * position is a boundary of two views. + * @param p1 the position to convert >= 0 + * @param b1 the bias toward the previous character or the + * next character represented by p1, in case the + * position is a boundary of two views. + * @param a the allocated region to render into + * @return the bounding box of the given position is returned + * @throws BadLocationException if the given position does + * not represent a valid location in the associated document + * @throws IllegalArgumentException for an invalid bias argument + * @see View#viewToModel + */ + public Shape modelToView ( final int p0, final Position.Bias b0, final int p1, + final Position.Bias b1, final Shape a ) throws BadLocationException + { + return view.modelToView ( p0, b0, p1, b1, a ); + } + + /** + * Provides a mapping from the view coordinate space to the logical + * coordinate space of the model. + * + * @param x x coordinate of the view location to convert + * @param y y coordinate of the view location to convert + * @param a the allocated region to render into + * @return the location within the model that best represents the + * given point in the view + */ + public int viewToModel ( final float x, final float y, final Shape a, final Position.Bias[] bias ) + { + return view.viewToModel ( x, y, a, bias ); + } + + /** + * Returns the document model underlying the view. + * + * @return the model + */ + public Document getDocument () + { + return view.getDocument (); + } + + /** + * Returns the starting offset into the model for this view. + * + * @return the starting offset + */ + public int getStartOffset () + { + return view.getStartOffset (); + } + + /** + * Returns the ending offset into the model for this view. + * + * @return the ending offset + */ + public int getEndOffset () + { + return view.getEndOffset (); + } + + /** + * Gets the element that this view is mapped to. + * + * @return the view + */ + public Element getElement () + { + return view.getElement (); + } + + /** + * Sets the view size. + * + * @param width the width + * @param height the height + */ + public void setSize ( final float width, final float height ) + { + this.width = ( int ) width; + view.setSize ( width, height ); + } + + /** + * Fetches the container hosting the view. This is useful for + * things like scheduling a repaint, finding out the host + * components font, etc. The default implementation + * of this is to forward the query to the parent view. + * + * @return the container + */ + public Container getContainer () + { + return null; + } + + /** + * Fetches the factory to be used for building the + * various view fragments that make up the view that + * represents the model. This is what determines + * how the model will be represented. This is implemented + * to fetch the factory provided by the associated + * EditorKit. + * + * @return the factory + */ + public ViewFactory getViewFactory () + { + return factory; + } + } +} \ No newline at end of file