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 }