Changeset 1101


Ignore:
Timestamp:
05/10/18 16:04:37 (6 years ago)
Author:
davidb
Message:

Merging of Bryce's change in getPixelBoundsUnion() with Corey's logic/graphics separation

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/expeditee/items/Text.java

    r1095 r1101  
    1919package org.expeditee.items;
    2020
    21 import java.awt.BasicStroke;
    22 import java.awt.Color;
    23 import java.awt.Dimension;
    24 import java.awt.Font;
    25 import java.awt.GradientPaint;
    26 import java.awt.Graphics2D;
    27 import java.awt.GraphicsEnvironment;
    28 import java.awt.Point;
    29 import java.awt.Polygon;
    30 import java.awt.Rectangle;
    31 import java.awt.Shape;
    32 import java.awt.Stroke;
    33 import java.awt.event.KeyEvent;
    34 import java.awt.event.MouseEvent;
    35 import java.awt.font.FontRenderContext;
    36 import java.awt.font.LineBreakMeasurer;
    37 import java.awt.font.TextAttribute;
    38 import java.awt.font.TextHitInfo;
    39 import java.awt.font.TextLayout;
    40 import java.awt.geom.AffineTransform;
    41 import java.awt.geom.Point2D;
    42 import java.awt.geom.Rectangle2D;
    4321import java.io.File;
    4422
    45 import java.text.AttributedString;
    4623import java.util.ArrayList;
    4724import java.util.Collection;
     
    5229import java.util.StringTokenizer;
    5330
     31import org.expeditee.core.Colour;
     32import org.expeditee.core.Dimension;
     33import org.expeditee.core.Fill;
     34import org.expeditee.core.Font;
     35import org.expeditee.core.GradientFill;
     36import org.expeditee.core.Point;
     37import org.expeditee.core.Range;
     38import org.expeditee.core.Stroke;
     39import org.expeditee.core.TextHitInfo;
     40import org.expeditee.core.TextLayout;
     41import org.expeditee.core.bounds.AxisAlignedBoxBounds;
     42import org.expeditee.core.bounds.CombinationBoxBounds;
     43import org.expeditee.core.bounds.PolygonBounds;
     44import org.expeditee.gio.EcosystemManager;
     45import org.expeditee.gio.GraphicsManager;
     46import org.expeditee.gio.gesture.StandardGestureActions;
    5447import org.expeditee.gui.AttributeValuePair;
    55 import org.expeditee.gui.DisplayIO;
     48import org.expeditee.gui.DisplayController;
    5649import org.expeditee.gui.Frame;
    57 import org.expeditee.gui.FrameGraphics;
    5850import org.expeditee.gui.FrameIO;
    59 import org.expeditee.gui.FrameKeyboardActions;
    60 import org.expeditee.gui.FrameMouseActions;
    6151import org.expeditee.gui.FrameUtils;
    6252import org.expeditee.gui.FreeItems;
     53import org.expeditee.gui.MessageBay;
    6354import org.expeditee.items.MagneticConstraint.MagneticConstraints;
    6455import org.expeditee.math.ExpediteeJEP;
    6556import org.expeditee.settings.experimental.ExperimentalFeatures;
     57import org.expeditee.stats.Formatter;
    6658import org.nfunk.jep.Node;
    6759
     
    7567public class Text extends Item {
    7668        private static final int ADJUST_WIDTH_THRESHOLD = 200;
     69       
     70        public static final char DELETE_CHARACTER = 0x7F;
     71       
     72        public static final char BACKSPACE_CHARACTER = '\b';
     73       
     74        public static final char TAB_CHARACTER = '\t';
     75       
     76        public static final char ESC_CHARACTER = 0x1B;
    7777
    7878        public static String LINE_SEPARATOR = System.getProperty("line.separator");
     
    9797        public static final String FRAME_NAME_SEPARATOR = " on frame ";
    9898
    99         /**
    100          * The default font used to display text items if no font is specified.
    101          */
     99        /** The default font used to display text items if no font is specified. */
    102100        public static final String DEFAULT_FONT = "Serif-Plain-18";
    103101
    104         public static final Color DEFAULT_COLOR = Color.BLACK;
     102        public static final Colour DEFAULT_COLOR = Colour.BLACK;
    105103
    106104        public static final int MINIMUM_RANGED_CHARS = 2;
     
    128126        public static final int PAGE_UP = 8;
    129127
    130         /*
     128        /**
    131129         * Set the width to be IMPLICIT, but as wide as possible, a negative width value
    132130         * is one that is implicitly set by the system... a positive value is one
    133131         * explicitly set by the user.
    134132         */
    135         private Integer _maxWidth = Integer.MIN_VALUE + 1;
     133        /**
     134         * The maximum allowable width of the Text item. Actual width may be less than this
     135         * value, subject to text wrapping. Negative values indicate the width was implicitly
     136         * set by the system, positive values indicate explicit setting by the user. Initially
     137         * set to be as wide as possible.
     138         */
     139        private Integer _maxWidth = -Integer.MAX_VALUE;
    136140
    137141        private Justification _justification = Justification.left;
     
    150154        private int _selectionEnd = -1;
    151155
     156        /** Keeps track of the last Text item selected. */
     157        private static Text _lastSelected = null;
     158       
     159        // Range selection colours
     160        /** Colour of selected range when for selecting text. */
     161        public static final Colour RANGE_SELECT_COLOUR = Colour.FromRGB255(255, 160, 160);
     162        /** Colour of selected range when for cutting text. */
     163        public static final Colour RANGE_CUT_COLOUR = Colour.FromRGB255(160, 255, 160);
     164        /** Colour of selected range when for copying text. */
     165        public static final Colour RANGE_COPY_COLOUR = Colour.FromRGB255(160, 160, 255);
     166        /** Colour of selected range when for deleting text. */
     167        public static final Colour RANGE_DELETE_COLOUR = Colour.FromRGB255(235, 235, 140);
     168       
     169        /** The colour to draw range selections in. */
     170        private Colour _selectionColour = RANGE_SELECT_COLOUR;
     171               
    152172        // whether autowrap is on/off for this item
    153173        protected boolean _autoWrap = false;
     
    158178        private List<TextLayout> _textLayouts = new LinkedList<TextLayout>();
    159179
    160         private List<Integer> _lineOffsets = new LinkedList<Integer>();
    161 
    162         private FontRenderContext frc = null;
    163         private LineBreakMeasurer _lineBreaker = null;
    164 
    165180        // The font to display this text in
    166181        private Font _font;
    167182
    168         protected static void InitFontFamily(GraphicsEnvironment ge, File fontFamilyDir) {
     183        protected static void InitFontFamily(File fontFamilyDir)
     184        {
    169185                File[] fontFiles = fontFamilyDir.listFiles();
    170186
     
    183199
    184200                                try {
    185                                         Font font = Font.createFont(Font.TRUETYPE_FONT, fontFile);
    186 
    187                                         boolean registered_status_ok = ge.registerFont(font);
    188 
    189                                         if (registered_status_ok) {
    190 
    191                                                 String font_family = font.getFamily();
    192                                                 if (!FONT_WHEEL_ADDITIONAL_LOOKUP.containsKey(font_family)) {
     201                                        Font font = EcosystemManager.getFontManager().registerFontFile(fontFile);
     202
     203                                        if (font != null) {
     204                                           
     205                                            String font_family = font.getFamilyName();
     206                                            if (!FONT_WHEEL_ADDITIONAL_LOOKUP.containsKey(font_family)) {
    193207
    194208                                                        if (FONT_WHEEL_ADDITIONAL_LOOKUP.size() > 0) {
    195209                                                                System.out.print(", ");
    196210                                                        }
    197                                                         System.out.print("'" + font.getFamily() + "'");
     211                                                        System.out.print("'" + font_family + "'");
    198212
    199213                                                        FONT_WHEEL_ADDITIONAL_LOOKUP.put(font_family, font);
    200214
     215                                                        /*
    201216                                                        int cdut = font.canDisplayUpTo("09AZaz");
    202217                                                        if (cdut >= 0) {
     
    205220                                                                System.out.println(" [Non-ASCII font]");
    206221                                                        }
     222                                                        */
    207223                                                        System.out.flush();
    208224                                                }
    209225
     226                                                System.out.print("'" + font_family + "'");
    210227                                        } else {
    211228                                                System.err.println("Error: Failed to add custom True-Type Font file: " + fontFile);
     
    221238        public static void InitFonts() {
    222239
    223                 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    224 
    225                 if (ge != null) {
    226 
    227                         File fontDirectory = new File(FrameIO.FONT_PATH);
    228                         if (fontDirectory != null) {
    229                                 File[] fontFamilyDirs = fontDirectory.listFiles();
    230                                 if (fontFamilyDirs != null) {
    231 
    232                                         if (fontFamilyDirs.length > 0) {
    233                                                 System.out.println("Loading custom fonts:");
     240                File fontDirectory = new File(FrameIO.FONT_PATH);
     241                if (fontDirectory != null) {
     242                        File[] fontFamilyDirs = fontDirectory.listFiles();
     243                        if (fontFamilyDirs != null) {
     244
     245                                if (fontFamilyDirs.length > 0) {
     246                                        System.out.println("Loading custom fonts:");
     247                                }
     248
     249                                for (File fontFamilyDir : fontFamilyDirs) {
     250                                        if (fontFamilyDir.isDirectory()) {
     251                                                InitFontFamily(fontFamilyDir);
    234252                                        }
    235 
    236                                         boolean first_item = true;
    237                                         for (File fontFamilyDir : fontFamilyDirs) {
    238                                                 if (fontFamilyDir.isDirectory()) {
    239                                                         InitFontFamily(ge, fontFamilyDir);
    240                                                 }
    241                                         }
    242                                         System.out.println();
    243253                                }
    244                         }
    245                 } else {
    246                         System.err.println("No graphics environment detected.  Skipping the loading of the custom fonts");
     254                                System.out.println();     
     255                        }
    247256                }
    248257        }
     
    359368        }
    360369
    361         public Text(int i, String string, Color foreground, Color background) {
     370        public Text(int i, String string, Colour foreground, Colour background) {
    362371                this(i, string);
    363372                this.setColor(foreground);
     
    366375
    367376        /**
     377<<<<<<< .mine
     378         * Sets the maximum width of this Text item when justification is used.
     379         * passing in 0 or -1 means there is no maximum width
     380||||||| .r1094
     381         * Sets the maximum width of this Text item when justifcation is used.
     382         * passing in 0 or -1 means there is no maximum width
     383=======
    368384         * Sets the maximum width of this Text item when justifcation is used. passing
    369385         * in 0 or -1 means there is no maximum width
     386>>>>>>> .r1100
    370387         *
    371388         * @param width
     
    379396                if (width == null) {
    380397                        setJustification(Justification.left);
    381                         setRightMargin(FrameGraphics.getMaxFrameSize().width, false);
     398                        setRightMargin(DisplayController.getFramePaintArea().getWidth(), false);
    382399                        return;
    383400                }
     
    389406
    390407        /**
     408<<<<<<< .mine
     409         * Returns the maximum width of this Text item when justification is used. If
     410         * the width is negative, it means no explicit width has been set
     411||||||| .r1094
     412         * Returns the maximum width of this Text item when justifcation is used. If
     413         * the width is negative, it means no explicit width has been set
     414=======
    391415         * Returns the maximum width of this Text item when justifcation is used. If the
    392416         * width is negative, it means no explicit width has been set
     417>>>>>>> .r1100
    393418         *
    394419         * @return The maximum width of this Text item when justification is used
    395420         */
    396421        @Override
    397         public Integer getWidth() {
    398                 if (_maxWidth == null || _maxWidth <= 0)
    399                         return null;
     422        public Integer getWidth()
     423        {
     424                if (_maxWidth == null || _maxWidth <= 0) return null;
     425               
    400426                return _maxWidth;
    401427        }
     
    410436
    411437        @Override
    412         public Color getHighlightColor() {
    413                 if (_highlightColor.equals(getPaintBackgroundColor()))
     438        public Colour getHighlightColor() {
     439                if (_highlightColour.equals(getPaintBackgroundColor()))
    414440                        return ALTERNATE_HIGHLIGHT;
    415                 return _highlightColor;
     441                return _highlightColour;
    416442        }
    417443
     
    441467         * justification.
    442468         *
     469         * TODO: Why return null when justification is set to left? cts16
     470         *
    443471         * @return The justification of this Text item
    444472         */
    445         public Justification getJustification() {
    446                 if (_justification == null || _justification.equals(Justification.left))
    447                         return null;
     473        public Justification getJustification()
     474        {
     475                if (_justification == null || _justification.equals(Justification.left)) return null;
     476               
    448477                return _justification;
    449478        }
    450479
     480        /**
     481         * Gets the distance from the left of the bounding box that the given layout
     482         * should be shifted to justify it.
     483         *
     484         * @param layout
     485         *              The line of text to calculate the justification offset for.
     486         *
     487         * @return
     488         *              The distance to shift the line of text by.
     489         */
    451490        private int getJustOffset(TextLayout layout) {
    452491                if (getJustification() == Justification.center)
     
    519558         *            The String to insert.
    520559         */
    521         public void prependText(String text) {
    522 
     560        public void prependText(String text)
     561        {
     562                invalidateAll();
    523563                _text.insert(0, text);
    524564                rebuild(false);
     565                invalidateAll();
    525566        }
    526567
     
    534575        public void removeText(String text) {
    535576                if (_text.length() > 0 && _text.indexOf(text) == 0) {
    536                         // Need the invalidate all for dateStamp toggling
    537577                        invalidateAll();
    538578                        _text.delete(0, text.length());
     579                        invalidateAll();
    539580                }
    540581
     
    544585                int length = _text.length();
    545586                if (length > 0) {
    546                         // invalidateAll();
    547587                        int pos = _text.indexOf(textToRemove);
    548588                        int textToRemoveLength = textToRemove.length();
    549589                        if (pos + textToRemoveLength == length) {
     590                                // Need the invalidate all for dateStamp toggling
     591                                invalidateAll();
    550592                                _text.delete(pos, length);
     593                                invalidateAll();
    551594                        }
    552595                }
     
    561604         */
    562605        public void appendText(String text) {
     606                invalidateAll();
    563607                _text.append(text);
    564608                rebuild(false);
     609                invalidateAll();
    565610        }
    566611
     
    614659         *            The Y position to insert the Strings at.
    615660         */
    616         public Point2D.Float insertChar(char ch, float mouseX, float mouseY) {
     661        public Point insertChar(char ch, float mouseX, float mouseY) {
    617662                if (ch != '\t') /* && ch != '\n' */
    618663                        return insertText("" + ch, mouseX, mouseY);
     
    641686        }
    642687
    643         public Point2D.Float getLineEndPosition(float mouseY) {
     688        public Point getLineEndPosition(float mouseY) {
    644689                return getEdgePosition(getLinePosition(mouseY), false);
    645690        }
    646691
    647         public Point2D.Float getLineStartPosition(float mouseY) {
     692        public Point getLineStartPosition(float mouseY) {
    648693                return getEdgePosition(getLinePosition(mouseY), true);
    649694        }
    650695
    651         public Point2D.Float getParagraphEndPosition() {
     696        public Point getParagraphEndPosition() {
    652697                return getEdgePosition(_textLayouts.size() - 1, false);
    653698        }
    654699
    655         public Point2D.Float getParagraphStartPosition() {
     700        public Point getParagraphStartPosition() {
    656701                return getEdgePosition(0, true);
    657702        }
    658703
    659         private Point2D.Float getEdgePosition(int line, boolean start) {
     704        private Point getEdgePosition(int line, boolean start) {
    660705                // if there is no text yet, or the line is invalid
    661706                if (_text == null || _text.length() == 0 || line < 0 || line > _textLayouts.size() - 1)
    662                         return new Point2D.Float(getX(), getY());
     707                        return new Point(getX(), getY());
    663708
    664709                TextLayout last = _textLayouts.get(line);
     
    674719
    675720                float x = getX() + caret[0] + getJustOffset(last);
    676                 x = Math.min(x, (getX() - Item.MARGIN_RIGHT - (2 * getGravity()) + getBoundsWidth()));
    677                 return new Point2D.Float(x, getY() + y + caret[1]);
    678         }
    679 
     721
     722                x = Math.min(
     723                                                x,
     724                                                (getX() - Item.MARGIN_RIGHT - (2 * getGravity()) + getBoundsWidth())
     725                );
     726                return new Point((int) x, (int) (getY() + y + caret[1]));
     727        }
     728
     729        public void setSelectionStart(Point p)
     730        {
     731                setSelectionStart(p.x, p.y);
     732        }
     733       
    680734        public void setSelectionStart(float mouseX, float mouseY) {
    681735                // determine what line is being pointed to
     
    684738                // get the character being pointed to
    685739                TextHitInfo hit = getCharPosition(line, mouseX);
    686                 _selectionStart = hit.getInsertionIndex() + _lineOffsets.get(line);
    687                 invalidateAll();
     740                _selectionStart = hit.getInsertionIndex() + _textLayouts.get(line).getStartCharIndex();
     741               
     742                // Clear the last selected
     743                updateLastSelected();
     744               
     745                invalidateAll();
     746        }
     747
     748        public void setSelectionEnd(Point p)
     749        {
     750                setSelectionEnd(p.x, p.y);
    688751        }
    689752
     
    694757                // get the character being pointed to
    695758                TextHitInfo hit = getCharPosition(line, mouseX);
    696                 _selectionEnd = hit.getInsertionIndex() + _lineOffsets.get(line);
     759                _selectionEnd = hit.getInsertionIndex() + _textLayouts.get(line).getStartCharIndex();
     760               
     761                // Clear the last selected
     762                updateLastSelected();
     763               
    697764                invalidateAll();
    698765        }
     
    707774                _selectionEnd = -1;
    708775                invalidateAll();
     776        }
     777       
     778        /** Makes sure only one text has a selection at a time. */
     779        public void updateLastSelected()
     780        {
     781                if (_lastSelected != this) {
     782                        if (_lastSelected != null) _lastSelected.clearSelection();
     783                        _lastSelected = this;
     784                }
    709785        }
    710786
     
    784860         * @return The new location that the mouse cursor should be moved to
    785861         */
    786         public Point2D.Float insertText(String text, float mouseX, float mouseY) {
    787                 final Point2D.Float newPos = insertText(text, mouseX, mouseY, -1);
     862        public Point insertText(String text, float mouseX, float mouseY)
     863        {
     864                final Point newPos = insertText(text, mouseX, mouseY, -1);
    788865                return newPos;
    789866        }
    790867
    791         public Point2D.Float insertText(String text, float mouseX, float mouseY, int insertPos) {
     868        public Point insertText(String text, float mouseX, float mouseY, int insertPos)
     869        {
    792870                TextHitInfo hit;
    793871                TextLayout current = null;
    794                 int line;
     872                int lineIndex;
    795873
    796874                invalidateAll();
     
    798876                // check for empty string
    799877                if (text == null || text.length() == 0)
    800                         return new Point2D.Float(mouseX, mouseY);
     878                        return new Point((int) mouseX, (int) mouseY);
    801879
    802880                // if there is no text yet
     
    808886                        current = _textLayouts.get(0);
    809887                        hit = current.getNextRightHit(0);
    810                         line = 0;
     888                        lineIndex = 0;
    811889
    812890                        // otherwise, we are inserting text
     
    814892                        clearCache();
    815893                        // determine what line is being pointed to
    816                         line = getLinePosition(mouseY);
     894                        lineIndex = getLinePosition(mouseY);
    817895
    818896                        // get the character being pointed to
    819                         hit = getCharPosition(line, mouseX);
    820 
    821                         int pos = hit.getInsertionIndex() + _lineOffsets.get(line);
    822 
    823                         if (line > 0 && hit.getInsertionIndex() == 0) {
     897                        hit = getCharPosition(lineIndex, mouseX);
     898
     899                        int insertionIndex = hit.getInsertionIndex() + _textLayouts.get(lineIndex).getStartCharIndex();
     900
     901                        if (lineIndex > 0 && hit.getInsertionIndex() == 0) {
    824902                                // Only move forward a char if the line begins with a hard line
    825903                                // break... not a soft line break
    826                                 if (_text.charAt(pos) == '\n') {
    827                                         pos++;
     904                                if (_text.charAt(insertionIndex) == '\n') {
     905                                        insertionIndex++;
    828906                                }
    829907                        }
    830908
    831909                        if (insertPos < 0)
    832                                 insertPos = pos;
     910                                insertPos = insertionIndex;
    833911
    834912                        // if this is a backspace key
    835                         if (text.charAt(0) == KeyEvent.VK_BACK_SPACE) {
     913                        if (text.charAt(0) == '\b') {
    836914                                if (hasSelection()) {
    837                                         pos = deleteSelection(pos);
     915                                        insertionIndex = deleteSelection(insertionIndex);
    838916                                } else if (insertPos > 0) {
    839917                                        deleteChar(insertPos - 1);
    840                                         if (pos > 0)
    841                                                 pos--;
     918                                        if (insertionIndex > 0)
     919                                                insertionIndex--;
    842920                                }
    843                                 // if this is a delete key
    844                         } else if (text.charAt(0) == KeyEvent.VK_DELETE) {
     921                        // if this is a delete key
     922                        } else if (text.charAt(0) == (char) 0x7F) {
    845923                                if (hasSelection()) {
    846                                         pos = deleteSelection(pos);
     924                                        insertionIndex = deleteSelection(insertionIndex);
    847925                                } else if (insertPos < _text.length()) {
    848926                                        deleteChar(insertPos);
    849927                                }
    850                                 // this is a tab
    851                         } else if (text.charAt(0) == KeyEvent.VK_TAB) {
     928                        // this is a tab
     929                        } else if (text.charAt(0) == '\t') {
    852930                                // Text length greater than 1 signals a backwards tab
    853931                                if (text.length() > 1) {
     
    867945                                                if (_text.length() > 0 && Character.isSpaceChar(_text.charAt(0))) {
    868946                                                        deleteChar(0);
    869                                                         pos--;
     947                                                        insertionIndex--;
    870948                                                } else
    871949                                                        break;
    872950                                        }
    873                                         _lineBreaker = null;
    874951                                } else {
    875952                                        // / Find the first non space char to see if its a bullet
     
    887964                                        // Insert the spacing at the start
    888965                                        insertString(TAB_STRING, 0);
    889                                         pos += TAB_STRING.length();
     966                                        insertionIndex += TAB_STRING.length();
    890967                                }
    891                                 // this is a normal insert
     968                        // this is a normal insert
    892969                        } else {
    893970                                insertString(text, insertPos);
    894                                 pos += text.length();
     971                                insertionIndex += text.length();
    895972                        }
    896973
    897974                        if (_text.length() == 0) {
    898975                                rebuild(false);
    899                                 return new Point2D.Float(this._x, this._y);
    900                         }
    901 
    902                         int newLine = line;
     976                                return new Point((int) this._x, (int) this._y);
     977                        }
     978
     979                        int newLine = lineIndex;
    903980
    904981                        // if a rebuild is required
     
    906983
    907984                        // determine the new position the cursor should have
    908                         for (int i = 1; i < _lineOffsets.size(); i++) {
    909                                 if (_lineOffsets.get(i) >= pos) {
    910                                         newLine = i - 1;
     985                        for (int i = 0; i < _textLayouts.size(); i++) {
     986                                if (_textLayouts.get(i).getEndCharIndex() + 1 >= insertionIndex) {
     987                                        newLine = i;
    911988                                        break;
    912989                                }
     
    914991
    915992                        current = _textLayouts.get(newLine);
    916                         pos -= _lineOffsets.get(newLine);
    917 
    918                         if (newLine == line) {
    919                                 if (pos > 0)
    920                                         hit = current.getNextRightHit(pos - 1);
     993                        insertionIndex -= current.getStartCharIndex();
     994
     995                        if (newLine == lineIndex) {
     996                                if (insertionIndex > 0)
     997                                        hit = current.getNextRightHit(insertionIndex - 1);
    921998                                else
    922999                                        hit = current.getNextLeftHit(1);
    923                         } else if (newLine < line) {
    924                                 hit = current.getNextRightHit(pos - 1);
     1000                        } else if (newLine < lineIndex) {
     1001                                hit = current.getNextRightHit(insertionIndex - 1);
    9251002                        } else {
    926                                 hit = current.getNextRightHit(pos - 1);
    927                         }
    928 
    929                         line = newLine;
     1003                                hit = current.getNextRightHit(insertionIndex - 1);
     1004                        }
     1005
     1006                        lineIndex = newLine;
    9301007                }
    9311008
    9321009                // move the cursor to the new location
    9331010                float[] caret = current.getCaretInfo(hit);
    934                 float y = getLineDrop(current) * line;
     1011                float y = getLineDrop(current) * lineIndex;
    9351012
    9361013                float x = getX() + caret[0] + getJustOffset(current);
    937                 x = Math.min(x, (getX() - Item.MARGIN_RIGHT - (2 * getGravity()) + getBoundsWidth()));
    938 
    939                 invalidateAll();
    940 
    941                 return new Point2D.Float(Math.round(x), Math.round(getY() + y + caret[1]));
     1014                x = Math.min(
     1015                                                x,
     1016                                                (getX() - Item.MARGIN_RIGHT - (2 * getGravity()) + getBoundsWidth())
     1017                );
     1018
     1019                invalidateAll();
     1020
     1021                return new Point(Math.round(x), Math.round(getY() + y + caret[1]));
    9421022        }
    9431023
     
    9631043        }
    9641044
    965         public Point2D.Float moveCursor(int direction, float mouseX, float mouseY, boolean setSelection,
    966                         boolean wholeWord) {
     1045        public Point moveCursor(int direction, float mouseX, float mouseY, boolean setSelection, boolean wholeWord)
     1046        {
    9671047                if (setSelection) {
    9681048                        if (!hasSelection()) {
     
    9731053                }
    9741054
    975                 Point2D.Float resultPos = null;
     1055                Point resultPos = null;
    9761056
    9771057                // check for home or end keys
     
    9961076                        // if there is no text yet
    9971077                        if (_text == null || _text.length() == 0) {
    998                                 return new Point2D.Float(mouseX, mouseY);
     1078                                return new Point((int) mouseX, (int) mouseY);
    9991079                                // otherwise, move the cursor
    10001080                        } else {
     
    10241104                                                        // Keep going if the char to the left is a
    10251105                                                        // letterOrDigit
    1026                                                         prevChar = _text.charAt(hit.getInsertionIndex() - 1 + _lineOffsets.get(line));
     1106                                                        prevChar = _text.charAt(hit.getInsertionIndex() - 1 + _textLayouts.get(line).getStartCharIndex());
    10271107                                                } while (wholeWord && Character.isLetterOrDigit(prevChar));
    1028                                                 // TODO Go to the start of the word instead of before
    1029                                                 // the word
    1030                                                 char nextChar = _text.charAt(hit.getInsertionIndex() + _lineOffsets.get(line));
    1031                                                 /*
    1032                                                  * This takes care of hard line break in
    1033                                                  */
     1108                                               
     1109                                                // TODO Go to the start of the word instead of before the word
     1110                                                char nextChar = _text.charAt(hit.getInsertionIndex() + _textLayouts.get(line).getStartCharIndex());
     1111                                               
     1112                                                // This takes care of hard line break in
    10341113                                                if (line > 0 && nextChar == '\n') {
    10351114                                                        line--;
    1036                                                         hit = _textLayouts.get(line)
    1037                                                                         .getNextRightHit(_textLayouts.get(line).getCharacterCount() - 1);
     1115                                                        hit = _textLayouts.get(line).getNextRightHit(_textLayouts.get(line).getCharacterCount() - 1);
    10381116                                                }
    1039                                                 // This takes care of soft line breaks.
     1117                                               
     1118                                        // This takes care of soft line breaks.
    10401119                                        } else if (line > 0) {
    10411120                                                line--;
    10421121                                                hit = _textLayouts.get(line).getNextRightHit(_textLayouts.get(line).getCharacterCount() - 1);
    1043                                                 /*
    1044                                                  * Skip the spaces at the end of a line with soft linebreak
    1045                                                  */
    1046                                                 while (hit.getCharIndex() > 0
    1047                                                                 && _text.charAt(_lineOffsets.get(line) + hit.getCharIndex() - 1) == ' ') {
     1122                                               
     1123                                                // Skip the spaces at the end of a line with soft linebreak
     1124                                                while (hit.getCharIndex() > 0 && _text.charAt(_textLayouts.get(line).getStartCharIndex() + hit.getCharIndex() - 1) == ' ') {
    10481125                                                        hit = _textLayouts.get(line).getNextLeftHit(hit);
    10491126                                                }
     
    10531130                                                hit = _textLayouts.get(line).getNextRightHit(hit);
    10541131                                                // Skip whole word if needs be
    1055                                                 while (wholeWord && hit.getCharIndex() > 0
    1056                                                                 && hit.getCharIndex() < _textLayouts.get(line).getCharacterCount() && Character
    1057                                                                                 .isLetterOrDigit(_text.charAt(_lineOffsets.get(line) + hit.getCharIndex() - 1)))
     1132                                                while (wholeWord
     1133                                                                && hit.getCharIndex() > 0
     1134                                                                && hit.getCharIndex() < _textLayouts.get(line).getCharacterCount()
     1135                                                                && Character.isLetterOrDigit(_text.charAt(_textLayouts.get(line).getStartCharIndex() + hit.getCharIndex() - 1)))
     1136                                                {
    10581137                                                        hit = _textLayouts.get(line).getNextRightHit(hit);
     1138                                                }
    10591139                                        } else if (line < _textLayouts.size() - 1) {
    10601140                                                line++;
     
    10691149                        float y = getLineDrop(current) * line;
    10701150
    1071                         resultPos = new Point2D.Float(getX() + caret[0] + getJustOffset(current), getY() + y + caret[1]);
     1151                        resultPos = new Point((int) (getX() + caret[0] + getJustOffset(current)), (int) (getY() + y + caret[1]));
     1152
    10721153                        break;
    10731154                }
    1074                 if (setSelection)
    1075                         setSelectionEnd(resultPos.x, resultPos.y);
     1155               
     1156                if (setSelection) setSelectionEnd(resultPos.x, resultPos.y);
     1157               
    10761158                return resultPos;
    10771159        }
     
    10981180        }
    10991181
     1182        /**
     1183         * Gets the index into the <code>_textLayout</code> list which corresponds to the line
     1184         * covered by the given <code>mouseY</code> position.
     1185         *
     1186         * @param mouseY
     1187         *              The y-coordinate to test for line coverage.
     1188         *
     1189         * @return
     1190         *              The line which occupies the given y-coordinate, or the last line if none do.
     1191         */
    11001192        public int getLinePosition(float mouseY) {
    11011193                mouseY += getOffset().y;
     
    11051197                for (TextLayout text : _textLayouts) {
    11061198                        // calculate X to ensure it is in the shape
    1107                         Rectangle2D bounds = text.getLogicalHighlightShape(0, text.getCharacterCount()).getBounds2D();
    1108 
    1109                         if (bounds.getWidth() < 1)
    1110                                 bounds.setRect(bounds.getMinX(), bounds.getMinY(), 10, bounds.getHeight());
    1111 
    1112                         double x = bounds.getCenterX();
    1113 
    1114                         if (bounds.contains(x, mouseY - getY() - (y - getY())))
     1199                        AxisAlignedBoxBounds bounds = text.getLogicalHighlightShape(0, text.getCharacterCount());
     1200
     1201                        if (bounds.getWidth() < 1) bounds.getSize().width = 10;
     1202
     1203                        double x = bounds.getCentreX();
     1204
     1205                        if (bounds.contains((int) x, (int) (mouseY - y)))
    11151206                                return _textLayouts.indexOf(text);
    11161207
    11171208                        // check if the cursor is between lines
    1118                         if (mouseY - getY() - (y - getY()) < bounds.getMinY())
     1209                        if (mouseY - y < bounds.getMinY())
    11191210                                return Math.max(0, _textLayouts.indexOf(text) - 1);
    11201211
     
    11311222         *            The Font to display the Text of this Item in.
    11321223         */
    1133         public void setFont(Font font) {
     1224        public void setFont(Font font)
     1225        {
    11341226                invalidateAll();
    11351227                // all decoding occurs in the Utils class
     
    11421234
    11431235        /**
    1144          * Returns the Font that this Text is currently using when painting to the
    1145          * screen
    1146          *
    1147          * @return The Font used to display this Text on the screen.
     1236         * Gets the font of this text item.
     1237         *
     1238         * @return The Font assigned to this text item, or null if none is assigned.
    11481239         */
    11491240        public Font getFont() {
     
    11511242        }
    11521243
    1153         public Font getPaintFont() {
    1154                 if (getFont() == null)
    1155                         return Font.decode(DEFAULT_FONT);
     1244        /**
     1245         * Gets the font that should be used to paint this text item during drawing.
     1246         *
     1247         * @return The font to paint the text item with.
     1248         */
     1249        public Font getPaintFont()
     1250        {
     1251                if (getFont() == null) return EcosystemManager.getFontManager().getDefaultFont();
    11561252
    11571253                return getFont();
     
    11591255
    11601256        public String getFamily() {
    1161                 return getPaintFont().getFamily();
    1162         }
    1163 
    1164         public void setFamily(String newFamily) {
    1165                 String toDecode = newFamily + "-" + getFontStyle() + "-" + Math.round(getSize());
    1166                 setFont(Font.decode(toDecode));
     1257                return getPaintFont().getFamilyName();
     1258        }
     1259
     1260        public void setFamily(String newFamily)
     1261        {
     1262                setFont(new Font(newFamily, getFontStyle(), Math.round(getSize())));
    11671263
    11681264                setLetterSpacing(this._letter_spacing);
    11691265        }
    11701266
    1171         public String getFontStyle() {
     1267        public Font.Style getFontStyle() {
    11721268                Font f = getPaintFont();
    1173                 String s = "";
    1174 
    1175                 if (f.isPlain())
    1176                         s += "Plain";
    1177 
    1178                 if (f.isBold())
    1179                         s += "Bold";
    1180 
    1181                 if (f.isItalic())
    1182                         s += "Italic";
    1183 
    1184                 return s;
     1269                return f.getStyle();
    11851270        }
    11861271
     
    12151300                invalidateAll();
    12161301                Font currentFont = getPaintFont();
    1217                 if (currentFont.isPlain())
    1218                         setFont(currentFont.deriveFont(Font.BOLD));
    1219                 else if (currentFont.isBold() && currentFont.isItalic())
    1220                         setFont(currentFont.deriveFont(Font.PLAIN));
    1221                 else if (currentFont.isBold())
    1222                         setFont(currentFont.deriveFont(Font.ITALIC));
    1223                 else
    1224                         setFont(currentFont.deriveFont(Font.ITALIC + Font.BOLD));
     1302                Font.Style currentStyle = currentFont.getStyle();
     1303                Font.Style newStyle = Font.Style.PLAIN;
     1304                switch (currentStyle) {
     1305                        case PLAIN:
     1306                                newStyle = Font.Style.BOLD;
     1307                                break;
     1308                        case BOLD:
     1309                                newStyle = Font.Style.ITALIC;
     1310                                break;
     1311                        case ITALIC:
     1312                                newStyle = Font.Style.BOLD_ITALIC;
     1313                                break;
     1314                        default:
     1315                                newStyle = Font.Style.PLAIN;
     1316                                break;
     1317                }
     1318                setFont(new Font(currentFont.getFamilyName(), newStyle, currentFont.getSize()));
    12251319                rebuild(true);
    12261320                invalidateAll();
     
    12301324                invalidateAll();
    12311325                Font currentFont = getPaintFont();
    1232                 int newStyle = currentFont.getStyle();
    1233                 if (currentFont.isBold()) {
    1234                         newStyle -= Font.BOLD;
    1235                 } else {
    1236                         newStyle += Font.BOLD;
    1237                 }
    1238                 setFont(currentFont.deriveFont(newStyle));
     1326                currentFont.toggleBold();
     1327                setFont(currentFont);
    12391328                rebuild(true);
    12401329                invalidateAll();
     
    12441333                invalidateAll();
    12451334                Font currentFont = getPaintFont();
    1246                 int newStyle = currentFont.getStyle();
    1247                 if (currentFont.isItalic()) {
    1248                         newStyle -= Font.ITALIC;
    1249                 } else {
    1250                         newStyle += Font.ITALIC;
    1251                 }
    1252                 setFont(currentFont.deriveFont(newStyle));
     1335                currentFont.toggleItalic();
     1336                setFont(currentFont);
    12531337                rebuild(true);
    12541338                invalidateAll();
    12551339        }
    12561340
    1257         public void setFontStyle(String newFace) {
     1341        public void setFontStyle(String newFace)
     1342        {
     1343                Font currentFont = getPaintFont();
    12581344                if (newFace == null || newFace.trim().length() == 0) {
    1259                         setFont(getPaintFont().deriveFont(Font.PLAIN));
     1345                        currentFont.setStyle(Font.Style.PLAIN);
     1346                        setFont(currentFont);
    12601347                        return;
    12611348                }
     
    12641351
    12651352                if (newFace.equals("plain") || newFace.equals("p")) {
    1266                         setFont(getPaintFont().deriveFont(Font.PLAIN));
     1353                        currentFont.setStyle(Font.Style.PLAIN);
    12671354                } else if (newFace.equals("bold") || newFace.equals("b")) {
    1268                         setFont(getPaintFont().deriveFont(Font.BOLD));
     1355                        currentFont.setStyle(Font.Style.BOLD);
    12691356                } else if (newFace.equals("italic") || newFace.equals("i")) {
    1270                         setFont(getPaintFont().deriveFont(Font.ITALIC));
    1271                 } else if (newFace.equals("bolditalic") || newFace.equals("italicbold") || newFace.equals("bi")
    1272                                 || newFace.equals("ib")) {
    1273                         setFont(getPaintFont().deriveFont(Font.BOLD + Font.ITALIC));
    1274                 }
     1357                        currentFont.setStyle(Font.Style.ITALIC);
     1358                } else if (newFace.equals("bolditalic") || newFace.equals("italicbold") || newFace.equals("bi") || newFace.equals("ib")) {
     1359                        currentFont.setStyle(Font.Style.BOLD_ITALIC);
     1360                }
     1361               
     1362                setFont(currentFont);
    12751363
    12761364        }
     
    12891377
    12901378                        // Rebuilding prevents errors when displaying frame bitmaps
    1291                         if (_lineOffsets.size() == 0) {
     1379                        if (_textLayouts.size() == 0) {
    12921380                                rebuild(false);
    12931381                        }
    12941382
    1295                         int last = 0;
    1296                         for (int offset : _lineOffsets) {
    1297                                 if (offset != last) {
    1298                                         list.add(_text.substring(last, offset).replaceAll("\n", ""));
    1299                                 }
    1300                                 last = offset;
     1383                        for (TextLayout layout : _textLayouts) {
     1384                                String text = layout.getLine().replaceAll("\n", "");
     1385                                if (!text.equals("")) list.add(text);
    13011386                        }
    13021387
     
    13441429        public void setSpacing(float spacing) {
    13451430                _spacing = spacing;
    1346                 updatePolygon();
     1431                invalidateBounds();
    13471432        }
    13481433
     
    13561441        }
    13571442
    1358         private float getLineDrop(TextLayout layout) {
    1359                 if (getSpacing() < 0)
     1443        /**
     1444         * Gets the y-distance that should be advanced between this layout and the next.
     1445         *
     1446         * @param layout
     1447         *              The TextLayout to calculate line-drop for.
     1448         * 
     1449         * @return
     1450         *              The distance to advance in the y-direction before the next line.
     1451         */
     1452        private float getLineDrop(TextLayout layout)
     1453        {
     1454                if (getSpacing() < 0) {
    13601455                        return layout.getAscent() + layout.getDescent() + layout.getLeading();
     1456                }
    13611457
    13621458                return layout.getAscent() + layout.getDescent() + getSpacing();
     
    13801476        public void setLetterSpacing(float spacing) {
    13811477                _letter_spacing = spacing;
    1382                 HashMap<TextAttribute, Object> attr = new HashMap<TextAttribute, Object>();
    1383                 attr.put(TextAttribute.TRACKING, spacing);
    1384 
    1385                 if (this._font == null) {
    1386                         this._font = Font.decode(DEFAULT_FONT);
    1387                 }
    1388 
    1389                 this.setFont(this._font.deriveFont(attr));
     1478
     1479                Font currentFont = getPaintFont();
     1480                currentFont.setSpacing(spacing);
     1481                setFont(currentFont);
    13901482        }
    13911483
     
    14071499
    14081500        // @Override
    1409         public boolean intersectsOLD(Polygon p) {
     1501/*      public boolean intersectsOLD(Polygon p) {
    14101502                if (super.intersects(p)) {
    14111503                        float textY = getY();
     
    14221514                }
    14231515                return false;
    1424         }
     1516        }*/
    14251517
    14261518        // The following version of intersect uses a tighter definition for the text,
    14271519        // based on
    14281520        @Override
    1429         public boolean intersects(Polygon p) {
     1521        public boolean intersects(PolygonBounds p) {
    14301522                if (super.intersects(p)) {
    14311523                        // float textY = getY();
     
    14331525                        for (TextLayout text : _textLayouts) {
    14341526
    1435                                 Rectangle text_pixel_bounds_rect = this.getPixelBounds(text);
     1527                                AxisAlignedBoxBounds text_pixel_bounds_rect = getPixelBounds(text);
    14361528
    14371529                                if (p.intersects(text_pixel_bounds_rect)) {
     
    14451537
    14461538        @Override
    1447         public boolean contains(int mouseX, int mouseY) {
    1448                 return contains(mouseX, mouseY, getGravity() * NEARBY_GRAVITY);
    1449         }
    1450 
    1451         public boolean contains(int mouseX, int mouseY, int gravity) {
     1539        public boolean contains(Point mousePosition) {
     1540                return contains(mousePosition.x, mousePosition.y, getGravity() * NEARBY_GRAVITY);
     1541        }
     1542       
     1543        public boolean contains(int mouseX, int mouseY)
     1544        {
     1545                return contains(new Point(mouseX, mouseY));
     1546        }
     1547
     1548        public boolean contains(int mouseX, int mouseY, int gravity)
     1549        {
    14521550                mouseX += getOffset().x;
    14531551                mouseY += getOffset().y;
     
    14561554                float textX = getX();
    14571555
    1458                 Rectangle2D outline = getPolygon().getBounds2D();
     1556                AxisAlignedBoxBounds outline = getBoundingBox();
     1557               
     1558                if (outline == null) return false;
    14591559
    14601560                // Check if its outside the top and left and bottom bounds
    1461                 if (outline.getX() - mouseX > gravity || outline.getY() - mouseY > gravity
    1462                                 || mouseY - (outline.getY() + outline.getHeight()) > gravity
    1463                                 || mouseX - (outline.getX() + outline.getWidth()) > gravity) {
     1561                if (outline.getMinX() - mouseX > gravity
     1562                                || outline.getMinY() - mouseY > gravity
     1563                                || mouseY - (outline.getMinY() + outline.getHeight()) > gravity
     1564                                || mouseX - (outline.getMinX() + outline.getWidth()) > gravity) {
    14641565                        return false;
    14651566                }
     
    14671568                for (TextLayout text : _textLayouts) {
    14681569                        // check left and right of each box
    1469                         Rectangle2D textOutline = text.getLogicalHighlightShape(0, text.getCharacterCount()).getBounds2D();
     1570                        AxisAlignedBoxBounds textOutline = text.getLogicalHighlightShape(0, text.getCharacterCount());
    14701571
    14711572                        // check if the cursor is within the top, bottom and within the
     
    14731574                        int justOffset = getJustOffset(text);
    14741575
    1475                         if (mouseY - textY > textOutline.getY() && mouseY - textY < textOutline.getY() + textOutline.getHeight()
    1476                                         && mouseX - textX - justOffset < textOutline.getWidth() + gravity + Item.MARGIN_RIGHT
    1477                         /* &&(justOffset == 0 || mouseX > textX + justOffset - gravity ) */)
     1576                        if (mouseY - textY > textOutline.getMinY() &&
     1577                                        mouseY - textY < textOutline.getMinY() + textOutline.getHeight() &&
     1578                                        mouseX - textX - justOffset < textOutline.getWidth() + gravity + Item.MARGIN_RIGHT)
     1579                        {
    14781580                                return true;
     1581                        }
     1582                       
    14791583                        textY += getLineDrop(text);
    14801584                }
     
    14861590         * Updates the Polygon (rectangle) that surrounds this Text on the screen.
    14871591         */
    1488         public void updatePolygon() {
     1592        public AxisAlignedBoxBounds updateBounds()
     1593        {
    14891594                // if there is no text, there is nothing to do
    1490                 if (_text == null)
    1491                         return;
     1595                if (_text == null) return null;
     1596
     1597                if (_textLayouts == null || _textLayouts.size() < 1) return null;
    14921598
    14931599                int preChangeWidth = 0;
    1494                 if (_poly != null)
    1495                         preChangeWidth = _poly.getBounds().width;
    1496 
    1497                 _poly = new Polygon();
    1498 
    1499                 if (_textLayouts.size() < 1)
    1500                         return;
     1600                if (getOldBounds() != null) {
     1601                        preChangeWidth = AxisAlignedBoxBounds.getEnclosing(getOldBounds()).getWidth();
     1602                }
    15011603
    15021604                int minX = Integer.MAX_VALUE;
     
    15151617
    15161618                for (TextLayout layout : tmpTextLayouts) {
    1517                         Rectangle2D bounds = layout.getLogicalHighlightShape(0, layout.getCharacterCount()).getBounds2D();
     1619                        AxisAlignedBoxBounds bounds = layout.getLogicalHighlightShape(0, layout.getCharacterCount());
    15181620
    15191621                        if (y < 0)
     
    15361638                }
    15371639
    1538                 _poly.addPoint(minX - getGravity(), minY - getGravity());
    1539                 _poly.addPoint(maxX + getGravity(), minY - getGravity());
    1540                 _poly.addPoint(maxX + getGravity(), maxY + getGravity());
    1541                 _poly.addPoint(minX - getGravity(), maxY + getGravity());
    1542 
    1543                 _poly.translate(getX(), getY());
    1544 
    1545                 if (preChangeWidth != 0 && preChangeWidth != _poly.getBounds().width)
    1546                         if (_poly.getBounds().width > preChangeWidth)
    1547                                 MagneticConstraints.getInstance().textGrown(this, _poly.getBounds().width - preChangeWidth);
    1548                         else
    1549                                 MagneticConstraints.getInstance().textShrunk(this, preChangeWidth - _poly.getBounds().width);
    1550         }
    1551 
    1552         // TODO it seems like this method has some exponencial processing which
     1640                AxisAlignedBoxBounds ret = new AxisAlignedBoxBounds(getX() + minX - getGravity(),
     1641                                                                                                                        getY() + minY - getGravity(),
     1642                                                                                                                        2 * getGravity() + maxX - minX,
     1643                                                                                                                        2 * getGravity() + maxY - minY);
     1644               
     1645                Dimension polySize = ret.getSize();
     1646                                               
     1647                if(preChangeWidth != 0 && preChangeWidth != polySize.width) {
     1648                        if (polySize.width > preChangeWidth) {
     1649                                MagneticConstraints.getInstance().textGrown(this, polySize.width - preChangeWidth);
     1650                        } else {
     1651                                MagneticConstraints.getInstance().textShrunk(this, preChangeWidth - polySize.width);
     1652                        }
     1653                }
     1654               
     1655                return ret;
     1656        }
     1657
     1658        // TODO it seems like this method has some exponential processing which
    15531659        // makes items copy really slowly when there are lots of lines of text!
    15541660        // This needs to be fixed!!
     
    15641670         */
    15651671        private void rebuild(boolean limitWidth, boolean newLinebreakerAlways) {
    1566                 // TODO make this more efficient so it only clears annotation list when
    1567                 // it really has to
     1672                // TODO make this more efficient so it only clears annotation list when it really has to
    15681673                if (isAnnotation()) {
    15691674                        Frame parent = getParent();
     
    15821687                }
    15831688
    1584                 if (_lineBreaker == null || newLinebreakerAlways) {
    1585                         AttributedString paragraphText = new AttributedString(_text.toString());
    1586                         paragraphText.addAttribute(TextAttribute.FONT, getPaintFont());
    1587                         frc = new FontRenderContext(null, true, true);
    1588                         _lineBreaker = new LineBreakMeasurer(paragraphText.getIterator(), frc);
    1589                 }
    1590 
    1591                 /*
    1592                  * float width = Float.MAX_VALUE;
    1593                  *
    1594                  * if (limitWidth) { width = getAbsoluteWidth(); // else if (getMaxWidth() > 0)
    1595                  * // width = Math.max(50, getMaxWidth() - getX() // - Item.MARGIN_RIGHT); }
    1596                  */
    1597 
    1598                 _textLayouts.clear();
    1599                 _lineOffsets.clear();
    1600                 // the first line always has a 0 offset
    1601                 _lineOffsets.add(0);
    1602 
    1603                 TextLayout layout;
    1604 
    1605                 float width;
    1606                 float lineHeight = Float.NaN;
    1607                 List<Point[]> lines = null;
    1608 
    1609                 if (_autoWrap || ExperimentalFeatures.AutoWrap.get()) {
    1610                         lines = new LinkedList<Point[]>();
    1611                         if (DisplayIO.getCurrentFrame() == null) {
    1612                                 return;
    1613                         }
    1614                         for (Item item : DisplayIO.getCurrentFrame().getItems()) {
    1615                                 if (item instanceof Line) {
    1616                                         lines.add(new Point[] { ((Line) item).getStartItem().getPosition(),
    1617                                                         ((Line) item).getEndItem().getPosition() });
    1618                                 }
    1619                                 if (item instanceof Picture) {
    1620                                         lines.add(
    1621                                                         new Point[] { item.getPosition(), new Point(item.getX(), item.getY() + item.getHeight()) });
    1622                                 }
    1623                         }
    1624                         for (Item item : FreeItems.getInstance()) {
    1625                                 if (item instanceof Line) {
    1626                                         lines.add(new Point[] { ((Line) item).getStartItem().getPosition(),
    1627                                                         ((Line) item).getEndItem().getPosition() });
    1628                                 }
    1629                                 if (item instanceof Picture) {
    1630                                         lines.add(
    1631                                                         new Point[] { item.getPosition(), new Point(item.getX(), item.getY() + item.getHeight()) });
    1632                                 }
    1633                         }
    1634                         width = getLineWidth(getX(), getY(), lines);
    1635                 } else {
    1636                         width = Float.MAX_VALUE;
    1637                         if (limitWidth) {
    1638                                 if (_maxWidth == null) {
    1639                                         width = FrameGraphics.getMaxFrameSize().width - getX();
    1640                                 } else {
    1641                                         width = getAbsoluteWidth();
    1642                                 }
    1643                                 // else if (getMaxWidth() > 0)
    1644                                 // width = Math.max(50, getMaxWidth() - getX()
    1645                                 // - Item.MARGIN_RIGHT);
    1646                         }
    1647                 }
    1648 
    1649                 _lineBreaker.setPosition(0);
    1650                 boolean requireNextWord = false;
    1651 
    1652                 // --- Get the output of the LineBreakMeasurer and store it in a
    1653                 while (_lineBreaker.getPosition() < _text.length()) {
    1654 
    1655                         if (_autoWrap || ExperimentalFeatures.AutoWrap.get()) {
    1656                                 requireNextWord = width < FrameGraphics.getMaxFrameSize().width - getX();
    1657                         }
    1658 
    1659                         layout = _lineBreaker.nextLayout(width, _text.length(), requireNextWord);
    1660 
    1661                         // lineBreaker does not break on newline
    1662                         // characters so they have to be check manually
    1663                         int start = _lineOffsets.get(_lineOffsets.size() - 1);
    1664 
    1665                         // int y = getY() + (getLineDrop(layout) * (_lineOffsets.size() - 1)
    1666 
    1667                         // check through the current line for newline characters
    1668                         for (int i = start + 1; i < _text.length(); i++) {
    1669                                 if (_text.charAt(i) == '\n') {// || c == '\t'){
    1670                                         _lineBreaker.setPosition(start);
    1671                                         layout = _lineBreaker.nextLayout(width, i, requireNextWord);
    1672                                         break;
    1673                                 }
    1674                         }
    1675 
    1676                         _lineOffsets.add(_lineBreaker.getPosition());
    1677 
    1678                         if (layout == null) {
    1679                                 layout = new TextLayout(" ", getPaintFont(), frc);
    1680                         }
    1681 
    1682                         if (/* hasWidth() && */getJustification() == Justification.full
    1683                                         && _lineBreaker.getPosition() < _text.length())
    1684                                 layout = layout.getJustifiedLayout(width);
    1685 
    1686                         _textLayouts.add(layout);
    1687 
    1688                         if (_autoWrap || ExperimentalFeatures.AutoWrap.get()) {
    1689 
    1690                                 if (lineHeight != Float.NaN) {
    1691                                         lineHeight = getLineDrop(layout);
    1692                                 }
    1693                                 width = getLineWidth(getX(), getY() + (lineHeight * (_textLayouts.size() - 1)), lines);
    1694                         }
    1695                 }
    1696 
    1697                 updatePolygon();
    1698 
    1699         }
    1700 
    1701         private float getLineWidth(int x, float y, List<Point[]> lines) {
     1689                EcosystemManager.getTextLayoutManager().releaseLayouts(_textLayouts);
     1690                if (_textLayouts != null) _textLayouts.clear();
     1691
     1692                // Calculate the maximum allowable width of this line of text
     1693                List<org.expeditee.core.Line> lines = null;
     1694                if(_autoWrap || ExperimentalFeatures.AutoWrap.get()) {
     1695                        lines = new LinkedList<org.expeditee.core.Line>();
     1696                if(DisplayController.getCurrentFrame() == null) {
     1697                        return;
     1698                }
     1699                for(Item item : DisplayController.getCurrentFrame().getItems()) {
     1700                                if(item instanceof Line) {
     1701                                        lines.add(new org.expeditee.core.Line (((Line) item).getStartItem().getPosition(), ((Line) item).getEndItem().getPosition()));
     1702                                }
     1703                                if(item instanceof Picture) {
     1704                                        lines.add(new org.expeditee.core.Line(item.getPosition(), new Point(item.getX(), item.getY() + item.getHeight())));
     1705                                }
     1706                }
     1707                for(Item item : FreeItems.getInstance()) {
     1708                                if(item instanceof Line) {
     1709                                        lines.add(new org.expeditee.core.Line(((Line) item).getStartItem().getPosition(), ((Line) item).getEndItem().getPosition()));
     1710                                }
     1711                                if(item instanceof Picture) {
     1712                                        lines.add(new org.expeditee.core.Line(item.getPosition(), new Point(item.getX(), item.getY() + item.getHeight())));
     1713                                }
     1714                }
     1715                }
     1716
     1717                float width = Float.MAX_VALUE;
     1718                if (limitWidth) {
     1719                        if(_maxWidth == null) {
     1720                                width = DisplayController.getFramePaintArea().getWidth() - getX();
     1721                        } else {
     1722                                width = getAbsoluteWidth();
     1723                        }
     1724                }
     1725               
     1726                _textLayouts = EcosystemManager.getTextLayoutManager().layoutString(_text.toString(),
     1727                                                                                                                                        getPaintFont(),
     1728                                                                                                                                        new Point(getX(), getY()),
     1729                                                                                                                                        lines != null ? lines.toArray(new org.expeditee.core.Line[1]) : null,
     1730                                                                                                                                        (int) width,
     1731                                                                                                                                        (int) getSpacing(),
     1732                                                                                                                                        true,
     1733                                                                                                                                        getJustification() == Justification.full);
     1734
     1735                invalidateBounds();
     1736
     1737        }
     1738
     1739        /**
     1740         * Calculates the maximum possible distance a line can extend to the right from a given (x,y) point
     1741         * without crossing any of the lines in the given list.
     1742         *
     1743         * @param x
     1744         *              The x-coordinate of the beginning point.
     1745         *
     1746         * @param y
     1747         *              The y-coordinate of the beginning point.
     1748         *
     1749         * @param lines
     1750         *              A list of pairs of points describing the lines that should stop the extension.
     1751         *
     1752         * @return
     1753         *              The length of the extended line.
     1754         */
     1755/*      private float getLineWidth(int x, float y, List<Point[]> lines) {
    17021756                float width = FrameGraphics.getMaxFrameSize().width;
    17031757                for (Point[] l : lines) {
     
    17251779                }
    17261780                return width - x;
    1727         }
     1781        }*/
    17281782
    17291783        private boolean hasFixedWidth() {
     
    17411795        }
    17421796
    1743         private Point getSelectedRange(int line) {
     1797        private Range<Integer> getSelectedRange(int line) {
    17441798                if (_selectionEnd >= _text.length()) {
    17451799                        _selectionEnd = _text.length();
     
    17561810
    17571811                // if the selection is after this line, return null
    1758                 if (_lineOffsets.get(line) > selectionRight)
    1759                         return null;
     1812                if (_textLayouts.get(line).getStartCharIndex() > selectionRight) return null;
    17601813
    17611814                // if the selection is before this line, return null
    1762                 if (_lineOffsets.get(line) < selectionLeft
    1763                                 && _lineOffsets.get(line) + _textLayouts.get(line).getCharacterCount() < selectionLeft)
    1764                         return null;
     1815                if (_textLayouts.get(line).getEndCharIndex() < selectionLeft) return null;
    17651816
    17661817                // Dont highlight a single char
     
    17701821                // the selection occurs on this line, determine where it lies on the
    17711822                // line
    1772                 int start = Math.max(0, selectionLeft - _lineOffsets.get(line));
     1823                int start = Math.max(0, selectionLeft - _textLayouts.get(line).getStartCharIndex());
    17731824                // int end = Math.min(_lineOffsets.get(line) +
    17741825                // _textLayouts.get(line).getCharacterCount(), _selectionEnd);
    1775                 int end = Math.min(selectionRight - _lineOffsets.get(line), +_textLayouts.get(line).getCharacterCount());
     1826                int end = Math.min(selectionRight - _textLayouts.get(line).getStartCharIndex(), _textLayouts.get(line).getCharacterCount());
    17761827
    17771828                // System.out.println(line + ": " + start + "x" + end + " (" +
    17781829                // _selectionStart + "x" + _selectionEnd + ")");
    1779                 return new Point(start, end);
    1780         }
    1781 
    1782         /**
    1783          * @param mouseButton
    1784          *            Either MouseEvent.BUTTON1, MouseEvent.BUTTON2 or
    1785          *            MouseEvent.BUTTON3.
    1786          *
    1787          * @return The color for the text selection based on the given mouse click
    1788          */
    1789         protected Color getSelectionColor(int mouseButton) {
    1790 
    1791                 /*
    1792                  * Color main = getPaintColor(); Color back = getPaintBackgroundColor();
    1793                  *
    1794                  * if (Math.abs(main.getRed() - back.getRed()) < 10 && Math.abs(main.getGreen()
    1795                  * - back.getGreen()) < 10 && Math.abs(main.getBlue() - back.getBlue()) < 10) {
    1796                  * selection = new Color(Math.abs(255 - main.getRed()), Math .abs(255 -
    1797                  * main.getGreen()), Math.abs(255 - main.getBlue())); } else { selection = new
    1798                  * Color((main.getRed() + (back.getRed() * 2)) / 3, (main.getGreen() +
    1799                  * (back.getGreen() * 2)) / 3, (main .getBlue() + (back.getBlue() * 2)) / 3); }
    1800                  */
    1801                 int green = 160;
    1802                 int red = 160;
    1803                 int blue = 160;
    1804 
    1805                 if (FrameMouseActions.wasDeleteClicked()) {
    1806                         green = 235;
    1807                         red = 235;
    1808                         blue = 140;
    1809                 } else if (mouseButton == MouseEvent.BUTTON1) {
    1810                         red = 255;
    1811                 } else if (mouseButton == MouseEvent.BUTTON2) {
    1812                         green = 255;
    1813                 } else if (mouseButton == MouseEvent.BUTTON3) {
    1814                         blue = 255;
    1815                 }
    1816 
    1817                 return new Color(red, green, blue);
     1830                return new Range<Integer>(start, end, true, true);
     1831        }
     1832
     1833        /** Sets the colour that should be used to render the selected range. */
     1834        public void setSelectionColour(Colour colour)
     1835        {
     1836                if (colour == null) colour = RANGE_SELECT_COLOUR;
     1837               
     1838                _selectionColour = colour;
     1839        }
     1840       
     1841        /** Gets the colour that should be used to render the selected range. */
     1842        public Colour getSelectionColour()
     1843        {
     1844                return _selectionColour;
    18181845        }
    18191846
    18201847        @Override
    1821         public void paint(Graphics2D g) {
    1822                 if (!isVisible())
    1823                         return;
     1848        public void paint()
     1849        {
     1850                if (!isVisible()) return;
    18241851
    18251852                // if there is no text to paint, do nothing.
    1826                 if (_text == null || _text.length() == 0)
    1827                         return;
     1853                if (_text == null || _text.length() == 0) return;
    18281854
    18291855                if (_autoWrap || ExperimentalFeatures.AutoWrap.get()) {
     
    18411867                // This will allow for dragging vectors around the place!
    18421868                if (hasVector() && isFloating()) {
    1843                         FrameGraphics.requestRefresh(false);
     1869                        DisplayController.requestRefresh(false);
    18441870                        // TODO make this use a more efficient paint method...
    18451871                        // Have the text item return a bigger repaint area if it has an
    18461872                        // associated vector
    18471873                }
     1874               
     1875                GraphicsManager g = EcosystemManager.getGraphicsManager();
     1876                AxisAlignedBoxBounds bounds = (AxisAlignedBoxBounds) getBounds();
    18481877
    18491878                // the background is only cleared if required
    18501879                if (getBackgroundColor() != null) {
    1851                         Color bgc = getBackgroundColor();
     1880                        Colour bgc = getBackgroundColor();
    18521881                        if (_alpha > 0) {
    1853                                 bgc = new Color(bgc.getRed(), bgc.getGreen(), bgc.getBlue(), _alpha);
    1854                         }
    1855                         g.setColor(bgc);
    1856 
    1857                         Color gradientColor = getGradientColor();
    1858                         if (gradientColor != null) {
    1859                                 // The painting is not efficient enough for gradients...
    1860                                 Shape s = getPolygon();
    1861                                 if (s != null) {
    1862                                         Rectangle b = s.getBounds();
    1863                                         GradientPaint gp = new GradientPaint((int) (b.x + b.width * 0.3), b.y, bgc,
    1864                                                         (int) (b.x + b.width * 1.3), b.y, gradientColor);
    1865                                         g.setPaint(gp);
    1866                                 }
    1867                         }
    1868 
    1869                         g.fillPolygon(getPolygon());
     1882                                bgc = new Colour(bgc.getRed(), bgc.getGreen(), bgc.getBlue(),
     1883                                                Colour.FromComponent255(_alpha));
     1884                        }
     1885
     1886                        Colour gradientColor = getGradientColor();
     1887                       
     1888                        Fill fill;
     1889                       
     1890                        if (gradientColor != null && bounds != null) {
     1891                                fill = new GradientFill(bgc, new Point((int) (bounds.getMinX() + bounds.getWidth() * 0.3), bounds.getMinY()), gradientColor, new Point((int) (bounds.getMinX() + bounds.getWidth() * 1.3), bounds.getMinY()));
     1892                        } else {
     1893                                fill = new Fill(bgc);
     1894                        }
     1895
     1896                        g.drawRectangle(bounds, 0.0, fill, null, null, null);
    18701897                }
    18711898
    18721899                if (hasVisibleBorder()) {
    1873                         g.setColor(getPaintBorderColor());
    1874                         Stroke borderStroke = new BasicStroke(getThickness(), CAP, JOIN);
    1875                         g.setStroke(borderStroke);
    1876                         g.drawPolygon(getPolygon());
     1900                        Stroke borderStroke = new Stroke(getThickness(), DEFAULT_CAP, DEFAULT_JOIN);
     1901                        g.drawRectangle(bounds, 0.0, null, getPaintBorderColor(), borderStroke, null);
    18771902                }
    18781903
    18791904                if (hasFormula()) {
    1880                         g.setColor(getPaintHighlightColor());
    1881                         Stroke highlightStroke = new BasicStroke(1F, CAP, JOIN);
    1882                         g.setStroke(highlightStroke);
    1883 
    1884                         Point2D.Float start = getEdgePosition(0, true);
    1885                         Point2D.Float end = getEdgePosition(0, false);
    1886                         g.drawLine(Math.round(start.x), Math.round(start.y), Math.round(end.x), Math.round(end.y));
     1905                        Stroke highlightStroke = new Stroke(1F, DEFAULT_CAP, DEFAULT_JOIN);
     1906                        Point start = getEdgePosition(0, true);
     1907                        Point end = getEdgePosition(0, false);
     1908                        g.drawLine(start, end, getPaintHighlightColor(), highlightStroke);
    18871909                }
    18881910
    18891911                if (isHighlighted()) {
    1890                         g.setColor(getPaintHighlightColor());
    1891                         Stroke highlightStroke = new BasicStroke((float) getHighlightThickness(), CAP, JOIN);
    1892                         g.setStroke(highlightStroke);
    1893                         if (HighlightMode.Enclosed.equals(getHighlightMode()))
    1894                                 g.fillPolygon(getPolygon());
    1895                         else
    1896                                 g.drawPolygon(getPolygon());
     1912                        Stroke highlightStroke = new Stroke((float) getHighlightThickness(), DEFAULT_CAP, DEFAULT_JOIN);
     1913                        Fill fill;
     1914                        if (HighlightMode.Enclosed.equals(getHighlightMode())) {
     1915                                fill = new Fill(getPaintHighlightColor());
     1916                        } else {
     1917                                fill = null;
     1918                        }
     1919                        g.drawRectangle(bounds, 0.0, fill, getPaintHighlightColor(), highlightStroke, null);
    18971920                }
    18981921
    18991922                float y = getY();
    1900                 Color c = getPaintColor();
    1901                 if (_alpha > 0)
    1902                         c = new Color(c.getRed(), c.getGreen(), c.getBlue(), _alpha);
    1903 
    1904                 g.setColor(c);
    1905 
    1906                 Color selection = getSelectionColor(FrameMouseActions.getLastMouseButton());
     1923                Colour paintColour = getPaintColor();
     1924                if (_alpha > 0) {
     1925                        paintColour = new Colour(paintColour);
     1926                        paintColour.setAlpha(Colour.FromComponent255(_alpha));
     1927                       
     1928                }
     1929
     1930                Colour selectionColour = getSelectionColour();
    19071931
    19081932                // width -= getX();
     
    19131937                                TextLayout layout = _textLayouts.get(i);
    19141938
    1915                                 Point p = getSelectedRange(i);
    1916                                 if (p != null) {
    1917                                         AffineTransform at = new AffineTransform();
    1918                                         AffineTransform orig = g.getTransform();
    1919                                         at.translate(getX() + getJustOffset(layout), y);
    1920                                         g.setTransform(at);
    1921 
    1922                                         g.setColor(selection);
    1923                                         g.fill(layout.getLogicalHighlightShape(p.x, p.y));
    1924 
    1925                                         g.setTransform(orig);
    1926                                         g.setColor(c);
     1939                                Range<Integer> selectedRange = getSelectedRange(i);
     1940                                if (selectedRange != null) {
     1941                                        AxisAlignedBoxBounds highlight = layout.getLogicalHighlightShape(selectedRange.lowerBound, selectedRange.upperBound);
     1942                                        highlight.getTopLeft().add(getX() + getJustOffset(layout), (int) y);
     1943                                        g.drawRectangle(highlight,
     1944                                                                        0.0,
     1945                                                                        new Fill(selectionColour),
     1946                                                                        null, null, null);
    19271947                                }
    19281948
    19291949                                int ldx = 1 + getX() + getJustOffset(layout); // Layout draw x
    19301950
    1931                                 boolean debug = false;
    1932                                 if (debug) {
    1933                                         g.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), 40));
    1934                                         Rectangle layout_rect = layout.getPixelBounds(null, ldx, y);
    1935                                         g.fillRect(layout_rect.x, layout_rect.y, layout_rect.width, layout_rect.height);
    1936                                         g.setColor(c);
    1937                                 }
    1938 
    1939                                 layout.draw(g, ldx, y);
    1940 
    1941                                 /*
    1942                                  * AffineTransform at = new AffineTransform(); AffineTransform orig =
    1943                                  * g.getTransform(); at.translate(getX() + getJustOffset(layout), y);
    1944                                  * g.setTransform(at); g.draw(layout.getLogicalHighlightShape(0,
    1945                                  * layout.getCharacterCount())); g.setTransform(orig); /*
    1946                                  * if(_text.charAt(_lineOffsets.get(line) + (layout.getCharacterCount() - 1)) ==
    1947                                  * '\t'){ tab = true; x = (int) (getX() + x + 20 + layout.getVisibleAdvance());
    1948                                  * }else{
    1949                                  */
     1951                                g.drawTextLayout(layout, new Point(ldx, (int) y), paintColour);
     1952
    19501953                                y += getLineDrop(layout);
    1951                                 /*
    1952                                  * tab = false; }
    1953                                  *
    1954                                  * line++;
    1955                                  */
    1956                         }
    1957                 }
    1958 
    1959                 paintLink(g);
    1960         }
    1961 
     1954                        }
     1955                }
     1956
     1957                paintLink();
     1958        }
     1959
     1960        // TODO: Revise
    19621961        @Override
    1963         protected Rectangle getLinkDrawArea() { // TODO: Revise
    1964                 return getDrawingArea()[0];
     1962        protected AxisAlignedBoxBounds getLinkDrawArea()
     1963        {
     1964                return getDrawingArea();
    19651965        }
    19661966
     
    19701970         * @return True if this Item has no text in it, false otherwise.
    19711971         */
    1972         public boolean isEmpty() {
    1973                 if (_text == null || _text.length() == 0)
    1974                         return true;
    1975 
    1976                 return false;
     1972        public boolean isEmpty()
     1973        {
     1974                return (_text == null || _text.length() == 0);
    19771975        }
    19781976
     
    20042002        @Override
    20052003        public float getSize() {
    2006                 return getPaintFont().getSize2D();
     2004                return getPaintFont().getSize();
    20072005        }
    20082006
     
    20262024                if (size < MINIMUM_FONT_SIZE)
    20272025                        size = MINIMUM_FONT_SIZE;
    2028                 setFont(getPaintFont().deriveFont(size));
     2026                Font currentFont = getPaintFont();
     2027                currentFont.setSize((int) size);
     2028                setFont(currentFont);
    20292029                rebuild(true);
    20302030                invalidateAll();
     
    20332033        @Override
    20342034        public void setAnnotation(boolean val) {
    2035                 float mouseX = DisplayIO.getFloatMouseX();
    2036                 float mouseY = FrameMouseActions.MouseY;
    2037                 Point2D.Float newPoint = new Point2D.Float();
     2035                float mouseX = DisplayController.getFloatMouseX();
     2036                float mouseY = DisplayController.getFloatMouseY();
     2037                Point newPoint = new Point();
    20382038                if (val) {
    20392039                        // if this is already an annotation, do nothing
     
    20412041                                return;
    20422042                        if (!isLineEnd() && _text.length() > 0 && _text.charAt(0) == DEFAULT_BULLET) {
    2043                                 newPoint.setLocation(insertText("" + (char) KeyEvent.VK_BACK_SPACE, mouseX, mouseY, 1));
     2043                                newPoint.set(insertText("\b", mouseX, mouseY, 1));
    20442044                                if (_text.length() > 0 && _text.charAt(0) == ' ')
    2045                                         newPoint.setLocation(insertText("" + (char) KeyEvent.VK_BACK_SPACE, newPoint.x, newPoint.y, 1));
     2045                                        newPoint.set(insertText("\b", newPoint.x, newPoint.y, 1));
    20462046                        } else {
    2047                                 newPoint.setLocation(insertText("@", mouseX, mouseY, 0));
     2047                                newPoint.set(insertText("@", mouseX, mouseY, 0));
    20482048                        }
    20492049                } else {
     
    20522052                                return;
    20532053                        if (!isLineEnd() && _text.charAt(0) == '@') {
    2054                                 newPoint.setLocation(insertText("" + (char) KeyEvent.VK_BACK_SPACE, mouseX, mouseY, 1));
    2055                                 newPoint.setLocation(insertText(DEFAULT_BULLET_STRING, newPoint.x, newPoint.y, 0));
     2054                                newPoint.set(insertText("\b", mouseX, mouseY, 1));
     2055                                newPoint.set(insertText(DEFAULT_BULLET_STRING, newPoint.x, newPoint.y, 0));
    20562056                        } else {
    2057                                 newPoint.setLocation(insertText("" + (char) KeyEvent.VK_BACK_SPACE, mouseX, mouseY, 1));
     2057                                newPoint.set(insertText("\b", mouseX, mouseY, 1));
    20582058                        }
    20592059                }
    20602060                FrameUtils.setLastEdited(this);
    20612061                rebuild(true);
    2062                 DisplayIO.setCursorPosition(newPoint.x, newPoint.y, false);
     2062                DisplayController.setCursorPosition(newPoint.x, newPoint.y, false);
    20632063        }
    20642064
     
    20682068        private void insertString(String toInsert, int pos) {
    20692069                assert (toInsert.length() > 0);
    2070 
     2070                invalidateAll();
    20712071                _text.insert(pos, toInsert);
    2072 
    2073                 if (toInsert.length() > 1) {
    2074                         _lineBreaker = null;
    2075                 }
    2076 
    2077                 if (_lineBreaker != null) {
    2078                         AttributedString inserting = new AttributedString(_text.toString());
    2079                         inserting.addAttribute(TextAttribute.FONT, getPaintFont());
    2080                         _lineBreaker.insertChar(inserting.getIterator(), pos);
    2081                 }
     2072                rebuild(false);
     2073                invalidateAll();
    20822074        }
    20832075
     
    20882080                        if (this.isLineEnd()) {
    20892081                                // Remove and replace with a dot
    2090                                 FrameKeyboardActions.replaceText(this);
    2091                                 DisplayIO.setCursorPosition(this._x, this._y);
     2082                                Item.replaceText(this);
     2083                                DisplayController.setCursorPosition(this._x, this._y);
    20922084                        }
    20932085                        return;
    2094                 }
    2095 
    2096                 if (_lineBreaker != null) {
    2097                         AttributedString inserting = new AttributedString(_text.toString());
    2098                         inserting.addAttribute(TextAttribute.FONT, getPaintFont());
    2099                         _lineBreaker.deleteChar(inserting.getIterator(), pos);
    21002086                }
    21012087
     
    22292215        }
    22302216
     2217        /**
     2218         * Checks if the given point is 'near' any line of the text item.
     2219         */
    22312220        @Override
    22322221        public boolean isNear(int x, int y) {
     
    22422231                        for (TextLayout text : _textLayouts) {
    22432232                                // check left and right of each box
    2244                                 Rectangle2D textOutline = text.getLogicalHighlightShape(0, text.getCharacterCount()).getBounds2D();
     2233                                AxisAlignedBoxBounds textOutline = text.getLogicalHighlightShape(0, text.getCharacterCount());
    22452234
    22462235                                // check if the cursor is within the top, bottom and within the
    22472236                                // gravity of right
    2248                                 if (y - textY > textOutline.getY() - NEAR_DISTANCE
    2249                                                 && y - textY < textOutline.getY() + textOutline.getHeight() + NEAR_DISTANCE
    2250                                                 && x - textX < textOutline.getWidth() + NEAR_DISTANCE)
     2237                                if (y - textY > textOutline.getMinY() - NEAR_DISTANCE &&
     2238                                        y - textY < textOutline.getMinY() + textOutline.getHeight() + NEAR_DISTANCE &&
     2239                                        x - textX < textOutline.getWidth() + NEAR_DISTANCE)
     2240                                {
    22512241                                        return true;
     2242                                }
     2243                               
    22522244                                textY += getLineDrop(text);
    22532245                        }
     
    22632255                setAlpha(0);
    22642256                if (isLineEnd())
    2265                         DisplayIO.setCursor(Item.DEFAULT_CURSOR);
     2257                        DisplayController.setCursor(Item.DEFAULT_CURSOR);
    22662258
    22672259                String text = _text.toString().trim();
     
    22712263                // Show the overlay stuff immediately if this is an overlay item
    22722264                if (hasLink() && (text.startsWith("@ao") || text.startsWith("@o"))) {
    2273                         FrameKeyboardActions.Refresh();
     2265                        StandardGestureActions.Refresh();
    22742266                }
    22752267        }
     
    22772269        private void clipFrameMargin() {
    22782270                if (!hasFixedWidth()) {
    2279                         int frameWidth = FrameGraphics.getMaxFrameSize().width;
     2271                        int frameWidth = DisplayController.getFramePaintArea().getWidth();
    22802272                        /*
    22812273                         * Only change width if it is more than 150 pixels from the right of the screen
     
    22922284        }
    22932285
    2294         public void justify(boolean fixWidth, Polygon enclosure) {
     2286        public void justify(boolean fixWidth, PolygonBounds enclosure) {
    22952287                // if autowrap is on, wrapping is done every time we draw
    2296                 if (ExperimentalFeatures.AutoWrap.get()) {
    2297                         return;
    2298                 }
    2299 
    2300                 Integer width = FrameGraphics.getMaxFrameSize().width;
     2288                if(ExperimentalFeatures.AutoWrap.get()) return;
     2289
     2290                Integer width = DisplayController.getFramePaintArea().getWidth();
    23012291
    23022292                // Check if that text item is inside an enclosing rectangle...
    23032293                // Set its max width accordingly
    23042294                if (enclosure != null) {
    2305                         Rectangle bounds = enclosure.getBounds();
    2306                         if (bounds.width > 200 && getX() < bounds.width / 3 + bounds.x) {
    2307                                 width = bounds.x + bounds.width;
    2308                         }
    2309                 }
    2310 
    2311                 if (getWidth() == null)
    2312                         setRightMargin(width, fixWidth);
    2313 
    2314                 // Check for the annotation that restricts the width of text items
    2315                 // on the frame
     2295                        AxisAlignedBoxBounds bounds = AxisAlignedBoxBounds.getEnclosing(enclosure);
     2296                        if (bounds.getWidth() > 200 && getX() < bounds.getWidth() / 3 + bounds.getMinX()) {
     2297                                width = bounds.getMinX() + bounds.getWidth();
     2298                        }
     2299                }
     2300
     2301                if (getWidth() == null) setRightMargin(width, fixWidth);
     2302
     2303                // Check for the annotation that restricts the width of text items on the frame
    23162304                String widthString;
    23172305                if ((widthString = getParentOrCurrentFrame().getAnnotationValue("maxwidth")) != null) {
     
    23292317        public void justify(boolean fixWidth) {
    23302318                // if autowrap is on, wrapping is done every time we draw
    2331                 if (ExperimentalFeatures.AutoWrap.get()) {
    2332                         return;
    2333                 }
     2319                if(ExperimentalFeatures.AutoWrap.get()) return;
     2320
    23342321                this.justify(fixWidth, FrameUtils.getEnlosingPolygon());
    23352322        }
    23362323
    23372324        public void resetFrameNamePosition() {
    2338                 Dimension maxSize = FrameGraphics.getMaxFrameSize();
     2325                Dimension maxSize = DisplayController.getFramePaintArea().getSize();
    23392326                if (maxSize != null) {
    23402327                        // setMaxWidth(maxSize.width);
     
    23572344        public static final String TAB_STRING = "      ";
    23582345
    2359         public Point2D.Float insertTab(char ch, float mouseX, float mouseY) {
     2346        public Point insertTab(char ch, float mouseX, float mouseY) {
    23602347                return insertText("" + ch, mouseX, mouseY);
    23612348        }
    23622349
    2363         public Point2D.Float removeTab(char ch, float mouseX, float mouseY) {
     2350        public Point removeTab(char ch, float mouseX, float mouseY) {
    23642351                // Insert a space as a flag that it is a backwards tab
    23652352                return insertText(ch + " ", mouseX, mouseY);
     
    23792366                String text = getText().toLowerCase();
    23802367                // TODO make it so can just check the _overlay variable
    2381                 // Mike cant remember the reason _overlay var cant be use! opps
     2368                // Mike can't remember the reason _overlay var can't be use! oops
    23822369                if (!text.startsWith("@"))
    23832370                        return false;
     
    24012388        @Override
    24022389        public boolean calculate(String formula) {
    2403                 if (FrameGraphics.isXRayMode())
     2390                if (DisplayController.isXRayMode())
    24042391                        return false;
    24052392
     
    25532540
    25542541        @Override
    2555         public void setAnchorLeft(Float anchor) {
     2542        public void setAnchorLeft(Integer anchor) {
    25562543                if (!isLineEnd()) {
    25572544                        super.setAnchorLeft(anchor);
     
    25652552                invalidateCommonTrait(ItemAppearence.PreMoved);
    25662553
    2567                 this._anchorLeft = anchor;
    2568                 this._anchorRight = null;
     2554                this._anchoring.setLeftAnchor(anchor);
    25692555
    25702556                int oldX = getX();
     
    25792565
    25802566        @Override
    2581         public void setAnchorRight(Float anchor) {
     2567        public void setAnchorRight(Integer anchor) {
    25822568                if (!isLineEnd()) {
    25832569                        super.setAnchorRight(anchor);
    25842570                        // Subtract off the link width
    25852571                        if (anchor != null) {
    2586                                 setX(FrameGraphics.getMaxFrameSize().width - anchor - getBoundsWidth() + getLeftMargin());
     2572                                setX(DisplayController.getFramePaintArea().getWidth() - anchor
     2573                                                - getBoundsWidth() + getLeftMargin());
    25872574                        }
    25882575                        return;
     
    25912578                invalidateCommonTrait(ItemAppearence.PreMoved);
    25922579
    2593                 this._anchorRight = anchor;
    2594                 this._anchorLeft = null;
     2580                this._anchoring.setRightAnchor(anchor);
    25952581
    25962582                int oldX = getX();
    25972583                if (anchor != null) {
    2598                         float deltaX = FrameGraphics.getMaxFrameSize().width - anchor - getBoundsWidth() + getLeftMargin() - oldX;
     2584                        float deltaX = DisplayController.getFramePaintArea().getWidth() - anchor
     2585                                        - getBoundsWidth() + getLeftMargin() - oldX;
    25992586                        anchorConnected(AnchorEdgeType.Right, deltaX);
    26002587                }
     
    26052592
    26062593        @Override
    2607         public void setAnchorTop(Float anchor) {
     2594        public void setAnchorTop(Integer anchor) {
    26082595                if (!isLineEnd()) {
    26092596                        super.setAnchorTop(anchor);
     
    26162603                invalidateCommonTrait(ItemAppearence.PreMoved);
    26172604
    2618                 this._anchorTop = anchor;
    2619                 this._anchorBottom = null;
     2605                this._anchoring.setTopAnchor(anchor);
    26202606
    26212607                int oldY = getY();
     
    26302616
    26312617        @Override
    2632         public void setAnchorBottom(Float anchor) {
     2618        public void setAnchorBottom(Integer anchor) {
    26332619                if (!isLineEnd()) {
    26342620                        super.setAnchorBottom(anchor);
    26352621                        if (anchor != null) {
    2636                                 setY(FrameGraphics.getMaxFrameSize().height - (anchor + this.getBoundsHeight()
    2637                                                 - _textLayouts.get(0).getAscent() - _textLayouts.get(0).getDescent()));
     2622                                setY(DisplayController.getFramePaintArea().getHeight() - (anchor + this.getBoundsHeight() - _textLayouts.get(0).getAscent() - _textLayouts.get(0).getDescent()));
    26382623                        }
    26392624                        return;
     
    26422627                invalidateCommonTrait(ItemAppearence.PreMoved);
    26432628
    2644                 this._anchorBottom = anchor;
    2645                 this._anchorTop = null;
     2629                this._anchoring.setBottomAnchor(anchor);
    26462630
    26472631                int oldY = getY();
    26482632                if (anchor != null) {
    26492633
    2650                         float deltaY = FrameGraphics.getMaxFrameSize().height - anchor - oldY;
     2634                        float deltaY = DisplayController.getFramePaintArea().getHeight() - anchor - oldY;
    26512635                        anchorConnected(AnchorEdgeType.Bottom, deltaY);
    26522636                }
     
    26692653        }
    26702654
    2671         protected Rectangle getPixelBounds(TextLayout layout) {
     2655        protected AxisAlignedBoxBounds getPixelBounds(TextLayout layout)
     2656        {
    26722657                // Does 'layout' need to be synchronized (similar to _textLayouts below)??
    26732658                int x = getX();
     
    26752660
    26762661                int ldx = 1 + x + getJustOffset(layout); // Layout draw x
    2677                 Rectangle layout_rect = layout.getPixelBounds(null, ldx, y);
     2662                AxisAlignedBoxBounds layout_rect = layout.getPixelBounds(ldx, y);
    26782663
    26792664                return layout_rect;
    26802665        }
    2681 
     2666       
    26822667        /**
    26832668         * Creates the smallest possible rectangle object to enclose the Text Item
     
    26892674         * @see #getPixelBoundsUnionTight()
    26902675         */
    2691         public Rectangle getPixelBoundsUnion() {
    2692                 final Rectangle rect = getPixelBounds(_textLayouts.get(0));
    2693 
    2694                 int cumulativeHeight = rect.height;
    2695                 int maxWidth = rect.width;
     2676        public AxisAlignedBoxBounds getPixelBoundsUnion()
     2677        {
     2678                final AxisAlignedBoxBounds rect = getPixelBounds(_textLayouts.get(0));
     2679
     2680                int cumulativeHeight = rect.getSize().height;
     2681                int maxWidth = rect.getSize().width;
    26962682
    26972683                if (_textLayouts.size() > 1) {
    26982684                        for (int i = 1; i < _textLayouts.size(); i++) {
    2699                                 final Rectangle r = getPixelBounds(_textLayouts.get(i));
     2685                                final AxisAlignedBoxBounds r = getPixelBounds(_textLayouts.get(i));
    27002686                                cumulativeHeight += _textLayouts.get(i).getDescent() + _textLayouts.get(i).getAscent();
    2701                                 if (r.width > maxWidth)
    2702                                         maxWidth = r.width;
    2703                         }
    2704                 }
    2705 
    2706                 rect.setSize(maxWidth, cumulativeHeight);
     2687                                if (r.getSize().width > maxWidth)
     2688                                        maxWidth = r.getSize().width;
     2689                        }
     2690                }
     2691               
     2692                rect.getSize().width = maxWidth;
     2693                rect.getSize().height = cumulativeHeight;
    27072694
    27082695                return rect;
     
    27172704         * @see #getPixelBoundsUnion()
    27182705         */
    2719         public Polygon getPixelBoundsUnionTight() {
    2720                 final Rectangle rect = getPixelBounds(_textLayouts.get(0));
     2706        public PolygonBounds getPixelBoundsUnionTight()
     2707        {
     2708                final AxisAlignedBoxBounds rect = getPixelBounds(_textLayouts.get(0));
    27212709                if (_textLayouts.size() == 1) {
    2722                         int x = (int) rect.getX();
    2723                         int y = (int) rect.getY();
    2724                         return new Polygon(new int[] { x, x + rect.width, x + rect.width, x },
    2725                                         new int[] { y, y, y + rect.height, y + rect.height }, 4);
     2710                        return PolygonBounds.fromBox(rect);
    27262711                } else {
    2727                         final Polygon poly = new Polygon();
    2728                         poly.addPoint(rect.x, rect.y);
    2729                         poly.addPoint(rect.x + rect.width, rect.y);
    2730                         poly.addPoint(rect.x + rect.width, Math.round(rect.y + rect.height + _textLayouts.get(0).getDescent()));
    2731                         int y = (int) (rect.y + rect.height + _textLayouts.get(0).getDescent());
     2712                        final PolygonBounds poly = new PolygonBounds();
     2713                        poly.addPoint(rect.getMinX(), rect.getMinY());
     2714                        poly.addPoint(rect.getMaxX(), rect.getMinY());
     2715                        poly.addPoint(rect.getMaxX(), Math.round(rect.getMaxY() + _textLayouts.get(0).getDescent()));
     2716                        int y = (int) (rect.getMaxY() + _textLayouts.get(0).getDescent());
    27322717                        for (int i = 1; i < _textLayouts.size(); i++) {
    2733                                 final Rectangle r = getPixelBounds(_textLayouts.get(i));
    2734                                 poly.addPoint(r.x + r.width, y);
    2735                                 poly.addPoint(r.x + r.width, Math.round(y + r.height + _textLayouts.get(i).getDescent()));
    2736                                 y = Math.round(y + r.height + _textLayouts.get(i).getDescent());
    2737                         }
    2738                         poly.addPoint(rect.x + getPixelBounds(_textLayouts.get(_textLayouts.size() - 1)).width, Math.round(y + _textLayouts.get(_textLayouts.size() - 1).getDescent()));
    2739                         poly.addPoint(rect.x, Math.round(y + _textLayouts.get(_textLayouts.size() - 1).getDescent()));
     2718                                final AxisAlignedBoxBounds r = getPixelBounds(_textLayouts.get(i));
     2719                                poly.addPoint(r.getMaxX(), y);
     2720                                poly.addPoint(r.getMaxX(), Math.round(y + r.getHeight() + _textLayouts.get(i).getDescent()));
     2721                                y = Math.round(y + r.getHeight() + _textLayouts.get(i).getDescent());
     2722                        }
     2723                        poly.addPoint(rect.getMinX() + getPixelBounds(_textLayouts.get(_textLayouts.size() - 1)).getWidth(), Math.round(y + _textLayouts.get(_textLayouts.size() - 1).getDescent()));
     2724                        poly.addPoint(rect.getMinX(), Math.round(y + _textLayouts.get(_textLayouts.size() - 1).getDescent()));
    27402725                        return poly;
    27412726                }
    27422727        }
     2728       
     2729        /*
     2730        public AxisAlignedBoxBounds getPixelBoundsUnion()
     2731        {
     2732                synchronized (_textLayouts) {
     2733                       
     2734                        CombinationBoxBounds c = null;
     2735                       
     2736                        for (TextLayout layout: _textLayouts) {
     2737                                if (c == null) {
     2738                                        c = new CombinationBoxBounds(getPixelBounds(layout));
     2739                                } else {
     2740                                        c.add(getPixelBounds(layout));
     2741                                }
     2742                        }
     2743                       
     2744                        return AxisAlignedBoxBounds.getEnclosing(c);
     2745               
     2746                }
     2747        }
     2748        */
    27432749
    27442750        // public Rectangle getPixelBoundsUnion()
     
    28092815                _autoWrap = autoWrap;
    28102816        }
     2817
     2818        /**
     2819         * Creates a new Text Item whose text contains the given character. This
     2820         * method also moves the mouse cursor to be pointing at the newly created
     2821         * Text Item ready to insert the next character.
     2822         *
     2823         * @param start
     2824         *            The character to use as the initial text of this Item.
     2825         * @return The newly created Text Item
     2826         */
     2827        public static Text createText(char start) {
     2828                Text t = DisplayController.getCurrentFrame().createBlankText(
     2829                                "" + start);
     2830
     2831                Point newMouse = t.insertChar(start, DisplayController.getMouseX(), DisplayController.getMouseY());
     2832                DisplayController.setCursorPosition(newMouse.x, newMouse.y, false);
     2833
     2834                return t;
     2835        }
     2836
     2837        /**
     2838         * Creates a new Text Item with no text. The newly created Item is a copy of
     2839         * any ItemTemplate if one is present, and inherits all the attributes of
     2840         * the Template
     2841         *
     2842         * @return The newly created Text Item
     2843         */
     2844        public static Text createText() {
     2845                return DisplayController.getCurrentFrame().createNewText();
     2846        }
     2847
     2848        /**
     2849         * If the given Item is null, then a new Text item is created with the
     2850         * current date. If the given Item is not null, then the current date is
     2851         * prepended to the Item's text
     2852         *
     2853         * @param toAdd
     2854         *            The Item to prepend the date to, or null
     2855         */
     2856        public static void AddDate(Item toAdd) {
     2857                String date1 = Formatter.getDateTime();
     2858                String date2 = Formatter.getDate();
     2859                final String leftSeparator = " :";
     2860                final String rightSeparator = ": ";
     2861                String dateToAdd = date1 + rightSeparator;
     2862                boolean prepend = false;
     2863                boolean append = false;
     2864
     2865                // if the user is pointing at an item, add the date where ever the
     2866                // cursor is pointing
     2867                if (toAdd != null && toAdd instanceof Text) {
     2868                        // permission check
     2869                        if (!toAdd.hasPermission(UserAppliedPermission.full)) {
     2870                                MessageBay.displayMessage("Insufficicent permission to add the date to that item");
     2871                                return;
     2872                        }
     2873
     2874                        Text textItem = (Text) toAdd;
     2875
     2876                        String text = textItem.getText();
     2877
     2878                        // check if the default date has already been put on this item
     2879                        if (text.startsWith(date1 + rightSeparator)) {
     2880                                textItem.removeText(date1 + rightSeparator);
     2881                                dateToAdd = date2 + rightSeparator;
     2882                                prepend = true;
     2883                        } else if (text.startsWith(date2 + rightSeparator)) {
     2884                                textItem.removeText(date2 + rightSeparator);
     2885                                dateToAdd = leftSeparator + date2;
     2886                                append = true;
     2887                        } else if (text.endsWith(leftSeparator + date2)) {
     2888                                textItem.removeEndText(leftSeparator + date2);
     2889                                append = true;
     2890                                dateToAdd = leftSeparator + date1;
     2891                        } else if (text.endsWith(leftSeparator + date1)) {
     2892                                textItem.removeEndText(leftSeparator + date1);
     2893                                if (textItem.getLength() > 0) {
     2894                                        dateToAdd = "";
     2895                                        prepend = true;
     2896                                } else {
     2897                                        // use the default date format
     2898                                        prepend = true;
     2899                                }
     2900                        }
     2901
     2902                        if (prepend) {
     2903                                // add the date to the text item
     2904                                textItem.prependText(dateToAdd);
     2905                                if (dateToAdd.length() == textItem.getLength())
     2906                                        DisplayController.setCursorPosition(textItem.getParagraphEndPosition());
     2907                        } else if (append) {
     2908                                textItem.appendText(dateToAdd);
     2909                                if (dateToAdd.length() == textItem.getLength())
     2910                                        DisplayController.setCursorPosition(textItem.getPosition());
     2911                        } else {
     2912                                for (int i = 0; i < date1.length(); i++) {
     2913                                        StandardGestureActions.processChar(date1.charAt(i), false);
     2914                                }
     2915                        }
     2916
     2917                        textItem.getParent().setChanged(true);
     2918                        DisplayController.requestRefresh(true);
     2919                        // } else {
     2920                        // MessageBay
     2921                        // .displayMessage("Only text items can have the date prepended to
     2922                        // them");
     2923                        // }
     2924                        // otherwise, create a new text item
     2925                } else {
     2926                        Text newText = createText();
     2927                        newText.setText(dateToAdd);
     2928                        DisplayController.getCurrentFrame().addItem(newText);
     2929                        DisplayController.getCurrentFrame().setChanged(true);
     2930                        DisplayController.requestRefresh(true);
     2931
     2932                        DisplayController.setCursorPosition(newText.getParagraphEndPosition());
     2933                }
     2934
     2935        }
    28112936}
Note: See TracChangeset for help on using the changeset viewer.