/* * {{{ header & license * Copyright (c) 2005 Joshua Marinacci * Copyright (c) 2005 Wisconsin Court System * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * This program 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * }}} */ package org.xhtmlrenderer.render; import java.awt.Rectangle; import org.w3c.dom.Text; import org.xhtmlrenderer.extend.FSGlyphVector; import org.xhtmlrenderer.layout.FunctionData; import org.xhtmlrenderer.layout.LayoutContext; import org.xhtmlrenderer.layout.WhitespaceStripper; import org.xhtmlrenderer.util.Uu; /** * A lightweight object which contains a chunk of text from an inline element. * It will never extend across a line break nor will it extend across an element * nested within its inline element. */ public class InlineText { private InlineLayoutBox _parent; private int _x; private String _masterText; private int _start; private int _end; private int _width; private FunctionData _functionData; private boolean _containedLF = false; private short _selectionStart; private short _selectionEnd; private float[] _glyphPositions; private boolean _trimmedLeadingSpace; private boolean _trimmedTrailingSpace; private Text _textNode; public void trimTrailingSpace(LayoutContext c) { if (! isEmpty() && _masterText.charAt(_end-1) == ' ') { _end--; setWidth(c.getTextRenderer().getWidth(c.getFontContext(), getParent().getStyle().getFSFont(c), getSubstring())); setTrimmedTrailingSpace(true); } } public boolean isEmpty() { return _start == _end && ! _containedLF; } public String getSubstring() { if (getMasterText() != null) { if (_start == -1 || _end == -1) { throw new RuntimeException("negative index in InlineBox"); } if (_end < _start) { throw new RuntimeException("end is less than setStartStyle"); } return getMasterText().substring(_start, _end); } else { throw new RuntimeException("No master text set!"); } } public void setSubstring(int start, int end) { if (end < start) { Uu.p("setting substring to: " + start + " " + end); throw new RuntimeException("set substring length too long: " + this); } else if (end < 0 || start < 0) { throw new RuntimeException("Trying to set negative index to inline box"); } _start = start; _end = end; if (_end > 0 && _masterText.charAt(_end-1) == WhitespaceStripper.EOLC) { _containedLF = true; _end--; } } public String getMasterText() { return _masterText; } public void setMasterText(String masterText) { _masterText = masterText; } public int getX() { return _x; } public void setX(int x) { _x = x; } public int getWidth() { return _width; } public void setWidth(int width) { _width = width; } public void paint(RenderingContext c) { c.getOutputDevice().drawText(c, this); } public void paintSelection(RenderingContext c) { c.getOutputDevice().drawSelection(c, this); } public InlineLayoutBox getParent() { return _parent; } public void setParent(InlineLayoutBox parent) { _parent = parent; } public boolean isDynamicFunction() { return _functionData != null; } public FunctionData getFunctionData() { return _functionData; } public void setFunctionData(FunctionData functionData) { _functionData = functionData; } public void updateDynamicValue(RenderingContext c) { String value = _functionData.getContentFunction().calculate( c, _functionData.getFunction(), this); _start = 0; _end = value.length(); _masterText = value; _width = c.getTextRenderer().getWidth( c.getFontContext(), getParent().getStyle().getFSFont(c), value); } public String toString() { StringBuffer result = new StringBuffer(); result.append("InlineText: "); if (_containedLF || isDynamicFunction()) { result.append("("); if (_containedLF) { result.append('L'); } if (isDynamicFunction()) { result.append('F'); } result.append(") "); } result.append('('); result.append(getSubstring()); result.append(')'); return result.toString(); } public boolean updateSelection(RenderingContext c, Rectangle selection) { ensureGlyphPositions(c); float[] positions = _glyphPositions; int y = getParent().getAbsY(); int offset = getParent().getAbsX() + getX(); int prevSelectionStart = _selectionStart; int prevSelectionEnd = _selectionEnd; boolean found = false; _selectionStart = 0; _selectionEnd = 0; for (int i = 0; i < positions.length - 2; i += 2) { Rectangle target = new Rectangle( (int)(offset + (positions[i] + positions[i+2]) / 2), y, 1, getParent().getHeight()); if (selection.intersects(target)) { if (! found) { found = true; _selectionStart = (short)(i / 2); _selectionEnd = (short)(i / 2 + 1); } else { _selectionEnd++; } } } return prevSelectionStart != _selectionStart || prevSelectionEnd != _selectionEnd; } private void ensureGlyphPositions(RenderingContext c) { if (_glyphPositions == null) { FSGlyphVector glyphVector = c.getTextRenderer().getGlyphVector( c.getOutputDevice(), getParent().getStyle().getFSFont(c), getSubstring()); _glyphPositions = c.getTextRenderer().getGlyphPositions( c.getOutputDevice(), getParent().getStyle().getFSFont(c), glyphVector); } } public boolean clearSelection() { boolean result = _selectionStart != 0 || _selectionEnd != 0; _selectionStart = 0; _selectionEnd = 0; return result; } public boolean isSelected() { return _selectionStart != _selectionEnd; } public short getSelectionEnd() { return _selectionEnd; } public short getSelectionStart() { return _selectionStart; } public String getSelection() { return getSubstring().substring(_selectionStart, _selectionEnd); } public void selectAll() { _selectionStart = 0; _selectionEnd = (short)getSubstring().length(); } public String getTextExportText() { char[] ch = getSubstring().toCharArray(); StringBuffer result = new StringBuffer(); if (isTrimmedLeadingSpace()) { result.append(' '); } for (int i = 0; i < ch.length; i++) { char c = ch[i]; if (c != '\n') { result.append(c); } } if (isTrimmedTrailingSpace()) { result.append(' '); } return result.toString(); } public boolean isTrimmedLeadingSpace() { return _trimmedLeadingSpace; } public void setTrimmedLeadingSpace(boolean trimmedLeadingSpace) { _trimmedLeadingSpace = trimmedLeadingSpace; } private void setTrimmedTrailingSpace(boolean trimmedTrailingSpace) { _trimmedTrailingSpace = trimmedTrailingSpace; } private boolean isTrimmedTrailingSpace() { return _trimmedTrailingSpace; } public void countJustifiableChars(CharCounts counts) { String s = getSubstring(); int len = s.length(); int spaces = 0; int other = 0; for (int i = 0; i < len; i++) { char c = s.charAt(i); if (c == ' ' || c == '\u00a0' || c == '\u3000') { spaces++; } else { other++; } } counts.setSpaceCount(counts.getSpaceCount() + spaces); counts.setNonSpaceCount(counts.getNonSpaceCount() + other); } public float calcTotalAdjustment(JustificationInfo info) { String s = getSubstring(); int len = s.length(); float result = 0.0f; for (int i = 0; i < len; i++) { char c = s.charAt(i); if (c == ' ' || c == '\u00a0' || c == '\u3000') { result += info.getSpaceAdjust(); } else { result += info.getNonSpaceAdjust(); } } return result; } public int getStart(){ return _start; } public int getEnd(){ return _end; } public void setSelectionStart(short s){ _selectionStart = s; } public void setSelectionEnd(short s){ _selectionEnd = s; } public Text getTextNode() { return this._textNode; } public void setTextNode(Text node) { this._textNode = node; } }