1. /*
  2. * @(#)JViewport.java 1.87 01/02/09
  3. *
  4. * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing;
  11. import java.awt.*;
  12. import java.awt.event.*;
  13. import java.applet.Applet;
  14. import javax.swing.plaf.ViewportUI;
  15. import javax.swing.event.*;
  16. import javax.swing.border.*;
  17. import javax.accessibility.*;
  18. import java.io.Serializable;
  19. /**
  20. * The "viewport" or "porthole" through which you see the underlying
  21. * information. When you scroll, what moves is the viewport. It is like
  22. * peering through a camera's viewfinder. Moving the viewfinder upwards
  23. * brings new things into view at the top of the picture and loses
  24. * things that were at the bottom.
  25. * <p>
  26. * By default, <code>JViewport</code> is opaque. To change this, use the
  27. * <code>setOpaque</code> method.
  28. * <p>
  29. * <b>NOTE:</b>We have implemented a faster scrolling algorithm that
  30. * does not require a buffer to draw in. The algorithm works as follows:
  31. * <ol><li>The view and parent view and checked to see if they are
  32. * <code>JComponents</code>,
  33. * if they aren't, stop and repaint the whole viewport.
  34. * <li>If the viewport is obscured by an ancestor, stop and repaint the whole
  35. * viewport.
  36. * <li>Compute the region that will become visible, if it is as big as
  37. * the viewport, stop and repaint the whole view region.
  38. * <li>Obtain the ancestor Windows graphics and do a <code>copyArea</code>
  39. * on the scrolled region.
  40. * <li>Message the view to repaint the newly visible region.
  41. * <li>The next time paint is invoked on the viewport, if the clip region
  42. * is smaller than the viewport size a timer is kicked off to repaint the
  43. * whole region.
  44. * </ol>
  45. * In general this approach is much faster. Compared to the backing store
  46. * approach this avoids the overhead of maintaining an offscreen buffer and
  47. * having to do two <code>copyArea</code>s.
  48. * Compared to the non backing store case this
  49. * approach will greatly reduce the painted region.
  50. * <p>
  51. * This approach can cause slower times than the backing store approach
  52. * when the viewport is obscured by another window, or partially offscreen.
  53. * When another window
  54. * obscures the viewport the copyArea will copy garbage and a
  55. * paint event will be generated by the system to inform us we need to
  56. * paint the newly exposed region. The only way to handle this is to
  57. * repaint the whole viewport, which can cause slower performance than the
  58. * backing store case. In most applications very rarely will the user be
  59. * scrolling while the viewport is obscured by another window or offscreen,
  60. * so this optimization is usually worth the performance hit when obscured.
  61. * <strong>Warning:</strong>
  62. * Serialized objects of this class will not be compatible with
  63. * future Swing releases. The current serialization support is appropriate
  64. * for short term storage or RMI between applications running the same
  65. * version of Swing. A future release of Swing will provide support for
  66. * long term persistence.
  67. *
  68. * @version 1.87 02/09/01
  69. * @author Hans Muller
  70. * @author Philip Milne
  71. * @see JScrollPane
  72. */
  73. public class JViewport extends JComponent implements Accessible
  74. {
  75. /**
  76. * @see #getUIClassID
  77. * @see #readObject
  78. */
  79. private static final String uiClassID = "ViewportUI";
  80. /** Property used to indicate window blitting should not be done.
  81. */
  82. static final Object EnableWindowBlit = "EnableWindowBlit";
  83. /**
  84. * True when the viewport dimensions have been determined.
  85. * The default is false.
  86. */
  87. protected boolean isViewSizeSet = false;
  88. /**
  89. * The last <code>viewPosition</code> that we've painted, so we know how
  90. * much of the backing store image is valid.
  91. */
  92. protected Point lastPaintPosition = null;
  93. /**
  94. * True when this viewport is maintaining an offscreen image of its
  95. * contents, so that some scrolling can take place using fast "bit-blit"
  96. * operations instead of by accessing the view object to construct the
  97. * display. The default is false.
  98. *
  99. * @deprecated As of Java 2 platform v1.3
  100. * @see #setScrollMode
  101. */
  102. protected boolean backingStore = false;
  103. /** The view image used for a backing store. */
  104. transient protected Image backingStoreImage = null;
  105. /**
  106. * The <code>scrollUnderway</code> flag is used for components like
  107. * <code>JList</code>. When the downarrow key is pressed on a
  108. * <code>JList</code> and the selected
  109. * cell is the last in the list, the <code>scrollpane</code> autoscrolls.
  110. * Here, the old selected cell needs repainting and so we need
  111. * a flag to make the viewport do the optimized painting
  112. * only when there is an explicit call to
  113. * <code>setViewPosition(Point)</code>.
  114. * When <code>setBounds</code> is called through other routes,
  115. * the flag is off and the view repaints normally. Another approach
  116. * would be to remove this from the <code>JViewport</code>
  117. * class and have the <code>JList</code> manage this case by using
  118. * <code>setBackingStoreEnabled</code>. The default is false.
  119. */
  120. protected boolean scrollUnderway = false;
  121. /*
  122. * Listener that's notified each time the view changes size.
  123. */
  124. private ComponentListener viewListener = null;
  125. /* Only one <code>ChangeEvent</code> is needed per
  126. * <code>JViewport</code> instance since the
  127. * event's only (read-only) state is the source property. The source
  128. * of events generated here is always "this".
  129. */
  130. private transient ChangeEvent changeEvent = null;
  131. /**
  132. * Use <code>graphics.copyArea()</code> to implement scrolling.
  133. * This is the fastest for most applications.
  134. *
  135. * @see #setScrollMode
  136. * @since 1.3
  137. */
  138. public static final int BLIT_SCROLL_MODE = 1;
  139. /**
  140. * Draws viewport contents into an offscreen image.
  141. * This was previously the default mode for <code>JTable</code>.
  142. * This mode may offer advantages over "blit mode"
  143. * in some cases, but it requires a large chunk of extra RAM.
  144. *
  145. * @see #setScrollMode
  146. * @since 1.3
  147. */
  148. public static final int BACKINGSTORE_SCROLL_MODE = 2;
  149. /**
  150. * This mode uses the very simple method of redrawing the entire
  151. * contents of the scrollpane each time it is scrolled.
  152. * This was the default behavior in Swing 1.0 and Swing 1.1.
  153. * Either of the other two options will provide better performance
  154. * in most cases.
  155. *
  156. * @see #setScrollMode
  157. * @since 1.3
  158. */
  159. public static final int SIMPLE_SCROLL_MODE = 0;
  160. /**
  161. * @see #setScrollMode
  162. * @since 1.3
  163. */
  164. private int scrollMode = BLIT_SCROLL_MODE;
  165. //
  166. // Window blitting:
  167. //
  168. // As mentioned in the javadoc when using windowBlit a paint event
  169. // will be generated by the system if copyArea copies a non-visible
  170. // portion of the view (in other words, it copies garbage). We are
  171. // not guaranteed to receive the paint event before other mouse events,
  172. // so we can not be sure we haven't already copied garbage a bunch of
  173. // times to different parts of the view. For that reason when a blit
  174. // happens the ivar repaintAll is set to true. When paint is received
  175. // if repaintAll is true (we previously did a blit) it is set to
  176. // false, and if the clip region is smaller than the viewport
  177. // waitingForRepaint is set to true and a timer is started. When
  178. // the timer fires if waitingForRepaint is true, repaint is invoked.
  179. // In the mean time, if the view is asked to scroll and waitingForRepaint
  180. // is true, a blit will not happen, instead the non-backing store case
  181. // of scrolling will happen, which will reset waitingForRepaint.
  182. // waitingForRepaint is set to false in paint when the clip rect is
  183. // bigger (or equal) to the size of the viewport.
  184. // A Timer is used instead of just a repaint as it appeared to offer
  185. // better performance.
  186. /**
  187. * This is set to true in <code>setViewPosition</code>
  188. * if doing a window blit.
  189. */
  190. private transient boolean repaintAll;
  191. /**
  192. * This is set to true in paint, if <code>repaintAll</code>
  193. * is true and the clip rectangle does not match the bounds.
  194. * If true, and scrolling happens the
  195. * repaint manager is not cleared which then allows for the repaint
  196. * previously invoked to succeed.
  197. */
  198. private transient boolean waitingForRepaint;
  199. /**
  200. * Instead of directly invoking repaint, a <code>Timer</code>
  201. * is started and when it fires, repaint is invoked.
  202. */
  203. private transient Timer repaintTimer;
  204. /** Creates a <code>JViewport</code>. */
  205. public JViewport() {
  206. super();
  207. updateUI();
  208. setLayout(createLayoutManager());
  209. setOpaque(true);
  210. }
  211. /**
  212. * Returns the L&F object that renders this component.
  213. *
  214. * @return ViewportUI object
  215. */
  216. public ViewportUI getUI() {
  217. return (ViewportUI)ui;
  218. }
  219. /**
  220. * Sets the L&F object that renders this component.
  221. *
  222. * @param ui the <code>ViewportUI</code> L&F object
  223. * @see UIDefaults#getUI
  224. * @beaninfo
  225. * expert: true
  226. * description: The L&F object that renders this component.
  227. */
  228. public void setUI(ViewportUI ui) {
  229. super.setUI(ui);
  230. }
  231. /**
  232. * Notification from the <code>UIFactory</code> that the L&F
  233. * has changed.
  234. *
  235. * @see JComponent#updateUI
  236. */
  237. public void updateUI() {
  238. setUI((ViewportUI)UIManager.getUI(this));
  239. }
  240. /**
  241. * Returns a string that specifies the name of the L&F class
  242. * that renders this component.
  243. *
  244. * @return the string "ViewportUI"
  245. *
  246. * @see JComponent#getUIClassID
  247. * @see UIDefaults#getUI
  248. */
  249. public String getUIClassID() {
  250. return uiClassID;
  251. }
  252. /**
  253. * Sets the <code>JViewport</code>s one lightweight child,
  254. * which can be <code>null</code>.
  255. * (Since there is only one child which occupies the entire viewport,
  256. * the constraints and index arguments are ignored.)
  257. *
  258. * @param child the lightweight <code>child</code> of the viewport
  259. * @param constraints the <code>constraints</code> to be respected
  260. * @param index the index
  261. * @see #setView
  262. */
  263. protected void addImpl(Component child, Object constraints, int index) {
  264. setView(child);
  265. }
  266. /**
  267. * Removes the <code>Viewport</code>s one lightweight child.
  268. *
  269. * @see #setView
  270. */
  271. public void remove(Component child) {
  272. child.removeComponentListener(viewListener);
  273. super.remove(child);
  274. }
  275. /**
  276. * Overridden to scroll the view so that <code>Rectangle</code>
  277. * within the view becomes visible.
  278. *
  279. * @param contentRect the <code>Rectangle</code> to display
  280. */
  281. public void scrollRectToVisible(Rectangle contentRect) {
  282. Component view = getView();
  283. if (view == null) {
  284. return;
  285. } else {
  286. if (!view.isValid()) {
  287. // If the view is not valid, validate. scrollRectToVisible
  288. // may fail if the view is not valid first, contentRect
  289. // could be bigger than invalid size.
  290. validateView();
  291. }
  292. int dx = 0, dy = 0;
  293. dx = positionAdjustment(getWidth(), contentRect.width, contentRect.x);
  294. dy = positionAdjustment(getHeight(), contentRect.height, contentRect.y);
  295. if (dx != 0 || dy != 0) {
  296. Point viewPosition = getViewPosition();
  297. Dimension viewSize = view.getSize();
  298. int startX = viewPosition.x;
  299. int startY = viewPosition.y;
  300. Dimension extent = getExtentSize();
  301. viewPosition.x -= dx;
  302. if (viewPosition.x + extent.width > viewSize.width) {
  303. viewPosition.x = Math.max(0,viewSize.width - extent.width);
  304. }
  305. else if (viewPosition.x < 0) {
  306. viewPosition.x = 0;
  307. }
  308. viewPosition.y -= dy;
  309. if (viewPosition.y + extent.height > viewSize.height) {
  310. viewPosition.y = Math.max(0, viewSize.height -
  311. extent.height);
  312. }
  313. else if (viewPosition.y < 0) {
  314. viewPosition.y = 0;
  315. }
  316. if (viewPosition.x != startX || viewPosition.y != startY) {
  317. setViewPosition(viewPosition);
  318. // NOTE: How JViewport currently works with the
  319. // backing store is not foolproof. The sequence of
  320. // events when setViewPosition
  321. // (scrollRectToVisible) is called is to reset the
  322. // views bounds, which causes a repaint on the
  323. // visible region and sets an ivar indicating
  324. // scrolling (scrollUnderway). When
  325. // JViewport.paint is invoked if scrollUnderway is
  326. // true, the backing store is blitted. This fails
  327. // if between the time setViewPosition is invoked
  328. // and paint is received another repaint is queued
  329. // indicating part of the view is invalid. There
  330. // is no way for JViewport to notice another
  331. // repaint has occured and it ends up blitting
  332. // what is now a dirty region and the repaint is
  333. // never delivered.
  334. // It just so happens JTable encounters this
  335. // behavior by way of scrollRectToVisible, for
  336. // this reason scrollUnderway is set to false
  337. // here, which effectively disables the backing
  338. // store.
  339. scrollUnderway = false;
  340. }
  341. }
  342. }
  343. }
  344. /**
  345. * This will ascend the <code>Viewport</code>s parents stopping when
  346. * a component is
  347. * found that returns true to <code>isValidateRoot</code>.
  348. * If all the <code>Component</code>s parents are visible,
  349. * <code>validate</code> will then be invoked on it. The
  350. * <code>RepaintManager</code> is then invoked with
  351. * <code>removeInvalidComponent</code>. This
  352. * is the synchronous version of a <code>revalidate</code>.
  353. */
  354. private void validateView() {
  355. Component validateRoot = null;
  356. /* Find the first JComponent ancestor of this component whose
  357. * isValidateRoot() method returns true.
  358. */
  359. for(Component c = this; c != null; c = c.getParent()) {
  360. if ((c instanceof CellRendererPane) || (c.getPeer() == null)) {
  361. return;
  362. }
  363. if ((c instanceof JComponent) &&
  364. (((JComponent)c).isValidateRoot())) {
  365. validateRoot = c;
  366. break;
  367. }
  368. }
  369. // If no validateRoot, nothing to validate from.
  370. if (validateRoot == null) {
  371. return;
  372. }
  373. // Make sure all ancestors are visible.
  374. Component root = null;
  375. for(Component c = validateRoot; c != null; c = c.getParent()) {
  376. if (!c.isVisible() || (c.getPeer() == null)) {
  377. return;
  378. }
  379. if ((c instanceof Window) || (c instanceof Applet)) {
  380. root = c;
  381. break;
  382. }
  383. }
  384. // Make sure there is a Window ancestor.
  385. if (root == null) {
  386. return;
  387. }
  388. // Validate the root.
  389. validateRoot.validate();
  390. // And let the RepaintManager it does not have to validate from
  391. // validateRoot anymore.
  392. RepaintManager rm = RepaintManager.currentManager(this);
  393. if (rm != null) {
  394. rm.removeInvalidComponent((JComponent)validateRoot);
  395. }
  396. }
  397. /* This method is used by the scrollToRect method to determine the
  398. * proper direction and amount to move by. The integer variables are named
  399. * width, but this method is applicable to height also. The code assumes that
  400. * parentWidth/childWidth are positive and childAt can be negative.
  401. */
  402. private int positionAdjustment(int parentWidth, int childWidth, int childAt) {
  403. // +-----+
  404. // | --- | No Change
  405. // +-----+
  406. if (childAt >= 0 && childWidth + childAt <= parentWidth) {
  407. return 0;
  408. }
  409. // +-----+
  410. // --------- No Change
  411. // +-----+
  412. if (childAt <= 0 && childWidth + childAt >= parentWidth) {
  413. return 0;
  414. }
  415. // +-----+ +-----+
  416. // | ---- -> | ----|
  417. // +-----+ +-----+
  418. if (childAt > 0 && childWidth <= parentWidth) {
  419. return -childAt + parentWidth - childWidth;
  420. }
  421. // +-----+ +-----+
  422. // | -------- -> |--------
  423. // +-----+ +-----+
  424. if (childAt >= 0 && childWidth >= parentWidth) {
  425. return -childAt;
  426. }
  427. // +-----+ +-----+
  428. // ---- | -> |---- |
  429. // +-----+ +-----+
  430. if (childAt <= 0 && childWidth <= parentWidth) {
  431. return -childAt;
  432. }
  433. // +-----+ +-----+
  434. //-------- | -> --------|
  435. // +-----+ +-----+
  436. if (childAt < 0 && childWidth >= parentWidth) {
  437. return -childAt + parentWidth - childWidth;
  438. }
  439. return 0;
  440. }
  441. /**
  442. * The viewport "scrolls" it's child (called the "view") by the
  443. * normal parent/child clipping (typically the view is moved in
  444. * the opposite direction of the scroll). A non-<code>null</code> border,
  445. * or non-zero insets, isn't supported, to prevent the geometry
  446. * of this component from becoming complex enough to inhibit
  447. * subclassing. To create a <code>JViewport</code> with a border,
  448. * add it to a <code>JPanel</code> that has a border.
  449. * <p>Note: If <code>border</code> is non-<code>null</code>, this
  450. * method will throw an exception as borders are not supported on
  451. * a <code>JViewPort</code>.
  452. *
  453. * @param border the <code>Border</code> to set
  454. * @exception IllegalArgumentException this method is not implemented
  455. */
  456. public final void setBorder(Border border) {
  457. if (border != null) {
  458. throw new IllegalArgumentException("JViewport.setBorder() not supported");
  459. }
  460. }
  461. /**
  462. * Returns the insets (border) dimensions as (0,0,0,0), since borders
  463. * are not supported on a <code>JViewport</code>.
  464. *
  465. * @return a <code>Rectange</code> of zero dimension and zero origin
  466. * @see #setBorder
  467. */
  468. public final Insets getInsets() {
  469. return new Insets(0, 0, 0, 0);
  470. }
  471. /**
  472. * Returns an <code>Insets</code> object containing this
  473. * <code>JViewport</code>s inset values. The passed-in
  474. * <code>Insets</code> object will be reinitialized, and
  475. * all existing values within this object are overwritten.
  476. *
  477. * @param insets the <code>Insets</code> object which can be reused
  478. * @return this viewports inset values
  479. * @see #getInsets
  480. * @beaninfo
  481. * expert: true
  482. */
  483. public final Insets getInsets(Insets insets) {
  484. insets.left = insets.top = insets.right = insets.bottom = 0;
  485. return insets;
  486. }
  487. private Graphics getBackingStoreGraphics(Graphics g) {
  488. Graphics bsg = backingStoreImage.getGraphics();
  489. bsg.setColor(g.getColor());
  490. bsg.setFont(g.getFont());
  491. bsg.setClip(g.getClipBounds());
  492. return bsg;
  493. }
  494. private void paintViaBackingStore(Graphics g) {
  495. Graphics bsg = getBackingStoreGraphics(g);
  496. try {
  497. super.paint(bsg);
  498. g.drawImage(backingStoreImage, 0, 0, this);
  499. } finally {
  500. bsg.dispose();
  501. }
  502. }
  503. private void paintViaBackingStore(Graphics g, Rectangle oClip) {
  504. Graphics bsg = getBackingStoreGraphics(g);
  505. try {
  506. super.paint(bsg);
  507. g.setClip(oClip);
  508. g.drawImage(backingStoreImage, 0, 0, this);
  509. } finally {
  510. bsg.dispose();
  511. }
  512. }
  513. /**
  514. * The <code>JViewport</code> overrides the default implementation of
  515. * this method (in <code>JComponent</code>) to return false.
  516. * This ensures
  517. * that the drawing machinery will call the <code>Viewport</code>s
  518. * <code>paint</code>
  519. * implementation rather than messaging the <code>JViewport</code>s
  520. * children directly.
  521. *
  522. * @return false
  523. */
  524. public boolean isOptimizedDrawingEnabled() {
  525. return false;
  526. }
  527. /**
  528. * Only used by the paint method below.
  529. */
  530. private Point getViewLocation() {
  531. Component view = getView();
  532. if (view != null) {
  533. return view.getLocation();
  534. }
  535. else {
  536. return new Point(0,0);
  537. }
  538. }
  539. /**
  540. * Depending on whether the <code>backingStore</code> is enabled,
  541. * either paint the image through the backing store or paint
  542. * just the recently exposed part, using the backing store
  543. * to "blit" the remainder.
  544. * <blockquote>
  545. * The term "blit" is the pronounced version of the PDP-10
  546. * BLT (BLock Transfer) instruction, which copied a block of
  547. * bits. (In case you were curious.)
  548. * </blockquote>
  549. *
  550. * @param g the <code>Graphics</code> context within which to paint
  551. */
  552. public void paint(Graphics g)
  553. {
  554. int width = getWidth();
  555. int height = getHeight();
  556. if ((width <= 0) || (height <= 0)) {
  557. return;
  558. }
  559. if (repaintAll) {
  560. repaintAll = false;
  561. Rectangle clipB = g.getClipBounds();
  562. if (clipB.width < getWidth() ||
  563. clipB.height < getHeight()) {
  564. waitingForRepaint = true;
  565. if (repaintTimer == null) {
  566. repaintTimer = createRepaintTimer();
  567. }
  568. repaintTimer.stop();
  569. repaintTimer.start();
  570. // We really don't need to paint, a future repaint will
  571. // take care of it, but if we don't we get an ugly flicker.
  572. }
  573. else {
  574. if (repaintTimer != null) {
  575. repaintTimer.stop();
  576. }
  577. waitingForRepaint = false;
  578. }
  579. }
  580. else if (waitingForRepaint) {
  581. // Need a complete repaint before resetting waitingForRepaint
  582. Rectangle clipB = g.getClipBounds();
  583. if (clipB.width >= getWidth() &&
  584. clipB.height >= getHeight()) {
  585. waitingForRepaint = false;
  586. repaintTimer.stop();
  587. }
  588. }
  589. if (!backingStore || isBlitting() || getView() == null) {
  590. super.paint(g);
  591. lastPaintPosition = getViewLocation();
  592. return;
  593. }
  594. // If the view is smaller than the viewport and we are not opaque
  595. // (that is, we won't paint our background), we should set the
  596. // clip. Otherwise, as the bounds of the view vary, we will
  597. // blit garbage into the exposed areas.
  598. Rectangle viewBounds = getView().getBounds();
  599. if (!isOpaque()) {
  600. g.clipRect(0, 0, viewBounds.width, viewBounds.height);
  601. }
  602. if (backingStoreImage == null) {
  603. // Backing store is enabled but this is the first call to paint.
  604. // Create the backing store, paint it and then copy to g.
  605. // The backing store image will be created with the size of
  606. // the viewport. We must make sure the clip region is the
  607. // same size, otherwise when scrolling the backing image
  608. // the region outside of the clipped region will not be painted,
  609. // and result in empty areas.
  610. backingStoreImage = createImage(width, height);
  611. Rectangle clip = g.getClipBounds();
  612. if (clip.width != width || clip.height != height) {
  613. if (!isOpaque()) {
  614. g.setClip(0, 0, Math.min(viewBounds.width, width),
  615. Math.min(viewBounds.height, height));
  616. }
  617. else {
  618. g.setClip(0, 0, width, height);
  619. }
  620. paintViaBackingStore(g, clip);
  621. }
  622. else {
  623. paintViaBackingStore(g);
  624. }
  625. }
  626. else {
  627. if (!scrollUnderway || lastPaintPosition.equals(getViewLocation())) {
  628. // No scrolling happened: repaint required area via backing store.
  629. paintViaBackingStore(g);
  630. } else {
  631. // The image was scrolled. Manipulate the backing store and flush it to g.
  632. Point blitFrom = new Point();
  633. Point blitTo = new Point();
  634. Dimension blitSize = new Dimension();
  635. Rectangle blitPaint = new Rectangle();
  636. Point newLocation = getViewLocation();
  637. int dx = newLocation.x - lastPaintPosition.x;
  638. int dy = newLocation.y - lastPaintPosition.y;
  639. boolean canBlit = computeBlit(dx, dy, blitFrom, blitTo, blitSize, blitPaint);
  640. if (!canBlit) {
  641. // The image was either moved diagonally or
  642. // moved by more than the image size: paint normally.
  643. paintViaBackingStore(g);
  644. } else {
  645. int bdx = blitTo.x - blitFrom.x;
  646. int bdy = blitTo.y - blitFrom.y;
  647. // Move the relevant part of the backing store.
  648. Rectangle clip = g.getClipBounds();
  649. // We don't want to inherit the clip region when copying
  650. // bits, if it is inherited it will result in not moving
  651. // all of the image resulting in garbage appearing on
  652. // the screen.
  653. g.setClip(0, 0, width, height);
  654. Graphics bsg = getBackingStoreGraphics(g);
  655. try {
  656. bsg.copyArea(blitFrom.x, blitFrom.y, blitSize.width, blitSize.height, bdx, bdy);
  657. g.setClip(clip.x, clip.y, clip.width, clip.height);
  658. // Paint the rest of the view; the part that has just been exposed.
  659. Rectangle r = viewBounds.intersection(blitPaint);
  660. bsg.setClip(r);
  661. super.paint(bsg);
  662. // Copy whole of the backing store to g.
  663. g.drawImage(backingStoreImage, 0, 0, this);
  664. } finally {
  665. bsg.dispose();
  666. }
  667. }
  668. }
  669. }
  670. lastPaintPosition = getViewLocation();
  671. scrollUnderway = false;
  672. }
  673. /**
  674. * Sets the bounds of this viewport. If the viewports width
  675. * or height has changed, fire a <code>StateChanged</code> event.
  676. *
  677. * @param x left edge of the origin
  678. * @param y top edge of the origin
  679. * @param w width in pixels
  680. * @param h height in pixels
  681. *
  682. * @see JComponent#reshape(int, int, int, int)
  683. */
  684. public void reshape(int x, int y, int w, int h) {
  685. boolean sizeChanged = (getWidth() != w) || (getHeight() != h);
  686. if (sizeChanged) {
  687. backingStoreImage = null;
  688. }
  689. super.reshape(x, y, w, h);
  690. if (sizeChanged) {
  691. fireStateChanged();
  692. }
  693. }
  694. /**
  695. * Used to control the method of scrolling the viewport contents.
  696. * You may want to change this mode to get maximum performance for your
  697. * use case.
  698. *
  699. * @param mode one of the following values:
  700. * <ul>
  701. * <li> JViewport.BLIT_SCROLL_MODE
  702. * <li> JViewport.BACKINGSTORE_SCROLL_MODE
  703. * <li> JViewport.SIMPLE_SCROLL_MODE
  704. * </ul>
  705. *
  706. * @see #BLIT_SCROLL_MODE
  707. * @see #BACKINGSTORE_SCROLL_MODE
  708. * @see #SIMPLE_SCROLL_MODE
  709. *
  710. * @beaninfo
  711. * bound: false
  712. * description: Method of moving contents for incremental scrolls.
  713. * enum: BLIT_SCROLL_MODE JViewport.BLIT_SCROLL_MODE
  714. * BACKINGSTORE_SCROLL_MODE JViewport.BACKINGSTORE_SCROLL_MODE
  715. * SIMPLE_SCROLL_MODE JViewport.SIMPLE_SCROLL_MODE
  716. *
  717. * @since 1.3
  718. */
  719. public void setScrollMode(int mode) {
  720. scrollMode = mode;
  721. if (mode == BACKINGSTORE_SCROLL_MODE) {
  722. backingStore = true;
  723. } else {
  724. backingStore = false;
  725. }
  726. }
  727. /**
  728. * Returns the current scrolling mode.
  729. *
  730. * @return the <code>scrollMode</code> property
  731. * @see #setScrollMode
  732. * @since 1.3
  733. */
  734. public int getScrollMode() {
  735. return scrollMode;
  736. }
  737. /**
  738. * Returns true if this viewport is maintaining an offscreen
  739. * image of its contents.
  740. * @return true if <code>scrollMode</code> is BACKINGSTORE_SCROLL_MODE
  741. *
  742. * @deprecated As of Java 2 platform v1.3, replaced by
  743. * <code>getScrollMode()</code>.
  744. */
  745. public boolean isBackingStoreEnabled() {
  746. return scrollMode == BACKINGSTORE_SCROLL_MODE;
  747. }
  748. /**
  749. * If true if this viewport will maintain an offscreen
  750. * image of its contents. The image is used to reduce the cost
  751. * of small one dimensional changes to the <code>viewPosition</code>.
  752. * Rather than repainting the entire viewport we use
  753. * <code>Graphics.copyArea()</code> to effect some of the scroll.
  754. * @param enabled if true, maintain an offscreen backing store
  755. *
  756. * @deprecated As of Java 2 platform v1.3, replaced by
  757. * <code>setScrollMode()</code>.
  758. */
  759. public void setBackingStoreEnabled(boolean enabled) {
  760. if (enabled) {
  761. setScrollMode(BACKINGSTORE_SCROLL_MODE);
  762. } else {
  763. setScrollMode(BLIT_SCROLL_MODE);
  764. }
  765. }
  766. private final boolean isBlitting() {
  767. Component view = getView();
  768. return (scrollMode == BLIT_SCROLL_MODE) &&
  769. (view instanceof JComponent) && ((JComponent)view).isOpaque();
  770. }
  771. /**
  772. * Returns the <code>JViewport</code>s one child or <code>null</code>.
  773. * @return the viewports child, or <code>null</code> if none exists
  774. *
  775. * @see #setView
  776. */
  777. public Component getView() {
  778. try {
  779. return getComponent(0);
  780. } catch (ArrayIndexOutOfBoundsException e) {
  781. return null;
  782. }
  783. }
  784. /**
  785. * Sets the <code>JViewport</code>s one lightweight child
  786. * (<code>view</code>), which can be <code>null</code>.
  787. * @param view the viewports new lightweight child
  788. *
  789. * @see #getView
  790. */
  791. public void setView(Component view) {
  792. /* Remove the viewport's existing children, if any.
  793. * Note that removeAll() isn't used here because it
  794. * doesn't call remove() (which JViewport overrides).
  795. */
  796. int n = getComponentCount();
  797. for(int i = n - 1; i >= 0; i--) {
  798. remove(i);
  799. }
  800. isViewSizeSet = false;
  801. if (view != null) {
  802. super.addImpl(view, null, -1);
  803. viewListener = createViewListener();
  804. view.addComponentListener(viewListener);
  805. }
  806. revalidate();
  807. repaint();
  808. }
  809. /**
  810. * If the view's size hasn't been explicitly set, return the
  811. * preferred size, otherwise return the view's current size.
  812. * If there is no view, return 0,0.
  813. *
  814. * @return a <code>Dimension</code> object specifying the size of the view
  815. */
  816. public Dimension getViewSize() {
  817. Component view = getView();
  818. if (view == null) {
  819. return new Dimension(0,0);
  820. }
  821. else if (isViewSizeSet) {
  822. return view.getSize();
  823. }
  824. else {
  825. return view.getPreferredSize();
  826. }
  827. }
  828. /**
  829. * Sets the view coordinates that appear in the upper left
  830. * hand corner of the viewport, and the size of the view.
  831. *
  832. * @param newSize a <code>Dimension</code> object specifying the size and
  833. * location of the new view coordinates,
  834. * or <code>null</code> if there
  835. * is no view
  836. */
  837. public void setViewSize(Dimension newSize) {
  838. Component view = getView();
  839. if (view != null) {
  840. Dimension oldSize = view.getSize();
  841. if (!newSize.equals(oldSize)) {
  842. // scrollUnderway will be true if this is invoked as the
  843. // result of a validate and setViewPosition was previously
  844. // invoked.
  845. scrollUnderway = false;
  846. view.setSize(newSize);
  847. isViewSizeSet = true;
  848. fireStateChanged();
  849. }
  850. }
  851. }
  852. /**
  853. * Returns the view coordinates that appear in the upper left
  854. * hand corner of the viewport, or 0,0 if there's no view.
  855. *
  856. * @return a <code>Point</code> object giving the upper left coordinates
  857. */
  858. public Point getViewPosition() {
  859. Component view = getView();
  860. if (view != null) {
  861. Point p = view.getLocation();
  862. p.x = -p.x;
  863. p.y = -p.y;
  864. return p;
  865. }
  866. else {
  867. return new Point(0,0);
  868. }
  869. }
  870. /**
  871. * Sets the view coordinates that appear in the upper left
  872. * hand corner of the viewport, does nothing if there's no view.
  873. *
  874. * @param p a <code>Point</code> object giving the upper left coordinates
  875. */
  876. public void setViewPosition(Point p)
  877. {
  878. Component view = getView();
  879. if (view == null) {
  880. return;
  881. }
  882. int oldX, oldY, x = p.x, y = p.y;
  883. /* Collect the old x,y values for the views location
  884. * and do the song and dance to avoid allocating
  885. * a Rectangle object if we don't have to.
  886. */
  887. if (view instanceof JComponent) {
  888. JComponent c = (JComponent)view;
  889. oldX = c.getX();
  890. oldY = c.getY();
  891. }
  892. else {
  893. Rectangle r = view.getBounds();
  894. oldX = r.x;
  895. oldY = r.y;
  896. }
  897. /* The view scrolls in the opposite direction to mouse
  898. * movement.
  899. */
  900. int newX = -x;
  901. int newY = -y;
  902. if ((oldX != newX) || (oldY != newY)) {
  903. if (!waitingForRepaint && isBlitting() && canUseWindowBlitter()) {
  904. Graphics g = getGraphics();
  905. flushViewDirtyRegion(g);
  906. // This calls setBounds(), and then repaint().
  907. view.setLocation(newX, newY);
  908. // The cast to JComponent here is valid, if view is not
  909. // a JComponent, isBlitting will return false.
  910. g.setClip(0,0,getWidth(), Math.min(getHeight(),
  911. ((JComponent)view).getHeight()));
  912. // Forces a repaint of the whole component on the next
  913. // call to paint.
  914. repaintAll = windowBlitPaint(g);
  915. g.dispose();
  916. RepaintManager rm = RepaintManager.currentManager(this);
  917. rm.markCompletelyClean((JComponent)getParent());
  918. rm.markCompletelyClean(this);
  919. rm.markCompletelyClean((JComponent)view);
  920. }
  921. else {
  922. scrollUnderway = true;
  923. // This calls setBounds(), and then repaint().
  924. view.setLocation(newX, newY);
  925. repaintAll = false;
  926. }
  927. fireStateChanged();
  928. }
  929. }
  930. /**
  931. * Returns a rectangle whose origin is <code>getViewPosition</code>
  932. * and size is <code>getExtentSize</code>.
  933. * This is the visible part of the view, in view coordinates.
  934. *
  935. * @return a <code>Rectangle</code> giving the visible part of
  936. * the view using view coordinates.
  937. */
  938. public Rectangle getViewRect() {
  939. return new Rectangle(getViewPosition(), getExtentSize());
  940. }
  941. /**
  942. * Computes the parameters for a blit where the backing store image
  943. * currently contains <code>oldLoc</code> in the upper left hand corner
  944. * and we're scrolling to <code>newLoc</code>.
  945. * The parameters are modified
  946. * to return the values required for the blit.
  947. * @param dx the horizontal delta
  948. * @param dy the vertical delta
  949. * @param blitFrom the <code>Point</code> we're blitting from
  950. * @param blitTo the <code>Point</code> we're blitting to
  951. * @param blitSize the <code>Dimension</code> of the area to blit
  952. * @param blitPaint the area to blit
  953. * @return true if the parameters are modified and we're ready to blit;
  954. * false otherwise
  955. */
  956. protected boolean computeBlit(
  957. int dx,
  958. int dy,
  959. Point blitFrom,
  960. Point blitTo,
  961. Dimension blitSize,
  962. Rectangle blitPaint)
  963. {
  964. int dxAbs = Math.abs(dx);
  965. int dyAbs = Math.abs(dy);
  966. Dimension extentSize = getExtentSize();
  967. if ((dx == 0) && (dy != 0) && (dyAbs < extentSize.height)) {
  968. if (dy < 0) {
  969. blitFrom.y = -dy;
  970. blitTo.y = 0;
  971. blitPaint.y = extentSize.height + dy;
  972. }
  973. else {
  974. blitFrom.y = 0;
  975. blitTo.y = dy;
  976. blitPaint.y = 0;
  977. }
  978. blitPaint.x = blitFrom.x = blitTo.x = 0;
  979. blitSize.width = extentSize.width;
  980. blitSize.height = extentSize.height - dyAbs;
  981. blitPaint.width = extentSize.width;
  982. blitPaint.height = dyAbs;
  983. return true;
  984. }
  985. else if ((dy == 0) && (dx != 0) && (dxAbs < extentSize.width)) {
  986. if (dx < 0) {
  987. blitFrom.x = -dx;
  988. blitTo.x = 0;
  989. blitPaint.x = extentSize.width + dx;
  990. }
  991. else {
  992. blitFrom.x = 0;
  993. blitTo.x = dx;
  994. blitPaint.x = 0;
  995. }
  996. blitPaint.y = blitFrom.y = blitTo.y = 0;
  997. blitSize.width = extentSize.width - dxAbs;
  998. blitSize.height = extentSize.height;
  999. blitPaint.y = 0;
  1000. blitPaint.width = dxAbs;
  1001. blitPaint.height = extentSize.height;
  1002. return true;
  1003. }
  1004. else {
  1005. return false;
  1006. }
  1007. }
  1008. /**
  1009. * Returns the size of the visible part of the view in view coordinates.
  1010. *
  1011. * @return a <code>Dimension</code> object giving the size of the view
  1012. */
  1013. public Dimension getExtentSize() {
  1014. return getSize();
  1015. }
  1016. /**
  1017. * Converts a size in pixel coordinates to view coordinates.
  1018. * Subclasses of viewport that support "logical coordinates"
  1019. * will override this method.
  1020. *
  1021. * @param size a <code>Dimension</code> object using pixel coordinates
  1022. * @return a <code>Dimension</code> object converted to view coordinates
  1023. */
  1024. public Dimension toViewCoordinates(Dimension size) {
  1025. return new Dimension(size);
  1026. }
  1027. /**
  1028. * Converts a point in pixel coordinates to view coordinates.
  1029. * Subclasses of viewport that support "logical coordinates"
  1030. * will override this method.
  1031. *
  1032. * @param p a <code>Point</code> object using pixel coordinates
  1033. * @return a <code>Point</code> object converted to view coordinates
  1034. */
  1035. public Point toViewCoordinates(Point p) {
  1036. return new Point(p);
  1037. }
  1038. /**
  1039. * Sets the size of the visible part of the view using view coordinates.
  1040. *
  1041. * @param newExtent a <code>Dimension</code> object specifying
  1042. * the size of the view
  1043. */
  1044. public void setExtentSize(Dimension newExtent) {
  1045. Dimension oldExtent = getExtentSize();
  1046. if (!newExtent.equals(oldExtent)) {
  1047. setSize(newExtent);
  1048. fireStateChanged();
  1049. }
  1050. }
  1051. /**
  1052. * A listener for the view.
  1053. * <p>
  1054. * <strong>Warning:</strong>
  1055. * Serialized objects of this class will not be compatible with
  1056. * future Swing releases. The current serialization support is appropriate
  1057. * for short term storage or RMI between applications running the same
  1058. * version of Swing. A future release of Swing will provide support for
  1059. * long term persistence.
  1060. */
  1061. protected class ViewListener extends ComponentAdapter implements Serializable
  1062. {
  1063. public void componentResized(ComponentEvent e) {
  1064. fireStateChanged();
  1065. }
  1066. }
  1067. /**
  1068. * Creates a listener for the view.
  1069. * @return a <code>ViewListener</code>
  1070. */
  1071. protected ViewListener createViewListener() {
  1072. return new ViewListener();
  1073. }
  1074. /**
  1075. * Subclassers can override this to install a different
  1076. * layout manager (or <code>null</code>) in the constructor. Returns
  1077. * a new <code>ViewportLayout</code> object.
  1078. *
  1079. * @return a <code>LayoutManager</code>
  1080. */
  1081. protected LayoutManager createLayoutManager() {
  1082. return new ViewportLayout();
  1083. }
  1084. /**
  1085. * Adds a <code>ChangeListener</code> to the list that is
  1086. * notified each time the view's
  1087. * size, position, or the viewport's extent size has changed.
  1088. *
  1089. * @param l the <code>ChangeListener</code> to add
  1090. * @see #removeChangeListener
  1091. * @see #setViewPosition
  1092. * @see #setViewSize
  1093. * @see #setExtentSize
  1094. */
  1095. public void addChangeListener(ChangeListener l) {
  1096. listenerList.add(ChangeListener.class, l);
  1097. }
  1098. /**
  1099. * Removes a <code>ChangeListener</code> from the list that's notified each
  1100. * time the views size, position, or the viewports extent size
  1101. * has changed.
  1102. *
  1103. * @param l the <code>ChangeListener</code> to remove
  1104. * @see #addChangeListener
  1105. */
  1106. public void removeChangeListener(ChangeListener l) {
  1107. listenerList.remove(ChangeListener.class, l);
  1108. }
  1109. /**
  1110. * Notifies all <code>ChangeListeners</code> when the views
  1111. * size, position, or the viewports extent size has changed.
  1112. *
  1113. * @see #addChangeListener
  1114. * @see #removeChangeListener
  1115. * @see EventListenerList
  1116. */
  1117. protected void fireStateChanged()
  1118. {
  1119. Object[] listeners = listenerList.getListenerList();
  1120. for (int i = listeners.length - 2; i >= 0; i -= 2) {
  1121. if (listeners[i] == ChangeListener.class) {
  1122. if (changeEvent == null) {
  1123. changeEvent = new ChangeEvent(this);
  1124. }
  1125. ((ChangeListener)listeners[i + 1]).stateChanged(changeEvent);
  1126. }
  1127. }
  1128. }
  1129. /**
  1130. * Always repaint in the parents coordinate system to make sure
  1131. * only one paint is performed by the <code>RepaintManager</code>.
  1132. *
  1133. * @param tm maximum time in milliseconds before update
  1134. * @param x the <code>x</code> coordinate (pixels over from left)
  1135. * @param y the <code>y</code> coordinate (pixels down from top)
  1136. * @param width the width
  1137. * @param height the height
  1138. * @see java.awt.Component#update(java.awt.Graphics)
  1139. */
  1140. public void repaint(long tm, int x, int y, int w, int h) {
  1141. Container parent = getParent();
  1142. if(parent != null)
  1143. parent.repaint(tm,x+getX(),y+getY(),w,h);
  1144. else
  1145. super.repaint(tm,x,y,w,h);
  1146. }
  1147. /**
  1148. * Returns a string representation of this <code>JViewport</code>.
  1149. * This method
  1150. * is intended to be used only for debugging purposes, and the
  1151. * content and format of the returned string may vary between
  1152. * implementations. The returned string may be empty but may not
  1153. * be <code>null</code>.
  1154. *
  1155. * @return a string representation of this <code>JViewport</code>
  1156. */
  1157. protected String paramString() {
  1158. String isViewSizeSetString = (isViewSizeSet ?
  1159. "true" : "false");
  1160. String lastPaintPositionString = (lastPaintPosition != null ?
  1161. lastPaintPosition.toString() : "");
  1162. String scrollUnderwayString = (scrollUnderway ?
  1163. "true" : "false");
  1164. return super.paramString() +
  1165. ",isViewSizeSet=" + isViewSizeSetString +
  1166. ",lastPaintPosition=" + lastPaintPositionString +
  1167. ",scrollUnderway=" + scrollUnderwayString;
  1168. }
  1169. //
  1170. // Following is used when doBlit is true.
  1171. //
  1172. /**
  1173. * Notifies listeners of a property change. This is subclassed to update
  1174. * the <code>windowBlit</code> property.
  1175. * (The <code>putClientProperty</code> property is final).
  1176. *
  1177. * @param propertyName a string containing the property name
  1178. * @param oldValue the old value of the property
  1179. * @param newValue the new value of the property
  1180. */
  1181. protected void firePropertyChange(String propertyName, Object oldValue,
  1182. Object newValue) {
  1183. super.firePropertyChange(propertyName, oldValue, newValue);
  1184. if (propertyName.equals(EnableWindowBlit)) {
  1185. if (newValue != null) {
  1186. setScrollMode(BLIT_SCROLL_MODE);
  1187. } else {
  1188. setScrollMode(SIMPLE_SCROLL_MODE);
  1189. }
  1190. }
  1191. }
  1192. private Timer createRepaintTimer() {
  1193. Timer timer = new Timer(300, new ActionListener() {
  1194. public void actionPerformed(ActionEvent ae) {
  1195. // waitingForRepaint will be false if a paint came down
  1196. // with the complete clip rect, in which case we don't
  1197. // have to cause a repaint.
  1198. if (waitingForRepaint) {
  1199. repaint();
  1200. }
  1201. }
  1202. });
  1203. timer.setRepeats(false);
  1204. return timer;
  1205. }
  1206. /**
  1207. * If the repaint manager has a dirty region for the view, the view is
  1208. * asked to paint.
  1209. *
  1210. * @param g the <code>Graphics</code> context within which to paint
  1211. */
  1212. private void flushViewDirtyRegion(Graphics g) {
  1213. RepaintManager rm = RepaintManager.currentManager(this);
  1214. JComponent view = (JComponent) getView();
  1215. Rectangle dirty;
  1216. dirty = rm.getDirtyRegion(view);
  1217. if(dirty != null && dirty.width > 0 && dirty.height > 0) {
  1218. dirty.x += view.getX();
  1219. dirty.y += view.getY();
  1220. Rectangle clip = g.getClipBounds();
  1221. if (clip == null) {
  1222. // Only happens in 1.2
  1223. g.setClip(0, 0, getWidth(), getHeight());
  1224. }
  1225. g.clipRect(dirty.x, dirty.y, dirty.width, dirty.height);
  1226. paintView(g);
  1227. }
  1228. }
  1229. /**
  1230. * Used when blitting.
  1231. *
  1232. * @param g the <code>Graphics</code> context within which to paint
  1233. * @return true if blitting succeeded; otherwise false
  1234. */
  1235. private boolean windowBlitPaint(Graphics g) {
  1236. int width = getWidth();
  1237. int height = getHeight();
  1238. if ((width == 0) || (height == 0)) {
  1239. return false;
  1240. }
  1241. boolean retValue;
  1242. RepaintManager rm = RepaintManager.currentManager(this);
  1243. JComponent view = (JComponent) getView();
  1244. if (lastPaintPosition == null ||
  1245. lastPaintPosition.equals(getViewLocation())) {
  1246. paintView(g);
  1247. retValue = false;
  1248. } else {
  1249. // The image was scrolled. Manipulate the backing store and flush
  1250. // it to g.
  1251. Point blitFrom = new Point();
  1252. Point blitTo = new Point();
  1253. Dimension blitSize = new Dimension();
  1254. Rectangle blitPaint = new Rectangle();
  1255. Point newLocation = getViewLocation();
  1256. int dx = newLocation.x - lastPaintPosition.x;
  1257. int dy = newLocation.y - lastPaintPosition.y;
  1258. boolean canBlit = computeBlit(dx, dy, blitFrom, blitTo, blitSize,
  1259. blitPaint);
  1260. if (!canBlit) {
  1261. paintView(g);
  1262. retValue = false;
  1263. } else {
  1264. boolean isDBE = rm.isDoubleBufferingEnabled();
  1265. int bdx = blitTo.x - blitFrom.x;
  1266. int bdy = blitTo.y - blitFrom.y;
  1267. // Prepare the rest of the view; the part that has just been
  1268. // exposed.
  1269. Rectangle r = view.getBounds().intersection(blitPaint);
  1270. r.x -= view.getX();
  1271. r.y -= view.getY();
  1272. Image off = rm.getOffscreenBuffer(this, width, height);
  1273. Graphics og = off.getGraphics();
  1274. og.translate(-r.x,-r.y);
  1275. og.setClip(r.x,r.y,r.width,r.height);
  1276. rm.setDoubleBufferingEnabled(false);
  1277. view.paint(og);
  1278. rm.setDoubleBufferingEnabled(isDBE);
  1279. // Move the relevant part of the backing store.
  1280. blitWindowGraphics(blitFrom.x, blitFrom.y, blitSize.width,
  1281. blitSize.height, bdx, bdy);
  1282. r.x += view.getX();
  1283. r.y += view.getY();
  1284. g.setClip(r.x,r.y,r.width,r.height);
  1285. g.drawImage(off,r.x,r.y,null);
  1286. og.dispose();
  1287. retValue = true;
  1288. }
  1289. }
  1290. lastPaintPosition = getViewLocation();
  1291. return retValue;
  1292. }
  1293. /**
  1294. * Called to paint the view, usually when <code>blitPaint</code>
  1295. * can not blit.
  1296. *
  1297. * @param g the <code>Graphics</code> context within which to paint
  1298. */
  1299. private void paintView(Graphics g) {
  1300. Rectangle r = g.getClipBounds();
  1301. RepaintManager rm = RepaintManager.currentManager(this);
  1302. boolean dblbEnable = rm.isDoubleBufferingEnabled();
  1303. JComponent view = (JComponent) getView();
  1304. r.x -= view.getX();
  1305. r.y -= view.getY();
  1306. Image off = rm.getOffscreenBuffer(this,r.width,r.height);
  1307. Graphics og = off.getGraphics();
  1308. if (view.getWidth() < r.width) {
  1309. og.setColor(getBackground());
  1310. og.fillRect(0, 0, r.width, r.height);
  1311. }
  1312. og.translate(-r.x,-r.y);
  1313. og.setClip(r.x,r.y,r.width,r.height);
  1314. rm.setDoubleBufferingEnabled(false);
  1315. view.paint(og);
  1316. if(dblbEnable)
  1317. rm.setDoubleBufferingEnabled(true);
  1318. g.drawImage(off,r.x + view.getX(),r.y + view.getY(),null);
  1319. og.dispose();
  1320. }
  1321. /**
  1322. * Blits the parent windows graphics from the given region offset
  1323. * to <code>ox</code>, <code>oy</code>.
  1324. */
  1325. private void blitWindowGraphics(int x, int y, int w, int h, int ox,
  1326. int oy) {
  1327. Container parent;
  1328. for(parent = getParent() ; isLightweightComponent(parent) ;
  1329. parent = parent.getParent());
  1330. Graphics wg = parent.getGraphics();
  1331. Rectangle r = new Rectangle(x,y,w,h);
  1332. r = SwingUtilities.convertRectangle(this, r, parent);
  1333. wg.copyArea(r.x,r.y,r.width,r.height, ox, oy);
  1334. wg.dispose();
  1335. }
  1336. /**
  1337. * Returns true if the viewport is not obscured by one of its ancestors,
  1338. * or its ancestors children and if the viewport is showing. Blitting
  1339. * when the view isn't showing will work,
  1340. * or rather <code>copyArea</code> will work,
  1341. * but will not produce the expected behavior.
  1342. */
  1343. private boolean canUseWindowBlitter() {
  1344. if (!isShowing() || (!(getParent() instanceof JComponent) &&
  1345. !(getView() instanceof JComponent))) {
  1346. return false;
  1347. }
  1348. Rectangle clip = new Rectangle(0,0,getWidth(),getHeight());
  1349. Rectangle oldClip = new Rectangle();
  1350. Rectangle tmp2;
  1351. Rectangle b;
  1352. Container parent;
  1353. Component lastParent = null;
  1354. for(parent = this; parent != null && isLightweightComponent(parent); parent = parent.getParent()) {
  1355. if(parent instanceof JComponent) {
  1356. b = ((JComponent)parent)._bounds;
  1357. } else {
  1358. b = parent.getBounds();
  1359. }
  1360. oldClip.setBounds(clip);
  1361. SwingUtilities.computeIntersection(0, 0, b.width, b.height, clip);
  1362. if(!clip.equals(oldClip))
  1363. return false;
  1364. if(lastParent != null && parent instanceof JComponent &&
  1365. !((JComponent)parent).isOptimizedDrawingEnabled()) {
  1366. Component comps[] = parent.getComponents();
  1367. int index = 0;
  1368. for(int i = comps.length - 1 ;i >= 0; i--) {
  1369. if(comps[i] == lastParent) {
  1370. index = i - 1;
  1371. break;
  1372. }
  1373. }
  1374. while(index >= 0) {
  1375. if(comps[index] instanceof JComponent) {
  1376. tmp2 = ((JComponent)comps[index])._bounds;
  1377. } else {
  1378. tmp2 = comps[index].getBounds();
  1379. }
  1380. if(tmp2.intersects(clip))
  1381. return false;
  1382. index--;
  1383. }
  1384. }
  1385. clip.x += b.x;
  1386. clip.y += b.y;
  1387. lastParent = parent;
  1388. }
  1389. if (parent == null) {
  1390. // No Window parent.
  1391. return false;
  1392. }
  1393. return true;
  1394. }
  1395. /////////////////
  1396. // Accessibility support
  1397. ////////////////
  1398. /**
  1399. * Gets the AccessibleContext associated with this JViewport.
  1400. * For viewports, the AccessibleContext takes the form of an
  1401. * AccessibleJViewport.
  1402. * A new AccessibleJViewport instance is created if necessary.
  1403. *
  1404. * @return an AccessibleJViewport that serves as the
  1405. * AccessibleContext of this JViewport
  1406. */
  1407. public AccessibleContext getAccessibleContext() {
  1408. if (accessibleContext == null) {
  1409. accessibleContext = new AccessibleJViewport();
  1410. }
  1411. return accessibleContext;
  1412. }
  1413. /**
  1414. * This class implements accessibility support for the
  1415. * <code>JViewport</code> class. It provides an implementation of the
  1416. * Java Accessibility API appropriate to viewport user-interface elements.
  1417. * <p>
  1418. * <strong>Warning:</strong>
  1419. * Serialized objects of this class will not be compatible with
  1420. * future Swing releases. The current serialization support is appropriate
  1421. * for short term storage or RMI between applications running the same
  1422. * version of Swing. A future release of Swing will provide support for
  1423. * long term persistence.
  1424. */
  1425. protected class AccessibleJViewport extends AccessibleJComponent {
  1426. /**
  1427. * Get the role of this object.
  1428. *
  1429. * @return an instance of AccessibleRole describing the role of
  1430. * the object
  1431. */
  1432. public AccessibleRole getAccessibleRole() {
  1433. return AccessibleRole.VIEWPORT;
  1434. }
  1435. } // inner class AccessibleJViewport
  1436. }