41 |
import java.awt.Component; |
import java.awt.Component; |
42 |
import java.awt.Dimension; |
import java.awt.Dimension; |
43 |
import java.awt.Graphics; |
import java.awt.Graphics; |
44 |
|
import java.awt.Image; |
45 |
import java.awt.Insets; |
import java.awt.Insets; |
46 |
import java.awt.LayoutManager; |
import java.awt.LayoutManager; |
47 |
import java.awt.Point; |
import java.awt.Point; |
130 |
|
|
131 |
/** |
/** |
132 |
* A {@link java.awt.event.ComponentListener} that listens for |
* A {@link java.awt.event.ComponentListener} that listens for |
133 |
* changes of the view's size. This class forbids changes of the view |
* changes of the view's size. This triggers a revalidate() call on the |
134 |
* component's size that would exceed the viewport's size. |
* viewport. |
135 |
*/ |
*/ |
136 |
protected class ViewListener |
protected class ViewListener extends ComponentAdapter implements Serializable |
|
extends ComponentAdapter |
|
|
implements Serializable |
|
137 |
{ |
{ |
138 |
private static final long serialVersionUID = -2812489404285958070L; |
private static final long serialVersionUID = -2812489404285958070L; |
139 |
|
|
142 |
*/ |
*/ |
143 |
protected ViewListener() |
protected ViewListener() |
144 |
{ |
{ |
145 |
|
// Nothing to do here. |
146 |
} |
} |
147 |
|
|
148 |
/** |
/** |
149 |
* Receives notification when a component (in this case: the view |
* Receives notification when a component (in this case: the view |
150 |
* component) changes it's size. |
* component) changes it's size. This simply triggers a revalidate() on the |
151 |
|
* viewport. |
152 |
* |
* |
153 |
* @param ev the ComponentEvent describing the change |
* @param ev the ComponentEvent describing the change |
154 |
*/ |
*/ |
155 |
public void componentResized(ComponentEvent ev) |
public void componentResized(ComponentEvent ev) |
156 |
{ |
{ |
157 |
// According to some tests that I did with Sun's implementation |
revalidate(); |
|
// this class is supposed to make sure that the view component |
|
|
// is not resized to a larger size than the viewport. |
|
|
// This is not documented anywhere. What I did is: I subclassed JViewport |
|
|
// and ViewListener and 'disabled' the componentResized method by |
|
|
// overriding it and not calling super.componentResized(). |
|
|
// When this method is disabled I can set the size on the view component |
|
|
// normally, when it is enabled, it gets immediatly resized back, |
|
|
// after a resize attempt that would exceed the Viewport's size. |
|
|
Component comp = ev.getComponent(); |
|
|
Dimension newSize = comp.getSize(); |
|
|
Dimension viewportSize = getSize(); |
|
|
boolean revert = false; |
|
|
if (newSize.width > viewportSize.width) |
|
|
{ |
|
|
newSize.width = viewportSize.width; |
|
|
revert = true; |
|
|
} |
|
|
if (newSize.height > viewportSize.height) |
|
|
{ |
|
|
newSize.height = viewportSize.height; |
|
|
revert = true; |
|
|
} |
|
|
if (revert == true) |
|
|
comp.setSize(newSize); |
|
158 |
} |
} |
159 |
} |
} |
160 |
|
|
|
private static final long serialVersionUID = -6925142919680527970L; |
|
|
|
|
161 |
public static final int SIMPLE_SCROLL_MODE = 0; |
public static final int SIMPLE_SCROLL_MODE = 0; |
162 |
public static final int BLIT_SCROLL_MODE = 1; |
public static final int BLIT_SCROLL_MODE = 1; |
163 |
public static final int BACKINGSTORE_SCROLL_MODE = 2; |
public static final int BACKINGSTORE_SCROLL_MODE = 2; |
164 |
|
|
165 |
|
private static final long serialVersionUID = -6925142919680527970L; |
166 |
|
|
167 |
|
protected boolean scrollUnderway; |
168 |
|
protected boolean isViewSizeSet; |
169 |
|
|
170 |
|
/** |
171 |
|
* This flag indicates whether we use a backing store for drawing. |
172 |
|
* |
173 |
|
* @deprecated since JDK 1.3 |
174 |
|
*/ |
175 |
|
protected boolean backingStore; |
176 |
|
|
177 |
|
/** |
178 |
|
* The backingstore image used for the backingstore and blit scroll methods. |
179 |
|
*/ |
180 |
|
protected Image backingStoreImage; |
181 |
|
|
182 |
|
/** |
183 |
|
* The position at which the view has been drawn the last time. This is used |
184 |
|
* to determine the bittable area. |
185 |
|
*/ |
186 |
|
protected Point lastPaintPosition; |
187 |
|
|
188 |
ChangeEvent changeEvent = new ChangeEvent(this); |
ChangeEvent changeEvent = new ChangeEvent(this); |
189 |
|
|
190 |
int scrollMode; |
int scrollMode; |
191 |
|
|
|
protected boolean scrollUnderway; |
|
|
protected boolean isViewSizeSet; |
|
|
|
|
192 |
/** |
/** |
193 |
* The width and height of the Viewport's area in terms of view |
* The width and height of the Viewport's area in terms of view |
194 |
* coordinates. Typically this will be the same as the width and height |
* coordinates. Typically this will be the same as the width and height |
196 |
* width and height, which it may do, for example if it magnifies or |
* width and height, which it may do, for example if it magnifies or |
197 |
* rotates its view. |
* rotates its view. |
198 |
* |
* |
199 |
* @see #toViewCoordinates |
* @see #toViewCoordinates(Dimension) |
200 |
*/ |
*/ |
201 |
Dimension extentSize; |
Dimension extentSize; |
202 |
|
|
203 |
/** |
/** |
204 |
* The width and height of the view in its own coordinate space. |
* The width and height of the view in its own coordinate space. |
205 |
*/ |
*/ |
|
|
|
206 |
Dimension viewSize; |
Dimension viewSize; |
207 |
|
|
|
Point lastPaintPosition; |
|
|
|
|
208 |
/** |
/** |
209 |
* The ViewListener instance. |
* The ViewListener instance. |
210 |
*/ |
*/ |
211 |
ViewListener viewListener; |
ViewListener viewListener; |
212 |
|
|
213 |
/** |
/** |
214 |
* The accessible context of this <code>JViewport</code>. |
* Stores the location from where to blit. This is a cached Point object used |
215 |
|
* in blitting calculations. |
216 |
|
*/ |
217 |
|
Point cachedBlitFrom; |
218 |
|
|
219 |
|
/** |
220 |
|
* Stores the location where to blit to. This is a cached Point object used |
221 |
|
* in blitting calculations. |
222 |
|
*/ |
223 |
|
Point cachedBlitTo; |
224 |
|
|
225 |
|
/** |
226 |
|
* Stores the width of the blitted area. This is a cached Dimension object |
227 |
|
* used in blitting calculations. |
228 |
|
*/ |
229 |
|
Dimension cachedBlitSize; |
230 |
|
|
231 |
|
/** |
232 |
|
* Stores the bounds of the area that needs to be repainted. This is a cached |
233 |
|
* Rectangle object used in blitting calculations. |
234 |
*/ |
*/ |
235 |
AccessibleContext accessibleContext; |
Rectangle cachedBlitPaint; |
236 |
|
|
237 |
|
boolean damaged = true; |
238 |
|
|
239 |
|
/** |
240 |
|
* A flag indicating if the size of the viewport has changed since the |
241 |
|
* last repaint. This is used in double buffered painting to check if we |
242 |
|
* need a new double buffer, or can reuse the old one. |
243 |
|
*/ |
244 |
|
boolean sizeChanged = true; |
245 |
|
|
246 |
public JViewport() |
public JViewport() |
247 |
{ |
{ |
248 |
setOpaque(true); |
setOpaque(true); |
249 |
setScrollMode(BLIT_SCROLL_MODE); |
setScrollMode(BLIT_SCROLL_MODE); |
|
setLayout(createLayoutManager()); |
|
250 |
updateUI(); |
updateUI(); |
251 |
|
setLayout(createLayoutManager()); |
252 |
|
lastPaintPosition = new Point(); |
253 |
|
cachedBlitFrom = new Point(); |
254 |
|
cachedBlitTo = new Point(); |
255 |
|
cachedBlitSize = new Dimension(); |
256 |
|
cachedBlitPaint = new Rectangle(); |
257 |
} |
} |
258 |
|
|
259 |
public Dimension getExtentSize() |
public Dimension getExtentSize() |
339 |
|
|
340 |
public void setViewPosition(Point p) |
public void setViewPosition(Point p) |
341 |
{ |
{ |
342 |
|
if (getViewPosition().equals(p)) |
343 |
|
return; |
344 |
Component view = getView(); |
Component view = getView(); |
345 |
if (view != null) |
if (view != null) |
346 |
{ |
{ |
349 |
isViewSizeSet = false; |
isViewSizeSet = false; |
350 |
fireStateChanged(); |
fireStateChanged(); |
351 |
} |
} |
352 |
|
repaint(); |
353 |
} |
} |
354 |
|
|
355 |
public Rectangle getViewRect() |
public Rectangle getViewRect() |
399 |
|
|
400 |
public void setView(Component v) |
public void setView(Component v) |
401 |
{ |
{ |
402 |
while (getComponentCount() > 0) |
if (viewListener != null) |
403 |
{ |
getView().removeComponentListener(viewListener); |
|
if (viewListener != null) |
|
|
getView().removeComponentListener(viewListener); |
|
|
remove(0); |
|
|
} |
|
404 |
|
|
405 |
if (v != null) |
if (v != null) |
406 |
{ |
{ |
410 |
add(v); |
add(v); |
411 |
fireStateChanged(); |
fireStateChanged(); |
412 |
} |
} |
413 |
} |
revalidate(); |
414 |
|
repaint(); |
|
public void revalidate() |
|
|
{ |
|
|
fireStateChanged(); |
|
|
super.revalidate(); |
|
415 |
} |
} |
416 |
|
|
417 |
public void reshape(int x, int y, int w, int h) |
public void reshape(int x, int y, int w, int h) |
418 |
{ |
{ |
419 |
boolean changed = |
if (w != getWidth() || h != getHeight()) |
420 |
(x != getX()) |
sizeChanged = true; |
|
|| (y != getY()) |
|
|
|| (w != getWidth()) |
|
|
|| (h != getHeight()); |
|
421 |
super.reshape(x, y, w, h); |
super.reshape(x, y, w, h); |
422 |
if (changed) |
if (sizeChanged) |
423 |
fireStateChanged(); |
{ |
424 |
} |
damaged = true; |
425 |
|
fireStateChanged(); |
426 |
protected void addImpl(Component comp, Object constraints, int index) |
} |
|
{ |
|
|
if (getComponentCount() > 0) |
|
|
remove(getComponents()[0]); |
|
|
|
|
|
super.addImpl(comp, constraints, index); |
|
427 |
} |
} |
428 |
|
|
429 |
public final Insets getInsets() |
public final Insets getInsets() |
430 |
{ |
{ |
431 |
return new Insets(0,0,0,0); |
return new Insets(0, 0, 0, 0); |
432 |
} |
} |
433 |
|
|
434 |
public final Insets getInsets(Insets insets) |
public final Insets getInsets(Insets insets) |
442 |
return insets; |
return insets; |
443 |
} |
} |
444 |
|
|
445 |
|
|
446 |
|
/** |
447 |
|
* Overridden to return <code>false</code>, so the JViewport's paint method |
448 |
|
* gets called instead of directly calling the children. This is necessary |
449 |
|
* in order to get a useful clipping and translation on the children. |
450 |
|
* |
451 |
|
* @return <code>false</code> |
452 |
|
*/ |
453 |
public boolean isOptimizedDrawingEnabled() |
public boolean isOptimizedDrawingEnabled() |
454 |
{ |
{ |
455 |
return false; |
return false; |
457 |
|
|
458 |
public void paint(Graphics g) |
public void paint(Graphics g) |
459 |
{ |
{ |
460 |
paintComponent(g); |
Component view = getView(); |
461 |
|
|
462 |
|
if (view == null) |
463 |
|
return; |
464 |
|
|
465 |
|
Point pos = getViewPosition(); |
466 |
|
Rectangle viewBounds = view.getBounds(); |
467 |
|
Rectangle portBounds = getBounds(); |
468 |
|
|
469 |
|
if (viewBounds.width == 0 |
470 |
|
|| viewBounds.height == 0 |
471 |
|
|| portBounds.width == 0 |
472 |
|
|| portBounds.height == 0) |
473 |
|
return; |
474 |
|
|
475 |
|
switch (getScrollMode()) |
476 |
|
{ |
477 |
|
|
478 |
|
case JViewport.BACKINGSTORE_SCROLL_MODE: |
479 |
|
paintBackingStore(g); |
480 |
|
break; |
481 |
|
case JViewport.BLIT_SCROLL_MODE: |
482 |
|
paintBlit(g); |
483 |
|
break; |
484 |
|
case JViewport.SIMPLE_SCROLL_MODE: |
485 |
|
default: |
486 |
|
paintSimple(g); |
487 |
|
break; |
488 |
|
} |
489 |
|
damaged = false; |
490 |
} |
} |
491 |
|
|
492 |
public void addChangeListener(ChangeListener listener) |
public void addChangeListener(ChangeListener listener) |
504 |
return (ChangeListener[]) getListeners(ChangeListener.class); |
return (ChangeListener[]) getListeners(ChangeListener.class); |
505 |
} |
} |
506 |
|
|
|
protected void fireStateChanged() |
|
|
{ |
|
|
ChangeListener[] listeners = getChangeListeners(); |
|
|
for (int i = 0; i < listeners.length; ++i) |
|
|
listeners[i].stateChanged(changeEvent); |
|
|
} |
|
|
|
|
507 |
/** |
/** |
508 |
* This method returns the String ID of the UI class of Separator. |
* This method returns the String ID of the UI class of Separator. |
509 |
* |
* |
549 |
} |
} |
550 |
|
|
551 |
/** |
/** |
552 |
|
* Scrolls the view so that contentRect becomes visible. |
553 |
|
* |
554 |
|
* @param contentRect the rectangle to make visible within the view |
555 |
|
*/ |
556 |
|
public void scrollRectToVisible(Rectangle contentRect) |
557 |
|
{ |
558 |
|
Component view = getView(); |
559 |
|
if (view == null) |
560 |
|
return; |
561 |
|
|
562 |
|
Point pos = getViewPosition(); |
563 |
|
Rectangle viewBounds = getView().getBounds(); |
564 |
|
Rectangle portBounds = getBounds(); |
565 |
|
|
566 |
|
if (isShowing()) |
567 |
|
getView().validate(); |
568 |
|
|
569 |
|
// If the bottom boundary of contentRect is below the port |
570 |
|
// boundaries, scroll up as necessary. |
571 |
|
if (contentRect.y + contentRect.height + viewBounds.y > portBounds.height) |
572 |
|
pos.y = contentRect.y + contentRect.height - portBounds.height; |
573 |
|
// If contentRect.y is above the port boundaries, scroll down to |
574 |
|
// contentRect.y. |
575 |
|
if (contentRect.y + viewBounds.y < 0) |
576 |
|
pos.y = contentRect.y; |
577 |
|
// If the right boundary of contentRect is right from the port |
578 |
|
// boundaries, scroll left as necessary. |
579 |
|
if (contentRect.x + contentRect.width + viewBounds.x > portBounds.width) |
580 |
|
pos.x = contentRect.x + contentRect.width - portBounds.width; |
581 |
|
// If contentRect.x is left from the port boundaries, scroll right to |
582 |
|
// contentRect.x. |
583 |
|
if (contentRect.x + viewBounds.x < 0) |
584 |
|
pos.x = contentRect.x; |
585 |
|
setViewPosition(pos); |
586 |
|
} |
587 |
|
|
588 |
|
/** |
589 |
|
* Returns the accessible context for this <code>JViewport</code>. This |
590 |
|
* will be an instance of {@link AccessibleJViewport}. |
591 |
|
* |
592 |
|
* @return the accessible context for this <code>JViewport</code> |
593 |
|
*/ |
594 |
|
public AccessibleContext getAccessibleContext() |
595 |
|
{ |
596 |
|
if (accessibleContext == null) |
597 |
|
accessibleContext = new AccessibleJViewport(); |
598 |
|
return accessibleContext; |
599 |
|
} |
600 |
|
|
601 |
|
/** |
602 |
|
* Forward repaint to parent to make sure only one paint is performed by the |
603 |
|
* RepaintManager. |
604 |
|
* |
605 |
|
* @param tm number of milliseconds to defer the repaint request |
606 |
|
* @param x the X coordinate of the upper left corner of the dirty area |
607 |
|
* @param y the Y coordinate of the upper left corner of the dirty area |
608 |
|
* @param w the width of the dirty area |
609 |
|
* @param h the height of the dirty area |
610 |
|
*/ |
611 |
|
public void repaint(long tm, int x, int y, int w, int h) |
612 |
|
{ |
613 |
|
Component parent = getParent(); |
614 |
|
if (parent != null) |
615 |
|
{ |
616 |
|
parent.repaint(tm, x + getX(), y + getY(), w, h); |
617 |
|
} |
618 |
|
} |
619 |
|
|
620 |
|
protected void addImpl(Component comp, Object constraints, int index) |
621 |
|
{ |
622 |
|
if (getComponentCount() > 0) |
623 |
|
remove(getComponents()[0]); |
624 |
|
|
625 |
|
super.addImpl(comp, constraints, index); |
626 |
|
} |
627 |
|
|
628 |
|
protected void fireStateChanged() |
629 |
|
{ |
630 |
|
ChangeListener[] listeners = getChangeListeners(); |
631 |
|
for (int i = 0; i < listeners.length; ++i) |
632 |
|
listeners[i].stateChanged(changeEvent); |
633 |
|
} |
634 |
|
|
635 |
|
/** |
636 |
* Creates a {@link ViewListener} that is supposed to listen for |
* Creates a {@link ViewListener} that is supposed to listen for |
637 |
* size changes on the view component. |
* size changes on the view component. |
638 |
* |
* |
655 |
} |
} |
656 |
|
|
657 |
/** |
/** |
658 |
* Scrolls the view so that contentRect becomes visible. |
* Computes the parameters for the blitting scroll method. <code>dx</code> |
659 |
|
* and <code>dy</code> specifiy the X and Y offset by which the viewport |
660 |
|
* is scrolled. All other arguments are output parameters and are filled by |
661 |
|
* this method. |
662 |
* |
* |
663 |
* @param contentRect the rectangle to make visible within the view |
* <code>blitFrom</code> holds the position of the blit rectangle in the |
664 |
|
* viewport rectangle before scrolling, <code>blitTo</code> where the blitArea |
665 |
|
* is copied to. |
666 |
|
* |
667 |
|
* <code>blitSize</code> holds the size of the blit area and |
668 |
|
* <code>blitPaint</code> is the area of the view that needs to be painted. |
669 |
|
* |
670 |
|
* This method returns <code>true</code> if blitting is possible and |
671 |
|
* <code>false</code> if the viewport has to be repainted completetly without |
672 |
|
* blitting. |
673 |
|
* |
674 |
|
* @param dx the horizontal delta |
675 |
|
* @param dy the vertical delta |
676 |
|
* @param blitFrom the position from where to blit; set by this method |
677 |
|
* @param blitTo the position where to blit area is copied to; set by this |
678 |
|
* method |
679 |
|
* @param blitSize the size of the blitted area; set by this method |
680 |
|
* @param blitPaint the area that needs repainting; set by this method |
681 |
|
* |
682 |
|
* @return <code>true</code> if blitting is possible, |
683 |
|
* <code>false</code> otherwise |
684 |
*/ |
*/ |
685 |
public void scrollRectToVisible(Rectangle contentRect) |
protected boolean computeBlit(int dx, int dy, Point blitFrom, Point blitTo, |
686 |
|
Dimension blitSize, Rectangle blitPaint) |
687 |
|
{ |
688 |
|
if ((dx != 0 && dy != 0) || damaged) |
689 |
|
// We cannot blit if the viewport is scrolled in both directions at |
690 |
|
// once. |
691 |
|
return false; |
692 |
|
|
693 |
|
Rectangle portBounds = SwingUtilities.calculateInnerArea(this, getBounds()); |
694 |
|
|
695 |
|
// Compute the blitFrom and blitTo parameters. |
696 |
|
blitFrom.x = portBounds.x; |
697 |
|
blitFrom.y = portBounds.y; |
698 |
|
blitTo.x = portBounds.x; |
699 |
|
blitTo.y = portBounds.y; |
700 |
|
|
701 |
|
if (dy > 0) |
702 |
|
{ |
703 |
|
blitFrom.y = portBounds.y + dy; |
704 |
|
} |
705 |
|
else if (dy < 0) |
706 |
|
{ |
707 |
|
blitTo.y = portBounds.y - dy; |
708 |
|
} |
709 |
|
else if (dx > 0) |
710 |
|
{ |
711 |
|
blitFrom.x = portBounds.x + dx; |
712 |
|
} |
713 |
|
else if (dx < 0) |
714 |
|
{ |
715 |
|
blitTo.x = portBounds.x - dx; |
716 |
|
} |
717 |
|
|
718 |
|
// Compute size of the blit area. |
719 |
|
if (dx != 0) |
720 |
|
{ |
721 |
|
blitSize.width = portBounds.width - Math.abs(dx); |
722 |
|
blitSize.height = portBounds.height; |
723 |
|
} |
724 |
|
else if (dy != 0) |
725 |
|
{ |
726 |
|
blitSize.width = portBounds.width; |
727 |
|
blitSize.height = portBounds.height - Math.abs(dy); |
728 |
|
} |
729 |
|
|
730 |
|
// Compute the blitPaint parameter. |
731 |
|
blitPaint.setBounds(portBounds); |
732 |
|
if (dy > 0) |
733 |
|
{ |
734 |
|
blitPaint.y = portBounds.y + portBounds.height - dy; |
735 |
|
blitPaint.height = dy; |
736 |
|
} |
737 |
|
else if (dy < 0) |
738 |
|
{ |
739 |
|
blitPaint.height = -dy; |
740 |
|
} |
741 |
|
if (dx > 0) |
742 |
|
{ |
743 |
|
blitPaint.x = portBounds.x + portBounds.width - dx; |
744 |
|
blitPaint.width = dx; |
745 |
|
} |
746 |
|
else if (dx < 0) |
747 |
|
{ |
748 |
|
blitPaint.width = -dx; |
749 |
|
} |
750 |
|
|
751 |
|
return true; |
752 |
|
} |
753 |
|
|
754 |
|
/** |
755 |
|
* Paints the viewport in case we have a scrollmode of |
756 |
|
* {@link #SIMPLE_SCROLL_MODE}. |
757 |
|
* |
758 |
|
* This simply paints the view directly on the surface of the viewport. |
759 |
|
* |
760 |
|
* @param g the graphics context to use |
761 |
|
*/ |
762 |
|
void paintSimple(Graphics g) |
763 |
{ |
{ |
764 |
Point pos = getViewPosition(); |
Point pos = getViewPosition(); |
765 |
Rectangle viewBounds = getView().getBounds(); |
Component view = getView(); |
766 |
Rectangle portBounds = getBounds(); |
boolean translated = false; |
767 |
|
try |
|
// FIXME: should validate the view if it is not valid, however |
|
|
// this may cause excessive validation when the containment |
|
|
// hierarchy is being created. |
|
|
|
|
|
// if contentRect is larger than the portBounds, center the view |
|
|
if (contentRect.height > portBounds.height || |
|
|
contentRect.width > portBounds.width) |
|
768 |
{ |
{ |
769 |
setViewPosition(new Point(contentRect.x, contentRect.y)); |
g.translate(-pos.x, -pos.y); |
770 |
return; |
translated = true; |
771 |
|
view.paint(g); |
772 |
|
} |
773 |
|
finally |
774 |
|
{ |
775 |
|
if (translated) |
776 |
|
g.translate (pos.x, pos.y); |
777 |
} |
} |
|
|
|
|
// Y-DIRECTION |
|
|
if (contentRect.y < -viewBounds.y) |
|
|
setViewPosition(new Point(pos.x, contentRect.y)); |
|
|
else if (contentRect.y + contentRect.height > |
|
|
-viewBounds.y + portBounds.height) |
|
|
setViewPosition (new Point(pos.x, contentRect.y - |
|
|
(portBounds.height - contentRect.height))); |
|
|
|
|
|
// X-DIRECTION |
|
|
pos = getViewPosition(); |
|
|
if (contentRect.x < -viewBounds.x) |
|
|
setViewPosition(new Point(contentRect.x, pos.y)); |
|
|
else if (contentRect.x + contentRect.width > |
|
|
-viewBounds.x + portBounds.width) |
|
|
setViewPosition (new Point(contentRect.x - |
|
|
(portBounds.width - contentRect.width), pos.y)); |
|
778 |
} |
} |
779 |
|
|
780 |
/** |
/** |
781 |
* Returns the accessible context for this <code>JViewport</code>. This |
* Paints the viewport in case we have a scroll mode of |
782 |
* will be an instance of {@link AccessibleJViewport}. |
* {@link #BACKINGSTORE_SCROLL_MODE}. |
783 |
* |
* |
784 |
* @return the accessible context for this <code>JViewport</code> |
* This method uses a backing store image to paint the view to, which is then |
785 |
|
* subsequently painted on the screen. This should make scrolling more |
786 |
|
* smooth. |
787 |
|
* |
788 |
|
* @param g the graphics context to use |
789 |
*/ |
*/ |
790 |
public AccessibleContext getAccessibleContext() |
void paintBackingStore(Graphics g) |
791 |
{ |
{ |
792 |
if (accessibleContext == null) |
// If we have no backing store image yet or the size of the component has |
793 |
accessibleContext = new AccessibleJViewport(); |
// changed, we need to rebuild the backing store. |
794 |
return accessibleContext; |
if (backingStoreImage == null || sizeChanged) |
795 |
|
{ |
796 |
|
backingStoreImage = createImage(getWidth(), getHeight()); |
797 |
|
sizeChanged = false; |
798 |
|
Graphics g2 = backingStoreImage.getGraphics(); |
799 |
|
paintSimple(g2); |
800 |
|
g2.dispose(); |
801 |
|
} |
802 |
|
// Otherwise we can perform the blitting on the backing store image: |
803 |
|
// First we move the part that remains visible after scrolling, then |
804 |
|
// we only need to paint the bit that becomes newly visible. |
805 |
|
else |
806 |
|
{ |
807 |
|
Graphics g2 = backingStoreImage.getGraphics(); |
808 |
|
Point viewPosition = getViewPosition(); |
809 |
|
int dx = viewPosition.x - lastPaintPosition.x; |
810 |
|
int dy = viewPosition.y - lastPaintPosition.y; |
811 |
|
boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo, |
812 |
|
cachedBlitSize, cachedBlitPaint); |
813 |
|
if (canBlit) |
814 |
|
{ |
815 |
|
// Copy the part that remains visible during scrolling. |
816 |
|
g2.copyArea(cachedBlitFrom.x, cachedBlitFrom.y, |
817 |
|
cachedBlitSize.width, cachedBlitSize.height, |
818 |
|
cachedBlitTo.x - cachedBlitFrom.x, |
819 |
|
cachedBlitTo.y - cachedBlitFrom.y); |
820 |
|
// Now paint the part that becomes newly visible. |
821 |
|
g2.setClip(cachedBlitPaint.x, cachedBlitPaint.y, |
822 |
|
cachedBlitPaint.width, cachedBlitPaint.height); |
823 |
|
paintSimple(g2); |
824 |
|
} |
825 |
|
// If blitting is not possible for some reason, fall back to repainting |
826 |
|
// everything. |
827 |
|
else |
828 |
|
{ |
829 |
|
paintSimple(g2); |
830 |
|
} |
831 |
|
g2.dispose(); |
832 |
|
} |
833 |
|
// Actually draw the backingstore image to the graphics context. |
834 |
|
g.drawImage(backingStoreImage, 0, 0, this); |
835 |
|
// Update the lastPaintPosition so that we know what is already drawn when |
836 |
|
// we paint the next time. |
837 |
|
lastPaintPosition.setLocation(getViewPosition()); |
838 |
|
} |
839 |
|
|
840 |
|
/** |
841 |
|
* Paints the viewport in case we have a scrollmode of |
842 |
|
* {@link #BLIT_SCROLL_MODE}. |
843 |
|
* |
844 |
|
* This paints the viewport using a backingstore and a blitting algorithm. |
845 |
|
* Only the newly exposed area of the view is painted from the view painting |
846 |
|
* methods, the remainder is copied from the backing store. |
847 |
|
* |
848 |
|
* @param g the graphics context to use |
849 |
|
*/ |
850 |
|
void paintBlit(Graphics g) |
851 |
|
{ |
852 |
|
// We cannot perform blitted painting as it is described in Sun's API docs. |
853 |
|
// There it is suggested that this painting method should blit directly |
854 |
|
// on the parent window's surface. This is not possible because when using |
855 |
|
// Swing's double buffering (at least our implementation), it would |
856 |
|
// immediatly be painted when the buffer is painted on the screen. For this |
857 |
|
// to work we would need a kind of hole in the buffer image. And honestly |
858 |
|
// I find this method not very elegant. |
859 |
|
// The alternative, blitting directly on the buffer image, is also not |
860 |
|
// possible because the buffer image gets cleared everytime when an opaque |
861 |
|
// parent component is drawn on it. |
862 |
|
|
863 |
|
// What we do instead is falling back to the backing store approach which |
864 |
|
// is in fact a mixed blitting/backing store approach where the blitting |
865 |
|
// is performed on the backing store image and this is then drawn to the |
866 |
|
// graphics context. This is very robust and works independent of the |
867 |
|
// painting mechanism that is used by Swing. And it should have comparable |
868 |
|
// performance characteristics as the blitting method. |
869 |
|
paintBackingStore(g); |
870 |
} |
} |
871 |
} |
} |