001    /*
002     * $Id: BasicHyperlinkUI.java 3344 2009-05-25 01:46:59Z kschaefe $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    package org.jdesktop.swingx.plaf.basic;
022    
023    import java.awt.Color;
024    import java.awt.Container;
025    import java.awt.Cursor;
026    import java.awt.Font;
027    import java.awt.FontMetrics;
028    import java.awt.Graphics;
029    import java.awt.Insets;
030    import java.awt.Rectangle;
031    import java.awt.Shape;
032    import java.beans.PropertyChangeEvent;
033    import java.beans.PropertyChangeListener;
034    import java.io.Reader;
035    import java.io.StringReader;
036    import java.lang.reflect.Method;
037    import java.net.URL;
038    import java.util.logging.Logger;
039    
040    import javax.swing.AbstractButton;
041    import javax.swing.BorderFactory;
042    import javax.swing.ButtonModel;
043    import javax.swing.Icon;
044    import javax.swing.JComponent;
045    import javax.swing.JToolBar;
046    import javax.swing.LookAndFeel;
047    import javax.swing.SwingUtilities;
048    import javax.swing.UIManager;
049    import javax.swing.event.ChangeEvent;
050    import javax.swing.plaf.BorderUIResource;
051    import javax.swing.plaf.ComponentUI;
052    import javax.swing.plaf.basic.BasicButtonListener;
053    import javax.swing.plaf.basic.BasicButtonUI;
054    import javax.swing.plaf.basic.BasicGraphicsUtils;
055    import javax.swing.plaf.basic.BasicHTML;
056    import javax.swing.text.AttributeSet;
057    import javax.swing.text.BadLocationException;
058    import javax.swing.text.Document;
059    import javax.swing.text.Element;
060    import javax.swing.text.Position;
061    import javax.swing.text.View;
062    import javax.swing.text.ViewFactory;
063    import javax.swing.text.html.HTMLDocument;
064    import javax.swing.text.html.HTMLEditorKit;
065    import javax.swing.text.html.ImageView;
066    import javax.swing.text.html.StyleSheet;
067    
068    import org.jdesktop.swingx.SwingXUtilities;
069    
070    /**
071     * Basic implementation of the <code>JXHyperlink</code> UI. <br>
072     * This is copied from org.jdesktop.jdnc.plaf.basic.BasicLinkButtonUI
073     */
074    public class BasicHyperlinkUI extends BasicButtonUI {
075    
076        @SuppressWarnings("unused")
077        private static final Logger LOG = Logger.getLogger(BasicHyperlinkUI.class
078                .getName());
079        
080        public static ComponentUI createUI(JComponent c) {
081            return new BasicHyperlinkUI();
082        }
083    
084        private static Rectangle viewRect = new Rectangle();
085    
086        private static Rectangle textRect = new Rectangle();
087    
088        private static Rectangle iconRect = new Rectangle();
089    
090    //    private static MouseListener handCursorListener = new HandCursor();
091    
092        protected int dashedRectGapX;
093    
094        protected int dashedRectGapY;
095    
096        protected int dashedRectGapWidth;
097    
098        protected int dashedRectGapHeight;
099    
100        private Color focusColor;
101    
102        private View ulv;
103    
104        private PropertyChangeListener pcListener = new PropertyChangeListener() {
105    
106            public void propertyChange(PropertyChangeEvent evt) {
107                // this method is called from the edt. only other place where ulv is used is in 
108                // painting which also happens on edt so it should be safe even without synchronization
109                // sole purpose of this call is to reinitialize view on every property change
110                ulv = null;
111            }};
112    
113        @Override
114        protected void installDefaults(AbstractButton b) {
115            super.installDefaults(b);
116    
117            LookAndFeel.installProperty(b, "opaque", false);
118            b.setBorderPainted(false);
119            b.setRolloverEnabled(true);
120            if (SwingXUtilities.isUIInstallable(b.getBorder())) {
121                b.setBorder(new BorderUIResource(BorderFactory.createEmptyBorder(0, 1, 0, 0)));
122            }
123    
124            dashedRectGapX = UIManager.getInt("ButtonUI.dashedRectGapX");
125            dashedRectGapY = UIManager.getInt("ButtonUI.dashedRectGapY");
126            dashedRectGapWidth = UIManager.getInt("ButtonUI.dashedRectGapWidth");
127            dashedRectGapHeight = UIManager.getInt("ButtonUI.dashedRectGapHeight");
128            focusColor = UIManager.getColor("ButtonUI.focus");
129    
130            b.setHorizontalAlignment(AbstractButton.LEADING);
131        }
132    
133        @Override
134        protected void installListeners(AbstractButton b) {
135            super.installListeners(b);
136    //        b.addMouseListener(handCursorListener);
137            b.addPropertyChangeListener(pcListener);
138        }
139    
140        @Override
141        protected void uninstallListeners(AbstractButton b) {
142            super.uninstallListeners(b);
143    //        b.removeMouseListener(handCursorListener);
144            b.removePropertyChangeListener(pcListener);
145        }
146    
147        protected Color getFocusColor() {
148            return focusColor;
149        }
150    
151        @Override
152        public void paint(Graphics g, JComponent c) {
153            AbstractButton b = (AbstractButton) c;
154            ButtonModel model = b.getModel();
155    
156            FontMetrics fm = g.getFontMetrics();
157    
158            Insets i = c.getInsets();
159    
160            viewRect.x = i.left;
161            viewRect.y = i.top;
162            viewRect.width = b.getWidth() - (i.right + viewRect.x);
163            viewRect.height = b.getHeight() - (i.bottom + viewRect.y);
164    
165            textRect.x = textRect.y = textRect.width = textRect.height = 0;
166            iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
167    
168            Font f = c.getFont();
169            g.setFont(f);
170    
171            // layout the text and icon
172            String text = SwingUtilities.layoutCompoundLabel(c, fm, b.getText(), b
173                    .getIcon(), b.getVerticalAlignment(), b
174                    .getHorizontalAlignment(), b.getVerticalTextPosition(), b
175                    .getHorizontalTextPosition(), viewRect, iconRect, textRect, b
176                    .getText() == null ? 0 : b.getIconTextGap());
177    
178            clearTextShiftOffset();
179    
180            // perform UI specific press action, e.g. Windows L&F shifts text
181            if (model.isArmed() && model.isPressed()) {
182                paintButtonPressed(g, b);
183            }
184    
185            // Paint the Icon
186            if (b.getIcon() != null) {
187                paintIcon(g, c, iconRect);
188            }
189    
190    //        Composite oldComposite = ((Graphics2D) g).getComposite();
191    //
192    //        if (model.isRollover()) {
193    //            ((Graphics2D) g).setComposite(AlphaComposite.getInstance(
194    //                    AlphaComposite.SRC_OVER, 0.5f));
195    //        }
196    
197            if (text != null && !text.equals("")) {
198                View v = (View) c.getClientProperty(BasicHTML.propertyKey);
199                if (v != null) {
200                    paintHTMLText(g, b, textRect, text, v);
201                } else {
202                    paintText(g, b, textRect, text);
203                }
204            }
205    
206            if (b.isFocusPainted() && b.hasFocus()) {
207                // paint UI specific focus
208                paintFocus(g, b, viewRect, textRect, iconRect);
209            }
210    
211    //        ((Graphics2D) g).setComposite(oldComposite);
212        }
213    
214        /**
215         * Method which renders the text of the current button if html.
216         * <p>
217         * @param g Graphics context
218         * @param b Current button to render
219         * @param textRect Bounding rectangle to render the text.
220         * @param text String to render
221         * @param v the View to use.
222         */
223        protected void paintHTMLText(Graphics g, AbstractButton b, 
224                Rectangle textRect, String text, View v) {
225            textRect.x += getTextShiftOffset();
226            textRect.y += getTextShiftOffset();
227            // fix #441-swingx - underline not painted for html
228            if (b.getModel().isRollover()) {
229                //paintUnderline(g, b, textRect, text);
230                if (ulv == null) {
231                    ulv = ULHtml.createHTMLView(b, text);
232                }
233                ulv.paint(g, textRect);
234            } else {
235                v.paint(g, textRect);
236            }
237            textRect.x -= getTextShiftOffset();
238            textRect.y -= getTextShiftOffset();
239        }
240    
241        /**
242         * {@inheritDoc} <p>
243         * Overridden to paint the underline on rollover.
244         */
245        @Override
246        protected void paintText(Graphics g, AbstractButton b, Rectangle textRect,
247                String text) {
248            //kgs -- SwingX #415: pixel-shift when disabled
249            //BasicButtonUI shifts disabled text to the left by 1 pixel
250            //we compensate for that here, so that all Hyperlinks paint
251            //at the same location regardless of state
252            if (!b.getModel().isEnabled()) {
253                textRect.x += 1;
254            }
255                
256            super.paintText(g, b, textRect, text);
257            if (b.getModel().isRollover()) {
258                paintUnderline(g, b, textRect, text);
259            }
260        }
261    
262        private void paintUnderline(Graphics g, AbstractButton b, Rectangle rect,
263                String text) {
264            // JW: copied from JXTable.LinkRenderer
265            FontMetrics fm = g.getFontMetrics();
266            int descent = fm.getDescent();
267    
268            // REMIND(aim): should we be basing the underline on
269            // the font's baseline instead of the text bounds?
270            g.drawLine(rect.x + getTextShiftOffset(),
271              (rect.y + rect.height) - descent + 1 + getTextShiftOffset(),
272              rect.x + rect.width + getTextShiftOffset(),
273              (rect.y + rect.height) - descent + 1 + getTextShiftOffset());
274        }
275    
276        @Override
277        protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect,
278                Rectangle textRect, Rectangle iconRect) {
279            if (b.getParent() instanceof JToolBar) {
280                // Windows doesn't draw the focus rect for buttons in a toolbar.
281                return;
282            }
283    
284            // focus painted same color as text
285            g.setColor(getFocusColor());
286            // paint the focus rect around the union of text rect and icon rect
287            // PENDING JW: duplicated to handle insets
288            Rectangle iconTextRect = getIconTextRect(b); 
289            // PENDING JW: better factor handling of insets - the bare union doesn't respect insets
290    //        Rectangle iconTextRect = textRect.union(iconRect);
291            BasicGraphicsUtils.drawDashedRect(g, iconTextRect.x, iconTextRect.y, 
292                    iconTextRect.width, iconTextRect.height);
293            // pre-#167-swingx: active area too large
294    //        int width = b.getWidth();
295    //        int height = b.getHeight();
296    //        BasicGraphicsUtils.drawDashedRect(g, dashedRectGapX, dashedRectGapY,
297    //                width - dashedRectGapWidth, height - dashedRectGapHeight);
298        }
299    
300        @Override
301        protected void paintButtonPressed(Graphics g, AbstractButton b) {
302            // setTextShiftOffset();
303        }
304    
305        
306        @Override
307        protected BasicButtonListener createButtonListener(AbstractButton b) {
308            return new BasicHyperlinkListener(b);
309        }
310    
311        /**
312         * {@inheritDoc} <p>
313         * 
314         * Overridden to return true if the position is inside the union of the
315         * text and icon rectangle, false otherwise.
316         */
317        @Override
318        public boolean contains(JComponent c, int x, int y) {
319            AbstractButton button = (AbstractButton) c;
320            return isInside(getIconTextRect(button), x, y);
321        }
322    
323        /**
324         * @param iconTextRect
325         * @param point
326         * @return
327         */
328        private boolean isInside(Rectangle iconTextRect, int x, int y) {
329            if (iconTextRect == null) return false;
330            return iconTextRect.contains(x, y);
331        }
332    
333        /**
334         * C&p'ed from BasicGraphicsUtils (getPreferredButtonSize).
335         * 
336         * @param b the button to analyse.
337         * @return the union of the text and icon rectangle of the AbstractButton
338         *   or null if the button has children (??) 
339         */
340        protected Rectangle getIconTextRect(AbstractButton b) {
341            if (b.getComponentCount() > 0) {
342                return null;
343            }
344    
345            Icon icon = (Icon) b.getIcon();
346            String text = b.getText();
347    
348            Font font = b.getFont();
349            FontMetrics fm = b.getFontMetrics(font);
350    
351            Rectangle iconR = new Rectangle();
352            Rectangle textR = new Rectangle();
353            Rectangle viewR = new Rectangle(b.getSize());
354    
355            SwingUtilities.layoutCompoundLabel((JComponent) b, fm, text, icon,
356                    b.getVerticalAlignment(), b.getHorizontalAlignment(), b
357                            .getVerticalTextPosition(), b
358                            .getHorizontalTextPosition(), viewR, iconR, textR,
359                    (text == null ? 0 : b.getIconTextGap()));
360    
361            /*
362             * The preferred size of the button is the size of the text and icon
363             * rectangles plus the buttons insets.
364             */
365    
366            Rectangle r = iconR.union(textR);
367    
368            Insets insets = b.getInsets();
369            r.width += insets.left + insets.right;
370            r.height += insets.top + insets.bottom;
371            // PENDING JW: why not?
372    //        r.x -= insets.left;
373            r.y -= insets.top;
374            return r;
375        }
376    
377        /**
378         * A BasicButtonListener specialized to the needs of a Hyperlink. 
379         * 
380         * @author Jeanette Winzenburg
381         */
382        public static class BasicHyperlinkListener extends BasicButtonListener {
383    
384            /**
385             * @param b
386             */
387            public BasicHyperlinkListener(AbstractButton b) {
388                super(b);
389            }
390    
391            
392            @Override
393            public void stateChanged(ChangeEvent e) {
394                AbstractButton button = (AbstractButton) e.getSource();
395                if (button.isRolloverEnabled()) {
396                    button.setCursor(button.getModel().isRollover() ? 
397                            // PENDING JW: support customizable cursor
398                            Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) : null);
399                }
400                super.stateChanged(e);
401            }
402        }
403        
404        static class ULHtml extends BasicHTML {
405            /**
406             * Create an html renderer for the given component and
407             * string of html.
408             */
409            public static View createHTMLView(JComponent c, String html) {
410                BasicEditorKit kit = getFactory();
411                Document doc = kit.createDefaultDocument(c.getFont(),
412                                                         c.getForeground());
413                Object base = c.getClientProperty(documentBaseKey);
414                if (base instanceof URL) {
415                    ((HTMLDocument)doc).setBase((URL)base);
416                }
417                Reader r = new StringReader(html);
418                try {
419                    kit.read(r, doc, 0);
420                } catch (Throwable e) {
421                }
422                ViewFactory f = kit.getViewFactory();
423                View hview = f.create(doc.getDefaultRootElement());
424                View v = new Renderer(c, f, hview);
425                return v;
426            }
427    
428            static BasicEditorKit getFactory() {
429                if (basicHTMLFactory == null) {
430                        basicHTMLViewFactory = new BasicHTMLViewFactory();
431                    basicHTMLFactory = new BasicEditorKit();
432                }
433                return basicHTMLFactory;
434            }
435    
436            /**
437             * The source of the html renderers
438             */
439            private static BasicEditorKit basicHTMLFactory;
440        
441            /**
442             * Creates the Views that visually represent the model.
443             */
444            private static ViewFactory basicHTMLViewFactory;
445        
446            /**
447             * Overrides to the default stylesheet.  Should consider
448             * just creating a completely fresh stylesheet.
449             */
450            private static final String styleChanges = 
451                "p { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0; text-decoration: underline }" +
452                "body { margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0; text-decoration: underline }"+
453                "font {text-decoration: underline}";
454    
455            static class BasicEditorKit extends HTMLEditorKit {
456                /** Shared base style for all documents created by us use. */
457                private static StyleSheet defaultStyles;
458        
459                /**
460                 * Overriden to return our own slimmed down style sheet.
461                 */
462                @Override
463                public StyleSheet getStyleSheet() {
464                    if (defaultStyles == null) {
465                    defaultStyles = new StyleSheet();
466                    StringReader r = new StringReader(styleChanges);
467                    try {
468                        defaultStyles.loadRules(r, null);
469                    } catch (Throwable e) {
470                        // don't want to die in static initialization... 
471                        // just display things wrong.
472                    }
473                    r.close();
474                    defaultStyles.addStyleSheet(super.getStyleSheet());
475                }
476                return defaultStyles;
477            }
478    
479            /**
480             * Sets the async policy to flush everything in one chunk, and
481             * to not display unknown tags.
482             */
483            public Document createDefaultDocument(Font defaultFont,
484                                                      Color foreground) {
485                StyleSheet styles = getStyleSheet();
486                StyleSheet ss = new StyleSheet();
487                ss.addStyleSheet(styles);
488                BasicDocument doc = new BasicDocument(ss, defaultFont, foreground);
489                doc.setAsynchronousLoadPriority(Integer.MAX_VALUE);
490                doc.setPreservesUnknownTags(false);
491                return doc;
492            }
493    
494                /**
495                 * Returns the ViewFactory that is used to make sure the Views don't
496                 * load in the background.
497                 */
498                @Override
499                public ViewFactory getViewFactory() {
500                    return basicHTMLViewFactory;
501                }
502            }
503    
504    
505            /**
506             * BasicHTMLViewFactory extends HTMLFactory to force images to be loaded
507             * synchronously.
508             */
509            static class BasicHTMLViewFactory extends HTMLEditorKit.HTMLFactory {
510                @Override
511                public View create(Element elem) {
512                    View view = super.create(elem);
513    
514                    if (view instanceof ImageView) {
515                        ((ImageView)view).setLoadsSynchronously(true);
516                    }
517                    return view;
518                }
519            }
520    
521    
522            /**
523             * The subclass of HTMLDocument that is used as the model. getForeground
524             * is overridden to return the foreground property from the Component this
525             * was created for.
526             */
527            static class BasicDocument extends HTMLDocument {
528            private static Class<?> clz;
529            private static Method displayPropertiesToCSS;
530    
531            /** The host, that is where we are rendering. */
532            // private JComponent host;
533            // --------- 1.5 x 1.6 incompatibility handling ....
534            static {
535                String j5 = "com.sun.java.swing.SwingUtilities2";
536                String j6 = "sun.swing.SwingUtilities2";
537                try {
538                    // assume 1.6
539                    clz = Class.forName(j6);
540                } catch (ClassNotFoundException e) {
541                    // or maybe not ..
542                    try {
543                        clz = Class.forName(j5);
544                    } catch (ClassNotFoundException e1) {
545                        throw new RuntimeException("Failed to find SwingUtilities2. Check the classpath.");
546                    }
547                }
548                try {
549                    displayPropertiesToCSS = clz.getMethod("displayPropertiesToCSS", new Class[] { Font.class, Color.class});
550                } catch (Exception e) {
551                    throw new RuntimeException("Failed to use SwingUtilities2. Check the permissions and class version.");
552                }
553            }
554    
555            private static String displayPropertiesToCSS(Font f, Color c) {
556                try {
557                    return (String) displayPropertiesToCSS.invoke(null, new Object[] { f, c });
558                } catch (Exception e) {
559                    throw new RuntimeException(e);
560                }
561            }
562    
563            // --------- EO 1.5 x 1.6 incompatibility handling ....
564    
565            BasicDocument(StyleSheet s, Font defaultFont, Color foreground) {
566                super(s);
567                setPreservesUnknownTags(false);
568                    setFontAndColor(defaultFont, foreground);
569            }
570    
571            /**
572             * Sets the default font and default color. These are set by
573             * adding a rule for the body that specifies the font and color.
574             * This allows the html to override these should it wish to have
575             * a custom font or color.
576             */
577            private void setFontAndColor(Font font, Color fg) {
578                    getStyleSheet().addRule(displayPropertiesToCSS(font,fg));
579            }
580        }
581    
582    
583        /**
584         * Root text view that acts as an HTML renderer.
585         */
586        static class Renderer extends View {
587    
588            Renderer(JComponent c, ViewFactory f, View v) {
589                    super(null);
590                host = c;
591                factory = f;
592                view = v;
593                view.setParent(this);
594                // initially layout to the preferred size
595                setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS));
596            }
597    
598            /**
599             * Fetches the attributes to use when rendering.  At the root
600             * level there are no attributes.  If an attribute is resolved
601             * up the view hierarchy this is the end of the line.
602             */
603                @Override
604                public AttributeSet getAttributes() {
605                return null;
606            }
607    
608            /**
609             * Determines the preferred span for this view along an axis.
610             *
611             * @param axis may be either X_AXIS or Y_AXIS
612             * @return the span the view would like to be rendered into.
613             *         Typically the view is told to render into the span
614             *         that is returned, although there is no guarantee.
615             *         The parent may choose to resize or break the view.
616             */
617            @Override
618            public float getPreferredSpan(int axis) {
619                if (axis == X_AXIS) {
620                    // width currently laid out to
621                    return width;
622                }
623                return view.getPreferredSpan(axis);
624            }
625    
626            /**
627             * Determines the minimum span for this view along an axis.
628             *
629             * @param axis may be either X_AXIS or Y_AXIS
630             * @return the span the view would like to be rendered into.
631             *         Typically the view is told to render into the span
632             *         that is returned, although there is no guarantee.
633             *         The parent may choose to resize or break the view.
634             */
635            @Override
636            public float getMinimumSpan(int axis) {
637                return view.getMinimumSpan(axis);
638            }
639    
640            /**
641             * Determines the maximum span for this view along an axis.
642             *
643             * @param axis may be either X_AXIS or Y_AXIS
644             * @return the span the view would like to be rendered into.
645             *         Typically the view is told to render into the span
646             *         that is returned, although there is no guarantee.
647             *         The parent may choose to resize or break the view.
648             */
649            @Override
650            public float getMaximumSpan(int axis) {
651                return Integer.MAX_VALUE;
652            }
653    
654            /**
655             * Specifies that a preference has changed.
656             * Child views can call this on the parent to indicate that
657             * the preference has changed.  The root view routes this to
658             * invalidate on the hosting component.
659             * <p>
660             * This can be called on a different thread from the
661             * event dispatching thread and is basically unsafe to
662             * propagate into the component.  To make this safe,
663             * the operation is transferred over to the event dispatching 
664             * thread for completion.  It is a design goal that all view
665             * methods be safe to call without concern for concurrency,
666             * and this behavior helps make that true.
667             *
668             * @param child the child view
669             * @param width true if the width preference has changed
670             * @param height true if the height preference has changed
671             */ 
672            @Override
673            public void preferenceChanged(View child, boolean width, boolean height) {
674                host.revalidate();
675                host.repaint();
676            }
677    
678            /**
679             * Determines the desired alignment for this view along an axis.
680             *
681             * @param axis may be either X_AXIS or Y_AXIS
682             * @return the desired alignment, where 0.0 indicates the origin
683             *     and 1.0 the full span away from the origin
684             */
685            @Override
686            public float getAlignment(int axis) {
687                return view.getAlignment(axis);
688            }
689    
690            /**
691             * Renders the view.
692             *
693             * @param g the graphics context
694             * @param allocation the region to render into
695             */
696            @Override
697            public void paint(Graphics g, Shape allocation) {
698                Rectangle alloc = allocation.getBounds();
699                view.setSize(alloc.width, alloc.height);
700                view.paint(g, allocation);
701            }
702            
703            /**
704             * Sets the view parent.
705             *
706             * @param parent the parent view
707             */
708            @Override
709            public void setParent(View parent) {
710                throw new Error("Can't set parent on root view");
711            }
712    
713            /** 
714             * Returns the number of views in this view.  Since
715             * this view simply wraps the root of the view hierarchy
716             * it has exactly one child.
717             *
718             * @return the number of views
719             * @see #getView
720             */
721            @Override
722            public int getViewCount() {
723                return 1;
724            }
725    
726            /** 
727             * Gets the n-th view in this container.
728             *
729             * @param n the number of the view to get
730             * @return the view
731             */
732            @Override
733            public View getView(int n) {
734                return view;
735            }
736    
737            /**
738             * Provides a mapping from the document model coordinate space
739             * to the coordinate space of the view mapped to it.
740             *
741             * @param pos the position to convert
742             * @param a the allocated region to render into
743             * @return the bounding box of the given position
744             */
745            @Override
746            public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
747                return view.modelToView(pos, a, b);
748            }
749    
750            /**
751             * Provides a mapping from the document model coordinate space
752             * to the coordinate space of the view mapped to it.
753             *
754             * @param p0 the position to convert >= 0
755             * @param b0 the bias toward the previous character or the
756             *  next character represented by p0, in case the 
757             *  position is a boundary of two views. 
758             * @param p1 the position to convert >= 0
759             * @param b1 the bias toward the previous character or the
760             *  next character represented by p1, in case the 
761             *  position is a boundary of two views. 
762             * @param a the allocated region to render into
763             * @return the bounding box of the given position is returned
764             * @exception BadLocationException  if the given position does
765             *   not represent a valid location in the associated document
766             * @exception IllegalArgumentException for an invalid bias argument
767             * @see View#viewToModel
768             */
769            @Override
770            public Shape modelToView(int p0, Position.Bias b0, int p1, 
771                         Position.Bias b1, Shape a) throws BadLocationException {
772                return view.modelToView(p0, b0, p1, b1, a);
773            }
774    
775            /**
776             * Provides a mapping from the view coordinate space to the logical
777             * coordinate space of the model.
778             *
779             * @param x x coordinate of the view location to convert
780             * @param y y coordinate of the view location to convert
781             * @param a the allocated region to render into
782             * @return the location within the model that best represents the
783             *    given point in the view
784             */
785            @Override
786            public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
787                return view.viewToModel(x, y, a, bias);
788            }
789    
790            /**
791             * Returns the document model underlying the view.
792             *
793             * @return the model
794             */
795            @Override
796            public Document getDocument() {
797                return view.getDocument();
798            }
799            
800            /**
801             * Returns the starting offset into the model for this view.
802             *
803             * @return the starting offset
804             */
805            @Override
806            public int getStartOffset() {
807                return view.getStartOffset();
808            }
809    
810            /**
811             * Returns the ending offset into the model for this view.
812             *
813             * @return the ending offset
814             */
815            @Override
816            public int getEndOffset() {
817                return view.getEndOffset();
818            }
819    
820            /**
821             * Gets the element that this view is mapped to.
822             *
823             * @return the view
824             */
825            @Override
826            public Element getElement() {
827                return view.getElement();
828            }
829    
830            /**
831             * Sets the view size.
832             *
833             * @param width the width
834             * @param height the height
835             */
836            @Override
837            public void setSize(float width, float height) {
838                this.width = (int) width;
839                view.setSize(width, height);
840            }
841    
842            /**
843             * Fetches the container hosting the view.  This is useful for
844             * things like scheduling a repaint, finding out the host 
845             * components font, etc.  The default implementation
846             * of this is to forward the query to the parent view.
847             *
848             * @return the container
849             */
850            @Override
851            public Container getContainer() {
852                return host;
853            }
854                
855            /**
856             * Fetches the factory to be used for building the
857             * various view fragments that make up the view that
858             * represents the model.  This is what determines
859             * how the model will be represented.  This is implemented
860             * to fetch the factory provided by the associated
861             * EditorKit.
862             *
863             * @return the factory
864             */
865            @Override
866            public ViewFactory getViewFactory() {
867                return factory;
868            }
869    
870            private int width;
871            private View view;
872            private ViewFactory factory;
873            private JComponent host;
874    
875            }
876        }
877    }