diff options
Diffstat (limited to 'awt/org/apache/harmony/awt/gl/font/CaretManager.java')
-rw-r--r-- | awt/org/apache/harmony/awt/gl/font/CaretManager.java | 530 |
1 files changed, 0 insertions, 530 deletions
diff --git a/awt/org/apache/harmony/awt/gl/font/CaretManager.java b/awt/org/apache/harmony/awt/gl/font/CaretManager.java deleted file mode 100644 index b18bdd5..0000000 --- a/awt/org/apache/harmony/awt/gl/font/CaretManager.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @author Oleg V. Khaschansky - * @version $Revision$ - * - * @date: Jun 14, 2005 - */ - -package org.apache.harmony.awt.gl.font; - -import java.awt.font.TextHitInfo; -import java.awt.font.TextLayout; -import java.awt.geom.Rectangle2D; -import java.awt.geom.GeneralPath; -import java.awt.geom.Line2D; -import java.awt.*; - -import org.apache.harmony.awt.internal.nls.Messages; - -/** - * This class provides functionality for creating caret and highlight shapes - * (bidirectional text is also supported, but, unfortunately, not tested yet). - */ -public class CaretManager { - private TextRunBreaker breaker; - - public CaretManager(TextRunBreaker breaker) { - this.breaker = breaker; - } - - /** - * Checks if TextHitInfo is not out of the text range and throws the - * IllegalArgumentException if it is. - * @param info - text hit info - */ - private void checkHit(TextHitInfo info) { - int idx = info.getInsertionIndex(); - - if (idx < 0 || idx > breaker.getCharCount()) { - // awt.42=TextHitInfo out of range - throw new IllegalArgumentException(Messages.getString("awt.42")); //$NON-NLS-1$ - } - } - - /** - * Calculates and returns visual position from the text hit info. - * @param hitInfo - text hit info - * @return visual index - */ - private int getVisualFromHitInfo(TextHitInfo hitInfo) { - final int idx = hitInfo.getCharIndex(); - - if (idx >= 0 && idx < breaker.getCharCount()) { - int visual = breaker.getVisualFromLogical(idx); - // We take next character for (LTR char + TRAILING info) and (RTL + LEADING) - if (hitInfo.isLeadingEdge() ^ ((breaker.getLevel(idx) & 0x1) == 0x0)) { - visual++; - } - return visual; - } else if (idx < 0) { - return breaker.isLTR() ? 0: breaker.getCharCount(); - } else { - return breaker.isLTR() ? breaker.getCharCount() : 0; - } - } - - /** - * Calculates text hit info from the visual position - * @param visual - visual position - * @return text hit info - */ - private TextHitInfo getHitInfoFromVisual(int visual) { - final boolean first = visual == 0; - - if (!(first || visual == breaker.getCharCount())) { - int logical = breaker.getLogicalFromVisual(visual); - return (breaker.getLevel(logical) & 0x1) == 0x0 ? - TextHitInfo.leading(logical) : // LTR - TextHitInfo.trailing(logical); // RTL - } else if (first) { - return breaker.isLTR() ? - TextHitInfo.trailing(-1) : - TextHitInfo.leading(breaker.getCharCount()); - } else { // Last - return breaker.isLTR() ? - TextHitInfo.leading(breaker.getCharCount()) : - TextHitInfo.trailing(-1); - } - } - - /** - * Creates caret info. Required for the getCaretInfo - * methods of the TextLayout - * @param hitInfo - specifies caret position - * @return caret info, see TextLayout.getCaretInfo documentation - */ - public float[] getCaretInfo(TextHitInfo hitInfo) { - checkHit(hitInfo); - float res[] = new float[2]; - - int visual = getVisualFromHitInfo(hitInfo); - float advance, angle; - TextRunSegment seg; - - if (visual < breaker.getCharCount()) { - int logIdx = breaker.getLogicalFromVisual(visual); - int segmentIdx = breaker.logical2segment[logIdx]; - seg = breaker.runSegments.get(segmentIdx); - advance = seg.x + seg.getAdvanceDelta(seg.getStart(), logIdx); - angle = seg.metrics.italicAngle; - - } else { // Last character - int logIdx = breaker.getLogicalFromVisual(visual-1); - int segmentIdx = breaker.logical2segment[logIdx]; - seg = breaker.runSegments.get(segmentIdx); - advance = seg.x + seg.getAdvanceDelta(seg.getStart(), logIdx+1); - } - - angle = seg.metrics.italicAngle; - - res[0] = advance; - res[1] = angle; - - return res; - } - - /** - * Returns the next position to the right from the current caret position - * @param hitInfo - current position - * @return next position to the right - */ - public TextHitInfo getNextRightHit(TextHitInfo hitInfo) { - checkHit(hitInfo); - int visual = getVisualFromHitInfo(hitInfo); - - if (visual == breaker.getCharCount()) { - return null; - } - - TextHitInfo newInfo; - - while(visual <= breaker.getCharCount()) { - visual++; - newInfo = getHitInfoFromVisual(visual); - - if (newInfo.getCharIndex() >= breaker.logical2segment.length) { - return newInfo; - } - - if (hitInfo.getCharIndex() >= 0) { // Don't check for leftmost info - if ( - breaker.logical2segment[newInfo.getCharIndex()] != - breaker.logical2segment[hitInfo.getCharIndex()] - ) { - return newInfo; // We crossed segment boundary - } - } - - TextRunSegment seg = breaker.runSegments.get(breaker.logical2segment[newInfo - .getCharIndex()]); - if (!seg.charHasZeroAdvance(newInfo.getCharIndex())) { - return newInfo; - } - } - - return null; - } - - /** - * Returns the next position to the left from the current caret position - * @param hitInfo - current position - * @return next position to the left - */ - public TextHitInfo getNextLeftHit(TextHitInfo hitInfo) { - checkHit(hitInfo); - int visual = getVisualFromHitInfo(hitInfo); - - if (visual == 0) { - return null; - } - - TextHitInfo newInfo; - - while(visual >= 0) { - visual--; - newInfo = getHitInfoFromVisual(visual); - - if (newInfo.getCharIndex() < 0) { - return newInfo; - } - - // Don't check for rightmost info - if (hitInfo.getCharIndex() < breaker.logical2segment.length) { - if ( - breaker.logical2segment[newInfo.getCharIndex()] != - breaker.logical2segment[hitInfo.getCharIndex()] - ) { - return newInfo; // We crossed segment boundary - } - } - - TextRunSegment seg = breaker.runSegments.get(breaker.logical2segment[newInfo - .getCharIndex()]); - if (!seg.charHasZeroAdvance(newInfo.getCharIndex())) { - return newInfo; - } - } - - return null; - } - - /** - * For each visual caret position there are two hits. For the simple LTR text one is - * a trailing of the previous char and another is the leading of the next char. This - * method returns the opposite hit for the given hit. - * @param hitInfo - given hit - * @return opposite hit - */ - public TextHitInfo getVisualOtherHit(TextHitInfo hitInfo) { - checkHit(hitInfo); - - int idx = hitInfo.getCharIndex(); - - int resIdx; - boolean resIsLeading; - - if (idx >= 0 && idx < breaker.getCharCount()) { // Hit info in the middle - int visual = breaker.getVisualFromLogical(idx); - - // Char is LTR + LEADING info - if (((breaker.getLevel(idx) & 0x1) == 0x0) ^ hitInfo.isLeadingEdge()) { - visual++; - if (visual == breaker.getCharCount()) { - if (breaker.isLTR()) { - resIdx = breaker.getCharCount(); - resIsLeading = true; - } else { - resIdx = -1; - resIsLeading = false; - } - } else { - resIdx = breaker.getLogicalFromVisual(visual); - if ((breaker.getLevel(resIdx) & 0x1) == 0x0) { - resIsLeading = true; - } else { - resIsLeading = false; - } - } - } else { - visual--; - if (visual == -1) { - if (breaker.isLTR()) { - resIdx = -1; - resIsLeading = false; - } else { - resIdx = breaker.getCharCount(); - resIsLeading = true; - } - } else { - resIdx = breaker.getLogicalFromVisual(visual); - if ((breaker.getLevel(resIdx) & 0x1) == 0x0) { - resIsLeading = false; - } else { - resIsLeading = true; - } - } - } - } else if (idx < 0) { // before "start" - if (breaker.isLTR()) { - resIdx = breaker.getLogicalFromVisual(0); - resIsLeading = (breaker.getLevel(resIdx) & 0x1) == 0x0; // LTR char? - } else { - resIdx = breaker.getLogicalFromVisual(breaker.getCharCount() - 1); - resIsLeading = (breaker.getLevel(resIdx) & 0x1) != 0x0; // RTL char? - } - } else { // idx == breaker.getCharCount() - if (breaker.isLTR()) { - resIdx = breaker.getLogicalFromVisual(breaker.getCharCount() - 1); - resIsLeading = (breaker.getLevel(resIdx) & 0x1) != 0x0; // LTR char? - } else { - resIdx = breaker.getLogicalFromVisual(0); - resIsLeading = (breaker.getLevel(resIdx) & 0x1) == 0x0; // RTL char? - } - } - - return resIsLeading ? TextHitInfo.leading(resIdx) : TextHitInfo.trailing(resIdx); - } - - public Line2D getCaretShape(TextHitInfo hitInfo, TextLayout layout) { - return getCaretShape(hitInfo, layout, true, false, null); - } - - /** - * Creates a caret shape. - * @param hitInfo - hit where to place a caret - * @param layout - text layout - * @param useItalic - unused for now, was used to create - * slanted carets for italic text - * @param useBounds - true if the cared should fit into the provided bounds - * @param bounds - bounds for the caret - * @return caret shape - */ - public Line2D getCaretShape( - TextHitInfo hitInfo, TextLayout layout, - boolean useItalic, boolean useBounds, Rectangle2D bounds - ) { - checkHit(hitInfo); - - float x1, x2, y1, y2; - - int charIdx = hitInfo.getCharIndex(); - - if (charIdx >= 0 && charIdx < breaker.getCharCount()) { - TextRunSegment segment = breaker.runSegments.get(breaker.logical2segment[charIdx]); - y1 = segment.metrics.descent; - y2 = - segment.metrics.ascent - segment.metrics.leading; - - x1 = x2 = segment.getCharPosition(charIdx) + (hitInfo.isLeadingEdge() ? - 0 : segment.getCharAdvance(charIdx)); - // Decided that straight cursor looks better even for italic fonts, - // especially combined with highlighting - /* - // Not graphics, need to check italic angle and baseline - if (layout.getBaseline() >= 0) { - if (segment.metrics.italicAngle != 0 && useItalic) { - x1 -= segment.metrics.italicAngle * segment.metrics.descent; - x2 += segment.metrics.italicAngle * - (segment.metrics.ascent + segment.metrics.leading); - - float baselineOffset = - layout.getBaselineOffsets()[layout.getBaseline()]; - y1 += baselineOffset; - y2 += baselineOffset; - } - } - */ - } else { - y1 = layout.getDescent(); - y2 = - layout.getAscent() - layout.getLeading(); - x1 = x2 = ((breaker.getBaseLevel() & 0x1) == 0 ^ charIdx < 0) ? - layout.getAdvance() : 0; - } - - if (useBounds) { - y1 = (float) bounds.getMaxY(); - y2 = (float) bounds.getMinY(); - - if (x2 > bounds.getMaxX()) { - x1 = x2 = (float) bounds.getMaxX(); - } - if (x1 < bounds.getMinX()) { - x1 = x2 = (float) bounds.getMinX(); - } - } - - return new Line2D.Float(x1, y1, x2, y2); - } - - /** - * Creates caret shapes for the specified offset. On the boundaries where - * the text is changing its direction this method may return two shapes - * for the strong and the weak carets, in other cases it would return one. - * @param offset - offset in the text. - * @param bounds - bounds to fit the carets into - * @param policy - caret policy - * @param layout - text layout - * @return one or two caret shapes - */ - public Shape[] getCaretShapes( - int offset, Rectangle2D bounds, - TextLayout.CaretPolicy policy, TextLayout layout - ) { - TextHitInfo hit1 = TextHitInfo.afterOffset(offset); - TextHitInfo hit2 = getVisualOtherHit(hit1); - - Shape caret1 = getCaretShape(hit1, layout); - - if (getVisualFromHitInfo(hit1) == getVisualFromHitInfo(hit2)) { - return new Shape[] {caret1, null}; - } - Shape caret2 = getCaretShape(hit2, layout); - - TextHitInfo strongHit = policy.getStrongCaret(hit1, hit2, layout); - return strongHit.equals(hit1) ? - new Shape[] {caret1, caret2} : - new Shape[] {caret2, caret1}; - } - - /** - * Connects two carets to produce a highlight shape. - * @param caret1 - 1st caret - * @param caret2 - 2nd caret - * @return highlight shape - */ - GeneralPath connectCarets(Line2D caret1, Line2D caret2) { - GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO); - path.moveTo((float) caret1.getX1(), (float) caret1.getY1()); - path.lineTo((float) caret2.getX1(), (float) caret2.getY1()); - path.lineTo((float) caret2.getX2(), (float) caret2.getY2()); - path.lineTo((float) caret1.getX2(), (float) caret1.getY2()); - - path.closePath(); - - return path; - } - - /** - * Creates a highlight shape from given two hits. This shape - * will always be visually contiguous - * @param hit1 - 1st hit - * @param hit2 - 2nd hit - * @param bounds - bounds to fit the shape into - * @param layout - text layout - * @return highlight shape - */ - public Shape getVisualHighlightShape( - TextHitInfo hit1, TextHitInfo hit2, - Rectangle2D bounds, TextLayout layout - ) { - checkHit(hit1); - checkHit(hit2); - - Line2D caret1 = getCaretShape(hit1, layout, false, true, bounds); - Line2D caret2 = getCaretShape(hit2, layout, false, true, bounds); - - return connectCarets(caret1, caret2); - } - - /** - * Suppose that the user visually selected a block of text which has - * several different levels (mixed RTL and LTR), so, in the logical - * representation of the text this selection may be not contigous. - * This methods returns a set of logical ranges for the arbitrary - * visual selection represented by two hits. - * @param hit1 - 1st hit - * @param hit2 - 2nd hit - * @return logical ranges for the selection - */ - public int[] getLogicalRangesForVisualSelection(TextHitInfo hit1, TextHitInfo hit2) { - checkHit(hit1); - checkHit(hit2); - - int visual1 = getVisualFromHitInfo(hit1); - int visual2 = getVisualFromHitInfo(hit2); - - if (visual1 > visual2) { - int tmp = visual2; - visual2 = visual1; - visual1 = tmp; - } - - // Max level is 255, so we don't need more than 512 entries - int results[] = new int[512]; - - int prevLogical, logical, runStart, numRuns = 0; - - logical = runStart = prevLogical = breaker.getLogicalFromVisual(visual1); - - // Get all the runs. We use the fact that direction is constant in all runs. - for (int i=visual1+1; i<=visual2; i++) { - logical = breaker.getLogicalFromVisual(i); - int diff = logical-prevLogical; - - // Start of the next run encountered - if (diff > 1 || diff < -1) { - results[(numRuns)*2] = Math.min(runStart, prevLogical); - results[(numRuns)*2 + 1] = Math.max(runStart, prevLogical); - numRuns++; - runStart = logical; - } - - prevLogical = logical; - } - - // The last unsaved run - results[(numRuns)*2] = Math.min(runStart, logical); - results[(numRuns)*2 + 1] = Math.max(runStart, logical); - numRuns++; - - int retval[] = new int[numRuns*2]; - System.arraycopy(results, 0, retval, 0, numRuns*2); - return retval; - } - - /** - * Creates a highlight shape from given two endpoints in the logical - * representation. This shape is not always visually contiguous - * @param firstEndpoint - 1st logical endpoint - * @param secondEndpoint - 2nd logical endpoint - * @param bounds - bounds to fit the shape into - * @param layout - text layout - * @return highlight shape - */ - public Shape getLogicalHighlightShape( - int firstEndpoint, int secondEndpoint, - Rectangle2D bounds, TextLayout layout - ) { - GeneralPath res = new GeneralPath(); - - for (int i=firstEndpoint; i<=secondEndpoint; i++) { - int endRun = breaker.getLevelRunLimit(i, secondEndpoint); - TextHitInfo hit1 = TextHitInfo.leading(i); - TextHitInfo hit2 = TextHitInfo.trailing(endRun-1); - - Line2D caret1 = getCaretShape(hit1, layout, false, true, bounds); - Line2D caret2 = getCaretShape(hit2, layout, false, true, bounds); - - res.append(connectCarets(caret1, caret2), false); - - i = endRun; - } - - return res; - } -} |