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