From 995a16ee911442342a73cce6a4e1761cc8bd1f81 Mon Sep 17 00:00:00 2001 From: Andrew John Hughes Date: Sun, 3 Dec 2006 17:06:05 +0000 Subject: 2006-12-03 Andrew John Hughes * Merge of HEAD-->generics-branch for 2006/11/29 to 0.93 branch point. --- .externalToolBuilders/ClasspathJar.launch | 4 +- ChangeLog | 236 ++++++++++++++ gnu/java/awt/peer/gtk/BufferedImageGraphics.java | 72 +++-- gnu/java/awt/peer/gtk/CairoGraphics2D.java | 11 +- gnu/javax/swing/text/html/css/BorderStyle.java | 64 ++++ gnu/javax/swing/text/html/css/BorderWidth.java | 12 + gnu/javax/swing/text/html/css/FontSize.java | 8 +- gnu/javax/swing/text/html/css/Length.java | 155 ++++++++- gnu/javax/swing/text/html/css/Selector.java | 17 +- .../swing/text/html/parser/support/Parser.java | 40 ++- include/gnu_java_awt_peer_gtk_GdkFontPeer.h | 16 + .../beancontext/BeanContextServicesSupport.java | 39 ++- java/lang/Thread.java | 8 +- java/text/DecimalFormat.java | 172 ++++++---- javax/swing/text/ElementIterator.java | 203 ++++++++---- javax/swing/text/html/BlockView.java | 17 +- javax/swing/text/html/CSS.java | 173 +++++++++- javax/swing/text/html/CSSBorder.java | 19 +- javax/swing/text/html/FormSubmitEvent.java | 123 +++++++ javax/swing/text/html/FormView.java | 357 ++++++++++++++++++++- javax/swing/text/html/FrameSetView.java | 274 ++++++++++++++++ javax/swing/text/html/FrameView.java | 233 ++++++++++++++ javax/swing/text/html/HTML.java | 10 + javax/swing/text/html/HTMLDocument.java | 301 ++++++++++++++--- javax/swing/text/html/HTMLEditorKit.java | 123 ++++++- javax/swing/text/html/ImageView.java | 56 ++-- javax/swing/text/html/InlineView.java | 2 + javax/swing/text/html/ParagraphView.java | 7 + javax/swing/text/html/StyleSheet.java | 178 +++++++--- javax/swing/text/html/TableView.java | 43 ++- 30 files changed, 2631 insertions(+), 342 deletions(-) create mode 100644 gnu/javax/swing/text/html/css/BorderStyle.java create mode 100644 javax/swing/text/html/FormSubmitEvent.java create mode 100644 javax/swing/text/html/FrameSetView.java create mode 100644 javax/swing/text/html/FrameView.java diff --git a/.externalToolBuilders/ClasspathJar.launch b/.externalToolBuilders/ClasspathJar.launch index e58a58b3a..269e62c0e 100644 --- a/.externalToolBuilders/ClasspathJar.launch +++ b/.externalToolBuilders/ClasspathJar.launch @@ -1,8 +1,8 @@ - - + + diff --git a/ChangeLog b/ChangeLog index 9d587fd59..5acb9eae6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,239 @@ +2006-12-01 Mark Wielaard + + * java/text/DecimalFormat.java (parse): Always increment parsing + index and adjust pos result. + +2006-12-01 Roman Kennke + + * javax/swing/text/html/HTML.java + (Attribute.DYNAMIC_CLASS): New field. + (Attribute.PSEUDO_CLASS): New field. + * javax/swing/text/html/HTMLDocument.java + (HTMLReader.CharacterAction.start): Initialize anchor with link + pseudo attribute. + (updateSpecialClass): New helper method. Updates the dynamic + or pseudo class for anchor tags. + * javax/swing/text/html/HTMLEditorKit.java + (LinkController.lastAnchorElement): New field. For tracking + enter/exit of anchors. + (LinkController.activateLink): Set pseudo class to 'visited'. + (LinkController.mouseMoved): Added support for tracking + the 'hover' dynamic class. + * javax/swing/text/html/InlineView.java + (changedUpdate): Fetch new properties. + * javax/swing/text/html/StyleSheet.java + (attributeSetToMap): New helper method. + (getRule): Also append dynamic and pseudo class to key. + (resolveStyle): Resolve style based generally on all attributes. + * javax/swing/text/html/TableView.java + (RowView.layoutMajorAxis): Make sure the grid is valid. + (updateGrid): Made package private. + * gnu/javax/swing/text/html/css/Selector.java + (calculateSpecificity): Added support for dynamic and pseudo classes. + (matches): Changed to operate on general attributes. + Added support for dynamic and pseudo classes. + +2006-12-01 Mario Torre + + * java/text/DecimalFormat.java (formatInternal): move the formatting of + fractional portion in a separate method. + Also fixes the handling of decimal separator and its associated field. + (handleFractionalPart): new method, needed to relax a bit + formatInternal. + +2006-12-01 Andrew John Hughes + + * java/beans/beancontext/BeanContextServicesSupport.java: + (BCSSProxyServiceProvider.getCurrentServiceSelectors(BeanContextServices, + Class)): Implemented. + (BCSSProxyServiceProvider.getService(BeanContextServices, Object, + Class, Object)): Implemented. + (BCSSProxyServiceProvider.releaseService(BeanContextServices, + Object, Object)): Implemented. + (BCSSProxyServiceProvider.serviceRevoked(BeanContextServiceRevokedEvent)): + Implemented. + (initialiseBeanContextResources()): Implemented. + (releaseBeanContextResoures()): Implemented. + +2006-12-01 Mark Wielaard + + * include/gnu_java_awt_peer_gtk_GdkFontPeer.h: Regenerated. + +2006-12-01 Roman Kennke + + * gnu/javax/swing/text/html/css/BorderStyle.java: New class for + handling border styles. + * gnu/javax/swing/text/html/css/BorderWidth.java + (isValid): New method. + * gnu/javax/swing/text/html/css/Length.java + (isValid): New method. + * javax/swing/text/html/CSS.java + (addInternal): Added shorthand parsing for border, padding and + margin. + (parseBackgroundShorthand): Added API docs. + (parsePaddingShorthand): New method. Handles padding shorthand + values. + (parseMarginShorthand): New method. Handles margin shorthand + values. + (parseBorderShorthand): New method. Handles border shorthand + values. + * javax/swing/text/html/StyleSheet.java + (translateHTMLToCSS): Set specific padding attributes. + (BoxPainter.BoxPainter): Don't handle PADDING and MARGIN here. + These shorthands are now handled in CSS. + (BoxPainter.paint): Exclude the outer margin. + +2006-12-01 Roman Kennke + + * gnu/javax/swing/text/html/css/Length.java + (emBase): New field. + (exBase): New field. + (isFontEMRelative): New field. + (isFontEXRelative): New field. + (Length): Recognize and setup EM and EX relative values. + (getValue): Handle EM and EX relative values. + (isEMRelative): New method. + (isEXRelative): New method. + (setEMBase): New method. + (setEXBase): New method. + (setFontBases): New method. + * gnu/javax/swing/text/html/parser/support/Parser.java + (_handleEmptyTag): Use new isBlock() helper method. + (_handleEndTag_remaining): Use new isBlock() helper method. + (_handleStartTag): Consume whitespace after block start tag. + (Comment): Consume whitespace after a comment. + (isBlock): New helper method. + (readAttributes): Consider all characters in unquoted attribute + values. + * javax/swing/text/html/BlockView.java + (layoutMinorAxis): Use cached span value. + (paint): Added debug code (commented out). + (setPropertiesFromAttributes): Set the EM and EX base on lengths. + * javax/swing/text/html/CSSBorder.java + (CSSBorder): Take StyleSheet as argument. Call getBorderWidth() + with stylesheet. + (getBorderWidth): Set the EM and EX base on the length values. + * javax/swing/text/html/HTMLDocument.java + (HTMLReader.ParagraphAction.end): Do not set the inParagraph field. + (HTMLReader.ParagraphAction.start): Do not set the inParagraph field. + (HTMLReader.inImpliedParagraph): Removed. + (HTMLReader.inParagraph): Removed. + (HTMLReader.parseStack): New field. + (HTMLReader.addContent): Use new paragraph handling. + (HTMLReader.addSpecialElement): Use new paragraph handling. + (HTMLReader.blockClose): Use new paragraph handling. + (HTMLReader.blockOpen): Use new paragraph handling. + (HTMLReader.inImpliedParagraph): New helper method. + (HTMLReader.inParagraph): New helper method. + * javax/swing/text/html/ImageView.java + (attributes): New field. Caches view attributes. + (spans): New field. Caches CSS spans. + (getAttributes): Correctly setup CSS view attributes. + (getPreferredSpan): Use caches spans. + (getStyleSheet): Use the view's getDocument() method. + (setPropertiesFromAttributes): Cache spans and setup EM and EX. + (updateSize): Use cached spans. + * javax/swing/text/html/ParagraphView.java + (setPropertiesFromAttributes): Setup EM and EX. + * javax/swing/text/html/StyleSheet.java + (BoxPainter.BoxPainter): Setup EM and EX correctly. + (getEMBase): New helper method. + (getEXBase): New helper method. + * javax/swing/text/html/TableView.java + (width): New field. Caches the table width. + (calculateMinorAxisRequirements): Use caches span. + (setPropertiesFromAttributes): Cache span and setup EM/EX. + (updateGrid): Correctly setup EM/EX. + +2006-11-30 Roman Kennke + + * javax/swing/text/html/FormSubmitEvent.java: New class. + * javax/swing/text/html/FormView.java + (SubmitThread): New class for submitting data in a separate thread. + (actionPerformed): Fetch the actual for data. + (addData): New helper method. + (getElementFormData): New helper method. + (getFormData): New helper method. + (getInputFormData): New helper method. + (submitData): Implemented. + * javax/swing/text/html/FrameView.java + (createComponent): Add this as hyperlink listener. + Set the target document as frame document. + (getTopEditorPane): New helper method. + (hyperlinkUpdate): Implementation of the HyperlinkListener interface. + (handleHyperlinkEvent): New helper method. + (handleFormSubmitEvent): New helper method. + * javax/swing/text/html/HTMLDocument.java + (HTMLReader.BaseAction.start): Track the base target. + (HTMLReader.BaseAction.end): Removed. + (baseTarget): New field. + (frameDocument): New field. + (getBaseTarget): New property accessor. + (isFrameDocument): New property accessor. + (processHTMLFrameHyperlinkEvent): Implemented. + (setFrameDocument): New property accessor. + (updateFrame): New helper method. + (updateFrameSet): New helper method. + * javax/swing/text/html/HTMLEditorKit.java + (LinkController.createHyperlinkEvent): Handle frame documents. + (autoFormSubmission): New field. + (HTMLEditorKit): Set autoFormSubmission to true. + (isAutoFormSubmission): New property accessor. + (setAutoFormSubmission): New property accessor. + +2006-11-30 Roman Kennke + + * javax/swing/text/ElementIterator.java + (ElementRef): New inner class. + (currentDepth): Removed. + (currentElement): Removed. + (previousItem): Removed. + (stack): New field. Holds the iteration stack. + (state): Removed. + (ElementIterator(Document)): Removed init of removed fields. + (ElementIterator(Element)): Removed init of removed fields. + (current): Changed to stack based algorithm. + (deepestLeaf): New helper method. + (depth): Changed to stack based algorithm. + (first): Changed to stack based algorithm. + (next): Changed to stack based algorithm. + (previous): Changed to stack based algorithm. + +2006-11-30 Francis Kung + + * .settings/org.eclipse.jdt.core.prefs: Set compilar compliance to 1.4. + * .externalToolBuilders/ClasspathJar.launch: Include sun.* classses. + +2006-11-30 Francis Kung + + * gnu/java/awt/peer/gtk/BufferedImageGraphics.java + (draw): Set transform in buffered composite. + (drawComposite): Do not transform bounds; round bounds. + (drawGlyphVector): Set transform in buffered composite. + (drawRenderedImage): Set transform in buffered composite. + (fill): Set transform in buffered composite. + (updateBufferedImage): Fix scanline & height calculations. + * gnu/java/awt/peer/gtk/CairoGraphics2D.java + (createPath): Simplify width & height calculation. + (drawImage): Also transform width & height. + +2006-11-30 Roman Kennke + + * javax/swing/text/html/FrameSetView.java: New class. Implements + HTML framesets. + * javax/swing/text/html/FrameView.java: New class. Implements + HTML frames. + * javax/swing/text/html/HTMLDocument.java: + (HTMLReader.addSpecialElement): Only add one artificial space. + * javax/swing/text/html/HTMLEditorKit.java + (HTMLFactory.create): Uncomment code for FrameSetView and FrameView. + * gnu/javax/swing/text/html/parser/support/Parser.java + (_handleEmptyTag): Also consume whitespace after frame tags. + +2006-11-30 Gary Benson + + * java/lang/Thread.java: Javadoc fixes. + 2006-11-29 Tom Tromey PR classpath/28203: diff --git a/gnu/java/awt/peer/gtk/BufferedImageGraphics.java b/gnu/java/awt/peer/gtk/BufferedImageGraphics.java index 7de9c057e..37ae498ad 100644 --- a/gnu/java/awt/peer/gtk/BufferedImageGraphics.java +++ b/gnu/java/awt/peer/gtk/BufferedImageGraphics.java @@ -248,13 +248,13 @@ public class BufferedImageGraphics extends CairoGraphics2D if (sm.getScanlineStride() == imageWidth && minX == 0) { System.arraycopy(pixels, y * imageWidth, - db, y * imageWidth - minY, + db, (y - minY) * imageWidth, height * imageWidth); } else { int scanline = sm.getScanlineStride(); - for (int i = y; i < height; i++) + for (int i = y; i < (height + y); i++) System.arraycopy(pixels, i * imageWidth + x, db, (i - minY) * scanline + x - minX, width); @@ -313,6 +313,7 @@ public class BufferedImageGraphics extends CairoGraphics2D Graphics2D g2d = (Graphics2D)buffer.getGraphics(); g2d.setStroke(this.getStroke()); g2d.setColor(this.getColor()); + g2d.setTransform(transform); g2d.draw(s); drawComposite(r.getBounds2D(), null); @@ -334,6 +335,7 @@ public class BufferedImageGraphics extends CairoGraphics2D Graphics2D g2d = (Graphics2D)buffer.getGraphics(); g2d.setPaint(this.getPaint()); g2d.setColor(this.getColor()); + g2d.setTransform(transform); g2d.fill(s); drawComposite(s.getBounds2D(), null); @@ -353,6 +355,7 @@ public class BufferedImageGraphics extends CairoGraphics2D Graphics2D g2d = (Graphics2D)buffer.getGraphics(); g2d.setRenderingHints(this.getRenderingHints()); + g2d.setTransform(transform); g2d.drawRenderedImage(image, xform); drawComposite(buffer.getRaster().getBounds(), null); @@ -427,43 +430,64 @@ public class BufferedImageGraphics extends CairoGraphics2D Graphics2D g2d = (Graphics2D)buffer.getGraphics(); g2d.setPaint(this.getPaint()); g2d.setStroke(this.getStroke()); + g2d.setTransform(transform); g2d.drawGlyphVector(gv, x, y); drawComposite(bounds, null); } } + /** + * Perform composite drawing from the buffer onto the main image. + * + * The image to be composited should already be drawn into the buffer, in the + * proper place, after all necessary transforms have been applied. + * + * @param bounds The bounds to draw, in user-space. + * @param observer The image observer, if any (may be null). + * @return True on success, false on failure. + */ private boolean drawComposite(Rectangle2D bounds, ImageObserver observer) { - // Clip source to visible areas that need updating - Rectangle2D clip = this.getClipBounds(); - Rectangle2D.intersect(bounds, clip, bounds); - clip = new Rectangle(buffer.getMinX(), buffer.getMinY(), - buffer.getWidth(), buffer.getHeight()); - Rectangle2D.intersect(bounds, clip, bounds); + // Find bounds in device space + double[] points = new double[] {bounds.getX(), bounds.getY(), + bounds.getMaxX(), bounds.getMaxY()}; + transform.transform(points, 0, points, 0, 2); + bounds = new Rectangle2D.Double(points[0], points[1], + (points[2] - points[0]), + (points[3] - points[1])); + + // Clip bounds by the stored clip, and by the internal buffer + Rectangle2D devClip = this.getClipInDevSpace(); + Rectangle2D.intersect(bounds, devClip, bounds); + devClip = new Rectangle(buffer.getMinX(), buffer.getMinY(), + buffer.getWidth(), buffer.getHeight()); + Rectangle2D.intersect(bounds, devClip, bounds); + + // Round bounds as needed, but be conservative in our rounding + // (otherwise it may leave unpainted stripes) + double x = bounds.getX(); + double y = bounds.getY(); + double w = bounds.getWidth(); + double h = bounds.getHeight(); + if (Math.floor(x) != x) + w--; + if (Math.floor(y) != y) + h--; + bounds.setRect(Math.ceil(x), Math.ceil(y), Math.floor(w), Math.floor(h)); + // Find subimage of internal buffer for updating BufferedImage buffer2 = buffer; if (!bounds.equals(buffer2.getRaster().getBounds())) buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(), (int)bounds.getWidth(), (int)bounds.getHeight()); - - // Get destination clip to bounds - double[] points = new double[] {bounds.getX(), bounds.getY(), - bounds.getMaxX(), bounds.getMaxY()}; - transform.transform(points, 0, points, 0, 2); - - Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1], - points[2] - points[0], - points[3] - points[1]); - - Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds); - + + // Find subimage of main image for updating BufferedImage current = image; - current = current.getSubimage((int)deviceBounds.getX(), - (int)deviceBounds.getY(), - (int)deviceBounds.getWidth(), - (int)deviceBounds.getHeight()); + current = current.getSubimage((int)bounds.getX(), (int)bounds.getY(), + (int)bounds.getWidth(), + (int)bounds.getHeight()); // Perform actual composite operation compCtx.compose(buffer2.getRaster(), current.getRaster(), diff --git a/gnu/java/awt/peer/gtk/CairoGraphics2D.java b/gnu/java/awt/peer/gtk/CairoGraphics2D.java index 16de95185..ec9890524 100644 --- a/gnu/java/awt/peer/gtk/CairoGraphics2D.java +++ b/gnu/java/awt/peer/gtk/CairoGraphics2D.java @@ -1153,8 +1153,8 @@ public abstract class CairoGraphics2D extends Graphics2D // does not get distorted by this shifting operation double x = shiftX(r.getX(),shiftDrawCalls && isDraw); double y = shiftY(r.getY(), shiftDrawCalls && isDraw); - double w = shiftX(r.getWidth() + r.getX(), shiftDrawCalls && isDraw) - x; - double h = shiftY(r.getHeight() + r.getY(), shiftDrawCalls && isDraw) - y; + double w = Math.round(r.getWidth()); + double h = Math.round(r.getHeight()); cairoRectangle(nativePointer, x, y, w, h); } @@ -1506,8 +1506,11 @@ public abstract class CairoGraphics2D extends Graphics2D setBackground(bgcolor); double[] origin = new double[] {0,0}; + double[] dimensions = new double[] {width, height}; xform.transform(origin, 0, origin, 0, 1); - clearRect((int)origin[0], (int)origin[1], width, height); + xform.deltaTransform(dimensions, 0, dimensions, 0, 1); + clearRect((int)origin[0], (int)origin[1], + (int)dimensions[0], (int)dimensions[1]); setBackground(oldColor); } @@ -2051,4 +2054,4 @@ public abstract class CairoGraphics2D extends Graphics2D return rect; } -} +} \ No newline at end of file diff --git a/gnu/javax/swing/text/html/css/BorderStyle.java b/gnu/javax/swing/text/html/css/BorderStyle.java new file mode 100644 index 000000000..d75beea52 --- /dev/null +++ b/gnu/javax/swing/text/html/css/BorderStyle.java @@ -0,0 +1,64 @@ +/* BorderStyle.java -- Utility for dealing with border styles + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.swing.text.html.css; + +/** + * Utility class for handling border styles. + */ +public class BorderStyle +{ + + /** + * Determines if a given value makes up a valid border style value. + * + * @param value the value to check + * + * @return true when this is a valid border style, + * false otherwise + */ + public static boolean isValidStyle(String value) + { + return value.equals("none") || value.equals("hidden") + || value.equals("dotted") || value.equals("dashed") + || value.equals("solid") || value.equals("double") + || value.equals("groove") || value.equals("ridge") + || value.equals("inset") || value.equals("outset"); + + } +} diff --git a/gnu/javax/swing/text/html/css/BorderWidth.java b/gnu/javax/swing/text/html/css/BorderWidth.java index b717020e3..ae64c2110 100644 --- a/gnu/javax/swing/text/html/css/BorderWidth.java +++ b/gnu/javax/swing/text/html/css/BorderWidth.java @@ -63,4 +63,16 @@ public class BorderWidth floatValue = 3.F; } + /** + * Checks if the specified value makes up a valid border-width value. + * + * @param value the value to check + * + * @return true if the value is a valid border-width + */ + public static boolean isValid(String value) + { + return value.equals("thin") || value.equals("medium") + || value.equals("thick") || Length.isValid(value); + } } diff --git a/gnu/javax/swing/text/html/css/FontSize.java b/gnu/javax/swing/text/html/css/FontSize.java index 7dc8d46c6..2795b6784 100644 --- a/gnu/javax/swing/text/html/css/FontSize.java +++ b/gnu/javax/swing/text/html/css/FontSize.java @@ -255,13 +255,13 @@ public class FontSize private int mapRelative(int par) { - if (value.contains("%")) + if (value.indexOf('%') != -1) size = mapPercent(par); - else if (value.contains("em")) + else if (value.indexOf("em") != -1) size = mapEM(par); - else if (value.contains("larger")) + else if (value.indexOf("larger") != -1) size = mapLarger(par); - else if (value.contains("smaller")) + else if (value.indexOf("smaller") != -1) size = mapSmaller(par); return size; } diff --git a/gnu/javax/swing/text/html/css/Length.java b/gnu/javax/swing/text/html/css/Length.java index 339e2a2e0..06fa36e3d 100644 --- a/gnu/javax/swing/text/html/css/Length.java +++ b/gnu/javax/swing/text/html/css/Length.java @@ -61,6 +61,26 @@ public class Length */ private boolean isPercentage; + /** + * Indicates a length value that is relative to the font size (em). + */ + private boolean isFontEMRelative; + + /** + * Indicates a length value that is relative to the font size (ex). + */ + private boolean isFontEXRelative; + + /** + * The EM base size. + */ + private float emBase; + + /** + * The EX base size. + */ + private float exBase; + /** * Creates a new length converter instance. * @@ -68,9 +88,14 @@ public class Length */ public Length(String val) { + isFontEMRelative = false; + isFontEXRelative = false; + isPercentage = false; value = val; int i = value.indexOf("px"); int percent = value.indexOf("%"); + int em = value.indexOf("em"); + int ex = value.indexOf("ex"); try { floatValue = 0.0F; @@ -85,13 +110,24 @@ public class Length String sub = value.substring(0, percent); floatValue = Float.parseFloat(sub) / 100; } + else if (em != -1) + { + isFontEMRelative = true; + String sub = value.substring(0, em); + floatValue = Float.parseFloat(sub); + } + else if (ex != -1) + { + isFontEXRelative = true; + String sub = value.substring(0, ex); + floatValue = Float.parseFloat(sub); + } else { - // TODO: Implement other length options. floatValue = Float.parseFloat(value); } } - catch (NumberFormatException ex) + catch (NumberFormatException exc) { // Don't let such small problems interrupt CSS parsing. System.err.println("couldn't parse: " + val); @@ -112,18 +148,78 @@ public class Length * Returns the absolute span for the case when this length value is * a relative value. * - * @param available the target span + * @param base the base span * * @return the absolute span */ - public float getValue(float available) + public float getValue(float base) { float span = floatValue; if (isPercentage) - span *= available; + span *= base; + else if (isFontEMRelative) + span *= emBase; + else if (isFontEXRelative) + span *= exBase; return span; } + /** + * Sets the font relative EM base. + * + * @param base the font relative EM base + */ + public void setEMBase(float base) + { + emBase = base; + } + + /** + * Sets the font relative EX base. + * + * @param base the font relative EX base + */ + public void setEXBase(float base) + { + exBase = base; + } + + /** + * Sets the font relative base values. + * + * @param emBase the EM base + * @param exBase the EX base + */ + public void setFontBases(float emBase, float exBase) + { + setEMBase(emBase); + setEXBase(exBase); + } + + /** + * Returns true when this length value is an em font relative value. In + * order to get correct results, you need the exBase property set up + * correctly. + * + * @return true when this length value is an ex font relative value + */ + public boolean isFontEMRelative() + { + return isFontEMRelative; + } + + /** + * Returns true when this length value is an ex font relative value. In + * order to get correct results, you need the emBase property set up + * correctly. + * + * @return true when this length value is an ex font relative value + */ + public boolean isFontEXRelative() + { + return isFontEXRelative; + } + /** * Returns true when the length value is a percentage * value, false otherwise. @@ -135,4 +231,53 @@ public class Length { return isPercentage; } + + /** + * Checks if the specified value makes up a valid length value. + * + * @param value the value to check + * + * @return true if the value is a valid length + */ + public static boolean isValid(String value) + { + boolean isValid = true; + int px = value.indexOf("px"); + int em = value.indexOf("em"); + int ex = value.indexOf("ex"); + int pc = value.indexOf('%'); + try + { + if (px != -1) + { + Integer.parseInt(value.substring(0, px)); + } + else if (em != -1) + { + Integer.parseInt(value.substring(0, em)); + } + else if (ex != -1) + { + Integer.parseInt(value.substring(0, ex)); + } + else if (pc != -1) + { + Integer.parseInt(value.substring(0, ex)); + } + else + { + Integer.parseInt(value); + } + } + catch (NumberFormatException nfe) + { + isValid = false; + } + return isValid; + } + + public String toString() + { + return value; + } } diff --git a/gnu/javax/swing/text/html/css/Selector.java b/gnu/javax/swing/text/html/css/Selector.java index b8128233b..210df3a7b 100644 --- a/gnu/javax/swing/text/html/css/Selector.java +++ b/gnu/javax/swing/text/html/css/Selector.java @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.javax.swing.text.html.css; +import java.util.Map; import java.util.StringTokenizer; /** @@ -98,7 +99,7 @@ public class Selector * @return true when this selector matches the element path, * false otherwise */ - public boolean matches(String[] tags, String[] pathClasses, String[] pathIds) + public boolean matches(String[] tags, Map[] attributes) { // TODO: This implements class, id and descendent matching. These are // the most commonly used selector matchers in CSS together with HTML. @@ -119,15 +120,22 @@ public class Selector boolean tagMatch = false; for (; tagIndex < numTags && tagMatch == false; tagIndex++) { + Object pathClass = attributes[tagIndex].get("class"); + // Try pseudo class too. + Object pseudoClass = attributes[tagIndex].get("_pseudo"); + Object dynClass = attributes[tagIndex].get("_dynamic"); + Object pathId = attributes[tagIndex].get("id"); String tag = elements[j]; String clazz = classes[j]; String id = ids[j]; tagMatch = tag.equals("") || tag.equals("*") || tag.equals(tags[tagIndex]); tagMatch = tagMatch && (clazz.equals("*") - || clazz.equals(pathClasses[tagIndex])); + || clazz.equals(dynClass) + || clazz.equals(pseudoClass) + || clazz.equals(pathClass)); tagMatch = tagMatch && (id.equals("*") - || id.equals(pathIds[tagIndex])); + || id.equals(pathId)); // For the last element in the selector we must not look // further. if (j == 0) @@ -190,6 +198,9 @@ public class Selector { String sel = selector[i]; int clazzIndex = sel.indexOf('.'); + // Try pseudo class too. + if (clazzIndex == -1) + clazzIndex = sel.indexOf(':'); int idIndex = sel.indexOf('#'); String clazz; if (clazzIndex == -1) diff --git a/gnu/javax/swing/text/html/parser/support/Parser.java b/gnu/javax/swing/text/html/parser/support/Parser.java index 98058e503..f04c58138 100644 --- a/gnu/javax/swing/text/html/parser/support/Parser.java +++ b/gnu/javax/swing/text/html/parser/support/Parser.java @@ -498,6 +498,9 @@ public class Parser mustBe(t.kind); } hTag = new Token(start, last); + + // Consume any whitespace immediately following a comment. + optional(WS); handleComment(); } @@ -983,13 +986,15 @@ public class Parser + next.getImage() + "'"); attrValue = value.getImage(); } - else if (next.kind == SLASH) - // The slash in this context is treated as the ordinary - // character, not as a token. The slash may be part of + else if (next.kind == SLASH || next.kind == OTHER) + // The slash and other characters (like %) in this context is + // treated as the ordinary + // character, not as a token. The character may be part of // the unquoted URL. { StringBuffer image = new StringBuffer(value.getImage()); - while (next.kind == NUMTOKEN || next.kind == SLASH) + while (next.kind == NUMTOKEN || next.kind == SLASH + || next.kind == OTHER) { image.append(getNextToken().getImage()); next = getTokenAhead(); @@ -1181,6 +1186,13 @@ public class Parser { validator.validateTag(tag, attributes); handleEmptyTag(tag); + HTML.Tag h = tag.getHTMLTag(); + // When a block tag is closed, consume whitespace that follows after + // it. + // For some unknown reason a FRAME tag is not treated as block element. + // However in this case it should be treated as such. + if (isBlock(h)) + optional(WS); } catch (ChangedCharSetException ex) { @@ -1219,7 +1231,7 @@ public class Parser // When a block tag is closed, consume whitespace that follows after // it. - if (h.isBlock()) + if (isBlock(h)) optional(WS); if (h == HTML.Tag.TITLE) @@ -1248,6 +1260,9 @@ public class Parser HTML.Tag h = tag.getHTMLTag(); + if (isBlock(h)) + optional(WS); + if (h.isPreformatted()) preformatted++; @@ -1495,4 +1510,19 @@ public class Parser { error("Whitespace here is not permitted"); } + + /** + * Returns true when the specified tag should be considered a block tag + * wrt whitespace handling. We need this special handling, since there + * are a couple of tags that we must treat as block tags but which aren't + * officially block tags. + * + * @param tag the tag to check + * @return true when the specified tag should be considered a block tag + * wrt whitespace handling + */ + private boolean isBlock(HTML.Tag tag) + { + return tag.isBlock() || tag == HTML.Tag.STYLE || tag == HTML.Tag.FRAME; + } } diff --git a/include/gnu_java_awt_peer_gtk_GdkFontPeer.h b/include/gnu_java_awt_peer_gtk_GdkFontPeer.h index 3b9ddd443..c23980ca5 100644 --- a/include/gnu_java_awt_peer_gtk_GdkFontPeer.h +++ b/include/gnu_java_awt_peer_gtk_GdkFontPeer.h @@ -18,6 +18,22 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkFontPeer_getFontMetrics (JN JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkFontPeer_getTextMetrics (JNIEnv *env, jobject, jstring, jdoubleArray); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkFontPeer_releasePeerGraphicsResource (JNIEnv *env, jobject); JNIEXPORT jbyteArray JNICALL Java_gnu_java_awt_peer_gtk_GdkFontPeer_getTrueTypeTable (JNIEnv *env, jobject, jbyte, jbyte, jbyte, jbyte); +#undef gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_ASCENT +#define gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_ASCENT 0L +#undef gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_MAX_ASCENT +#define gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_MAX_ASCENT 1L +#undef gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_DESCENT +#define gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_DESCENT 2L +#undef gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_MAX_DESCENT +#define gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_MAX_DESCENT 3L +#undef gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_MAX_ADVANCE +#define gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_MAX_ADVANCE 4L +#undef gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_HEIGHT +#define gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_HEIGHT 5L +#undef gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_UNDERLINE_OFFSET +#define gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_UNDERLINE_OFFSET 6L +#undef gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_UNDERLINE_THICKNESS +#define gnu_java_awt_peer_gtk_GdkFontPeer_FONT_METRICS_UNDERLINE_THICKNESS 7L #ifdef __cplusplus } diff --git a/java/beans/beancontext/BeanContextServicesSupport.java b/java/beans/beancontext/BeanContextServicesSupport.java index ce09b86cc..f354ff474 100644 --- a/java/beans/beancontext/BeanContextServicesSupport.java +++ b/java/beans/beancontext/BeanContextServicesSupport.java @@ -86,38 +86,39 @@ public class BeanContextServicesSupport { private static final long serialVersionUID = 7078212910685744490L; - private BCSSProxyServiceProvider() + private BeanContextServiceProvider provider; + + private BCSSProxyServiceProvider(BeanContextServiceProvider p) { + provider = p; } public Iterator getCurrentServiceSelectors (BeanContextServices bcs, Class serviceClass) - throws NotImplementedException { - throw new Error ("Not implemented"); + return provider.getCurrentServiceSelectors(bcs, serviceClass); } public Object getService (BeanContextServices bcs, Object requestor, Class serviceClass, Object serviceSelector) - throws NotImplementedException { - throw new Error ("Not implemented"); + return provider.getService(bcs, requestor, serviceClass, + serviceSelector); } public void releaseService (BeanContextServices bcs, Object requestor, Object service) - throws NotImplementedException { - throw new Error ("Not implemented"); + provider.releaseService(bcs, requestor, service); } public void serviceRevoked (BeanContextServiceRevokedEvent bcsre) - throws NotImplementedException { - throw new Error ("Not implemented"); + if (provider instanceof BeanContextServiceRevokedListener) + ((BeanContextServiceRevokedListener) provider).serviceRevoked(bcsre); } } @@ -772,16 +773,24 @@ public class BeanContextServicesSupport serviceLeases = new HashMap(); } - protected void initializeBeanContextResources () - throws NotImplementedException + /** + * Subclasses may override this method to allocate resources + * from the nesting bean context. + */ + protected void initializeBeanContextResources() { - throw new Error ("Not implemented"); + /* Purposefully left empty */ } - protected void releaseBeanContextResources () - throws NotImplementedException + /** + * Relinquishes any resources obtained from the parent context. + * Specifically, those services obtained from the parent are revoked. + * Subclasses may override this method to deallocate resources + * from the nesting bean context. + */ + protected void releaseBeanContextResources() { - throw new Error ("Not implemented"); + /* Purposefully left empty */ } /** diff --git a/java/lang/Thread.java b/java/lang/Thread.java index 2261bbcfc..551cd1b9e 100644 --- a/java/lang/Thread.java +++ b/java/lang/Thread.java @@ -853,11 +853,13 @@ public class Thread implements Runnable * are no guarantees which thread will be next to run, but most VMs will * choose the highest priority thread that has been waiting longest. * - * @param ms the number of milliseconds to sleep. + * @param ms the number of milliseconds to sleep, or 0 for forever * @throws InterruptedException if the Thread is (or was) interrupted; * it's interrupted status will be cleared * @throws IllegalArgumentException if ms is negative * @see #interrupt() + * @see #notify() + * @see #wait(long) */ public static void sleep(long ms) throws InterruptedException { @@ -877,13 +879,15 @@ public class Thread implements Runnable * immediately when time expires, because some other thread may be * active. So don't expect real-time performance. * - * @param ms the number of milliseconds to sleep + * @param ms the number of milliseconds to sleep, or 0 for forever * @param ns the number of extra nanoseconds to sleep (0-999999) * @throws InterruptedException if the Thread is (or was) interrupted; * it's interrupted status will be cleared * @throws IllegalArgumentException if ms or ns is negative * or ns is larger than 999999. * @see #interrupt() + * @see #notify() + * @see #wait(long, int) */ public static void sleep(long ms, int ns) throws InterruptedException { diff --git a/java/text/DecimalFormat.java b/java/text/DecimalFormat.java index 3b67a50da..529e57133 100644 --- a/java/text/DecimalFormat.java +++ b/java/text/DecimalFormat.java @@ -660,11 +660,11 @@ public class DecimalFormat extends NumberFormat int len = str.length(); if (len < stop) stop = len; - char ch; - int i = 0; - for (i = start; i < stop; i++) + int i = start; + while (i < stop) { - ch = str.charAt(i); + char ch = str.charAt(i); + i++; if (ch >= zero && ch <= (zero + 9)) { @@ -749,7 +749,7 @@ public class DecimalFormat extends NumberFormat if (isNegative) number.insert(0, '-'); - pos.setIndex(i); + pos.setIndex(i - 1); // now we handle the return type BigDecimal bigDecimal = new BigDecimal(number.toString()); @@ -1893,30 +1893,102 @@ public class DecimalFormat extends NumberFormat // add the INTEGER attribute addAttribute(Field.INTEGER, attributeStart, dest.length()); - if (this.decimalSeparatorAlwaysShown || - ((!isLong || this.useExponentialNotation) - && this.showDecimalSeparator && - this.maximumFractionDigits > 0) || - this.minimumFractionDigits > 0) + // ...update field position, if needed, and return... + if ((fieldPos.getField() == INTEGER_FIELD || + fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER)) + { + fieldPos.setBeginIndex(beginIndexInt); + fieldPos.setEndIndex(endIndexInt); + } + + handleFractionalPart(dest, fractPart, fieldPos, isLong); + + // and the exponent + if (this.useExponentialNotation) { attributeStart = dest.length(); + dest.append(symbols.getExponential()); + + addAttribute(Field.EXPONENT_SYMBOL, attributeStart, dest.length()); + attributeStart = dest.length(); + + if (exponent < 0) + { + dest.append(symbols.getMinusSign()); + exponent = -exponent; + + addAttribute(Field.EXPONENT_SIGN, attributeStart, dest.length()); + } + + attributeStart = dest.length(); + + String exponentString = String.valueOf(exponent); + int exponentLength = exponentString.length(); + + for (int i = 0; i < minExponentDigits - exponentLength; i++) + dest.append(symbols.getZeroDigit()); + + for (int i = 0; i < exponentLength; ++i) + dest.append(exponentString.charAt(i)); + + addAttribute(Field.EXPONENT, attributeStart, dest.length()); + } + + // now include the suffixes... + if (isNegative) + { + dest.append(negativeSuffix); + } + else + { + dest.append(positiveSuffix); + } + } + + /** + * Add to the input buffer the result of formatting the fractional + * portion of the number. + * + * @param dest + * @param fractPart + * @param fieldPos + * @param isLong + */ + private void handleFractionalPart(StringBuffer dest, String fractPart, + FieldPosition fieldPos, boolean isLong) + { + int dotStart = 0; + int dotEnd = 0; + boolean addDecimal = false; + + if (this.decimalSeparatorAlwaysShown || + ((!isLong || this.useExponentialNotation) && + this.showDecimalSeparator && this.maximumFractionDigits > 0) || + this.minimumFractionDigits > 0) + { + dotStart = dest.length(); + if (this.useCurrencySeparator) dest.append(symbols.getMonetaryDecimalSeparator()); else dest.append(symbols.getDecimalSeparator()); - // add the INTEGER attribute - addAttribute(Field.DECIMAL_SEPARATOR, attributeStart, dest.length()); + dotEnd = dest.length(); + addDecimal = true; } // now handle the fraction portion of the number + int fractStart = 0; + int fractEnd = 0; + boolean addFractional = false; + if ((!isLong || this.useExponentialNotation) && this.maximumFractionDigits > 0 || this.minimumFractionDigits > 0) { - attributeStart = dest.length(); - beginIndexFract = attributeStart; + fractStart = dest.length(); + fractEnd = fractStart; int digits = this.minimumFractionDigits; @@ -1939,83 +2011,41 @@ public class DecimalFormat extends NumberFormat if (fracts[i] != '0') allZeros = false; } - + if (!allZeros || (minimumFractionDigits > 0)) { appendDigit(fractPart, dest, false); - endIndexFract = dest.length(); - addAttribute(Field.FRACTION, attributeStart, endIndexFract); + fractEnd = dest.length(); + + addDecimal = true; + addFractional = true; } else if (!this.decimalSeparatorAlwaysShown) { dest.deleteCharAt(dest.length() - 1); + addDecimal = false; } else { - System.out.println("ayeeeee!"); - endIndexFract = dest.length(); - addAttribute(Field.FRACTION, attributeStart, endIndexFract); + fractEnd = dest.length(); + addFractional = true; } } - // and the exponent - if (this.useExponentialNotation) - { - attributeStart = dest.length(); - - dest.append(symbols.getExponential()); - - addAttribute(Field.EXPONENT_SYMBOL, attributeStart, dest.length()); - attributeStart = dest.length(); - - if (exponent < 0) - { - dest.append(symbols.getMinusSign()); - exponent = -exponent; - - addAttribute(Field.EXPONENT_SIGN, attributeStart, dest.length()); - } - - attributeStart = dest.length(); - - String exponentString = String.valueOf(exponent); - int exponentLength = exponentString.length(); - - for (int i = 0; i < minExponentDigits - exponentLength; i++) - dest.append(symbols.getZeroDigit()); - - for (int i = 0; i < exponentLength; ++i) - dest.append(exponentString.charAt(i)); - - addAttribute(Field.EXPONENT, attributeStart, dest.length()); - } - - // now include the suffixes... - if (isNegative) - { - dest.append(negativeSuffix); - } - else - { - dest.append(positiveSuffix); - } + if (addDecimal) + addAttribute(Field.DECIMAL_SEPARATOR, dotStart, dotEnd); - // ...update field position, if needed, and return... - if ((fieldPos.getField() == INTEGER_FIELD || - fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER)) - { - fieldPos.setBeginIndex(beginIndexInt); - fieldPos.setEndIndex(endIndexInt); - } + if (addFractional) + addAttribute(Field.FRACTION, fractStart, fractEnd); if ((fieldPos.getField() == FRACTION_FIELD || fieldPos.getFieldAttribute() == NumberFormat.Field.FRACTION)) { - fieldPos.setBeginIndex(beginIndexFract); - fieldPos.setEndIndex(endIndexFract); + fieldPos.setBeginIndex(fractStart); + fieldPos.setEndIndex(fractEnd); } } - + /** * Append to destthe give number of zeros. * Grouping is added if needed. diff --git a/javax/swing/text/ElementIterator.java b/javax/swing/text/ElementIterator.java index a6a5ff618..112d55e96 100644 --- a/javax/swing/text/ElementIterator.java +++ b/javax/swing/text/ElementIterator.java @@ -37,6 +37,8 @@ exception statement from your version. */ package javax.swing.text; +import java.util.Stack; + /** * This class can be used to iterate over the {@link Element} tree of * a {@link Document} or an {@link Element}. This iterator performs @@ -46,20 +48,41 @@ package javax.swing.text; */ public class ElementIterator implements Cloneable { + /** + * Uses to track the iteration on the stack. + */ + private class ElementRef + { + /** + * The element. + */ + Element element; + + /** + * The child index. -1 means the element itself. >= 0 values mean the + * n-th child of the element. + */ + int index; + + /** + * Creates a new ElementRef. + * + * @param el the element + */ + ElementRef(Element el) + { + element = el; + index = -1; + } + } + // The root element. private Element root; - // The current element. - private Element currentElement; - // The depth to which we have descended in the tree. - private int currentDepth; - - // This is at least as big as the current depth, and at index N - // contains the index of the child element we're currently - // examining. - private int[] state; - // The previous item. - private Element previousItem; + /** + * Holds ElementRefs. + */ + private Stack stack; /** * Create a new ElementIterator to iterate over the given document. @@ -67,9 +90,7 @@ public class ElementIterator implements Cloneable */ public ElementIterator(Document document) { - this.root = document.getDefaultRootElement(); - this.currentElement = root; - this.state = new int[5]; + root = document.getDefaultRootElement(); } /** @@ -79,8 +100,6 @@ public class ElementIterator implements Cloneable public ElementIterator(Element root) { this.root = root; - this.currentElement = root; - this.state = new int[5]; } /** @@ -105,7 +124,24 @@ public class ElementIterator implements Cloneable */ public Element current() { - return currentElement; + Element current; + if (stack == null) + current = first(); + else + { + current = null; + if (! stack.isEmpty()) + { + ElementRef ref = (ElementRef) stack.peek(); + Element el = ref.element; + int index = ref.index; + if (index == -1) + current = el; + else + current = el.getElement(index); + } + } + return current; } /** @@ -113,7 +149,10 @@ public class ElementIterator implements Cloneable */ public int depth() { - return currentDepth; + int depth = 0; + if (stack != null) + depth = stack.size(); + return depth; } /** @@ -121,11 +160,15 @@ public class ElementIterator implements Cloneable */ public Element first() { - // Reset the iterator. - currentElement = root; - currentDepth = 0; - previousItem = null; - return root; + Element first = null; + if (root != null) + { + stack = new Stack(); + if (root.getElementCount() > 0) + stack.push(new ElementRef(root)); + first = root; + } + return first; } /** @@ -134,48 +177,96 @@ public class ElementIterator implements Cloneable */ public Element next() { - previousItem = currentElement; - if (currentElement == null) - return null; - if (! currentElement.isLeaf()) + Element next; + if (stack == null) + next = first(); + else { - ++currentDepth; - if (currentDepth > state.length) - { - int[] newState = new int[state.length * 2]; - System.arraycopy(state, 0, newState, 0, state.length); - state = newState; - } - state[currentDepth] = 0; - currentElement = currentElement.getElement(0); - return currentElement; + next = null; + if (! stack.isEmpty()) + { + ElementRef ref = (ElementRef) stack.peek(); + Element el = ref.element; + int index = ref.index; + if (el.getElementCount() > index + 1) + { + Element child = el.getElement(index + 1); + if (child.isLeaf()) + ref.index++; + else + stack.push(new ElementRef(child)); + next = child; + next = child; + } + else + { + stack.pop(); + if (! stack.isEmpty()) + { + ElementRef top = (ElementRef) stack.peek(); + top.index++; + next = next(); + } + } + } + // else return null. } + return next; + } - while (currentDepth > 0) + /** + * Returns the previous item. Does not modify the iterator state. + */ + public Element previous() + { + Element previous = null; + int stackSize; + if (stack != null && (stackSize = stack.size()) > 0) { - // At a leaf, or done with a non-leaf's children, so go up a - // level. - --currentDepth; - currentElement = currentElement.getParentElement(); - ++state[currentDepth]; - if (state[currentDepth] < currentElement.getElementCount()) - { - currentElement = currentElement.getElement(state[currentDepth]); - return currentElement; - } + ElementRef ref = (ElementRef) stack.peek(); + Element el = ref.element; + int index = ref.index; + if (index > 0) + { + previous = deepestLeaf(el.getElement(--index)); + } + else if (index == 0) + { + previous = el; + } + else if (index == -1) + { + ElementRef top = (ElementRef) stack.pop(); + ElementRef item = (ElementRef) stack.peek(); + stack.push(top); + index = item.index; + el = item.element; + previous = index == -1 ? el : deepestLeaf(el.getElement(index)); + } } - - currentElement = null; - return currentElement; + return previous; } /** - * Returns the previous item. Does not modify the iterator state. + * Determines and returns the deepest leaf of the element el. + * + * @param el the base element + * + * @returnthe deepest leaf of the element el */ - public Element previous() + private Element deepestLeaf(Element el) { - if (currentElement == null || currentElement == root) - return null; - return previousItem; + Element leaf; + if (el.isLeaf()) + leaf = el; + else + { + int count = el.getElementCount(); + if (count == 0) + leaf = el; + else + leaf = deepestLeaf(el.getElement(count - 1)); + } + return leaf; } } diff --git a/javax/swing/text/html/BlockView.java b/javax/swing/text/html/BlockView.java index 2e781412c..82bd8604e 100644 --- a/javax/swing/text/html/BlockView.java +++ b/javax/swing/text/html/BlockView.java @@ -40,6 +40,7 @@ package javax.swing.text.html; import gnu.javax.swing.text.html.css.Length; +import java.awt.Color; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; @@ -255,16 +256,13 @@ public class BlockView extends BoxView int[] offsets, int[] spans) { int viewCount = getViewCount(); - CSS.Attribute spanAtt = axis == X_AXIS ? CSS.Attribute.WIDTH - : CSS.Attribute.HEIGHT; for (int i = 0; i < viewCount; i++) { View view = getView(i); int min = (int) view.getMinimumSpan(axis); int max; // Handle CSS span value of child. - AttributeSet atts = view.getAttributes(); - Length length = (Length) atts.getAttribute(spanAtt); + Length length = cssSpans[axis]; if (length != null) { min = Math.max((int) length.getValue(targetSpan), min); @@ -299,6 +297,11 @@ public class BlockView extends BoxView public void paint(Graphics g, Shape a) { Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + + // Debug output. Shows blocks in green rectangles. + // g.setColor(Color.GREEN); + // g.drawRect(rect.x, rect.y, rect.width, rect.height); + painter.paint(g, rect.x, rect.y, rect.width, rect.height, this); super.paint(g, a); } @@ -446,8 +449,14 @@ public class BlockView extends BoxView } // Fetch width and height. + float emBase = ss.getEMBase(attributes); + float exBase = ss.getEXBase(attributes); cssSpans[X_AXIS] = (Length) attributes.getAttribute(CSS.Attribute.WIDTH); + if (cssSpans[X_AXIS] != null) + cssSpans[X_AXIS].setFontBases(emBase, exBase); cssSpans[Y_AXIS] = (Length) attributes.getAttribute(CSS.Attribute.HEIGHT); + if (cssSpans[Y_AXIS] != null) + cssSpans[Y_AXIS].setFontBases(emBase, exBase); } /** diff --git a/javax/swing/text/html/CSS.java b/javax/swing/text/html/CSS.java index c82b6c537..0d1eeb762 100644 --- a/javax/swing/text/html/CSS.java +++ b/javax/swing/text/html/CSS.java @@ -37,6 +37,7 @@ exception statement from your version. */ package javax.swing.text.html; +import gnu.javax.swing.text.html.css.BorderStyle; import gnu.javax.swing.text.html.css.BorderWidth; import gnu.javax.swing.text.html.css.CSSColor; import gnu.javax.swing.text.html.css.FontSize; @@ -417,7 +418,7 @@ public class CSS implements Serializable new Attribute("border-right-color", false, null); static final Attribute BORDER_SPACING = new Attribute("border-spacing", false, null); - + /** * The attribute string. */ @@ -537,8 +538,23 @@ public class CSS implements Serializable { if (a == Attribute.BACKGROUND) parseBackgroundShorthand(atts, v); + else if (a == Attribute.PADDING) + parsePaddingShorthand(atts, v); + else if (a == Attribute.MARGIN) + parseMarginShorthand(atts, v); + else if (a == Attribute.BORDER || a == Attribute.BORDER_LEFT + || a == Attribute.BORDER_RIGHT || a == Attribute.BORDER_TOP + || a == Attribute.BORDER_BOTTOM) + parseBorderShorthand(atts, v, a); } + /** + * Parses the background shorthand and translates it to more specific + * background attributes. + * + * @param atts the attributes + * @param v the value + */ private static void parseBackgroundShorthand(MutableAttributeSet atts, String v) { @@ -551,4 +567,159 @@ public class CSS implements Serializable new CSSColor(token)); } } + + /** + * Parses the padding shorthand and translates to the specific padding + * values. + * + * @param atts the attributes + * @param v the actual value + */ + private static void parsePaddingShorthand(MutableAttributeSet atts, String v) + { + StringTokenizer tokens = new StringTokenizer(v, " "); + int numTokens = tokens.countTokens(); + if (numTokens == 1) + { + Length l = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.PADDING_BOTTOM, l); + atts.addAttribute(Attribute.PADDING_LEFT, l); + atts.addAttribute(Attribute.PADDING_RIGHT, l); + atts.addAttribute(Attribute.PADDING_TOP, l); + } + else if (numTokens == 2) + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.PADDING_BOTTOM, l1); + atts.addAttribute(Attribute.PADDING_TOP, l1); + atts.addAttribute(Attribute.PADDING_LEFT, l2); + atts.addAttribute(Attribute.PADDING_RIGHT, l2); + } + else if (numTokens == 3) + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + Length l3 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.PADDING_TOP, l1); + atts.addAttribute(Attribute.PADDING_LEFT, l2); + atts.addAttribute(Attribute.PADDING_RIGHT, l2); + atts.addAttribute(Attribute.PADDING_BOTTOM, l3); + } + else + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + Length l3 = new Length(tokens.nextToken()); + Length l4 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.PADDING_TOP, l1); + atts.addAttribute(Attribute.PADDING_RIGHT, l2); + atts.addAttribute(Attribute.PADDING_BOTTOM, l3); + atts.addAttribute(Attribute.PADDING_LEFT, l4); + } + } + + /** + * Parses the margin shorthand and translates to the specific margin + * values. + * + * @param atts the attributes + * @param v the actual value + */ + private static void parseMarginShorthand(MutableAttributeSet atts, String v) + { + StringTokenizer tokens = new StringTokenizer(v, " "); + int numTokens = tokens.countTokens(); + if (numTokens == 1) + { + Length l = new Length(tokens.nextToken()); + System.err.println("margin: " + l); + atts.addAttribute(Attribute.MARGIN_BOTTOM, l); + atts.addAttribute(Attribute.MARGIN_LEFT, l); + atts.addAttribute(Attribute.MARGIN_RIGHT, l); + atts.addAttribute(Attribute.MARGIN_TOP, l); + } + else if (numTokens == 2) + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.MARGIN_BOTTOM, l1); + atts.addAttribute(Attribute.MARGIN_TOP, l1); + atts.addAttribute(Attribute.MARGIN_LEFT, l2); + atts.addAttribute(Attribute.MARGIN_RIGHT, l2); + } + else if (numTokens == 3) + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + Length l3 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.MARGIN_TOP, l1); + atts.addAttribute(Attribute.MARGIN_LEFT, l2); + atts.addAttribute(Attribute.MARGIN_RIGHT, l2); + atts.addAttribute(Attribute.MARGIN_BOTTOM, l3); + } + else + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + Length l3 = new Length(tokens.nextToken()); + Length l4 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.MARGIN_TOP, l1); + atts.addAttribute(Attribute.MARGIN_RIGHT, l2); + atts.addAttribute(Attribute.MARGIN_BOTTOM, l3); + atts.addAttribute(Attribute.MARGIN_LEFT, l4); + } + } + + /** + * Parses the CSS border shorthand attribute and translates it to the + * more specific border attributes. + * + * @param atts the attribute + * @param value the value + */ + private static void parseBorderShorthand(MutableAttributeSet atts, + String value, Attribute cssAtt) + { + StringTokenizer tokens = new StringTokenizer(value, " "); + while (tokens.hasMoreTokens()) + { + String token = tokens.nextToken(); + if (BorderStyle.isValidStyle(token)) + { + if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_LEFT_STYLE, token); + if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_RIGHT_STYLE, token); + if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_BOTTOM_STYLE, token); + if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_TOP_STYLE, token); + } + else if (BorderWidth.isValid(token)) + { + BorderWidth w = new BorderWidth(token); + if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_LEFT_WIDTH, w); + if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_RIGHT_WIDTH, w); + if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_BOTTOM_WIDTH, w); + if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_TOP_WIDTH, w); + } + else if (CSSColor.isValidColor(token)) + { + CSSColor c = new CSSColor(token); + if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_LEFT_COLOR, c); + if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_RIGHT_COLOR, c); + if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_BOTTOM_COLOR, c); + if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_TOP_COLOR, c); + } + } + } } diff --git a/javax/swing/text/html/CSSBorder.java b/javax/swing/text/html/CSSBorder.java index 540955494..fff6b01a1 100644 --- a/javax/swing/text/html/CSSBorder.java +++ b/javax/swing/text/html/CSSBorder.java @@ -40,12 +40,10 @@ package javax.swing.text.html; import gnu.javax.swing.text.html.css.BorderWidth; import gnu.javax.swing.text.html.css.CSSColor; -import gnu.javax.swing.text.html.css.Length; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; -import java.awt.Graphics2D; import java.awt.Insets; import javax.swing.border.Border; @@ -140,7 +138,7 @@ class CSSBorder * * @param atts the attribute set that contains the border spec */ - CSSBorder(AttributeSet atts) + CSSBorder(AttributeSet atts, StyleSheet ss) { // Determine the border styles. int style = getBorderStyle(atts, CSS.Attribute.BORDER_STYLE); @@ -179,20 +177,20 @@ class CSSBorder rightColor = color; // Determine the border widths. - int width = getBorderWidth(atts, CSS.Attribute.BORDER_WIDTH); + int width = getBorderWidth(atts, CSS.Attribute.BORDER_WIDTH, ss); if (width == -1) width = 0; top = bottom = left = right = width; - width = getBorderWidth(atts, CSS.Attribute.BORDER_TOP_WIDTH); + width = getBorderWidth(atts, CSS.Attribute.BORDER_TOP_WIDTH, ss); if (width >= 0) top = width; - width = getBorderWidth(atts, CSS.Attribute.BORDER_BOTTOM_WIDTH); + width = getBorderWidth(atts, CSS.Attribute.BORDER_BOTTOM_WIDTH, ss); if (width >= 0) bottom = width; - width = getBorderWidth(atts, CSS.Attribute.BORDER_LEFT_WIDTH); + width = getBorderWidth(atts, CSS.Attribute.BORDER_LEFT_WIDTH, ss); if (width >= 0) left = width; - width = getBorderWidth(atts, CSS.Attribute.BORDER_RIGHT_WIDTH); + width = getBorderWidth(atts, CSS.Attribute.BORDER_RIGHT_WIDTH, ss); if (width >= 0) right = width; } @@ -264,12 +262,15 @@ class CSSBorder * * @return the width, or -1 of none has been set */ - private int getBorderWidth(AttributeSet atts, CSS.Attribute key) + private int getBorderWidth(AttributeSet atts, CSS.Attribute key, + StyleSheet ss) { int width = -1; Object o = atts.getAttribute(key); if (o instanceof BorderWidth) { + BorderWidth w = (BorderWidth) o; + w.setFontBases(ss.getEMBase(atts), ss.getEXBase(atts)); width = (int) ((BorderWidth) o).getValue(); } return width; diff --git a/javax/swing/text/html/FormSubmitEvent.java b/javax/swing/text/html/FormSubmitEvent.java new file mode 100644 index 000000000..bc7c36f4b --- /dev/null +++ b/javax/swing/text/html/FormSubmitEvent.java @@ -0,0 +1,123 @@ +/* FormSubmitEvent.java -- Event fired on form submit + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import java.net.URL; + +import javax.swing.text.Element; + +/** + * The event fired on form submit. + * + * @since 1.5 + */ +public class FormSubmitEvent + extends HTMLFrameHyperlinkEvent +{ + + // FIXME: Use enums when available. + /** + * The submit method. + */ + public static class MethodType + { + /** + * Indicates a form submit with HTTP method POST. + */ + public static final MethodType POST = new MethodType(); + + /** + * Indicates a form submit with HTTP method GET. + */ + public static final MethodType GET = new MethodType(); + + private MethodType() + { + } + } + + /** + * The submit method. + */ + private MethodType method; + + /** + * The actual submit data. + */ + private String data; + + /** + * Creates a new FormSubmitEvent. + * + * @param source the source + * @param type the type of hyperlink update + * @param url the action url + * @param el the associated element + * @param target the target attribute + * @param m the submit method + * @param d the submit data + */ + FormSubmitEvent(Object source, EventType type, URL url, Element el, + String target, MethodType m, String d) + { + super(source, type, url, el, target); + method = m; + data = d; + } + + /** + * Returns the submit data. + * + * @return the submit data + */ + public String getData() + { + return data; + } + + /** + * Returns the HTTP submit method. + * + * @return the HTTP submit method + */ + public MethodType getMethod() + { + return method; + } +} diff --git a/javax/swing/text/html/FormView.java b/javax/swing/text/html/FormView.java index 031a8b7a4..340f85490 100644 --- a/javax/swing/text/html/FormView.java +++ b/javax/swing/text/html/FormView.java @@ -44,24 +44,32 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; import javax.swing.ButtonModel; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; +import javax.swing.JEditorPane; import javax.swing.JPasswordField; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JToggleButton; +import javax.swing.SwingUtilities; import javax.swing.UIManager; +import javax.swing.event.HyperlinkEvent; import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; import javax.swing.text.ComponentView; import javax.swing.text.Document; import javax.swing.text.Element; +import javax.swing.text.ElementIterator; import javax.swing.text.StyleConstants; /** @@ -112,6 +120,214 @@ public class FormView } } + /** + * Actually submits the form data. + */ + private class SubmitThread + extends Thread + { + /** + * The submit data. + */ + private String data; + + /** + * Creates a new SubmitThread. + * + * @param d the submit data + */ + SubmitThread(String d) + { + data = d; + } + + /** + * Actually performs the submit. + */ + public void run() + { + if (data.length() > 0) + { + final String method = getMethod(); + final URL actionURL = getActionURL(); + final String target = getTarget(); + URLConnection conn; + final JEditorPane editor = (JEditorPane) getContainer(); + final HTMLDocument doc = (HTMLDocument) editor.getDocument(); + HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit(); + if (kit.isAutoFormSubmission()) + { + try + { + final URL url; + if (method != null && method.equals("post")) + { + // Perform POST. + url = actionURL; + conn = url.openConnection(); + postData(conn); + } + else + { + // Default to GET. + url = new URL(actionURL + "?" + data); + } + Runnable loadDoc = new Runnable() + { + public void run() + { + if (doc.isFrameDocument()) + { + editor.fireHyperlinkUpdate(createSubmitEvent(method, + actionURL, + target)); + } + else + { + try + { + editor.setPage(url); + } + catch (IOException ex) + { + // Oh well. + ex.printStackTrace(); + } + } + } + }; + SwingUtilities.invokeLater(loadDoc); + } + catch (MalformedURLException ex) + { + ex.printStackTrace(); + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } + else + { + editor.fireHyperlinkUpdate(createSubmitEvent(method,actionURL, + target)); + } + } + } + + /** + * Determines the submit method. + * + * @return the submit method + */ + private String getMethod() + { + AttributeSet formAtts = getFormAttributes(); + String method = null; + if (formAtts != null) + { + method = (String) formAtts.getAttribute(HTML.Attribute.METHOD); + } + return method; + } + + /** + * Determines the action URL. + * + * @return the action URL + */ + private URL getActionURL() + { + AttributeSet formAtts = getFormAttributes(); + HTMLDocument doc = (HTMLDocument) getElement().getDocument(); + URL url = doc.getBase(); + if (formAtts != null) + { + String action = + (String) formAtts.getAttribute(HTML.Attribute.ACTION); + if (action != null) + { + try + { + url = new URL(url, action); + } + catch (MalformedURLException ex) + { + url = null; + } + } + } + return url; + } + + /** + * Fetches the target attribute. + * + * @return the target attribute or _self if none is present + */ + private String getTarget() + { + AttributeSet formAtts = getFormAttributes(); + String target = null; + if (formAtts != null) + { + target = (String) formAtts.getAttribute(HTML.Attribute.TARGET); + if (target != null) + target = target.toLowerCase(); + } + if (target == null) + target = "_self"; + return target; + } + + /** + * Posts the form data over the specified connection. + * + * @param conn the connection + */ + private void postData(URLConnection conn) + { + // TODO: Implement. + } + + /** + * Determines the attributes from the relevant form tag. + * + * @return the attributes from the relevant form tag, null + * when there is no form tag + */ + private AttributeSet getFormAttributes() + { + AttributeSet atts = null; + Element form = getFormElement(); + if (form != null) + atts = form.getAttributes(); + return atts; + } + + /** + * Creates the submit event that should be fired. + * + * This is package private to avoid accessor methods. + * + * @param method the submit method + * @param actionURL the action URL + * @param target the target + * + * @return the submit event + */ + FormSubmitEvent createSubmitEvent(String method, URL actionURL, + String target) + { + FormSubmitEvent.MethodType m = "post".equals(method) + ? FormSubmitEvent.MethodType.POST + : FormSubmitEvent.MethodType.GET; + return new FormSubmitEvent(FormView.this, + HyperlinkEvent.EventType.ACTIVATED, + actionURL, getElement(), target, m, data); + } + } + /** * If the value attribute of an <input type="submit">> * tag is not specified, then this string is used. @@ -340,7 +556,7 @@ public class FormView AttributeSet atts = el.getAttributes(); String type = (String) atts.getAttribute(HTML.Attribute.TYPE); if (type.equals("submit")) - submitData(""); // FIXME: How to fetch the actual form data? + submitData(getFormData()); } // FIXME: Implement the remaining actions. } @@ -353,7 +569,8 @@ public class FormView */ protected void submitData(String data) { - // FIXME: Implement this. + SubmitThread submitThread = new SubmitThread(data); + submitThread.start(); } /** @@ -390,4 +607,140 @@ public class FormView } return data; } + + /** + * Determines and returns the enclosing form element if there is any. + * + * This is package private to avoid accessor methods. + * + * @return the enclosing form element, or null if there is no + * enclosing form element + */ + Element getFormElement() + { + Element form = null; + Element el = getElement(); + while (el != null && form == null) + { + AttributeSet atts = el.getAttributes(); + if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FORM) + form = el; + else + el = el.getParentElement(); + } + return form; + } + + /** + * Determines the form data that is about to be submitted. + * + * @return the form data + */ + private String getFormData() + { + Element form = getFormElement(); + StringBuilder b = new StringBuilder(); + if (form != null) + { + ElementIterator i = new ElementIterator(form); + Element next; + while ((next = i.next()) != null) + { + if (next.isLeaf()) + { + AttributeSet atts = next.getAttributes(); + String type = (String) atts.getAttribute(HTML.Attribute.TYPE); + if (type != null && type.equals("submit") + && next != getElement()) + { + // Skip this. This is not the actual submit trigger. + } + else if (type == null || ! type.equals("image")) + { + getElementFormData(next, b); + } + } + } + } + return b.toString(); + } + + /** + * Fetches the form data from the specified element and appends it to + * the data string. + * + * @param el the element from which to fetch form data + * @param b the data string + */ + private void getElementFormData(Element el, StringBuilder b) + { + AttributeSet atts = el.getAttributes(); + String name = (String) atts.getAttribute(HTML.Attribute.NAME); + if (name != null) + { + String value = null; + HTML.Tag tag = (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute); + if (tag == HTML.Tag.INPUT) + value = getInputFormData(atts); + // TODO: Implement textarea and select. + if (name != null && value != null) + { + addData(b, name, value); + } + } + } + + /** + * Fetches form data from an input tag. + * + * @param atts the attributes from which to fetch the data + * + * @return the field value + */ + private String getInputFormData(AttributeSet atts) + { + String type = (String) atts.getAttribute(HTML.Attribute.TYPE); + Object model = atts.getAttribute(StyleConstants.ModelAttribute); + String value = null; + if (type.equals("text") || type.equals("password")) + { + Document doc = (Document) model; + try + { + value = doc.getText(0, doc.getLength()); + } + catch (BadLocationException ex) + { + // Sigh. + assert false; + } + } + else if (type.equals("hidden") || type.equals("submit")) + { + value = (String) atts.getAttribute(HTML.Attribute.VALUE); + if (value == null) + value = ""; + } + // TODO: Implement the others. radio, checkbox and file. + return value; + } + + /** + * Actually adds the specified data to the string. It URL encodes + * the name and value and handles separation of the fields. + * + * @param b the string at which the form data to be added + * @param name the name of the field + * @param value the value + */ + private void addData(StringBuilder b, String name, String value) + { + if (b.length() > 0) + b.append('&'); + String encName = URLEncoder.encode(name); + b.append(encName); + b.append('='); + String encValue = URLEncoder.encode(value); + b.append(encValue); + } } diff --git a/javax/swing/text/html/FrameSetView.java b/javax/swing/text/html/FrameSetView.java new file mode 100644 index 000000000..e3252d79c --- /dev/null +++ b/javax/swing/text/html/FrameSetView.java @@ -0,0 +1,274 @@ +/* FrameSetView.java -- Implements HTML frameset + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import java.util.StringTokenizer; + +import javax.swing.text.AttributeSet; +import javax.swing.text.BoxView; +import javax.swing.text.Element; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; + +/** + * Implements HTML framesets. This is implemented as a vertical box that + * holds the rows of the frameset. Each row is again a horizontal box that + * holds the actual columns. + */ +public class FrameSetView + extends BoxView +{ + + /** + * A row of a frameset. + */ + private class FrameSetRow + extends BoxView + { + private int row; + FrameSetRow(Element el, int r) + { + super(el, X_AXIS); + row = r; + } + + protected void loadChildren(ViewFactory f) + { + // Load the columns here. + Element el = getElement(); + View[] columns = new View[numViews[X_AXIS]]; + int offset = row * numViews[X_AXIS]; + for (int c = 0; c < numViews[X_AXIS]; c++) + { + Element child = el.getElement(offset + c); + columns[c] = f.create(child); + } + replace(0, 0, columns); + } + + protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, + int[] spans) + { + int numRows = numViews[X_AXIS]; + int[] abs = absolute[X_AXIS]; + int[] rel = relative[X_AXIS]; + int[] perc = percent[X_AXIS]; + layoutViews(targetSpan, axis, offsets, spans, numRows, abs, rel, perc); + } + } + + /** + * Holds the absolute layout information for the views along one axis. The + * indices are absolute[axis][index], where axis is either X_AXIS (columns) + * or Y_AXIS (rows). Rows or columns that don't have absolute layout have + * a -1 in this array. + */ + int[][] absolute; + + /** + * Holds the relative (*) layout information for the views along one axis. + * The indices are relative[axis][index], where axis is either X_AXIS + * (columns) or Y_AXIS (rows). Rows or columns that don't have relative + * layout have a Float.NaN in this array. + */ + int[][] relative; + + /** + * Holds the relative (%) layout information for the views along one axis. + * The indices are relative[axis][index], where axis is either X_AXIS + * (columns) or Y_AXIS (rows). Rows or columns that don't have relative + * layout have a Float.NaN in this array. + * + * The percentage is divided by 100 so that we hold the actual fraction here. + */ + int[][] percent; + + /** + * The number of children in each direction. + */ + int[] numViews; + + FrameSetView(Element el) + { + super(el, Y_AXIS); + numViews = new int[2]; + absolute = new int[2][]; + relative = new int[2][]; + percent = new int[2][]; + } + + /** + * Loads the children and places them inside the grid. + */ + protected void loadChildren(ViewFactory f) + { + parseRowsCols(); + // Set up the rows. + View[] rows = new View[numViews[Y_AXIS]]; + for (int r = 0; r < numViews[Y_AXIS]; r++) + { + rows[r] = new FrameSetRow(getElement(), r); + } + replace(0, 0, rows); + } + + /** + * Parses the rows and cols attributes and sets up the layout info. + */ + private void parseRowsCols() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + String cols = (String) atts.getAttribute(HTML.Attribute.COLS); + if (cols == null) // Defaults to '100%' when not specified. + cols = "100%"; + parseLayout(cols, X_AXIS); + String rows = (String) atts.getAttribute(HTML.Attribute.ROWS); + if (rows == null) // Defaults to '100%' when not specified. + rows = "100%"; + parseLayout(rows, Y_AXIS); + } + + /** + * Parses the cols or rows attribute and places the layout info in the + * appropriate arrays. + * + * @param att the attributes to parse + * @param axis the axis + */ + private void parseLayout(String att, int axis) + { + StringTokenizer tokens = new StringTokenizer(att, ","); + numViews[axis] = tokens.countTokens(); + absolute[axis] = new int[numViews[axis]]; + relative[axis] = new int[numViews[axis]]; + percent[axis] = new int[numViews[axis]]; + for (int index = 0; tokens.hasMoreTokens(); index++) + { + String token = tokens.nextToken(); + int p = token.indexOf('%'); + int s = token.indexOf('*'); + if (p != -1) + { + // Percent value. + String number = token.substring(0, p); + try + { + percent[axis][index] = Integer.parseInt(number); + } + catch (NumberFormatException ex) + { + // Leave value as 0 then. + } + } + else if (s != -1) + { + // Star relative value. + String number = token.substring(0, s); + try + { + relative[axis][index] = Integer.parseInt(number); + } + catch (NumberFormatException ex) + { + // Leave value as 0 then. + } + } + else + { + // Absolute value. + try + { + absolute[axis][index] = Integer.parseInt(token); + } + catch (NumberFormatException ex) + { + // Leave value as 0 then. + } + } + } + } + + protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, + int[] spans) + { + int numRows = numViews[Y_AXIS]; + int[] abs = absolute[Y_AXIS]; + int[] rel = relative[Y_AXIS]; + int[] perc = percent[Y_AXIS]; + layoutViews(targetSpan, axis, offsets, spans, numRows, abs, rel, perc); + } + + void layoutViews(int targetSpan, int axis, int[] offsets, int[] spans, + int numViews, int[] abs, int[] rel, int[] perc) + { + // We need two passes. In the first pass we layout the absolute and + // percent values and accumulate the needed space. In the second pass + // the relative values are distributed and the offsets are set. + int total = 0; + int relTotal = 0; + for (int i = 0; i < numViews; i++) + { + if (abs[i] > 0) + { + spans[i] = abs[i]; + total += spans[i]; + } + else if (perc[i] > 0) + { + spans[i] = (targetSpan * perc[i]) / 100; + total += spans[i]; + } + else if (rel[i] > 0) + { + relTotal += rel[i]; + } + } + int offs = 0; + for (int i = 0; i < numViews; i++) + { + if (relTotal > 0 && rel[i] > 0) + { + spans[i] = targetSpan * (rel[i] / relTotal); + } + offsets[i] = offs; + offs += spans[i]; + } + } +} diff --git a/javax/swing/text/html/FrameView.java b/javax/swing/text/html/FrameView.java new file mode 100644 index 000000000..cd4e44a98 --- /dev/null +++ b/javax/swing/text/html/FrameView.java @@ -0,0 +1,233 @@ +/* FrameView.java -- Renders HTML frame tags + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import java.awt.Component; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.swing.JEditorPane; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.AttributeSet; +import javax.swing.text.ComponentView; +import javax.swing.text.Element; +import javax.swing.text.View; + +/** + * A view that is responsible for rendering HTML frame tags. + * This is accomplished by a specialized {@link ComponentView} + * that embeds a JEditorPane with an own document. + */ +class FrameView + extends ComponentView + implements HyperlinkListener +{ + + /** + * Creates a new FrameView for the specified element. + * + * @param el the element for the view + */ + FrameView(Element el) + { + super(el); + } + + /** + * Creates the element that will be embedded in the view. + * This will be a JEditorPane with the appropriate content set. + * + * @return the element that will be embedded in the view + */ + protected Component createComponent() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + JEditorPane html = new JEditorPane(); + html.addHyperlinkListener(this); + URL base = ((HTMLDocument) el.getDocument()).getBase(); + String srcAtt = (String) atts.getAttribute(HTML.Attribute.SRC); + if (srcAtt != null && ! srcAtt.equals("")) + { + try + { + URL page = new URL(base, srcAtt); + html.setPage(page); + ((HTMLDocument) html.getDocument()).setFrameDocument(true); + } + catch (MalformedURLException ex) + { + // Leave page empty. + } + catch (IOException ex) + { + // Leave page empty. + } + } + return html; + } + + /** + * Catches hyperlink events on that frame's editor and forwards it to + * the outermost editorpane. + */ + public void hyperlinkUpdate(HyperlinkEvent event) + { + JEditorPane outer = getTopEditorPane(); + if (outer != null) + { + if (event instanceof HTMLFrameHyperlinkEvent) + { + HTMLFrameHyperlinkEvent hfhe = (HTMLFrameHyperlinkEvent) event; + if (hfhe.getEventType() == HyperlinkEvent.EventType.ACTIVATED) + { + String target = hfhe.getTarget(); + if (event instanceof FormSubmitEvent) + { + handleFormSubmitEvent(hfhe, outer, target); + } + else // No FormSubmitEvent. + { + handleHyperlinkEvent(hfhe, outer, target); + } + } + } + else + { + // Simply forward this event. + outer.fireHyperlinkUpdate(event); + } + } + } + + /** + * Handles normal hyperlink events. + * + * @param event the event + * @param outer the top editor + * @param target the target + */ + private void handleHyperlinkEvent(HyperlinkEvent event, + JEditorPane outer, String target) + { + if (target.equals("_top")) + { + try + { + outer.setPage(event.getURL()); + } + catch (IOException ex) + { + // Well... + ex.printStackTrace(); + } + } + if (! outer.isEditable()) + { + outer.fireHyperlinkUpdate + (new HTMLFrameHyperlinkEvent(outer, + event.getEventType(), + event.getURL(), + event.getDescription(), + getElement(), + target)); + } + } + + /** + * Handles form submit events. + * + * @param event the event + * @param outer the top editor + * @param target the target + */ + private void handleFormSubmitEvent(HTMLFrameHyperlinkEvent event, + JEditorPane outer, + String target) + { + HTMLEditorKit kit = (HTMLEditorKit) outer.getEditorKit(); + if (kit != null && kit.isAutoFormSubmission()) + { + if (target.equals("_top")) + { + try + { + outer.setPage(event.getURL()); + } + catch (IOException ex) + { + // Well... + ex.printStackTrace(); + } + } + else + { + HTMLDocument doc = + (HTMLDocument) outer.getDocument(); + doc.processHTMLFrameHyperlinkEvent(event); + } + } + else + { + outer.fireHyperlinkUpdate(event); + } + } + + /** + * Determines the topmost editor in a nested frameset. + * + * @return the topmost editor in a nested frameset + */ + private JEditorPane getTopEditorPane() + { + View parent = getParent(); + View top = null; + while (parent != null) + { + if (parent instanceof FrameSetView) + top = parent; + } + JEditorPane editor = null; + if (top != null) + editor = (JEditorPane) top.getContainer(); + return editor; + } +} diff --git a/javax/swing/text/html/HTML.java b/javax/swing/text/html/HTML.java index b803e9637..93c05daa2 100644 --- a/javax/swing/text/html/HTML.java +++ b/javax/swing/text/html/HTML.java @@ -464,6 +464,16 @@ public class HTML */ public static final Attribute WIDTH = new Attribute("width"); + /** + * This is used to reflect the pseudo class for the a tag. + */ + static final Attribute PSEUDO_CLASS = new Attribute("_pseudo"); + + /** + * This is used to reflect the dynamic class for the a tag. + */ + static final Attribute DYNAMIC_CLASS = new Attribute("_dynamic"); + /** * The attribute name. */ diff --git a/javax/swing/text/html/HTMLDocument.java b/javax/swing/text/html/HTMLDocument.java index ee59d7025..25b44615e 100644 --- a/javax/swing/text/html/HTMLDocument.java +++ b/javax/swing/text/html/HTMLDocument.java @@ -52,6 +52,8 @@ import java.util.Vector; import javax.swing.DefaultButtonModel; import javax.swing.JEditorPane; import javax.swing.JToggleButton; +import javax.swing.event.DocumentEvent; +import javax.swing.event.UndoableEditEvent; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; @@ -89,7 +91,17 @@ public class HTMLDocument extends DefaultStyledDocument boolean preservesUnknownTags = true; int tokenThreshold = Integer.MAX_VALUE; HTMLEditorKit.Parser parser; - + + /** + * Indicates whether this document is inside a frame or not. + */ + private boolean frameDocument; + + /** + * Package private to avoid accessor methods. + */ + String baseTarget; + /** * Constructs an HTML document using the default buffer size and a default * StyleSheet. @@ -365,11 +377,119 @@ public class HTMLDocument extends DefaultStyledDocument } public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent event) - throws NotImplementedException { - // TODO: Implement this properly. + String target = event.getTarget(); + Element el = event.getSourceElement(); + URL url = event.getURL(); + if (target.equals("_self")) + { + updateFrame(el, url); + } + else if (target.equals("_parent")) + { + updateFrameSet(el.getParentElement(), url); + } + else + { + Element targetFrame = findFrame(target); + if (targetFrame != null) + updateFrame(targetFrame, url); + } } - + + /** + * Finds the named frame inside this document. + * + * @param target the name to look for + * + * @return the frame if there is a matching frame, null + * otherwise + */ + private Element findFrame(String target) + { + ElementIterator i = new ElementIterator(this); + Element next = null; + while ((next = i.next()) != null) + { + AttributeSet atts = next.getAttributes(); + if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FRAME) + { + String name = (String) atts.getAttribute(HTML.Attribute.NAME); + if (name != null && name.equals(target)) + break; + } + } + return next; + } + + /** + * Updates the frame that is represented by the specified element to + * refer to the specified URL. + * + * @param el the element + * @param url the new url + */ + private void updateFrame(Element el, URL url) + { + try + { + writeLock(); + DefaultDocumentEvent ev = + new DefaultDocumentEvent(el.getStartOffset(), 1, + DocumentEvent.EventType.CHANGE); + AttributeSet elAtts = el.getAttributes(); + AttributeSet copy = elAtts.copyAttributes(); + MutableAttributeSet matts = (MutableAttributeSet) elAtts; + ev.addEdit(new AttributeUndoableEdit(el, copy, false)); + matts.removeAttribute(HTML.Attribute.SRC); + matts.addAttribute(HTML.Attribute.SRC, url.toString()); + ev.end(); + fireChangedUpdate(ev); + fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); + } + finally + { + writeUnlock(); + } + } + + /** + * Updates the frameset that is represented by the specified element + * to create a frame that refers to the specified URL. + * + * @param el the element + * @param url the url + */ + private void updateFrameSet(Element el, URL url) + { + int start = el.getStartOffset(); + int end = el.getEndOffset(); + + StringBuilder html = new StringBuilder(); + html.append("'); + if (getParser() == null) + setParser(new HTMLEditorKit().getParser()); + try + { + setOuterHTML(el, html.toString()); + } + catch (BadLocationException ex) + { + ex.printStackTrace(); + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } + /** * Gets an iterator for the given HTML.Tag. * @param t the requested HTML.Tag @@ -529,8 +649,13 @@ public class HTMLDocument extends DefaultStyledDocument */ protected MutableAttributeSet charAttr = new SimpleAttributeSet(); - protected Vector parseBuffer = new Vector(); - + protected Vector parseBuffer = new Vector(); + + /** + * The parse stack. It holds the current element tree path. + */ + private Stack parseStack = new Stack(); + /** * A stack for character attribute sets * */ @@ -576,16 +701,6 @@ public class HTMLDocument extends DefaultStyledDocument */ boolean inPreTag = false; - /** - * True when we are inside a paragraph (P, H1-H6, P-IMPLIED). - */ - boolean inParagraph = false; - - /** - * True when we are currently inside an implied paragraph. - */ - boolean inImpliedParagraph = false; - /** * This is true when we are inside a style tag. This will add text * content inside this style tag beeing parsed as CSS. @@ -676,6 +791,10 @@ public class HTMLDocument extends DefaultStyledDocument // Put the old attribute set on the stack. pushCharacterStyle(); + // Initialize with link pseudo class. + if (t == HTML.Tag.A) + a.addAttribute(HTML.Attribute.PSEUDO_CLASS, "link"); + // Just add the attributes in a. charAttr.addAttribute(t, a.copyAttributes()); } @@ -828,7 +947,6 @@ public class HTMLDocument extends DefaultStyledDocument public void start(HTML.Tag t, MutableAttributeSet a) { super.start(t, a); - inParagraph = true; } /** @@ -838,7 +956,6 @@ public class HTMLDocument extends DefaultStyledDocument public void end(HTML.Tag t) { super.end(t); - inParagraph = false; } } @@ -954,20 +1071,9 @@ public class HTMLDocument extends DefaultStyledDocument * of tags associated with this Action. */ public void start(HTML.Tag t, MutableAttributeSet a) - throws NotImplementedException { - // FIXME: Implement. + baseTarget = (String) a.getAttribute(HTML.Attribute.TARGET); } - - /** - * Called when an end tag is seen for one of the types of tags associated - * with this Action. - */ - public void end(HTML.Tag t) - throws NotImplementedException - { - // FIXME: Implement. - } } class HeadAction extends BlockAction @@ -1529,9 +1635,12 @@ public class HTMLDocument extends DefaultStyledDocument */ protected void blockOpen(HTML.Tag t, MutableAttributeSet attr) { - if (inImpliedParagraph) + if (inImpliedParagraph()) blockClose(HTML.Tag.IMPLIED); + // Push the new tag on top of the stack. + parseStack.push(t); + DefaultStyledDocument.ElementSpec element; AbstractDocument.AttributeContext ctx = getAttributeContext(); @@ -1542,6 +1651,34 @@ public class HTMLDocument extends DefaultStyledDocument parseBuffer.addElement(element); } + /** + * Returns true when we are currently inside a paragraph, either + * a real one or an implied, false otherwise. + * + * @return + */ + private boolean inParagraph() + { + boolean inParagraph = false; + if (! parseStack.isEmpty()) + { + HTML.Tag top = parseStack.peek(); + inParagraph = top == HTML.Tag.P || top == HTML.Tag.IMPLIED; + } + return inParagraph; + } + + private boolean inImpliedParagraph() + { + boolean inParagraph = false; + if (! parseStack.isEmpty()) + { + HTML.Tag top = parseStack.peek(); + inParagraph = top == HTML.Tag.IMPLIED; + } + return inParagraph; + } + /** * Instructs the parse buffer to close the block element associated with * the given HTML.Tag @@ -1552,13 +1689,12 @@ public class HTMLDocument extends DefaultStyledDocument { DefaultStyledDocument.ElementSpec element; - if (inImpliedParagraph) - { - inImpliedParagraph = false; - inParagraph = false; - if (t != HTML.Tag.IMPLIED) - blockClose(HTML.Tag.IMPLIED); - } + if (inImpliedParagraph() && t != HTML.Tag.IMPLIED) + blockClose(HTML.Tag.IMPLIED); + + // Pull the token from the stack. + if (! parseStack.isEmpty()) // Just to be sure. + parseStack.pop(); // If the previous tag is a start tag then we insert a synthetic // content tag. @@ -1602,11 +1738,9 @@ public class HTMLDocument extends DefaultStyledDocument protected void addContent(char[] data, int offs, int length, boolean generateImpliedPIfNecessary) { - if (generateImpliedPIfNecessary && (! inParagraph) && (! inPreTag)) + if (generateImpliedPIfNecessary && ! inParagraph()) { blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); - inParagraph = true; - inImpliedParagraph = true; } AbstractDocument.AttributeContext ctx = getAttributeContext(); @@ -1651,11 +1785,9 @@ public class HTMLDocument extends DefaultStyledDocument */ protected void addSpecialElement(HTML.Tag t, MutableAttributeSet a) { - if (t != HTML.Tag.FRAME && ! inParagraph && ! inImpliedParagraph) + if (t != HTML.Tag.FRAME && ! inParagraph()) { blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); - inParagraph = true; - inImpliedParagraph = true; } a.addAttribute(StyleConstants.NameAttribute, t); @@ -1666,7 +1798,7 @@ public class HTMLDocument extends DefaultStyledDocument DefaultStyledDocument.ElementSpec spec = new DefaultStyledDocument.ElementSpec(a.copyAttributes(), DefaultStyledDocument.ElementSpec.ContentType, - new char[] {' ', ' '}, 0, 2 ); + new char[] {' '}, 0, 1 ); parseBuffer.add(spec); } @@ -1955,4 +2087,83 @@ public void setOuterHTML(Element elem, String htmlText) } super.insertUpdate(evt, att); } + + /** + * Returns true when this document is inside a frame, + * false otherwise. + * + * @return true when this document is inside a frame, + * false otherwise + */ + boolean isFrameDocument() + { + return frameDocument; + } + + /** + * Set true when this document is inside a frame, + * false otherwise. + * + * @param frameDoc true when this document is inside a frame, + * false otherwise + */ + void setFrameDocument(boolean frameDoc) + { + frameDocument = frameDoc; + } + + /** + * Returns the target that is specified in the base tag, if this is the case. + * + * @return the target that is specified in the base tag, if this is the case + */ + String getBaseTarget() + { + return baseTarget; + } + + /** + * Updates the A tag's pseudo class value in response to a hyperlink + * action. + * + * @param el the corresponding element + * @param value the new value + */ + void updateSpecialClass(Element el, HTML.Attribute cl, String value) + { + try + { + writeLock(); + DefaultDocumentEvent ev = + new DefaultDocumentEvent(el.getStartOffset(), 1, + DocumentEvent.EventType.CHANGE); + AttributeSet elAtts = el.getAttributes(); + AttributeSet anchorAtts = (AttributeSet) elAtts.getAttribute(HTML.Tag.A); + if (anchorAtts != null) + { + AttributeSet copy = elAtts.copyAttributes(); + StyleSheet ss = getStyleSheet(); + if (value != null) + { + anchorAtts = ss.addAttribute(anchorAtts, cl, value); + } + else + { + anchorAtts = ss.removeAttribute(anchorAtts, cl); + } + MutableAttributeSet matts = (MutableAttributeSet) elAtts; + ev.addEdit(new AttributeUndoableEdit(el, copy, false)); + matts.removeAttribute(HTML.Tag.A); + matts.addAttribute(HTML.Tag.A, anchorAtts); + ev.end(); + fireChangedUpdate(ev); + fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); + } + } + finally + { + writeUnlock(); + } + } + } diff --git a/javax/swing/text/html/HTMLEditorKit.java b/javax/swing/text/html/HTMLEditorKit.java index f3a3d90b6..3b122bb36 100644 --- a/javax/swing/text/html/HTMLEditorKit.java +++ b/javax/swing/text/html/HTMLEditorKit.java @@ -98,7 +98,12 @@ public class HTMLEditorKit extends MouseAdapter implements MouseMotionListener, Serializable { - + + /** + * The element of the last anchor tag. + */ + private Element lastAnchorElement; + /** * Constructor */ @@ -162,10 +167,41 @@ public class HTMLEditorKit AttributeSet aAtts = (AttributeSet) el.getAttributes().getAttribute(HTML.Tag.A); if (aAtts != null) - newCursor = kit.getLinkCursor(); + { + if (el != lastAnchorElement) + { + if (lastAnchorElement != null) + htmlDoc.updateSpecialClass(lastAnchorElement, + HTML.Attribute.DYNAMIC_CLASS, + null); + lastAnchorElement = el; + htmlDoc.updateSpecialClass(el, + HTML.Attribute.DYNAMIC_CLASS, + "hover"); + } + newCursor = kit.getLinkCursor(); + } + else + { + if (lastAnchorElement != null) + htmlDoc.updateSpecialClass(lastAnchorElement, + HTML.Attribute.DYNAMIC_CLASS, + null); + lastAnchorElement = null; + } + } + else + { + if (lastAnchorElement != null) + htmlDoc.updateSpecialClass(lastAnchorElement, + HTML.Attribute.DYNAMIC_CLASS, + null); + lastAnchorElement = null; } if (editor.getCursor() != newCursor) - editor.setCursor(newCursor); + { + editor.setCursor(newCursor); + } } } } @@ -198,6 +234,8 @@ public class HTMLEditorKit if (anchorAtts != null) { href = (String) anchorAtts.getAttribute(HTML.Attribute.HREF); + htmlDoc.updateSpecialClass(el, HTML.Attribute.PSEUDO_CLASS, + "visited"); } else { @@ -243,10 +281,25 @@ public class HTMLEditorKit { url = null; } - // TODO: Handle frame documents and target here. - HyperlinkEvent ev = - new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED, url, - href, el); + HyperlinkEvent ev; + if (doc.isFrameDocument()) + { + String target = null; + if (anchor != null) + target = (String) anchor.getAttribute(HTML.Attribute.TARGET); + if (target == null || target.equals("")) + target = doc.getBaseTarget(); + if (target == null || target.equals("")) + target = "_self"; + ev = new HTMLFrameHyperlinkEvent(editor, + HyperlinkEvent.EventType.ACTIVATED, + url, href, el, target); + } + else + { + ev = new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED, + url, href, el); + } return ev; } } @@ -773,14 +826,14 @@ public class HTMLEditorKit else if (tag.equals(HTML.Tag.MENU) || tag.equals(HTML.Tag.DIR) || tag.equals(HTML.Tag.UL) || tag.equals(HTML.Tag.OL)) view = new ListView(element); - // FIXME: Uncomment when the views have been implemented - /* - else if (tag.equals(HTML.Tag.OBJECT)) - view = new ObjectView(element); else if (tag.equals(HTML.Tag.FRAMESET)) view = new FrameSetView(element); else if (tag.equals(HTML.Tag.FRAME)) - view = new FrameView(element); */ + view = new FrameView(element); + // FIXME: Uncomment when the views have been implemented + /* + else if (tag.equals(HTML.Tag.OBJECT)) + view = new ObjectView(element); */ } if (view == null) { @@ -1065,13 +1118,22 @@ public class HTMLEditorKit /** The editor pane used. */ JEditorPane editorPane; - + + /** + * Whether or not the editor kit handles form submissions. + * + * @see #isAutoFormSubmission() + * @see #setAutoFormSubmission(boolean) + */ + private boolean autoFormSubmission; + /** * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet. */ public HTMLEditorKit() { linkController = new LinkController(); + autoFormSubmission = true; } /** @@ -1416,4 +1478,39 @@ public class HTMLEditorKit styleSheet = s; } + /** + * Returns true when forms should be automatically submitted + * by the editor kit. Set this to false when you want to + * intercept form submission. In this case you'd want to listen for + * hyperlink events on the document and handle FormSubmitEvents specially. + * + * The default is true. + * + * @return true when forms should be automatically submitted + * by the editor kit, false otherwise + * + * @since 1.5 + * + * @see #setAutoFormSubmission(boolean) + * @see FormSubmitEvent + */ + public boolean isAutoFormSubmission() + { + return autoFormSubmission; + } + + /** + * Sets whether or not the editor kit should automatically submit forms. + * + * @param auto true when the editor kit should handle form + * submission, false otherwise + * + * @since 1.5 + * + * @see #isAutoFormSubmission() + */ + public void setAutoFormSubmission(boolean auto) + { + autoFormSubmission = auto; + } } diff --git a/javax/swing/text/html/ImageView.java b/javax/swing/text/html/ImageView.java index f073c6d05..050eb16e2 100644 --- a/javax/swing/text/html/ImageView.java +++ b/javax/swing/text/html/ImageView.java @@ -110,6 +110,16 @@ public class ImageView extends View */ private ImageObserver observer; + /** + * The CSS width and height. + */ + private Length[] spans; + + /** + * The cached attributes. + */ + private AttributeSet attributes; + /** * Creates the image view that represents the given element. * @@ -221,12 +231,9 @@ public class ImageView extends View */ public AttributeSet getAttributes() { - StyleSheet styles = getStyleSheet(); - if (styles == null) - return super.getAttributes(); - else - return CombinedAttributes.combine(super.getAttributes(), - styles.getViewAttributes(this)); + if (attributes == null) + attributes = getStyleSheet().getViewAttributes(this); + return attributes; } /** @@ -318,9 +325,8 @@ public class ImageView extends View if (axis == View.X_AXIS) { - Object w = attrs.getAttribute(CSS.Attribute.WIDTH); - if (w instanceof Length) - return ((Length) w).getValue(); + if (spans[axis] != null) + return spans[axis].getValue(); else if (image != null) return image.getWidth(getContainer()); else @@ -328,9 +334,8 @@ public class ImageView extends View } else if (axis == View.Y_AXIS) { - Object w = attrs.getAttribute(CSS.Attribute.HEIGHT); - if (w instanceof Length) - return ((Length) w).getValue(); + if (spans[axis] != null) + return spans[axis].getValue(); else if (image != null) return image.getHeight(getContainer()); else @@ -347,11 +352,8 @@ public class ImageView extends View */ protected StyleSheet getStyleSheet() { - Document d = getElement().getDocument(); - if (d instanceof HTMLDocument) - return ((HTMLDocument) d).getStyleSheet(); - else - return null; + HTMLDocument doc = (HTMLDocument) getDocument(); + return doc.getStyleSheet(); } /** @@ -410,7 +412,21 @@ public class ImageView extends View */ protected void setPropertiesFromAttributes() { - // FIXME: Implement this properly. + AttributeSet atts = getAttributes(); + StyleSheet ss = getStyleSheet(); + float emBase = ss.getEMBase(atts); + float exBase = ss.getEXBase(atts); + spans = new Length[2]; + spans[X_AXIS] = (Length) atts.getAttribute(CSS.Attribute.WIDTH); + if (spans[X_AXIS] != null) + { + spans[X_AXIS].setFontBases(emBase, exBase); + } + spans[Y_AXIS] = (Length) atts.getAttribute(CSS.Attribute.HEIGHT); + if (spans[Y_AXIS] != null) + { + spans[Y_AXIS].setFontBases(emBase, exBase); + } } /** @@ -503,7 +519,7 @@ public class ImageView extends View { AttributeSet atts = getAttributes(); // Fetch width. - Length l = (Length) atts.getAttribute(CSS.Attribute.WIDTH); + Length l = spans[X_AXIS]; if (l != null) { newW = (int) l.getValue(); @@ -514,7 +530,7 @@ public class ImageView extends View newW = newIm.getWidth(observer); } // Fetch height. - l = (Length) atts.getAttribute(CSS.Attribute.HEIGHT); + l = spans[Y_AXIS]; if (l != null) { newH = (int) l.getValue(); diff --git a/javax/swing/text/html/InlineView.java b/javax/swing/text/html/InlineView.java index 6b134ae39..cea0782b1 100644 --- a/javax/swing/text/html/InlineView.java +++ b/javax/swing/text/html/InlineView.java @@ -139,6 +139,7 @@ public class InlineView StyleSheet ss = getStyleSheet(); attributes = ss.getViewAttributes(this); preferenceChanged(null, true, true); + setPropertiesFromAttributes(); } /** @@ -303,4 +304,5 @@ public class InlineView } return span; } + } diff --git a/javax/swing/text/html/ParagraphView.java b/javax/swing/text/html/ParagraphView.java index 8443515d3..d149627ff 100644 --- a/javax/swing/text/html/ParagraphView.java +++ b/javax/swing/text/html/ParagraphView.java @@ -153,8 +153,15 @@ public class ParagraphView (short) painter.getInset(BOTTOM, this), (short) painter.getInset(RIGHT, this)); + StyleSheet ss = getStyleSheet(); + float emBase = ss.getEMBase(attributes); + float exBase = ss.getEXBase(attributes); cssWidth = (Length) attributes.getAttribute(CSS.Attribute.WIDTH); + if (cssWidth != null) + cssWidth.setFontBases(emBase, exBase); cssHeight = (Length) attributes.getAttribute(CSS.Attribute.WIDTH); + if (cssHeight != null) + cssHeight.setFontBases(emBase, exBase); } } diff --git a/javax/swing/text/html/StyleSheet.java b/javax/swing/text/html/StyleSheet.java index 3322a390c..15384e16d 100644 --- a/javax/swing/text/html/StyleSheet.java +++ b/javax/swing/text/html/StyleSheet.java @@ -52,6 +52,8 @@ import java.awt.Font; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.geom.Rectangle2D; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -66,6 +68,7 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import javax.swing.border.Border; import javax.swing.event.ChangeListener; @@ -300,11 +303,21 @@ public class StyleSheet extends StyleContext selector.append('#'); selector.append(atts.getAttribute(HTML.Attribute.ID)); } - else if (atts.isDefined(HTML.Attribute.CLASS)) + if (atts.isDefined(HTML.Attribute.CLASS)) { selector.append('.'); selector.append(atts.getAttribute(HTML.Attribute.CLASS)); } + if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS)) + { + selector.append(':'); + selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS)); + } + if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS)) + { + selector.append(':'); + selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS)); + } selector.append(' '); } selector.append(t.toString()); @@ -326,11 +339,21 @@ public class StyleSheet extends StyleContext selector.append('#'); selector.append(atts.getAttribute(HTML.Attribute.ID)); } - else if (atts.isDefined(HTML.Attribute.CLASS)) + if (atts.isDefined(HTML.Attribute.CLASS)) { selector.append('.'); selector.append(atts.getAttribute(HTML.Attribute.CLASS)); } + if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS)) + { + selector.append(':'); + selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS)); + } + if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS)) + { + selector.append(':'); + selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS)); + } } return getResolvedStyle(selector.toString(), path, t); } @@ -357,7 +380,7 @@ public class StyleSheet extends StyleContext /** * Resolves a style. This creates arrays that hold the tag names, * class and id attributes and delegates the work to - * {@link #resolveStyle(String, String[], String[], String[])}. + * {@link #resolveStyle(String, String[], Map[])}. * * @param selector the selector * @param path the Element path @@ -369,8 +392,7 @@ public class StyleSheet extends StyleContext { int count = path.size(); String[] tags = new String[count]; - String[] ids = new String[count]; - String[] classes = new String[count]; + Map[] attributes = new Map[count]; for (int i = 0; i < count; i++) { Element el = (Element) path.get(i); @@ -391,24 +413,16 @@ public class StyleSheet extends StyleContext tags[i] = t.toString(); else tags[i] = null; - if (atts.isDefined(HTML.Attribute.CLASS)) - classes[i] = atts.getAttribute(HTML.Attribute.CLASS).toString(); - else - classes[i] = null; - if (atts.isDefined(HTML.Attribute.ID)) - ids[i] = atts.getAttribute(HTML.Attribute.ID).toString(); - else - ids[i] = null; + attributes[i] = attributeSetToMap(atts); } else { tags[i] = null; - classes[i] = null; - ids[i] = null; + attributes[i] = null; } } tags[0] = tag.toString(); - return resolveStyle(selector, tags, ids, classes); + return resolveStyle(selector, tags, attributes); } /** @@ -416,13 +430,11 @@ public class StyleSheet extends StyleContext * * @param selector the selector * @param tags the tags - * @param ids the corresponding ID attributes - * @param classes the corresponding CLASS attributes + * @param attributes the attributes of the tags * * @return the resolved style */ - private Style resolveStyle(String selector, String[] tags, String[] ids, - String[] classes) + private Style resolveStyle(String selector, String[] tags, Map[] attributes) { // FIXME: This style resolver is not correct. But it works good enough for // the default.css. @@ -431,7 +443,7 @@ public class StyleSheet extends StyleContext for (Iterator i = css.iterator(); i.hasNext();) { CSSStyle style = (CSSStyle) i.next(); - if (style.selector.matches(tags, classes, ids)) + if (style.selector.matches(tags, attributes)) styles.add(style); } @@ -444,7 +456,7 @@ public class StyleSheet extends StyleContext for (int j = ss.css.size() - 1; j >= 0; j--) { CSSStyle style = (CSSStyle) ss.css.get(j); - if (style.selector.matches(tags, classes, ids)) + if (style.selector.matches(tags, attributes)) styles.add(style); } } @@ -741,8 +753,13 @@ public class StyleSheet extends StyleContext .getAttributes(); o = tableAttrs.getAttribute(HTML.Attribute.CELLPADDING); if (o != null) - cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING, - new Length(o.toString())); + { + Length l = new Length(o.toString()); + cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_BOTTOM, l); + cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_LEFT, l); + cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_RIGHT, l); + cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_TOP, l); + } } // TODO: Add more mappings. return cssAttr; @@ -882,7 +899,37 @@ public class StyleSheet extends StyleContext style |= fStyle.getValue(); return new Font(family, style, realSize); } - + + /** + * Determines the EM base value based on the specified attributes. + * + * @param atts the attibutes + * + * @return the EM base value + */ + float getEMBase(AttributeSet atts) + { + Font font = getFont(atts); + FontRenderContext ctx = new FontRenderContext(null, false, false); + Rectangle2D bounds = font.getStringBounds("M", ctx); + return (float) bounds.getWidth(); + } + + /** + * Determines the EX base value based on the specified attributes. + * + * @param atts the attibutes + * + * @return the EX base value + */ + float getEXBase(AttributeSet atts) + { + Font font = getFont(atts); + FontRenderContext ctx = new FontRenderContext(null, false, false); + Rectangle2D bounds = font.getStringBounds("x", ctx); + return (float) bounds.getHeight(); + } + /** * Resolves the fontsize for a given set of attributes. * @@ -1120,49 +1167,62 @@ public class StyleSheet extends StyleContext */ BoxPainter(AttributeSet as, StyleSheet ss) { + float emBase = ss.getEMBase(as); + float exBase = ss.getEXBase(as); // Fetch margins. - Length l = (Length) as.getAttribute(CSS.Attribute.MARGIN); + Length l = (Length) as.getAttribute(CSS.Attribute.MARGIN_LEFT); if (l != null) { - topInset = bottomInset = leftInset = rightInset = l.getValue(); + l.setFontBases(emBase, exBase); + leftInset = l.getValue(); } - l = (Length) as.getAttribute(CSS.Attribute.MARGIN_LEFT); - if (l != null) - leftInset = l.getValue(); - else if (as.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.UL) - System.err.println("UL margin left value: " + l + " atts: " + as); l = (Length) as.getAttribute(CSS.Attribute.MARGIN_RIGHT); if (l != null) - rightInset = l.getValue(); + { + l.setFontBases(emBase, exBase); + rightInset = l.getValue(); + } l = (Length) as.getAttribute(CSS.Attribute.MARGIN_TOP); if (l != null) - topInset = l.getValue(); + { + l.setFontBases(emBase, exBase); + topInset = l.getValue(); + } l = (Length) as.getAttribute(CSS.Attribute.MARGIN_BOTTOM); if (l != null) - bottomInset = l.getValue(); + { + l.setFontBases(emBase, exBase); + bottomInset = l.getValue(); + } // Fetch padding. - l = (Length) as.getAttribute(CSS.Attribute.PADDING); + l = (Length) as.getAttribute(CSS.Attribute.PADDING_LEFT); if (l != null) { - leftPadding = rightPadding = topPadding = bottomPadding = - l.getValue(); + l.setFontBases(emBase, exBase); + leftPadding = l.getValue(); } - l = (Length) as.getAttribute(CSS.Attribute.PADDING_LEFT); - if (l != null) - leftPadding = l.getValue(); l = (Length) as.getAttribute(CSS.Attribute.PADDING_RIGHT); if (l != null) - rightPadding = l.getValue(); + { + l.setFontBases(emBase, exBase); + rightPadding = l.getValue(); + } l = (Length) as.getAttribute(CSS.Attribute.PADDING_TOP); if (l != null) - topPadding = l.getValue(); + { + l.setFontBases(emBase, exBase); + topPadding = l.getValue(); + } l = (Length) as.getAttribute(CSS.Attribute.PADDING_BOTTOM); if (l != null) - bottomPadding = l.getValue(); + { + l.setFontBases(emBase, exBase); + bottomPadding = l.getValue(); + } // Determine border. - border = new CSSBorder(as); + border = new CSSBorder(as, ss); // Determine background. background = ss.getBackground(as); @@ -1229,15 +1289,18 @@ public class StyleSheet extends StyleContext */ public void paint(Graphics g, float x, float y, float w, float h, View v) { - + int inX = (int) (x + leftInset); + int inY = (int) (y + topInset); + int inW = (int) (w - leftInset - rightInset); + int inH = (int) (h - topInset - bottomInset); if (background != null) { g.setColor(background); - g.fillRect((int) x, (int) y, (int) w, (int) h); + g.fillRect(inX, inY, inW, inH); } if (border != null) { - border.paintBorder(null, g, (int) x, (int) y, (int) w, (int) h); + border.paintBorder(null, g, inX, inY, inW, inH); } } } @@ -1342,4 +1405,23 @@ public class StyleSheet extends StyleContext } } + /** + * Converts an AttributeSet to a Map. This is used for CSS resolving. + * + * @param atts the attributes to convert + * + * @return the converted map + */ + private Map attributeSetToMap(AttributeSet atts) + { + HashMap map = new HashMap(); + Enumeration keys = atts.getAttributeNames(); + while (keys.hasMoreElements()) + { + Object key = keys.nextElement(); + Object value = atts.getAttribute(key); + map.put(key.toString(), value.toString()); + } + return map; + } } diff --git a/javax/swing/text/html/TableView.java b/javax/swing/text/html/TableView.java index 971d54cb6..90b3ecc4f 100644 --- a/javax/swing/text/html/TableView.java +++ b/javax/swing/text/html/TableView.java @@ -140,6 +140,7 @@ class TableView protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int spans[]) { + updateGrid(); int numCols = offsets.length; int realColumn = 0; for (int i = 0; i < numCols; i++) @@ -254,6 +255,11 @@ class TableView */ Length[] columnWidths; + /** + * The table width. + */ + private Length width; + /** * Indicates if the grid setup is ok. */ @@ -358,13 +364,11 @@ class TableView r = super.calculateMinorAxisRequirements(axis, r); // Try to set the CSS width if it fits. - AttributeSet atts = getAttributes(); - Length l = (Length) atts.getAttribute(CSS.Attribute.WIDTH); - if (l != null) + if (width != null) { - int width = (int) l.getValue(); - if (r.minimum < width) - r.minimum = width; + int w = (int) width.getValue(); + if (r.minimum < w) + r.minimum = w; } // Adjust requirements when we have cell spacing. @@ -373,6 +377,7 @@ class TableView r.preferred += adjust; // Apply the alignment. + AttributeSet atts = getAttributes(); Object o = atts.getAttribute(CSS.Attribute.TEXT_ALIGN); r.alignment = 0.0F; if (o != null) @@ -660,11 +665,17 @@ class TableView /** * Updates the arrays that contain the row and column data in response * to a change to the table structure. + * + * Package private to avoid accessor methods. */ - private void updateGrid() + void updateGrid() { if (! gridValid) { + AttributeSet atts = getAttributes(); + StyleSheet ss = getStyleSheet(); + float emBase = ss.getEMBase(atts); + float exBase = ss.getEXBase(atts); int maxColumns = 0; int numRows = getViewCount(); for (int r = 0; r < numRows; r++) @@ -697,7 +708,10 @@ class TableView cv.getAttributes().getAttribute(CSS.Attribute.WIDTH); if (o != null && columnWidths[colIndex] == null && o instanceof Length) - columnWidths[colIndex]= (Length) o; + { + columnWidths[colIndex]= (Length) o; + columnWidths[colIndex].setFontBases(emBase, exBase); + } colIndex += cv.colSpan; } } @@ -741,12 +755,23 @@ class TableView private void setPropertiesFromAttributes() { // Fetch and parse cell spacing. - Object o = getAttributes().getAttribute(CSS.Attribute.BORDER_SPACING); + AttributeSet atts = getAttributes(); + StyleSheet ss = getStyleSheet(); + float emBase = ss.getEMBase(atts); + float exBase = ss.getEXBase(atts); + Object o = atts.getAttribute(CSS.Attribute.BORDER_SPACING); if (o != null && o instanceof Length) { Length l = (Length) o; + l.setFontBases(emBase, exBase); cellSpacing = (int) l.getValue(); } + o = atts.getAttribute(CSS.Attribute.WIDTH); + if (o != null && o instanceof Length) + { + width = (Length) o; + width.setFontBases(emBase, exBase); + } } /** -- cgit v1.2.1