Source Code Cross Referenced for WrappedPlainView.java in  » 6.0-JDK-Core » swing » javax » swing » text » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001        /*
002         * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004         *
005         * This code is free software; you can redistribute it and/or modify it
006         * under the terms of the GNU General Public License version 2 only, as
007         * published by the Free Software Foundation.  Sun designates this
008         * particular file as subject to the "Classpath" exception as provided
009         * by Sun in the LICENSE file that accompanied this code.
010         *
011         * This code is distributed in the hope that it will be useful, but WITHOUT
012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014         * version 2 for more details (a copy is included in the LICENSE file that
015         * accompanied this code).
016         *
017         * You should have received a copy of the GNU General Public License version
018         * 2 along with this work; if not, write to the Free Software Foundation,
019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020         *
021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022         * CA 95054 USA or visit www.sun.com if you need additional information or
023         * have any questions.
024         */
025        package javax.swing.text;
026
027        import java.util.Vector;
028        import java.util.Properties;
029        import java.awt.*;
030        import java.lang.ref.SoftReference;
031        import javax.swing.event.*;
032
033        /**
034         * View of plain text (text with only one font and color)
035         * that does line-wrapping.  This view expects that its
036         * associated element has child elements that represent
037         * the lines it should be wrapping.  It is implemented
038         * as a vertical box that contains logical line views.
039         * The logical line views are nested classes that render
040         * the logical line as multiple physical line if the logical
041         * line is too wide to fit within the allocation.  The
042         * line views draw upon the outer class for its state
043         * to reduce their memory requirements.
044         * <p>
045         * The line views do all of their rendering through the
046         * <code>drawLine</code> method which in turn does all of
047         * its rendering through the <code>drawSelectedText</code>
048         * and <code>drawUnselectedText</code> methods.  This 
049         * enables subclasses to easily specialize the rendering
050         * without concern for the layout aspects.
051         *
052         * @author  Timothy Prinzing
053         * @version 1.49 05/23/07
054         * @see     View
055         */
056        public class WrappedPlainView extends BoxView implements  TabExpander {
057
058            /**
059             * Creates a new WrappedPlainView.  Lines will be wrapped
060             * on character boundaries.
061             *
062             * @param elem the element underlying the view
063             */
064            public WrappedPlainView(Element elem) {
065                this (elem, false);
066            }
067
068            /**
069             * Creates a new WrappedPlainView.  Lines can be wrapped on
070             * either character or word boundaries depending upon the
071             * setting of the wordWrap parameter.
072             *
073             * @param elem the element underlying the view
074             * @param wordWrap should lines be wrapped on word boundaries?
075             */
076            public WrappedPlainView(Element elem, boolean wordWrap) {
077                super (elem, Y_AXIS);
078                this .wordWrap = wordWrap;
079            }
080
081            /**
082             * Returns the tab size set for the document, defaulting to 8.
083             *
084             * @return the tab size
085             */
086            protected int getTabSize() {
087                Integer i = (Integer) getDocument().getProperty(
088                        PlainDocument.tabSizeAttribute);
089                int size = (i != null) ? i.intValue() : 8;
090                return size;
091            }
092
093            /**
094             * Renders a line of text, suppressing whitespace at the end
095             * and expanding any tabs.  This is implemented to make calls
096             * to the methods <code>drawUnselectedText</code> and 
097             * <code>drawSelectedText</code> so that the way selected and 
098             * unselected text are rendered can be customized.
099             *
100             * @param p0 the starting document location to use >= 0
101             * @param p1 the ending document location to use >= p1
102             * @param g the graphics context
103             * @param x the starting X position >= 0
104             * @param y the starting Y position >= 0
105             * @see #drawUnselectedText
106             * @see #drawSelectedText
107             */
108            protected void drawLine(int p0, int p1, Graphics g, int x, int y) {
109                Element lineMap = getElement();
110                Element line = lineMap.getElement(lineMap.getElementIndex(p0));
111                Element elem;
112
113                try {
114                    if (line.isLeaf()) {
115                        drawText(line, p0, p1, g, x, y);
116                    } else {
117                        // this line contains the composed text.
118                        int idx = line.getElementIndex(p0);
119                        int lastIdx = line.getElementIndex(p1);
120                        for (; idx <= lastIdx; idx++) {
121                            elem = line.getElement(idx);
122                            int start = Math.max(elem.getStartOffset(), p0);
123                            int end = Math.min(elem.getEndOffset(), p1);
124                            x = drawText(elem, start, end, g, x, y);
125                        }
126                    }
127                } catch (BadLocationException e) {
128                    throw new StateInvariantError("Can't render: " + p0 + ","
129                            + p1);
130                }
131            }
132
133            private int drawText(Element elem, int p0, int p1, Graphics g,
134                    int x, int y) throws BadLocationException {
135                p1 = Math.min(getDocument().getLength(), p1);
136                AttributeSet attr = elem.getAttributes();
137
138                if (Utilities.isComposedTextAttributeDefined(attr)) {
139                    g.setColor(unselected);
140                    x = Utilities
141                            .drawComposedText(this , attr, g, x, y, p0
142                                    - elem.getStartOffset(), p1
143                                    - elem.getStartOffset());
144                } else {
145                    if (sel0 == sel1 || selected == unselected) {
146                        // no selection, or it is invisible
147                        x = drawUnselectedText(g, x, y, p0, p1);
148                    } else if ((p0 >= sel0 && p0 <= sel1)
149                            && (p1 >= sel0 && p1 <= sel1)) {
150                        x = drawSelectedText(g, x, y, p0, p1);
151                    } else if (sel0 >= p0 && sel0 <= p1) {
152                        if (sel1 >= p0 && sel1 <= p1) {
153                            x = drawUnselectedText(g, x, y, p0, sel0);
154                            x = drawSelectedText(g, x, y, sel0, sel1);
155                            x = drawUnselectedText(g, x, y, sel1, p1);
156                        } else {
157                            x = drawUnselectedText(g, x, y, p0, sel0);
158                            x = drawSelectedText(g, x, y, sel0, p1);
159                        }
160                    } else if (sel1 >= p0 && sel1 <= p1) {
161                        x = drawSelectedText(g, x, y, p0, sel1);
162                        x = drawUnselectedText(g, x, y, sel1, p1);
163                    } else {
164                        x = drawUnselectedText(g, x, y, p0, p1);
165                    }
166                }
167
168                return x;
169            }
170
171            /**
172             * Renders the given range in the model as normal unselected
173             * text.  
174             *
175             * @param g the graphics context
176             * @param x the starting X coordinate >= 0
177             * @param y the starting Y coordinate >= 0
178             * @param p0 the beginning position in the model >= 0
179             * @param p1 the ending position in the model >= p0
180             * @return the X location of the end of the range >= 0
181             * @exception BadLocationException if the range is invalid
182             */
183            protected int drawUnselectedText(Graphics g, int x, int y, int p0,
184                    int p1) throws BadLocationException {
185                g.setColor(unselected);
186                Document doc = getDocument();
187                Segment segment = SegmentCache.getSharedSegment();
188                doc.getText(p0, p1 - p0, segment);
189                int ret = Utilities.drawTabbedText(this , segment, x, y, g,
190                        this , p0);
191                SegmentCache.releaseSharedSegment(segment);
192                return ret;
193            }
194
195            /**
196             * Renders the given range in the model as selected text.  This
197             * is implemented to render the text in the color specified in
198             * the hosting component.  It assumes the highlighter will render
199             * the selected background.
200             *
201             * @param g the graphics context
202             * @param x the starting X coordinate >= 0
203             * @param y the starting Y coordinate >= 0
204             * @param p0 the beginning position in the model >= 0
205             * @param p1 the ending position in the model >= p0
206             * @return the location of the end of the range.
207             * @exception BadLocationException if the range is invalid
208             */
209            protected int drawSelectedText(Graphics g, int x, int y, int p0,
210                    int p1) throws BadLocationException {
211                g.setColor(selected);
212                Document doc = getDocument();
213                Segment segment = SegmentCache.getSharedSegment();
214                doc.getText(p0, p1 - p0, segment);
215                int ret = Utilities.drawTabbedText(this , segment, x, y, g,
216                        this , p0);
217                SegmentCache.releaseSharedSegment(segment);
218                return ret;
219            }
220
221            /**
222             * Gives access to a buffer that can be used to fetch 
223             * text from the associated document.
224             *
225             * @return the buffer
226             */
227            protected final Segment getLineBuffer() {
228                if (lineBuffer == null) {
229                    lineBuffer = new Segment();
230                }
231                return lineBuffer;
232            }
233
234            /**
235             * This is called by the nested wrapped line
236             * views to determine the break location.  This can
237             * be reimplemented to alter the breaking behavior.
238             * It will either break at word or character boundaries
239             * depending upon the break argument given at
240             * construction.
241             */
242            protected int calculateBreakPosition(int p0, int p1) {
243                int p;
244                Segment segment = SegmentCache.getSharedSegment();
245                loadText(segment, p0, p1);
246                int currentWidth = getWidth();
247                if (currentWidth == Integer.MAX_VALUE) {
248                    currentWidth = (int) getDefaultSpan(View.X_AXIS);
249                }
250                if (wordWrap) {
251                    p = p0
252                            + Utilities.getBreakLocation(segment, metrics,
253                                    tabBase, tabBase + currentWidth, this , p0);
254                } else {
255                    p = p0
256                            + Utilities.getTabbedTextOffset(segment, metrics,
257                                    tabBase, tabBase + currentWidth, this , p0,
258                                    false);
259                }
260                SegmentCache.releaseSharedSegment(segment);
261                return p;
262            }
263
264            /**
265             * Loads all of the children to initialize the view.
266             * This is called by the <code>setParent</code> method.
267             * Subclasses can reimplement this to initialize their
268             * child views in a different manner.  The default
269             * implementation creates a child view for each 
270             * child element.
271             *
272             * @param f the view factory
273             */
274            protected void loadChildren(ViewFactory f) {
275                Element e = getElement();
276                int n = e.getElementCount();
277                if (n > 0) {
278                    View[] added = new View[n];
279                    for (int i = 0; i < n; i++) {
280                        added[i] = new WrappedLine(e.getElement(i));
281                    }
282                    replace(0, 0, added);
283                }
284            }
285
286            /**
287             * Update the child views in response to a 
288             * document event.
289             */
290            void updateChildren(DocumentEvent e, Shape a) {
291                Element elem = getElement();
292                DocumentEvent.ElementChange ec = e.getChange(elem);
293                if (ec != null) {
294                    // the structure of this element changed.
295                    Element[] removedElems = ec.getChildrenRemoved();
296                    Element[] addedElems = ec.getChildrenAdded();
297                    View[] added = new View[addedElems.length];
298                    for (int i = 0; i < addedElems.length; i++) {
299                        added[i] = new WrappedLine(addedElems[i]);
300                    }
301                    replace(ec.getIndex(), removedElems.length, added);
302
303                    // should damge a little more intelligently.
304                    if (a != null) {
305                        preferenceChanged(null, true, true);
306                        getContainer().repaint();
307                    }
308                }
309
310                // update font metrics which may be used by the child views
311                updateMetrics();
312            }
313
314            /**
315             * Load the text buffer with the given range
316             * of text.  This is used by the fragments 
317             * broken off of this view as well as this 
318             * view itself.
319             */
320            final void loadText(Segment segment, int p0, int p1) {
321                try {
322                    Document doc = getDocument();
323                    doc.getText(p0, p1 - p0, segment);
324                } catch (BadLocationException bl) {
325                    throw new StateInvariantError("Can't get line text");
326                }
327            }
328
329            final void updateMetrics() {
330                Component host = getContainer();
331                Font f = host.getFont();
332                metrics = host.getFontMetrics(f);
333                tabSize = getTabSize() * metrics.charWidth('m');
334            }
335
336            /**
337             * Return reasonable default values for the view dimensions.  The standard
338             * text terminal size 80x24 is pretty suitable for the wrapped plain view.
339             */
340            private float getDefaultSpan(int axis) {
341                switch (axis) {
342                case View.X_AXIS:
343                    return 80 * metrics.getWidths()['M'];
344                case View.Y_AXIS:
345                    return 24 * metrics.getHeight();
346                default:
347                    throw new IllegalArgumentException("Invalid axis: " + axis);
348                }
349            }
350
351            // --- TabExpander methods ------------------------------------------
352
353            /**
354             * Returns the next tab stop position after a given reference position.
355             * This implementation does not support things like centering so it
356             * ignores the tabOffset argument.
357             *
358             * @param x the current position >= 0
359             * @param tabOffset the position within the text stream
360             *   that the tab occurred at >= 0.
361             * @return the tab stop, measured in points >= 0
362             */
363            public float nextTabStop(float x, int tabOffset) {
364                if (tabSize == 0)
365                    return x;
366                int ntabs = ((int) x - tabBase) / tabSize;
367                return tabBase + ((ntabs + 1) * tabSize);
368            }
369
370            // --- View methods -------------------------------------
371
372            /**
373             * Renders using the given rendering surface and area 
374             * on that surface.  This is implemented to stash the
375             * selection positions, selection colors, and font
376             * metrics for the nested lines to use.
377             *
378             * @param g the rendering surface to use
379             * @param a the allocated region to render into
380             *
381             * @see View#paint
382             */
383            public void paint(Graphics g, Shape a) {
384                Rectangle alloc = (Rectangle) a;
385                tabBase = alloc.x;
386                JTextComponent host = (JTextComponent) getContainer();
387                sel0 = host.getSelectionStart();
388                sel1 = host.getSelectionEnd();
389                unselected = (host.isEnabled()) ? host.getForeground() : host
390                        .getDisabledTextColor();
391                Caret c = host.getCaret();
392                selected = c.isSelectionVisible()
393                        && host.getHighlighter() != null ? host
394                        .getSelectedTextColor() : unselected;
395                g.setFont(host.getFont());
396
397                // superclass paints the children
398                super .paint(g, a);
399            }
400
401            /**
402             * Sets the size of the view.  This should cause 
403             * layout of the view along the given axis, if it 
404             * has any layout duties.
405             *
406             * @param width the width >= 0
407             * @param height the height >= 0
408             */
409            public void setSize(float width, float height) {
410                updateMetrics();
411                if ((int) width != getWidth()) {
412                    // invalidate the view itself since the childrens
413                    // desired widths will be based upon this views width.
414                    preferenceChanged(null, true, true);
415                    widthChanging = true;
416                }
417                super .setSize(width, height);
418                widthChanging = false;
419            }
420
421            /**
422             * Determines the preferred span for this view along an
423             * axis.  This is implemented to provide the superclass
424             * behavior after first making sure that the current font
425             * metrics are cached (for the nested lines which use
426             * the metrics to determine the height of the potentially
427             * wrapped lines).
428             *
429             * @param axis may be either View.X_AXIS or View.Y_AXIS
430             * @return  the span the view would like to be rendered into.
431             *           Typically the view is told to render into the span
432             *           that is returned, although there is no guarantee.  
433             *           The parent may choose to resize or break the view.
434             * @see View#getPreferredSpan
435             */
436            public float getPreferredSpan(int axis) {
437                updateMetrics();
438                return super .getPreferredSpan(axis);
439            }
440
441            /**
442             * Determines the minimum span for this view along an
443             * axis.  This is implemented to provide the superclass
444             * behavior after first making sure that the current font
445             * metrics are cached (for the nested lines which use
446             * the metrics to determine the height of the potentially
447             * wrapped lines).
448             *
449             * @param axis may be either View.X_AXIS or View.Y_AXIS
450             * @return  the span the view would like to be rendered into.
451             *           Typically the view is told to render into the span
452             *           that is returned, although there is no guarantee.  
453             *           The parent may choose to resize or break the view.
454             * @see View#getMinimumSpan
455             */
456            public float getMinimumSpan(int axis) {
457                updateMetrics();
458                return super .getMinimumSpan(axis);
459            }
460
461            /**
462             * Determines the maximum span for this view along an
463             * axis.  This is implemented to provide the superclass
464             * behavior after first making sure that the current font
465             * metrics are cached (for the nested lines which use
466             * the metrics to determine the height of the potentially
467             * wrapped lines).
468             *
469             * @param axis may be either View.X_AXIS or View.Y_AXIS
470             * @return  the span the view would like to be rendered into.
471             *           Typically the view is told to render into the span
472             *           that is returned, although there is no guarantee.  
473             *           The parent may choose to resize or break the view.
474             * @see View#getMaximumSpan
475             */
476            public float getMaximumSpan(int axis) {
477                updateMetrics();
478                return super .getMaximumSpan(axis);
479            }
480
481            /**
482             * Gives notification that something was inserted into the 
483             * document in a location that this view is responsible for.
484             * This is implemented to simply update the children.
485             *
486             * @param e the change information from the associated document
487             * @param a the current allocation of the view
488             * @param f the factory to use to rebuild if the view has children
489             * @see View#insertUpdate
490             */
491            public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
492                updateChildren(e, a);
493
494                Rectangle alloc = ((a != null) && isAllocationValid()) ? getInsideAllocation(a)
495                        : null;
496                int pos = e.getOffset();
497                View v = getViewAtPosition(pos, alloc);
498                if (v != null) {
499                    v.insertUpdate(e, alloc, f);
500                }
501            }
502
503            /**
504             * Gives notification that something was removed from the 
505             * document in a location that this view is responsible for.
506             * This is implemented to simply update the children.
507             *
508             * @param e the change information from the associated document
509             * @param a the current allocation of the view
510             * @param f the factory to use to rebuild if the view has children
511             * @see View#removeUpdate
512             */
513            public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
514                updateChildren(e, a);
515
516                Rectangle alloc = ((a != null) && isAllocationValid()) ? getInsideAllocation(a)
517                        : null;
518                int pos = e.getOffset();
519                View v = getViewAtPosition(pos, alloc);
520                if (v != null) {
521                    v.removeUpdate(e, alloc, f);
522                }
523            }
524
525            /**
526             * Gives notification from the document that attributes were changed
527             * in a location that this view is responsible for.
528             *
529             * @param e the change information from the associated document
530             * @param a the current allocation of the view
531             * @param f the factory to use to rebuild if the view has children
532             * @see View#changedUpdate
533             */
534            public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
535                updateChildren(e, a);
536            }
537
538            // --- variables -------------------------------------------
539
540            FontMetrics metrics;
541            Segment lineBuffer;
542            boolean widthChanging;
543            int tabBase;
544            int tabSize;
545            boolean wordWrap;
546
547            int sel0;
548            int sel1;
549            Color unselected;
550            Color selected;
551
552            /**
553             * Simple view of a line that wraps if it doesn't
554             * fit withing the horizontal space allocated.
555             * This class tries to be lightweight by carrying little 
556             * state of it's own and sharing the state of the outer class 
557             * with it's sibblings.
558             */
559            class WrappedLine extends View {
560
561                WrappedLine(Element elem) {
562                    super (elem);
563                    lineCount = -1;
564                }
565
566                /**
567                 * Determines the preferred span for this view along an
568                 * axis.
569                 *
570                 * @param axis may be either X_AXIS or Y_AXIS
571                 * @return   the span the view would like to be rendered into.
572                 *           Typically the view is told to render into the span
573                 *           that is returned, although there is no guarantee.  
574                 *           The parent may choose to resize or break the view.
575                 * @see View#getPreferredSpan
576                 */
577                public float getPreferredSpan(int axis) {
578                    switch (axis) {
579                    case View.X_AXIS:
580                        float width = getWidth();
581                        if (width == Integer.MAX_VALUE) {
582                            // We have been initially set to MAX_VALUE, but we don't
583                            // want this as our preferred.
584                            width = getDefaultSpan(axis);
585                        }
586                        return width;
587                    case View.Y_AXIS:
588                        if (getDocument().getLength() > 0) {
589                            if ((lineCount < 0) || widthChanging) {
590                                breakLines(getStartOffset());
591                            }
592                            return lineCount * metrics.getHeight();
593                        } else {
594                            return getDefaultSpan(axis);
595                        }
596                    default:
597                        throw new IllegalArgumentException("Invalid axis: "
598                                + axis);
599                    }
600                }
601
602                /**
603                 * Renders using the given rendering surface and area on that
604                 * surface.  The view may need to do layout and create child
605                 * views to enable itself to render into the given allocation.
606                 *
607                 * @param g the rendering surface to use
608                 * @param a the allocated region to render into
609                 * @see View#paint
610                 */
611                public void paint(Graphics g, Shape a) {
612                    Rectangle alloc = (Rectangle) a;
613                    int y = alloc.y + metrics.getAscent();
614                    int x = alloc.x;
615
616                    JTextComponent host = (JTextComponent) getContainer();
617                    Highlighter h = host.getHighlighter();
618                    LayeredHighlighter dh = (h instanceof  LayeredHighlighter) ? (LayeredHighlighter) h
619                            : null;
620
621                    int start = getStartOffset();
622                    int end = getEndOffset();
623                    int p0 = start;
624                    int[] lineEnds = getLineEnds();
625                    for (int i = 0; i < lineCount; i++) {
626                        int p1 = (lineEnds == null) ? end : start + lineEnds[i];
627                        if (dh != null) {
628                            int hOffset = (p1 == end) ? (p1 - 1) : p1;
629                            dh.paintLayeredHighlights(g, p0, hOffset, a, host,
630                                    this );
631                        }
632                        drawLine(p0, p1, g, x, y);
633
634                        p0 = p1;
635                        y += metrics.getHeight();
636                    }
637                }
638
639                /**
640                 * Provides a mapping from the document model coordinate space
641                 * to the coordinate space of the view mapped to it.
642                 *
643                 * @param pos the position to convert
644                 * @param a the allocated region to render into
645                 * @return the bounding box of the given position is returned
646                 * @exception BadLocationException  if the given position does not represent a
647                 *   valid location in the associated document
648                 * @see View#modelToView
649                 */
650                public Shape modelToView(int pos, Shape a, Position.Bias b)
651                        throws BadLocationException {
652                    Rectangle alloc = a.getBounds();
653                    alloc.height = metrics.getHeight();
654                    alloc.width = 1;
655
656                    int p0 = getStartOffset();
657                    if (pos < p0 || pos > getEndOffset()) {
658                        throw new BadLocationException("Position out of range",
659                                pos);
660                    }
661
662                    int testP = (b == Position.Bias.Forward) ? pos : Math.max(
663                            p0, pos - 1);
664                    int line = 0;
665                    int[] lineEnds = getLineEnds();
666                    if (lineEnds != null) {
667                        line = findLine(testP - p0);
668                        if (line > 0) {
669                            p0 += lineEnds[line - 1];
670                        }
671                        alloc.y += alloc.height * line;
672                    }
673
674                    if (pos > p0) {
675                        Segment segment = SegmentCache.getSharedSegment();
676                        loadText(segment, p0, pos);
677                        alloc.x += Utilities.getTabbedTextWidth(segment,
678                                metrics, alloc.x, WrappedPlainView.this , p0);
679                        SegmentCache.releaseSharedSegment(segment);
680                    }
681                    return alloc;
682                }
683
684                /**
685                 * Provides a mapping from the view coordinate space to the logical
686                 * coordinate space of the model.
687                 *
688                 * @param fx the X coordinate
689                 * @param fy the Y coordinate
690                 * @param a the allocated region to render into
691                 * @return the location within the model that best represents the
692                 *  given point in the view
693                 * @see View#viewToModel
694                 */
695                public int viewToModel(float fx, float fy, Shape a,
696                        Position.Bias[] bias) {
697                    // PENDING(prinz) implement bias properly
698                    bias[0] = Position.Bias.Forward;
699
700                    Rectangle alloc = (Rectangle) a;
701                    int x = (int) fx;
702                    int y = (int) fy;
703                    if (y < alloc.y) {
704                        // above the area covered by this icon, so the the position
705                        // is assumed to be the start of the coverage for this view.
706                        return getStartOffset();
707                    } else if (y > alloc.y + alloc.height) {
708                        // below the area covered by this icon, so the the position
709                        // is assumed to be the end of the coverage for this view.
710                        return getEndOffset() - 1;
711                    } else {
712                        // positioned within the coverage of this view vertically,
713                        // so we figure out which line the point corresponds to.
714                        // if the line is greater than the number of lines contained, then
715                        // simply use the last line as it represents the last possible place
716                        // we can position to.
717                        alloc.height = metrics.getHeight();
718                        int line = (alloc.height > 0 ? (y - alloc.y)
719                                / alloc.height : lineCount - 1);
720                        if (line >= lineCount) {
721                            return getEndOffset() - 1;
722                        } else {
723                            int p0 = getStartOffset();
724                            int p1;
725                            if (lineCount == 1) {
726                                p1 = getEndOffset();
727                            } else {
728                                int[] lineEnds = getLineEnds();
729                                p1 = p0 + lineEnds[line];
730                                if (line > 0) {
731                                    p0 += lineEnds[line - 1];
732                                }
733                            }
734
735                            if (x < alloc.x) {
736                                // point is to the left of the line
737                                return p0;
738                            } else if (x > alloc.x + alloc.width) {
739                                // point is to the right of the line
740                                return p1 - 1;
741                            } else {
742                                // Determine the offset into the text
743                                Segment segment = SegmentCache
744                                        .getSharedSegment();
745                                loadText(segment, p0, p1);
746                                int n = Utilities.getTabbedTextOffset(segment,
747                                        metrics, alloc.x, x,
748                                        WrappedPlainView.this , p0);
749                                SegmentCache.releaseSharedSegment(segment);
750                                return Math.min(p0 + n, p1 - 1);
751                            }
752                        }
753                    }
754                }
755
756                public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
757                    update(e, a);
758                }
759
760                public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
761                    update(e, a);
762                }
763
764                private void update(DocumentEvent ev, Shape a) {
765                    int oldCount = lineCount;
766                    breakLines(ev.getOffset());
767                    if (oldCount != lineCount) {
768                        WrappedPlainView.this .preferenceChanged(this , false,
769                                true);
770                        // have to repaint any views after the receiver.
771                        getContainer().repaint();
772                    } else if (a != null) {
773                        Component c = getContainer();
774                        Rectangle alloc = (Rectangle) a;
775                        c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
776                    }
777                }
778
779                /**
780                 * Returns line cache. If the cache was GC'ed, recreates it.
781                 * If there's no cache, returns null
782                 */
783                final int[] getLineEnds() {
784                    if (lineCache == null) {
785                        return null;
786                    } else {
787                        int[] lineEnds = lineCache.get();
788                        if (lineEnds == null) {
789                            // Cache was GC'ed, so rebuild it
790                            return breakLines(getStartOffset());
791                        } else {
792                            return lineEnds;
793                        }
794                    }
795                }
796
797                /**
798                 * Creates line cache if text breaks into more than one physical line.
799                 * @param startPos position to start breaking from 
800                 * @return the cache created, ot null if text breaks into one line 
801                 */
802                final int[] breakLines(int startPos) {
803                    int[] lineEnds = (lineCache == null) ? null : lineCache
804                            .get();
805                    int[] oldLineEnds = lineEnds;
806                    int start = getStartOffset();
807                    int lineIndex = 0;
808                    if (lineEnds != null) {
809                        lineIndex = findLine(startPos - start);
810                        if (lineIndex > 0) {
811                            lineIndex--;
812                        }
813                    }
814
815                    int p0 = (lineIndex == 0) ? start : start
816                            + lineEnds[lineIndex - 1];
817                    int p1 = getEndOffset();
818                    while (p0 < p1) {
819                        int p = calculateBreakPosition(p0, p1);
820                        p0 = (p == p0) ? ++p : p; // 4410243
821
822                        if (lineIndex == 0 && p0 >= p1) {
823                            // do not use cache if there's only one line
824                            lineCache = null;
825                            lineEnds = null;
826                            lineIndex = 1;
827                            break;
828                        } else if (lineEnds == null
829                                || lineIndex >= lineEnds.length) {
830                            // we have 2+ lines, and the cache is not big enough
831                            // we try to estimate total number of lines
832                            double growFactor = ((double) (p1 - start) / (p0 - start));
833                            int newSize = (int) Math.ceil((lineIndex + 1)
834                                    * growFactor);
835                            newSize = Math.max(newSize, lineIndex + 2);
836                            int[] tmp = new int[newSize];
837                            if (lineEnds != null) {
838                                System
839                                        .arraycopy(lineEnds, 0, tmp, 0,
840                                                lineIndex);
841                            }
842                            lineEnds = tmp;
843                        }
844                        lineEnds[lineIndex++] = p0 - start;
845                    }
846
847                    lineCount = lineIndex;
848                    if (lineCount > 1) {
849                        // check if the cache is too big
850                        int maxCapacity = lineCount + lineCount / 3;
851                        if (lineEnds.length > maxCapacity) {
852                            int[] tmp = new int[maxCapacity];
853                            System.arraycopy(lineEnds, 0, tmp, 0, lineCount);
854                            lineEnds = tmp;
855                        }
856                    }
857
858                    if (lineEnds != null && lineEnds != oldLineEnds) {
859                        lineCache = new SoftReference<int[]>(lineEnds);
860                    }
861                    return lineEnds;
862                }
863
864                /**
865                 * Binary search in the cache for line containing specified offset
866                 * (which is relative to the beginning of the view). This method
867                 * assumes that cache exists.
868                 */
869                private int findLine(int offset) {
870                    int[] lineEnds = lineCache.get();
871                    if (offset < lineEnds[0]) {
872                        return 0;
873                    } else if (offset > lineEnds[lineCount - 1]) {
874                        return lineCount;
875                    } else {
876                        return findLine(lineEnds, offset, 0, lineCount - 1);
877                    }
878                }
879
880                private int findLine(int[] array, int offset, int min, int max) {
881                    if (max - min <= 1) {
882                        return max;
883                    } else {
884                        int mid = (max + min) / 2;
885                        return (offset < array[mid]) ? findLine(array, offset,
886                                min, mid) : findLine(array, offset, mid, max);
887                    }
888                }
889
890                int lineCount;
891                SoftReference<int[]> lineCache = null;
892            }
893        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.