summaryrefslogtreecommitdiffstats
path: root/awt/org/apache/harmony/awt/gl/font/CaretManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'awt/org/apache/harmony/awt/gl/font/CaretManager.java')
-rw-r--r--awt/org/apache/harmony/awt/gl/font/CaretManager.java530
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;
- }
-}