| /******************************************************************************* |
| * 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.List; |
| |
| import org.eclipse.draw2d.Graphics; |
| import org.eclipse.draw2d.geometry.Dimension; |
| import org.eclipse.draw2d.geometry.Rectangle; |
| import org.eclipse.jst.jsf.common.ui.internal.logging.Logger; |
| import org.eclipse.jst.pagedesigner.PDPlugin; |
| import org.eclipse.jst.pagedesigner.css2.property.TextDecorationMeta; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Font; |
| |
| /** |
| * @author mengbo |
| */ |
| public class TextLayoutSupport { |
| private static final Logger _log = PDPlugin |
| .getLogger(TextLayoutSupport.class); |
| |
| private static final String[] DELIMITERS = { "\r\n", //$NON-NLS-1$ |
| "\n", //$NON-NLS-1$ |
| "\r" //$NON-NLS-1$ |
| }; |
| |
| static private int delimeterLength; |
| |
| /** |
| * Reuses an existing <code>TextFragmentBox</code>, or creates a new one. |
| * |
| * @param i |
| * the index |
| * @param fragments |
| * the original list of fragments |
| * @return a TextFragmentBox |
| */ |
| // copied from TextLayout |
| protected static TextFragmentBox getFragment(int i, List fragments) { |
| if (fragments.size() > i) { |
| return (TextFragmentBox) fragments.get(i); |
| } |
| TextFragmentBox box = new TextFragmentBox(); |
| fragments.add(box); |
| return box; |
| } |
| |
| /** |
| * Returns the average character width of given TextFragmentbox |
| * |
| * @param fragment |
| * the TextFragmentBox |
| * @return the average character width |
| */ |
| public static float getAverageCharWidth(TextFragmentBox fragment) { |
| if (fragment._width != 0 && fragment._length != 0) { |
| return fragment._width / (float) fragment._length; |
| } |
| return 0.0f; |
| } |
| |
| // ---------------------------------------------------------------------------------------- |
| /** |
| * this method will create a set of TextFragment. Each fragment will offset |
| * to the original text (whole text for the text figure). |
| * @param context |
| * @param text |
| * @param fragments |
| * @param font |
| * @param wrappingStyle |
| * @param trimLeading |
| */ |
| public static void layoutNormal(FlowContext context, String text, |
| List fragments, Font font, int wrappingStyle, boolean trimLeading) { |
| int i = 0; // The index of the current fragment; |
| int offset = 0; |
| if (trimLeading) { |
| offset = 1; |
| text = text.substring(1); |
| } |
| |
| int length = 0; // The length of the current fragment |
| float prevAvgCharWidth; |
| LineBox currentLine; |
| TextFragmentBox fragment; |
| |
| while (text.length() > 0) { |
| fragment = null; |
| prevAvgCharWidth = 0f; |
| fragment = getFragment(i, fragments); |
| prevAvgCharWidth = getAverageCharWidth(fragment); |
| |
| // Check for newline, if it exists, call context.endLine and skip |
| // over the newline |
| // Exccept for first time through, don't do this. |
| if (i != 0) { |
| boolean changed = false; |
| if (text.charAt(0) == '\r') { |
| text = text.substring(1); |
| changed = true; |
| offset += 1; |
| } |
| if (text.length() != 0 && text.charAt(0) == '\n') { |
| text = text.substring(1); |
| changed = true; |
| offset += 1; |
| } |
| if (changed) { |
| context.endLine(); |
| } |
| } |
| |
| fragment._offset = offset; |
| |
| // This loop is done at most twice. |
| // The second time through, a context.endLine() |
| // was requested, and the loop will break. |
| while (true) { |
| currentLine = context.getCurrentLine(); |
| length = FlowUtilities.setupFragmentBasedOnTextSpace(fragment, |
| text, font, currentLine.getAvailableWidth(), |
| prevAvgCharWidth, wrappingStyle); |
| |
| if (fragment._width <= currentLine.getAvailableWidth() |
| || !context.isCurrentLineOccupied()) { |
| break; |
| } |
| context.endLine(); |
| } |
| // fragment.x = context.getCurrentX(); |
| context.addToCurrentLine(fragment); |
| text = text.substring(length); |
| offset += length; |
| if (text.length() > 0) { |
| context.endLine(); |
| } |
| i++; |
| } |
| |
| // Remove the remaining unused fragments. |
| while (i < fragments.size()) { |
| fragments.remove(fragments.size() - 1); |
| } |
| } |
| |
| /** |
| * @param context |
| * @param text |
| * @param fragments |
| * @param font |
| */ |
| public static void layoutNoWrap(FlowContext context, String text, |
| List fragments, Font font) { |
| TextFragmentBox fragment; |
| int i = 0; |
| int offset = 0; |
| |
| while (offset < text.length()) { |
| int result = nextLineBreak(text, offset); |
| fragment = getFragment(i++, fragments); |
| fragment._length = result - offset; |
| fragment._offset = offset; |
| FlowUtilities.setupFragment(fragment, font, text.substring(offset)); |
| context.getCurrentLine().add(fragment); |
| offset = result + delimeterLength; |
| if (delimeterLength != 0) { |
| // in nextLineBreak we fo |
| context.endLine(); |
| } |
| |
| } |
| // Remove the remaining unused fragments. |
| while (i < fragments.size()) { |
| fragments.remove(i++); |
| } |
| } |
| |
| private static int nextLineBreak(String text, int offset) { |
| int result = text.length(); |
| delimeterLength = 0; |
| int current; |
| for (int i = 0; i < DELIMITERS.length; i++) { |
| current = text.indexOf(DELIMITERS[i], offset); |
| if (current != -1 && current < result) { |
| result = current; |
| delimeterLength = DELIMITERS[i].length(); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * @param g |
| * @param fragments |
| * @param font |
| * @param textDecoration |
| */ |
| public static void paintTextFigure(Graphics g, List fragments, Font font, |
| int textDecoration) { |
| paintTextFigure(g, fragments, font, null, textDecoration); |
| } |
| |
| /** |
| * @param g |
| * @param rect |
| * @param textDecoration |
| */ |
| public static void paintTextDecoration(Graphics g, Rectangle rect, |
| int textDecoration) { |
| if ((textDecoration & TextDecorationMeta.UNDERLINE) != 0) { |
| g.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width |
| - 1, rect.y + rect.height - 1); |
| } |
| if ((textDecoration & TextDecorationMeta.OVERLINE) != 0) { |
| g.drawLine(rect.x, rect.y + 1, rect.x + rect.width - 1, rect.y + 1); |
| } |
| if ((textDecoration & TextDecorationMeta.LINETHROUGH) != 0) { |
| g.drawLine(rect.x, rect.y + rect.height / 2, rect.x + rect.width |
| - 1, rect.y + rect.height / 2); |
| } |
| } |
| |
| /** |
| * @param g |
| * @param fragments |
| * @param font |
| * @param color |
| * @param textDecoration |
| */ |
| public static void paintTextFigure(Graphics g, List fragments, Font font, |
| Color color, int textDecoration) { |
| // FIXME: It happens there is problem in this method's parameters. what |
| // exception should be catched? |
| try { |
| TextFragmentBox frag; |
| // XXX: adjust font. Here is not using setFont(), because that will |
| // result in revalidate |
| g.setFont(font); |
| |
| for (int i = 0; i < fragments.size(); i++) { |
| frag = (TextFragmentBox) fragments.get(i); |
| // if (!g.getClip(Rectangle.SINGLETON).intersects(frag)) |
| // continue; |
| String draw; |
| draw = frag.getTextData(); |
| |
| if (color != null) { |
| g.setForegroundColor(color); |
| } |
| g.drawText(draw, frag._x, frag._y); |
| if ((textDecoration & TextDecorationMeta.UNDERLINE) != 0) { |
| g.drawLine(frag._x, frag._y + frag.getHeight() - 1, frag._x |
| + frag.getWidth(), frag._y + frag.getHeight() - 1); |
| } |
| if ((textDecoration & TextDecorationMeta.OVERLINE) != 0) { |
| g.drawLine(frag._x, frag._y, frag._x + frag.getWidth(), |
| frag._y); |
| } |
| if ((textDecoration & TextDecorationMeta.LINETHROUGH) != 0) { |
| g.drawLine(frag._x, frag._y + frag.getHeight() / 2, frag._x |
| + frag.getWidth(), frag._y + frag.getHeight() / 2); |
| } |
| |
| if (Debug.DEBUG_BASELINE) { |
| g.drawLine(frag._x, frag._y + frag.getAscent(), frag._x |
| + frag.getWidth(), frag._y + frag.getAscent()); |
| } |
| } |
| } catch (Exception e) { |
| // "Error in text painting:" |
| _log.info("TextLayoutSupport.Info.1", e); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * |
| * @param g |
| * @param fragments |
| * @param text |
| * all the text in the Text figure. |
| * @param font |
| * @param color |
| * @param textDecoration |
| * @param start |
| * @param end |
| * @param selectionForeColor |
| * @param selectionBackColor |
| */ |
| public static void paintTextFigureWithSelection(Graphics g, List fragments, |
| String text, Font font, Color color, int textDecoration, int start, |
| int end, Color selectionForeColor, Color selectionBackColor) { |
| // FIXME: It happens there is problem in this method's parameters. what |
| // exception should be catched? |
| try { |
| TextFragmentBox frag; |
| |
| Color originalForeground = g.getForegroundColor(); |
| Color originalBackgroud = g.getBackgroundColor(); |
| |
| // XXX: adjust font. Here is not using setFont(), because that will |
| // result in revalidate |
| g.setFont(font); |
| |
| for (int i = 0, n = fragments.size(); i < n; i++) { |
| frag = (TextFragmentBox) fragments.get(i); |
| |
| // to make things simpler, we always draw the line using default |
| // color |
| if (color != null) { |
| g.setForegroundColor(color); |
| } |
| |
| // if (!g.getClip(Rectangle.SINGLETON).intersects(frag)) |
| // continue; |
| String draw; |
| draw = frag.getTextData(); |
| if (frag._offset >= end || frag._offset + frag._length <= start) { |
| // we are not in selection. no need to change color |
| g.drawText(draw, frag._x, frag._y); |
| paintTextDecoration(g, frag.getRectangle(), textDecoration); |
| } else if (frag._offset >= start |
| && frag._offset + frag._length <= end) { |
| // we are fully in selection |
| g.setForegroundColor(selectionForeColor); |
| g.setBackgroundColor(selectionBackColor); |
| g |
| .fillRectangle(frag._x, frag._y, FlowUtilities |
| .getTextExtents(draw, font).width, frag |
| .getHeight()); |
| g.drawText(draw, frag._x, frag._y); |
| paintTextDecoration(g, frag.getRectangle(), textDecoration); |
| } else { |
| // partial of the fragment's text is in selection. |
| |
| // draw the original string first |
| g.drawText(draw, frag._x, frag._y); |
| // then override with the selected parts. |
| g.setForegroundColor(selectionForeColor); |
| g.setBackgroundColor(selectionBackColor); |
| int partialStart = frag._offset > start ? frag._offset |
| : start; |
| int partialEnd = (frag._offset + frag._length > end) ? end |
| : (frag._offset + frag._length); |
| int x = 0; |
| String skip = text.substring(frag._offset, partialStart); |
| x = FlowUtilities.getTextExtents(skip, font).width; |
| String todraw = text.substring(partialStart, partialEnd); |
| if (todraw.length() > 0) { |
| Dimension dimension = FlowUtilities.getTextExtents(skip |
| + todraw, font); |
| g.fillRectangle(frag._x + x, frag._y, dimension.width |
| - x, dimension.height); |
| g.drawText(skip + todraw, frag._x, frag._y); |
| if (color != null) { |
| g.setForegroundColor(color); |
| } else { |
| g.setForegroundColor(originalForeground); |
| } |
| g.drawText(skip, frag._x, frag._y); |
| paintTextDecoration(g, frag.getRectangle(), |
| textDecoration); |
| g.setForegroundColor(selectionForeColor); |
| paintTextDecoration(g, |
| new Rectangle(frag._x + x, frag._y, |
| dimension.width - x, dimension.height), |
| textDecoration); |
| } |
| } |
| |
| // we do this in each loop, to make sure we are using correct |
| // color |
| g.setForegroundColor(originalForeground); |
| g.setBackgroundColor(originalBackgroud); |
| |
| } |
| } catch (Exception e) { |
| // "Error in text painting:" |
| _log.info("TextLayoutSupport.Info.1", e); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * @param textAlign |
| * @param rect |
| * @param textWidth |
| * @return the x value |
| */ |
| public static int getBeginX(Object textAlign, Rectangle rect, int textWidth) { |
| int x = rect.x; |
| if (textAlign != null) { |
| String align = textAlign.toString(); |
| if ("left".equalsIgnoreCase(align)) //$NON-NLS-1$ |
| { |
| x = rect.x + 1; |
| } else if ("right".equalsIgnoreCase(align)) //$NON-NLS-1$ |
| { |
| x = rect.x + rect.width - textWidth - 1; |
| if (x < 1) { |
| x = 1; |
| } |
| } else if ("center".equalsIgnoreCase(align)) //$NON-NLS-1$ |
| { |
| int offset = (rect.width - textWidth) / 2; |
| if (offset <= 0) { |
| offset = 0; |
| } |
| x = x + offset + 1; |
| } |
| } |
| return x; |
| } |
| |
| private TextLayoutSupport() |
| { |
| // no instantiation |
| } |
| } |