| /******************************************************************************* |
| * Copyright (c) 2006 Sybase, Inc. and others. |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Sybase, Inc. - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.pagedesigner.css2.layout; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.draw2d.ColorConstants; |
| import org.eclipse.draw2d.Graphics; |
| import org.eclipse.draw2d.IFigure; |
| import org.eclipse.draw2d.geometry.Point; |
| import org.eclipse.draw2d.geometry.Rectangle; |
| import org.eclipse.jst.pagedesigner.css2.ICSSStyle; |
| import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID; |
| import org.eclipse.jst.pagedesigner.css2.provider.ICSSTextProvider; |
| import org.eclipse.jst.pagedesigner.css2.style.DefaultStyle; |
| import org.eclipse.jst.pagedesigner.css2.style.StyleUtil; |
| import org.eclipse.jst.pagedesigner.viewer.CaretPositionResolver; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.RGB; |
| |
| /** |
| * @author mengbo |
| */ |
| public class CSSTextFigure extends FlowFigure implements ICSSFigure { |
| private ICSSTextProvider _provider; |
| |
| private List _fragments = new ArrayList(1); |
| |
| /** |
| * @param provider |
| */ |
| public CSSTextFigure(ICSSTextProvider provider) { |
| _provider = provider; |
| this.setLayoutManager(createDefaultFlowLayout()); |
| } |
| |
| public ICSSStyle getCSSStyle() { |
| IFigure parentFigure = this.getParent(); |
| if (parentFigure instanceof ICSSFigure) { |
| ICSSStyle style = ((ICSSFigure) parentFigure).getCSSStyle(); |
| if (style != null) { |
| return style; |
| } |
| } |
| return DefaultStyle.getInstance(); |
| } |
| |
| /** |
| * @see org.eclipse.draw2d.IFigure#containsPoint(int, int) |
| */ |
| public boolean containsPoint(int x, int y) { |
| if (!super.containsPoint(x, y)) { |
| return false; |
| } |
| List frags = getFragments(); |
| for (int i = 0, n = frags.size(); i < n; i++) { |
| if (((FlowBox) frags.get(i)).containsPoint(x, y)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @return the default flow layout |
| * |
| */ |
| protected FlowFigureLayout createDefaultFlowLayout() { |
| return new CSSTextLayout(this); |
| } |
| |
| /** |
| * Returns the <code>LineBox</code> fragments contained in this InlineFlow |
| * |
| * @return The fragments |
| */ |
| public List getFragments() { |
| return _fragments; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure#getFragmentsForRead() |
| */ |
| public List getFragmentsForRead() { |
| return getFragments(); |
| } |
| |
| /** |
| * @return the text |
| */ |
| public String getText() { |
| return _provider.getTextData(); |
| } |
| |
| /** |
| * @see FlowFigure#postValidate() |
| */ |
| public void postValidate() { |
| List list = getFragments(); |
| FlowBox box; |
| int left = Integer.MAX_VALUE, top = left; |
| int right = Integer.MIN_VALUE, bottom = right; |
| for (int i = 0, n = list.size(); i < n; i++) { |
| box = (FlowBox) list.get(i); |
| left = Math.min(left, box._x); |
| right = Math.max(right, box._x + box._width); |
| top = Math.min(top, box._y); |
| bottom = Math.max(bottom, box._y + box._height); |
| } |
| setBounds(new Rectangle(left, top, right - left, bottom - top)); |
| list = getChildren(); |
| for (int i = 0, n = list.size(); i < n; i++) { |
| ((FlowFigure) list.get(i)).postValidate(); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.draw2d.Figure#paintBorder(org.eclipse.draw2d.Graphics) |
| */ |
| protected void paintBorder(Graphics graphics) { |
| if (Debug.DEBUG_TEXTBORDER) { |
| if (_fragments != null) { |
| graphics.setForegroundColor(ColorConstants.darkBlue); |
| for (int i = 0, size = _fragments.size(); i < size; i++) { |
| FlowBox box = (FlowBox) _fragments.get(i); |
| BoxUtil.drawBox(graphics, box); |
| } |
| graphics.restoreState(); |
| } |
| } |
| } |
| |
| /** |
| * @see org.eclipse.draw2d.Figure#paintFigure(Graphics) |
| */ |
| protected void paintFigure(Graphics g) { |
| Object result = this.getCSSStyle().getColor(); |
| Color color; |
| if (result instanceof Color) { |
| color = (Color) result; |
| } else if (result instanceof RGB) { |
| color = new Color(null, (RGB) result); |
| } else { |
| color = null; |
| } |
| int[] range = null; |
| if (!StyleUtil.isInWidget(this.getCSSStyle())) { |
| range = _provider.getSelectedRange(); |
| } |
| if (range == null || range[0] == range[1]) { |
| // we are not in selection |
| TextLayoutSupport.paintTextFigure(g, _fragments, getCSSStyle() |
| .getCSSFont().getSwtFont(), color, ((Integer) getCSSStyle() |
| .getStyleProperty(ICSSPropertyID.ATTR_TEXTDECORATION)) |
| .intValue()); |
| } else { |
| TextLayoutSupport.paintTextFigureWithSelection(g, _fragments, |
| _provider.getTextData(), getCSSStyle().getCSSFont() |
| .getSwtFont(), color, ((Integer) getCSSStyle() |
| .getStyleProperty( |
| ICSSPropertyID.ATTR_TEXTDECORATION)) |
| .intValue(), range[0], range[1], |
| ColorConstants.white, ColorConstants.blue); |
| } |
| if (color != result && color != null) { |
| color.dispose(); |
| } |
| } |
| |
| /** |
| * Find out lines which has closer y coordinate to point, and then line |
| * which has closer x coordinate. |
| * |
| * @param relative |
| * @return return the offset |
| */ |
| // TODO: refactoring? |
| public int getNewInsertionOffset(Point relative) { |
| TextFragmentBox closestBox = null; |
| // if there is one which are at the same line with relative, calculate |
| // that line first; |
| for (int i = 0, n = _fragments.size(); i < n; i++) { |
| TextFragmentBox box = (TextFragmentBox) _fragments.get(i); |
| if (box.containsPoint(relative.x, relative.y)) { |
| int index = FlowUtilities.getTextInWidth(box.getTextData(), |
| getCSSStyle().getCSSFont().getSwtFont(), relative.x |
| - box._x, TextLayoutSupport |
| .getAverageCharWidth(box)); |
| return box._offset + index; |
| } |
| if (closestBox == null) { |
| closestBox = box; |
| } else { |
| // box is above point |
| TextFragmentBox tempBox = box; |
| int offset1 = Math |
| .abs(CaretPositionResolver.getYDistance( |
| new Rectangle(tempBox._x, tempBox._y, |
| tempBox._width, tempBox._height), |
| relative)); |
| tempBox = closestBox; |
| int offset2 = Math |
| .abs(CaretPositionResolver.getYDistance( |
| new Rectangle(tempBox._x, tempBox._y, |
| tempBox._width, tempBox._height), |
| relative)); |
| if (offset1 < offset2) { |
| closestBox = box; |
| } |
| } |
| // at the same line |
| if (box.containsPoint(box._x, relative.y)) { |
| TextFragmentBox tempBox = box; |
| int offset1 = Math |
| .abs(CaretPositionResolver.getXDistance( |
| new Rectangle(tempBox._x, tempBox._y, |
| tempBox._width, tempBox._height), |
| relative)); |
| tempBox = closestBox; |
| int offset2 = Math |
| .abs(CaretPositionResolver.getXDistance( |
| new Rectangle(tempBox._x, tempBox._y, |
| tempBox._width, tempBox._height), |
| relative)); |
| if (offset1 < offset2) { |
| closestBox = box; |
| } |
| } |
| } |
| |
| if (closestBox.containsPoint(closestBox._x, relative.y) |
| || closestBox.containsPoint(relative.x, closestBox._y)) { |
| int offset = relative.x - closestBox._x; |
| int index = FlowUtilities.getTextInWidth(closestBox.getTextData(), |
| getCSSStyle().getCSSFont().getSwtFont(), offset, |
| TextLayoutSupport.getAverageCharWidth(closestBox)); |
| return closestBox._offset + index; |
| } |
| return -1; |
| } |
| |
| /** |
| * @param relative |
| * @return the insertion offset |
| */ |
| public int getInsertionOffset(Point relative) { |
| for (int i = 0, n = _fragments.size(); i < n; i++) { |
| TextFragmentBox box = (TextFragmentBox) _fragments.get(i); |
| if (box.containsPoint(relative.x, relative.y)) { |
| int index = FlowUtilities.getTextInWidth(box.getTextData(), |
| getCSSStyle().getCSSFont().getSwtFont(), relative.x |
| - box._x, TextLayoutSupport |
| .getAverageCharWidth(box)); |
| return box._offset + index; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * the returned rectangle will be relative to this text figure. |
| * |
| * @param offset |
| * @return the caret position |
| */ |
| public Rectangle calculateCaretPosition(int offset) { |
| // search reverse order, find the latest box that has _offset small than |
| // the specified one |
| if (offset > 0) { |
| for (int i = _fragments.size() - 1; i >= 0; i--) { |
| TextFragmentBox box = (TextFragmentBox) _fragments.get(i); |
| if (box._offset <= offset) { |
| // ok, we find the box. |
| if (box._offset + box._length < offset) { |
| return new Rectangle(box._x + box._width, box._y, 1, |
| box._height); |
| } |
| String s = box.getTextData().substring(0, |
| offset - box._offset); |
| int width = FlowUtilities.getTextExtents(s, |
| getCSSStyle().getCSSFont().getSwtFont()).width; |
| return new Rectangle(box._x + width, box._y, 1, |
| box._height); |
| } |
| } |
| } else { |
| if (_fragments.size() > 0) { |
| TextFragmentBox box = (TextFragmentBox) _fragments.get(0); |
| return new Rectangle(box._x, box._y, 1, box._height); |
| } |
| } |
| // should only reach here when there is no fragments. |
| return new Rectangle(getBounds().x, getBounds().y, 1, getBounds().height); |
| } |
| |
| } |