< prev index next >

modules/graphics/src/main/java/javafx/scene/text/Text.java

Print this page


   1 /*
   2  * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.text;
  27 
  28 import javafx.css.converter.BooleanConverter;
  29 import javafx.css.converter.EnumConverter;
  30 import javafx.css.converter.SizeConverter;
  31 import com.sun.javafx.geom.BaseBounds;
  32 import com.sun.javafx.geom.Path2D;
  33 import com.sun.javafx.geom.RectBounds;
  34 import com.sun.javafx.geom.TransformedShape;
  35 import com.sun.javafx.geom.transform.BaseTransform;
  36 import com.sun.javafx.scene.DirtyBits;
  37 import com.sun.javafx.scene.text.*;




  38 import com.sun.javafx.sg.prism.NGNode;
  39 import com.sun.javafx.sg.prism.NGShape;
  40 import com.sun.javafx.sg.prism.NGText;
  41 import com.sun.javafx.tk.Toolkit;
  42 import javafx.beans.DefaultProperty;
  43 import javafx.beans.InvalidationListener;
  44 import javafx.beans.binding.DoubleBinding;
  45 import javafx.beans.binding.ObjectBinding;
  46 import javafx.beans.property.*;
  47 import javafx.css.*;
  48 import javafx.geometry.*;
  49 import javafx.scene.AccessibleAttribute;
  50 import javafx.scene.AccessibleRole;
  51 import javafx.scene.paint.Color;
  52 import javafx.scene.paint.Paint;
  53 import javafx.scene.shape.LineTo;
  54 import javafx.scene.shape.MoveTo;
  55 import javafx.scene.shape.PathElement;
  56 import javafx.scene.shape.Shape;
  57 import javafx.scene.shape.StrokeType;


 370         textProperty().set(value);
 371     }
 372 
 373     public final String getText() {
 374         return text == null ? "" : text.get();
 375     }
 376 
 377     private String getTextInternal() {
 378         // this might return null in case of bound property
 379         String localText = getText();
 380         return localText == null ? "" : localText;
 381     }
 382 
 383     public final StringProperty textProperty() {
 384         if (text == null) {
 385             text = new StringPropertyBase("") {
 386                 @Override public Object getBean() { return Text.this; }
 387                 @Override public String getName() { return "text"; }
 388                 @Override  public void invalidated() {
 389                     needsFullTextLayout();
 390                     setImpl_selectionStart(-1);
 391                     setImpl_selectionEnd(-1);
 392                     setImpl_caretPosition(-1);
 393                     setImpl_caretBias(true);
 394 
 395                     // MH: Functionality copied from store() method,
 396                     // which was removed.
 397                     // Wonder what should happen if text is bound
 398                     //  and becomes null?
 399                     final String value = get();
 400                     if ((value == null) && !isBound()) {
 401                         set("");
 402                     }
 403                     notifyAccessibleAttributeChanged(AccessibleAttribute.TEXT);
 404                 }
 405             };
 406         }
 407         return text;
 408     }
 409 
 410     /**
 411      * Defines the X coordinate of text origin.
 412      *
 413      * @defaultValue 0


 759      * @treatAsPrivate implementation detail
 760      * @deprecated This is an internal API that is not intended
 761      * for use and will be removed in the next version
 762      */
 763     @Deprecated
 764     @Override
 765     protected final void impl_geomChanged() {
 766         super.impl_geomChanged();
 767         if (attributes != null) {
 768             if (attributes.impl_caretBinding != null) {
 769                 attributes.impl_caretBinding.invalidate();
 770             }
 771             if (attributes.impl_selectionBinding != null) {
 772                 attributes.impl_selectionBinding.invalidate();
 773             }
 774         }
 775         impl_markDirty(DirtyBits.NODE_GEOMETRY);
 776     }
 777 
 778     /**
 779      * @treatAsPrivate implementation detail
 780      * @deprecated This is an internal API that is not intended
 781      * for use and will be removed in the next version
 782      */
 783     @Deprecated
 784     public final PathElement[] getImpl_selectionShape() {
 785         return impl_selectionShapeProperty().get();
 786     }
 787 
 788     /**
 789      * Shape of selection in local coordinates.
 790      *
 791      * @treatAsPrivate implementation detail
 792      * @deprecated This is an internal API that is not intended
 793      * for use and will be removed in the next version
 794      */
 795     @Deprecated
 796     public final ReadOnlyObjectProperty<PathElement[]> impl_selectionShapeProperty() {



 797         return getTextAttribute().impl_selectionShapeProperty();
 798     }
 799 
 800     /**
 801      * @treatAsPrivate implementation detail
 802      * @deprecated This is an internal API that is not intended
 803      * for use and will be removed in the next version

 804      */
 805     @Deprecated
 806     public final void setImpl_selectionStart(int value) {
 807         if (value == -1 &&
 808                 (attributes == null || attributes.impl_selectionStart == null)) {
 809             return;
 810         }
 811         impl_selectionStartProperty().set(value);
 812     }
 813 
 814     /**
 815      * @treatAsPrivate implementation detail
 816      * @deprecated This is an internal API that is not intended
 817      * for use and will be removed in the next version
 818      */
 819     @Deprecated
 820     public final int getImpl_selectionStart() {
 821         if (attributes == null || attributes.impl_selectionStart == null) {
 822             return DEFAULT_SELECTION_START;
 823         }
 824         return attributes.getImpl_selectionStart();
 825     }
 826 
 827     /**
 828      * Selection start index in the content.
 829      * set to {@code -1} to unset selection.
 830      *
 831      * @treatAsPrivate implementation detail
 832      * @deprecated This is an internal API that is not intended
 833      * for use and will be removed in the next version
 834      */
 835     @Deprecated
 836     public final IntegerProperty impl_selectionStartProperty() {
 837         return getTextAttribute().impl_selectionStartProperty();
 838     }
 839 
 840     /**
 841      * @treatAsPrivate implementation detail
 842      * @deprecated This is an internal API that is not intended
 843      * for use and will be removed in the next version

 844      */
 845     @Deprecated
 846     public final void setImpl_selectionEnd(int value) {
 847         if (value == -1 &&
 848                 (attributes == null || attributes.impl_selectionEnd == null)) {
 849             return;
 850         }
 851         impl_selectionEndProperty().set(value);
 852     }
 853 
 854     /**
 855      * @treatAsPrivate implementation detail
 856      * @deprecated This is an internal API that is not intended
 857      * for use and will be removed in the next version
 858      */
 859     @Deprecated
 860     public final int getImpl_selectionEnd() {
 861         if (attributes == null || attributes.impl_selectionEnd == null) {
 862             return DEFAULT_SELECTION_END;
 863         }
 864         return attributes.getImpl_selectionEnd();
 865     }
 866 
 867     /**
 868      * Selection end index in the content.
 869      * set to {@code -1} to unset selection.
 870      *
 871      * @treatAsPrivate implementation detail
 872      * @deprecated This is an internal API that is not intended
 873      * for use and will be removed in the next version
 874      */
 875     @Deprecated
 876     public final IntegerProperty impl_selectionEndProperty() {
 877         return getTextAttribute().impl_selectionEndProperty();
 878     }
 879 
 880     /**
 881      * @treatAsPrivate implementation detail
 882      * @deprecated This is an internal API that is not intended
 883      * for use and will be removed in the next version
 884      */
 885     @Deprecated
 886     public final ObjectProperty<Paint> impl_selectionFillProperty() {
 887         return getTextAttribute().impl_selectionFillProperty();
 888     }
 889 
 890     /**
 891      * @treatAsPrivate implementation detail
 892      * @deprecated This is an internal API that is not intended
 893      * for use and will be removed in the next version
 894      */
 895     @Deprecated
 896     public final PathElement[] getImpl_caretShape() {
 897         return impl_caretShapeProperty().get();
 898     }
 899 
 900     /**
 901      * Shape of caret in local coordinates.
 902      *
 903      * @treatAsPrivate implementation detail
 904      * @deprecated This is an internal API that is not intended
 905      * for use and will be removed in the next version
 906     */
 907     @Deprecated
 908     public final ReadOnlyObjectProperty<PathElement[]> impl_caretShapeProperty() {



 909         return getTextAttribute().impl_caretShapeProperty();
 910     }
 911 
 912     /**
 913      * @treatAsPrivate implementation detail
 914      * @deprecated This is an internal API that is not intended
 915      * for use and will be removed in the next version

 916      */
 917     @Deprecated
 918     public final void setImpl_caretPosition(int value) {
 919         if (value == -1 &&
 920                 (attributes == null || attributes.impl_caretPosition == null)) {
 921             return;
 922         }
 923         impl_caretPositionProperty().set(value);
 924     }
 925 
 926     /**
 927      * @treatAsPrivate implementation detail
 928      * @deprecated This is an internal API that is not intended
 929      * for use and will be removed in the next version
 930      */
 931     @Deprecated
 932     public final int getImpl_caretPosition() {
 933         if (attributes == null || attributes.impl_caretPosition == null) {
 934             return DEFAULT_CARET_POSITION;
 935         }
 936         return attributes.getImpl_caretPosition();
 937     }
 938 
 939     /**
 940      * caret index in the content.
 941      * set to {@code -1} to unset caret.
 942      *
 943      * @treatAsPrivate implementation detail
 944      * @deprecated This is an internal API that is not intended
 945      * for use and will be removed in the next version
 946      */
 947     @Deprecated
 948     public final IntegerProperty impl_caretPositionProperty() {
 949         return getTextAttribute().impl_caretPositionProperty();
 950     }
 951 
 952     /**
 953      * @treatAsPrivate implementation detail
 954      * @deprecated This is an internal API that is not intended
 955      * for use and will be removed in the next version

 956      */
 957     @Deprecated
 958     public final void setImpl_caretBias(boolean value) {
 959         if (value && (attributes == null || attributes.impl_caretBias == null)) {
 960             return;
 961         }
 962         impl_caretBiasProperty().set(value);
 963     }
 964 
 965     /**
 966      * @treatAsPrivate implementation detail
 967      * @deprecated This is an internal API that is not intended
 968      * for use and will be removed in the next version
 969      */
 970     @Deprecated
 971     public final boolean isImpl_caretBias() {
 972         if (attributes == null || attributes.impl_caretBias == null) {
 973             return DEFAULT_CARET_BIAS;
 974         }
 975         return getTextAttribute().isImpl_caretBias();
 976     }
 977 
 978     /**
 979      * caret bias in the content. true means a bias towards forward character
 980      * (true=leading/false=trailing)
 981      *
 982      * @treatAsPrivate implementation detail
 983      * @deprecated This is an internal API that is not intended
 984      * for use and will be removed in the next version
 985      */
 986     @Deprecated
 987     public final BooleanProperty impl_caretBiasProperty() {
 988         return getTextAttribute().impl_caretBiasProperty();
 989     }
 990 
 991     /**
 992      * Maps local point to index in the content.
 993      *
 994      * @treatAsPrivate implementation detail
 995      * @deprecated This is an internal API that is not intended
 996      * for use and will be removed in the next version
 997      */
 998     @Deprecated
 999     public final HitInfo impl_hitTestChar(Point2D point) {
1000         if (point == null) return null;
1001         TextLayout layout = getTextLayout();
1002         double x = point.getX() - getX();
1003         double y = point.getY() - getY() + getYRendering();
1004         return layout.getHitInfo((float)x, (float)y);


1005     }
1006 
1007     private PathElement[] getRange(int start, int end, int type) {
1008         int length = getTextInternal().length();
1009         if (0 <= start && start < end  && end <= length) {
1010             TextLayout layout = getTextLayout();
1011             float x = (float)getX();
1012             float y = (float)getY() - getYRendering();
1013             return layout.getRange(start, end, type, x, y);
1014         }
1015         return EMPTY_PATH_ELEMENT_ARRAY;
1016     }
1017 
1018     /**















1019      * Returns shape for the range of the text in local coordinates.
1020      *
1021      * @treatAsPrivate implementation detail
1022      * @deprecated This is an internal API that is not intended
1023      * for use and will be removed in the next version
1024      */
1025     @Deprecated
1026     public final PathElement[] impl_getRangeShape(int start, int end) {
1027         return getRange(start, end, TextLayout.TYPE_TEXT);
1028     }
1029 
1030     /**
1031      * Returns shape for the underline in local coordinates.
1032      *
1033      * @treatAsPrivate implementation detail
1034      * @deprecated This is an internal API that is not intended
1035      * for use and will be removed in the next version
1036      */
1037     @Deprecated
1038     public final PathElement[] impl_getUnderlineShape(int start, int end) {
1039         return getRange(start, end, TextLayout.TYPE_UNDERLINE);
1040     }
1041 
1042     /**
1043      * Shows/Hides on-screen keyboard if available (mobile platform)
1044      *
1045      * @treatAsPrivate implementation detail
1046      * @deprecated This is an internal API that is not intended
1047      * for use and will be removed in the next version
1048      */
1049     @Deprecated
1050     public final void impl_displaySoftwareKeyboard(boolean display) {
1051     }
1052 
1053     private float getYAdjustment(BaseBounds bounds) {
1054         VPos origin = getTextOrigin();
1055         if (origin == null) origin = DEFAULT_TEXT_ORIGIN;
1056         switch (origin) {
1057         case TOP: return -bounds.getMinY();
1058         case BASELINE: return 0;


1460         }
1461         if (impl_isDirty(DirtyBits.TEXT_FONT)) {
1462             peer.setFont(getFontInternal());
1463         }
1464         if (impl_isDirty(DirtyBits.NODE_CONTENTS)) {
1465             peer.setGlyphs(getRuns());
1466         }
1467         if (impl_isDirty(DirtyBits.NODE_GEOMETRY)) {
1468             if (isSpan()) {
1469                 BaseBounds spanBounds = getSpanBounds();
1470                 peer.setLayoutLocation(spanBounds.getMinX(), spanBounds.getMinY());
1471             } else {
1472                 float x = (float)getX();
1473                 float y = (float)getY();
1474                 float yadj = getYRendering();
1475                 peer.setLayoutLocation(-x, yadj - y);
1476             }
1477         }
1478         if (impl_isDirty(DirtyBits.TEXT_SELECTION)) {
1479             Object fillObj = null;
1480             int start = getImpl_selectionStart();
1481             int end = getImpl_selectionEnd();
1482             int length = getTextInternal().length();
1483             if (0 <= start && start < end  && end <= length) {
1484                 Paint fill = impl_selectionFillProperty().get();
1485                 fillObj = fill != null ? Toolkit.getPaintAccessor().getPlatformPaint(fill) : null;
1486             }
1487             peer.setSelection(start, end, fillObj);
1488         }
1489     }
1490 
1491     /**
1492      * @treatAsPrivate implementation detail
1493      * @deprecated This is an internal API that is not intended
1494      * for use and will be removed in the next version
1495      */
1496     @Deprecated
1497     @Override
1498     public final void impl_updatePeer() {
1499         super.impl_updatePeer();
1500         updatePGText();
1501     }
1502 
1503     /***************************************************************************
1504      *                                                                         *


1674                              * for the first line in the layout */
1675                             BaseBounds bounds = getLogicalBounds();
1676                             return -bounds.getMinY();
1677                         }
1678                     });}
1679                 };
1680             }
1681             return baselineOffset.getReadOnlyProperty();
1682         }
1683 
1684         @Deprecated
1685         private ObjectProperty<PathElement[]> impl_selectionShape;
1686         private ObjectBinding<PathElement[]> impl_selectionBinding;
1687 
1688         @Deprecated
1689         public final ReadOnlyObjectProperty<PathElement[]> impl_selectionShapeProperty() {
1690             if (impl_selectionShape == null) {
1691                 impl_selectionBinding = new ObjectBinding<PathElement[]>() {
1692                     {bind(impl_selectionStartProperty(), impl_selectionEndProperty());}
1693                     @Override protected PathElement[] computeValue() {
1694                         int start = getImpl_selectionStart();
1695                         int end = getImpl_selectionEnd();
1696                         return getRange(start, end, TextLayout.TYPE_TEXT);
1697                     }
1698               };
1699               impl_selectionShape = new SimpleObjectProperty<PathElement[]>(Text.this, "impl_selectionShape");
1700               impl_selectionShape.bind(impl_selectionBinding);
1701             }
1702             return impl_selectionShape;
1703         }
1704 
1705         private ObjectProperty<Paint> selectionFill;
1706 
1707         @Deprecated
1708         public final ObjectProperty<Paint> impl_selectionFillProperty() {
1709             if (selectionFill == null) {
1710                 selectionFill =
1711                     new ObjectPropertyBase<Paint>(DEFAULT_SELECTION_FILL) {
1712                         @Override public Object getBean() { return Text.this; }
1713                         @Override public String getName() { return "impl_selectionFill"; }
1714                         @Override protected void invalidated() {
1715                             impl_markDirty(DirtyBits.TEXT_SELECTION);


1750         public final int getImpl_selectionEnd() {
1751             return impl_selectionEnd == null ? DEFAULT_SELECTION_END : impl_selectionEnd.get();
1752         }
1753 
1754         @Deprecated
1755         public final IntegerProperty impl_selectionEndProperty() {
1756             if (impl_selectionEnd == null) {
1757                 impl_selectionEnd =
1758                     new IntegerPropertyBase(DEFAULT_SELECTION_END) {
1759                         @Override public Object getBean() { return Text.this; }
1760                         @Override public String getName() { return "impl_selectionEnd"; }
1761                         @Override protected void invalidated() {
1762                             impl_markDirty(DirtyBits.TEXT_SELECTION);
1763                             notifyAccessibleAttributeChanged(AccessibleAttribute.SELECTION_END);
1764                         }
1765                     };
1766             }
1767             return impl_selectionEnd;
1768         }
1769 
1770         @Deprecated
1771         private ObjectProperty<PathElement[]> impl_caretShape;
1772         private ObjectBinding<PathElement[]> impl_caretBinding;
1773 
1774         @Deprecated
1775         public final ReadOnlyObjectProperty<PathElement[]> impl_caretShapeProperty() {
1776             if (impl_caretShape == null) {
1777                 impl_caretBinding = new ObjectBinding<PathElement[]>() {
1778                     {bind(impl_caretPositionProperty(), impl_caretBiasProperty());}
1779                     @Override protected PathElement[] computeValue() {
1780                         int pos = getImpl_caretPosition();
1781                         int length = getTextInternal().length();
1782                         if (0 <= pos && pos <= length) {
1783                             boolean bias = isImpl_caretBias();
1784                             float x = (float)getX();
1785                             float y = (float)getY() - getYRendering();
1786                             TextLayout layout = getTextLayout();
1787                             return layout.getCaretShape(pos, bias, x, y);
1788                         }
1789                         return EMPTY_PATH_ELEMENT_ARRAY;
1790                     }


1880 
1881         Paint stroke = getStroke();
1882         if (stroke != null) {
1883             sb.append(", stroke=").append(stroke);
1884             sb.append(", strokeWidth=").append(getStrokeWidth());
1885         }
1886 
1887         return sb.append("]").toString();
1888     }
1889 
1890     @Override
1891     public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
1892         switch (attribute) {
1893             case TEXT: {
1894                 String accText = getAccessibleText();
1895                 if (accText != null && !accText.isEmpty()) return accText;
1896                 return getText();
1897             }
1898             case FONT: return getFont();
1899             case CARET_OFFSET: {
1900                 int sel = getImpl_caretPosition();
1901                 if (sel >=  0) return sel;
1902                 return getText().length();
1903             }
1904             case SELECTION_START: {
1905                 int sel = getImpl_selectionStart();
1906                 if (sel >=  0) return sel;
1907                 sel = getImpl_caretPosition();
1908                 if (sel >=  0) return sel;
1909                 return getText().length();
1910             }
1911             case SELECTION_END:  {
1912                 int sel = getImpl_selectionEnd();
1913                 if (sel >=  0) return sel;
1914                 sel = getImpl_caretPosition();
1915                 if (sel >=  0) return sel;
1916                 return getText().length();
1917             }
1918             case LINE_FOR_OFFSET: {
1919                 int offset = (Integer)parameters[0];
1920                 if (offset > getTextInternal().length()) return null;
1921                 TextLine[] lines = getTextLayout().getLines();
1922                 int lineIndex = 0;
1923                 for (int i = 1; i < lines.length; i++) {
1924                     TextLine line = lines[i];
1925                     if (line.getStart() > offset) break;
1926                     lineIndex++;
1927                 }
1928                 return lineIndex;
1929             }
1930             case LINE_START: {
1931                 int lineIndex = (Integer)parameters[0];
1932                 TextLine[] lines = getTextLayout().getLines();
1933                 if (0 <= lineIndex && lineIndex < lines.length) {
1934                     TextLine line = lines[lineIndex];
1935                     return line.getStart();
1936                 }
1937                 return null;
1938             }
1939             case LINE_END: {
1940                 int lineIndex = (Integer)parameters[0];
1941                 TextLine[] lines = getTextLayout().getLines();
1942                 if (0 <= lineIndex && lineIndex < lines.length) {
1943                     TextLine line = lines[lineIndex];
1944                     return line.getStart() + line.getLength();
1945                 }
1946                 return null;
1947             }
1948             case OFFSET_AT_POINT: {
1949                 Point2D point = (Point2D)parameters[0];
1950                 point = screenToLocal(point);
1951                 return impl_hitTestChar(point).getCharIndex();
1952             }
1953             case BOUNDS_FOR_RANGE: {
1954                 int start = (Integer)parameters[0];
1955                 int end = (Integer)parameters[1];
1956                 PathElement[] elements = impl_getRangeShape(start, end + 1);
1957                 /* Each bounds is defined by a MoveTo (top-left) followed by
1958                  * 4 LineTo (to top-right, bottom-right, bottom-left, back to top-left).
1959                  */
1960                 Bounds[] bounds = new Bounds[elements.length / 5];
1961                 int index = 0;
1962                 for (int i = 0; i < bounds.length; i++) {
1963                     MoveTo topLeft = (MoveTo)elements[index];
1964                     LineTo topRight = (LineTo)elements[index+1];
1965                     LineTo bottomRight = (LineTo)elements[index+2];
1966                     BoundingBox b = new BoundingBox(topLeft.getX(), topLeft.getY(),
1967                                                     topRight.getX() - topLeft.getX(),
1968                                                     bottomRight.getY() - topRight.getY());
1969                     bounds[i] = localToScreen(b);
1970                     index += 5;
1971                 }
1972                 return bounds;
1973             }
1974             default: return super.queryAccessibleAttribute(attribute, parameters);
1975         }
1976     }
   1 /*
   2  * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.text;
  27 
  28 import javafx.css.converter.BooleanConverter;
  29 import javafx.css.converter.EnumConverter;
  30 import javafx.css.converter.SizeConverter;
  31 import com.sun.javafx.geom.BaseBounds;
  32 import com.sun.javafx.geom.Path2D;
  33 import com.sun.javafx.geom.RectBounds;
  34 import com.sun.javafx.geom.TransformedShape;
  35 import com.sun.javafx.geom.transform.BaseTransform;
  36 import com.sun.javafx.scene.DirtyBits;
  37 import com.sun.javafx.scene.text.GlyphList;
  38 import com.sun.javafx.scene.text.TextLayout;
  39 import com.sun.javafx.scene.text.TextLayoutFactory;
  40 import com.sun.javafx.scene.text.TextLine;
  41 import com.sun.javafx.scene.text.TextSpan;
  42 import com.sun.javafx.sg.prism.NGNode;
  43 import com.sun.javafx.sg.prism.NGShape;
  44 import com.sun.javafx.sg.prism.NGText;
  45 import com.sun.javafx.tk.Toolkit;
  46 import javafx.beans.DefaultProperty;
  47 import javafx.beans.InvalidationListener;
  48 import javafx.beans.binding.DoubleBinding;
  49 import javafx.beans.binding.ObjectBinding;
  50 import javafx.beans.property.*;
  51 import javafx.css.*;
  52 import javafx.geometry.*;
  53 import javafx.scene.AccessibleAttribute;
  54 import javafx.scene.AccessibleRole;
  55 import javafx.scene.paint.Color;
  56 import javafx.scene.paint.Paint;
  57 import javafx.scene.shape.LineTo;
  58 import javafx.scene.shape.MoveTo;
  59 import javafx.scene.shape.PathElement;
  60 import javafx.scene.shape.Shape;
  61 import javafx.scene.shape.StrokeType;


 374         textProperty().set(value);
 375     }
 376 
 377     public final String getText() {
 378         return text == null ? "" : text.get();
 379     }
 380 
 381     private String getTextInternal() {
 382         // this might return null in case of bound property
 383         String localText = getText();
 384         return localText == null ? "" : localText;
 385     }
 386 
 387     public final StringProperty textProperty() {
 388         if (text == null) {
 389             text = new StringPropertyBase("") {
 390                 @Override public Object getBean() { return Text.this; }
 391                 @Override public String getName() { return "text"; }
 392                 @Override  public void invalidated() {
 393                     needsFullTextLayout();
 394                     setSelectionStart(-1);
 395                     setSelectionEnd(-1);
 396                     setCaretPosition(-1);
 397                     setCaretBias(true);
 398 
 399                     // MH: Functionality copied from store() method,
 400                     // which was removed.
 401                     // Wonder what should happen if text is bound
 402                     //  and becomes null?
 403                     final String value = get();
 404                     if ((value == null) && !isBound()) {
 405                         set("");
 406                     }
 407                     notifyAccessibleAttributeChanged(AccessibleAttribute.TEXT);
 408                 }
 409             };
 410         }
 411         return text;
 412     }
 413 
 414     /**
 415      * Defines the X coordinate of text origin.
 416      *
 417      * @defaultValue 0


 763      * @treatAsPrivate implementation detail
 764      * @deprecated This is an internal API that is not intended
 765      * for use and will be removed in the next version
 766      */
 767     @Deprecated
 768     @Override
 769     protected final void impl_geomChanged() {
 770         super.impl_geomChanged();
 771         if (attributes != null) {
 772             if (attributes.impl_caretBinding != null) {
 773                 attributes.impl_caretBinding.invalidate();
 774             }
 775             if (attributes.impl_selectionBinding != null) {
 776                 attributes.impl_selectionBinding.invalidate();
 777             }
 778         }
 779         impl_markDirty(DirtyBits.NODE_GEOMETRY);
 780     }
 781 
 782     /**










 783      * Shape of selection in local coordinates.
 784      *
 785      * @since 9


 786      */
 787     public final PathElement[] getSelectionShape() {
 788         return selectionShapeProperty().get();
 789     }
 790 
 791     public final ReadOnlyObjectProperty<PathElement[]> selectionShapeProperty() {
 792         return getTextAttribute().impl_selectionShapeProperty();
 793     }
 794 
 795     /**
 796      * Selection start index in the content.
 797      * set to {@code -1} to unset selection.
 798      *
 799      * @since 9
 800      */
 801     public final void setSelectionStart(int value) {

 802         if (value == -1 &&
 803                 (attributes == null || attributes.impl_selectionStart == null)) {
 804             return;
 805         }
 806         selectionStartProperty().set(value);
 807     }
 808 
 809     public final int getSelectionStart() {






 810         if (attributes == null || attributes.impl_selectionStart == null) {
 811             return DEFAULT_SELECTION_START;
 812         }
 813         return attributes.getImpl_selectionStart();
 814     }
 815 
 816     public final IntegerProperty selectionStartProperty() {









 817         return getTextAttribute().impl_selectionStartProperty();
 818     }
 819 
 820     /**
 821      * Selection end index in the content.
 822      * set to {@code -1} to unset selection.
 823      *
 824      * @since 9
 825      */
 826     public final void setSelectionEnd(int value) {

 827         if (value == -1 &&
 828                 (attributes == null || attributes.impl_selectionEnd == null)) {
 829             return;
 830         }
 831         selectionEndProperty().set(value);
 832     }
 833 
 834     public final int getSelectionEnd() {






 835         if (attributes == null || attributes.impl_selectionEnd == null) {
 836             return DEFAULT_SELECTION_END;
 837         }
 838         return attributes.getImpl_selectionEnd();
 839     }
 840 
 841     public final IntegerProperty selectionEndProperty() {









 842         return getTextAttribute().impl_selectionEndProperty();
 843     }
 844 
 845     /**
 846      * The fill color of selected text.
 847      *
 848      * @since 9
 849      */
 850     public final ObjectProperty<Paint> selectionFillProperty() {

 851         return getTextAttribute().impl_selectionFillProperty();
 852     }
 853 
 854     public final void setSelectionFill(Paint paint) {
 855         selectionFillProperty().set(paint);
 856     }
 857     public final Paint getSelectionFill() {
 858         return selectionFillProperty().get();



 859     }
 860 
 861     /**
 862      * Shape of caret in local coordinates.
 863      *
 864      * @since 9


 865      */
 866     public final PathElement[] getCaretShape() {
 867         return caretShapeProperty().get();
 868     }
 869 
 870     public final ReadOnlyObjectProperty<PathElement[]> caretShapeProperty() {
 871         return getTextAttribute().impl_caretShapeProperty();
 872     }
 873 
 874     /**
 875      * caret index in the content.
 876      * set to {@code -1} to unset caret.
 877      *
 878      * @since 9
 879      */
 880     public final void setCaretPosition(int value) {

 881         if (value == -1 &&
 882                 (attributes == null || attributes.impl_caretPosition == null)) {
 883             return;
 884         }
 885         caretPositionProperty().set(value);
 886     }
 887 
 888     public final int getCaretPosition() {






 889         if (attributes == null || attributes.impl_caretPosition == null) {
 890             return DEFAULT_CARET_POSITION;
 891         }
 892         return attributes.getImpl_caretPosition();
 893     }
 894 
 895     public final IntegerProperty caretPositionProperty() {









 896         return getTextAttribute().impl_caretPositionProperty();
 897     }
 898 
 899     /**
 900      * caret bias in the content. {@code true} means a bias towards the leading character edge.
 901      * (true=leading/false=trailing)
 902      *
 903      * @since 9
 904      */
 905     public final void setCaretBias(boolean value) {

 906         if (value && (attributes == null || attributes.impl_caretBias == null)) {
 907             return;
 908         }
 909         caretBiasProperty().set(value);
 910     }
 911 
 912     public final boolean isCaretBias() {






 913         if (attributes == null || attributes.impl_caretBias == null) {
 914             return DEFAULT_CARET_BIAS;
 915         }
 916         return getTextAttribute().isImpl_caretBias();
 917     }
 918 
 919     public final BooleanProperty caretBiasProperty() {









 920         return getTextAttribute().impl_caretBiasProperty();
 921     }
 922 
 923     /**
 924      * Maps local point to index in the content.
 925      *
 926      * @since 9


 927      */
 928     public final HitInfo hitTest(Point2D point) {

 929         if (point == null) return null;
 930         TextLayout layout = getTextLayout();
 931         double x = point.getX() - getX();
 932         double y = point.getY() - getY() + getYRendering();
 933         TextLayout.Hit layoutHit = layout.getHitInfo((float)x, (float)y);
 934         return new HitInfo(layoutHit.getCharIndex(), layoutHit.getInsertionIndex(),
 935                            layoutHit.isLeading(), getText());
 936     }
 937 
 938     private PathElement[] getRange(int start, int end, int type) {
 939         int length = getTextInternal().length();
 940         if (0 <= start && start < end  && end <= length) {
 941             TextLayout layout = getTextLayout();
 942             float x = (float)getX();
 943             float y = (float)getY() - getYRendering();
 944             return layout.getRange(start, end, type, x, y);
 945         }
 946         return EMPTY_PATH_ELEMENT_ARRAY;
 947     }
 948 
 949     /**
 950      * Returns shape for the caret at given index and bias.
 951      *
 952      * @since 9
 953      */
 954     public final PathElement[] caretShape(int charIndex, boolean caretBias) {
 955         if (0 <= charIndex && charIndex <= getTextInternal().length()) {
 956             float x = (float)getX();
 957             float y = (float)getY() - getYRendering();
 958             return getTextLayout().getCaretShape(charIndex, caretBias, x, y);
 959         } else {
 960             return null;
 961         }
 962     }
 963 
 964     /**
 965      * Returns shape for the range of the text in local coordinates.
 966      *
 967      * @since 9


 968      */
 969     public final PathElement[] rangeShape(int start, int end) {

 970         return getRange(start, end, TextLayout.TYPE_TEXT);
 971     }
 972 
 973     /**
 974      * Returns shape for the underline in local coordinates.
 975      *
 976      * @since 9


 977      */
 978     public final PathElement[] underlineShape(int start, int end) {

 979         return getRange(start, end, TextLayout.TYPE_UNDERLINE);
 980     }
 981 
 982     /**
 983      * Shows/Hides on-screen keyboard if available (mobile platform)
 984      *
 985      * @treatAsPrivate implementation detail
 986      * @deprecated This is an internal API that is not intended
 987      * for use and will be removed in the next version
 988      */
 989     @Deprecated
 990     public final void impl_displaySoftwareKeyboard(boolean display) {
 991     }
 992 
 993     private float getYAdjustment(BaseBounds bounds) {
 994         VPos origin = getTextOrigin();
 995         if (origin == null) origin = DEFAULT_TEXT_ORIGIN;
 996         switch (origin) {
 997         case TOP: return -bounds.getMinY();
 998         case BASELINE: return 0;


1400         }
1401         if (impl_isDirty(DirtyBits.TEXT_FONT)) {
1402             peer.setFont(getFontInternal());
1403         }
1404         if (impl_isDirty(DirtyBits.NODE_CONTENTS)) {
1405             peer.setGlyphs(getRuns());
1406         }
1407         if (impl_isDirty(DirtyBits.NODE_GEOMETRY)) {
1408             if (isSpan()) {
1409                 BaseBounds spanBounds = getSpanBounds();
1410                 peer.setLayoutLocation(spanBounds.getMinX(), spanBounds.getMinY());
1411             } else {
1412                 float x = (float)getX();
1413                 float y = (float)getY();
1414                 float yadj = getYRendering();
1415                 peer.setLayoutLocation(-x, yadj - y);
1416             }
1417         }
1418         if (impl_isDirty(DirtyBits.TEXT_SELECTION)) {
1419             Object fillObj = null;
1420             int start = getSelectionStart();
1421             int end = getSelectionEnd();
1422             int length = getTextInternal().length();
1423             if (0 <= start && start < end  && end <= length) {
1424                 Paint fill = selectionFillProperty().get();
1425                 fillObj = fill != null ? Toolkit.getPaintAccessor().getPlatformPaint(fill) : null;
1426             }
1427             peer.setSelection(start, end, fillObj);
1428         }
1429     }
1430 
1431     /**
1432      * @treatAsPrivate implementation detail
1433      * @deprecated This is an internal API that is not intended
1434      * for use and will be removed in the next version
1435      */
1436     @Deprecated
1437     @Override
1438     public final void impl_updatePeer() {
1439         super.impl_updatePeer();
1440         updatePGText();
1441     }
1442 
1443     /***************************************************************************
1444      *                                                                         *


1614                              * for the first line in the layout */
1615                             BaseBounds bounds = getLogicalBounds();
1616                             return -bounds.getMinY();
1617                         }
1618                     });}
1619                 };
1620             }
1621             return baselineOffset.getReadOnlyProperty();
1622         }
1623 
1624         @Deprecated
1625         private ObjectProperty<PathElement[]> impl_selectionShape;
1626         private ObjectBinding<PathElement[]> impl_selectionBinding;
1627 
1628         @Deprecated
1629         public final ReadOnlyObjectProperty<PathElement[]> impl_selectionShapeProperty() {
1630             if (impl_selectionShape == null) {
1631                 impl_selectionBinding = new ObjectBinding<PathElement[]>() {
1632                     {bind(impl_selectionStartProperty(), impl_selectionEndProperty());}
1633                     @Override protected PathElement[] computeValue() {
1634                         int start = getSelectionStart();
1635                         int end = getSelectionEnd();
1636                         return getRange(start, end, TextLayout.TYPE_TEXT);
1637                     }
1638               };
1639               impl_selectionShape = new SimpleObjectProperty<PathElement[]>(Text.this, "impl_selectionShape");
1640               impl_selectionShape.bind(impl_selectionBinding);
1641             }
1642             return impl_selectionShape;
1643         }
1644 
1645         private ObjectProperty<Paint> selectionFill;
1646 
1647         @Deprecated
1648         public final ObjectProperty<Paint> impl_selectionFillProperty() {
1649             if (selectionFill == null) {
1650                 selectionFill =
1651                     new ObjectPropertyBase<Paint>(DEFAULT_SELECTION_FILL) {
1652                         @Override public Object getBean() { return Text.this; }
1653                         @Override public String getName() { return "impl_selectionFill"; }
1654                         @Override protected void invalidated() {
1655                             impl_markDirty(DirtyBits.TEXT_SELECTION);


1690         public final int getImpl_selectionEnd() {
1691             return impl_selectionEnd == null ? DEFAULT_SELECTION_END : impl_selectionEnd.get();
1692         }
1693 
1694         @Deprecated
1695         public final IntegerProperty impl_selectionEndProperty() {
1696             if (impl_selectionEnd == null) {
1697                 impl_selectionEnd =
1698                     new IntegerPropertyBase(DEFAULT_SELECTION_END) {
1699                         @Override public Object getBean() { return Text.this; }
1700                         @Override public String getName() { return "impl_selectionEnd"; }
1701                         @Override protected void invalidated() {
1702                             impl_markDirty(DirtyBits.TEXT_SELECTION);
1703                             notifyAccessibleAttributeChanged(AccessibleAttribute.SELECTION_END);
1704                         }
1705                     };
1706             }
1707             return impl_selectionEnd;
1708         }
1709 

1710         private ObjectProperty<PathElement[]> impl_caretShape;
1711         private ObjectBinding<PathElement[]> impl_caretBinding;
1712 
1713         @Deprecated
1714         public final ReadOnlyObjectProperty<PathElement[]> impl_caretShapeProperty() {
1715             if (impl_caretShape == null) {
1716                 impl_caretBinding = new ObjectBinding<PathElement[]>() {
1717                     {bind(impl_caretPositionProperty(), impl_caretBiasProperty());}
1718                     @Override protected PathElement[] computeValue() {
1719                         int pos = getImpl_caretPosition();
1720                         int length = getTextInternal().length();
1721                         if (0 <= pos && pos <= length) {
1722                             boolean bias = isImpl_caretBias();
1723                             float x = (float)getX();
1724                             float y = (float)getY() - getYRendering();
1725                             TextLayout layout = getTextLayout();
1726                             return layout.getCaretShape(pos, bias, x, y);
1727                         }
1728                         return EMPTY_PATH_ELEMENT_ARRAY;
1729                     }


1819 
1820         Paint stroke = getStroke();
1821         if (stroke != null) {
1822             sb.append(", stroke=").append(stroke);
1823             sb.append(", strokeWidth=").append(getStrokeWidth());
1824         }
1825 
1826         return sb.append("]").toString();
1827     }
1828 
1829     @Override
1830     public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
1831         switch (attribute) {
1832             case TEXT: {
1833                 String accText = getAccessibleText();
1834                 if (accText != null && !accText.isEmpty()) return accText;
1835                 return getText();
1836             }
1837             case FONT: return getFont();
1838             case CARET_OFFSET: {
1839                 int sel = getCaretPosition();
1840                 if (sel >=  0) return sel;
1841                 return getText().length();
1842             }
1843             case SELECTION_START: {
1844                 int sel = getSelectionStart();
1845                 if (sel >=  0) return sel;
1846                 sel = getCaretPosition();
1847                 if (sel >=  0) return sel;
1848                 return getText().length();
1849             }
1850             case SELECTION_END:  {
1851                 int sel = getSelectionEnd();
1852                 if (sel >=  0) return sel;
1853                 sel = getCaretPosition();
1854                 if (sel >=  0) return sel;
1855                 return getText().length();
1856             }
1857             case LINE_FOR_OFFSET: {
1858                 int offset = (Integer)parameters[0];
1859                 if (offset > getTextInternal().length()) return null;
1860                 TextLine[] lines = getTextLayout().getLines();
1861                 int lineIndex = 0;
1862                 for (int i = 1; i < lines.length; i++) {
1863                     TextLine line = lines[i];
1864                     if (line.getStart() > offset) break;
1865                     lineIndex++;
1866                 }
1867                 return lineIndex;
1868             }
1869             case LINE_START: {
1870                 int lineIndex = (Integer)parameters[0];
1871                 TextLine[] lines = getTextLayout().getLines();
1872                 if (0 <= lineIndex && lineIndex < lines.length) {
1873                     TextLine line = lines[lineIndex];
1874                     return line.getStart();
1875                 }
1876                 return null;
1877             }
1878             case LINE_END: {
1879                 int lineIndex = (Integer)parameters[0];
1880                 TextLine[] lines = getTextLayout().getLines();
1881                 if (0 <= lineIndex && lineIndex < lines.length) {
1882                     TextLine line = lines[lineIndex];
1883                     return line.getStart() + line.getLength();
1884                 }
1885                 return null;
1886             }
1887             case OFFSET_AT_POINT: {
1888                 Point2D point = (Point2D)parameters[0];
1889                 point = screenToLocal(point);
1890                 return hitTest(point).getCharIndex();
1891             }
1892             case BOUNDS_FOR_RANGE: {
1893                 int start = (Integer)parameters[0];
1894                 int end = (Integer)parameters[1];
1895                 PathElement[] elements = rangeShape(start, end + 1);
1896                 /* Each bounds is defined by a MoveTo (top-left) followed by
1897                  * 4 LineTo (to top-right, bottom-right, bottom-left, back to top-left).
1898                  */
1899                 Bounds[] bounds = new Bounds[elements.length / 5];
1900                 int index = 0;
1901                 for (int i = 0; i < bounds.length; i++) {
1902                     MoveTo topLeft = (MoveTo)elements[index];
1903                     LineTo topRight = (LineTo)elements[index+1];
1904                     LineTo bottomRight = (LineTo)elements[index+2];
1905                     BoundingBox b = new BoundingBox(topLeft.getX(), topLeft.getY(),
1906                                                     topRight.getX() - topLeft.getX(),
1907                                                     bottomRight.getY() - topRight.getY());
1908                     bounds[i] = localToScreen(b);
1909                     index += 5;
1910                 }
1911                 return bounds;
1912             }
1913             default: return super.queryAccessibleAttribute(attribute, parameters);
1914         }
1915     }
< prev index next >