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}