summaryrefslogtreecommitdiffstats
path: root/src/demos/xtrans/OffscreenDesktopManager.java
diff options
context:
space:
mode:
authorKenneth Russel <[email protected]>2005-10-12 01:30:31 +0000
committerKenneth Russel <[email protected]>2005-10-12 01:30:31 +0000
commit82388fe004e7bc86100ad45377e69f771b45c977 (patch)
treef7d866cea1caca1f66a67ffc47f5b44b58ed3fdd /src/demos/xtrans/OffscreenDesktopManager.java
parent55370bacd5640ea9be8f6c137ec03a76c6572651 (diff)
Added XTrans (Accelerated Transitions) demo, which uses the
Java2D/JOGL bridge in a completely different way than the GLJPanel. The OffscreenDesktopPane and associated classes are an attempt at a generalized mechanism for supporting off-screen rendering of Swing components within a JDesktopPane and later composition (by subclasses) of those components' contents on-screen. The XTDesktopPane is intended to be a drop-in replacement for the JDesktopPane which supports OpenGL-accelerated animated transitions for components added to and removed from it. The XTBasicTransitionManager and XTBasicTransition classes define the default implementation of animated transition effects, supporting a combination of fade, rotation and scroll effects. More, and arbitrary, transitions are certainly possible. More experimentation by the community is needed. This demo is intended as a first step toward a more generalized framework in which arbitrary Swing rendering can be performed via the Java2D/JOGL bridge. Bugs remain, such as needing to preserve portions of the OffscreenDesktopManager's back buffer after components have been made not visible (during the process of closing them) in order to properly animate their close effects. The XTrans demo works properly in most cases but the JRefract demo (which now accepts an -xt command line argument to install an XTDesktopPane instead of a JDesktopPane) does not. More work also remains to be done, in particular on the layout of components on the back buffer. A 2D bin-packing algorithm is needed. When the Java2D/JOGL bridge supports Java2D's use of the Frame Buffer Object extension (and, implicitly, render-to-texture on all platforms) the glCopyTexSubImage2D operation to copy the off-screen back buffer to a VolatileImage can disappear; this is the principal expensive operation when the contents change of components which have been added to the OffscreenDesktopPane. git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/../svn-server-sync/jogl-demos/branches/JSR-231@140 3298f667-5e0e-4b4a-8ed4-a3559d26a5f4
Diffstat (limited to 'src/demos/xtrans/OffscreenDesktopManager.java')
-rwxr-xr-xsrc/demos/xtrans/OffscreenDesktopManager.java784
1 files changed, 784 insertions, 0 deletions
diff --git a/src/demos/xtrans/OffscreenDesktopManager.java b/src/demos/xtrans/OffscreenDesktopManager.java
new file mode 100755
index 0000000..656618f
--- /dev/null
+++ b/src/demos/xtrans/OffscreenDesktopManager.java
@@ -0,0 +1,784 @@
+package demos.xtrans;
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.image.*;
+import java.beans.*;
+import java.nio.*;
+import java.util.*;
+import javax.swing.*;
+
+// Internal JOGL API references
+import com.sun.opengl.impl.Debug;
+// FIXME: debugging only
+import com.sun.opengl.impl.Java2D;
+
+// FIXME: we need a way to lock a portion of the off-screen back
+// buffer to be persistent for a while during component removals. It
+// turns out that the removal process of JInternalFrames "mostly"
+// works OK with temporarily preserving a region of the back buffer
+// but in the case of redraws of the target component occurring after
+// it has been removed (such as in the case of a GLJPanel being
+// animated) we need to preserve that region.
+
+/** A DesktopManager implementation supporting off-screen rendering
+ and management of components' images for later compositing. */
+
+public class OffscreenDesktopManager implements DesktopManager {
+ protected final static String HAS_BEEN_ICONIFIED_PROPERTY = "wasIconOnce";
+
+ protected VolatileImage offscreenBackBuffer;
+
+ // Verious dirty states.
+ //
+ // STATE_CLEAN indicates that the OpenGL texture is in sync with the
+ // VolatileImage back buffer and that no (relatively expensive) copy
+ // back operations need be performed.
+ //
+ // STATE_COPY_BACK indicates that the VolatileImage back buffer must
+ // be copied back e.g. into an OpenGL texture, but that the back
+ // buffer's contents are clean.
+ //
+ // STATE_REDRAW indicates that all components need to be repainted
+ // on the back buffer. STATE_REDRAW implies STATE_COPY_BACK.
+ //
+ // STATE_RELAYOUT is the most expensive state, in which all
+ // components are re-laid out on the back buffer and repainted
+ // completely. This implies STATE_REDRAW, which in turn implies
+ // STATE_COPY_BACK.
+ public static final int STATE_CLEAN = 0;
+ public static final int STATE_COPY_BACK = 1;
+ public static final int STATE_REDRAW = 2;
+ public static final int STATE_RELAYOUT = 3;
+
+ // We start off assuming that we need to lay out everything
+ protected int dirtyState = STATE_RELAYOUT;
+
+ protected Map/*<Component, Rectangle>*/ componentPositionsOnBackBuffer = new WeakHashMap();
+
+ // For debugging
+ private static final boolean DEBUG = Debug.debug("OffscreenDesktopManager");
+ private static final boolean VERBOSE = Debug.verbose();
+ private JFrame debuggingFrame;
+ private JPanel debuggingPanel;
+
+ public OffscreenDesktopManager() {
+ if (DEBUG) {
+ debuggingFrame = new JFrame("Debugging frame");
+ debuggingPanel = new JPanel() {
+ public void paintComponent(Graphics g) {
+ if (offscreenBackBuffer != null) {
+ g.drawImage(offscreenBackBuffer, 0, 0, null);
+ }
+ }
+ };
+ debuggingPanel.setDoubleBuffered(false);
+ debuggingFrame.getContentPane().add(debuggingPanel);
+ debuggingFrame.pack();
+ debuggingFrame.setLocation(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth() / 2, 0);
+ debuggingFrame.setSize(256, 256);
+ debuggingFrame.setVisible(true);
+ }
+ }
+
+ /** Sets the state bit in the desktop manager indicating that the
+ offscreen texture has changed and may need to be copied back
+ e.g. to an OpenGL texture. */
+ public void setNeedsCopyBack() {
+ dirtyState = Math.max(dirtyState, STATE_COPY_BACK);
+ }
+
+ /** Sets the state bit in the desktop manager indicating that the
+ child components need to be redrawn to the off-screen buffer. */
+ public void setNeedsRedraw() {
+ dirtyState = Math.max(dirtyState, STATE_REDRAW);
+ }
+
+ /** Sets the state bit in the desktop manager indicating that the
+ components need to be re-laid out on the off-screen back buffer.
+ This implies that all of these components need to be repainted
+ and also implies that the off-screen back buffer may need to be
+ copied back (needsCopyBack()). */
+ public void setNeedsReLayout() {
+ dirtyState = Math.max(dirtyState, STATE_RELAYOUT);
+ }
+
+ /** Returns the state bit in the desktop manager indicating that the
+ offscreen texture has changed and may need to be copied back
+ e.g. to an OpenGL texture. */
+ public boolean needsCopyBack() {
+ return (dirtyState >= STATE_COPY_BACK);
+ }
+
+ /** Returns the state bit in the desktop manager indicating that the
+ child components need to be redrawn to the off-screen buffer. */
+ public boolean needsRedraw() {
+ return (dirtyState >= STATE_REDRAW);
+ }
+
+ /** Returns the state bit in the desktop manager indicating that the
+ components need to be re-laid out on the off-screen back buffer.
+ This implies that all of these components need to be repainted
+ and also implies that the off-screen back buffer may need to be
+ copied back (needsCopyBack()). */
+ public boolean needsReLayout() {
+ return (dirtyState >= STATE_RELAYOUT);
+ }
+
+ /** Returns the default graphics configuration of the default screen
+ device for the local graphics environment. */
+ protected GraphicsConfiguration getDefaultConfiguration() {
+ return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
+ }
+
+ /** Fetches the Graphics object corresponding to the off-screen back
+ buffer. */
+ public Graphics getOffscreenGraphics() {
+ return offscreenBackBuffer.getGraphics();
+ }
+
+ /** Fetches the Image being used as the back buffer. */
+ public Image getOffscreenBackBuffer() {
+ return offscreenBackBuffer;
+ }
+
+ /** Returns the width of the off-screen back buffer at this point in
+ time, or -1 if it has not been created yet. */
+ public int getOffscreenBackBufferWidth() {
+ return offscreenBackBuffer.getWidth();
+ }
+
+ /** Returns the height of the off-screen back buffer at this point
+ in time, or -1 if it has not been created yet. */
+ public int getOffscreenBackBufferHeight() {
+ return offscreenBackBuffer.getHeight();
+ }
+
+ /** Fetches the Rectangle corresponding to the bounds of the given
+ child component on the off-screen back buffer. This portion of
+ the back buffer can be drawn manually by the end user to display
+ the given component. */
+ public Rectangle getBoundsOnBackBuffer(Component c) {
+ return (Rectangle) componentPositionsOnBackBuffer.get(c);
+ }
+
+ /** Updates the layouts of the components on the off-screen back
+ buffer without repainting the back buffer. This should always be
+ called after adding, removing or resizing child components. It
+ is called automatically by {@link #updateOffscreenBuffer
+ updateOffscreenBuffer}. */
+ public void layoutOffscreenBuffer(OffscreenDesktopPane parent) {
+ if (needsReLayout()) {
+ // Must do the following:
+ // 1. Lay out the desktop pane's children on the off-screen back
+ // buffer, keeping track of where they went
+ // 2. Draw the children to the off-screen buffer
+ // (Not done here: 3. Use JOGL to copy the off-screen buffer to a
+ // texture for later consumption by the XTDesktopPane)
+
+ //////////////////////////////////////////////////////////////////
+ // //
+ // FIXME: must use a more efficient packing algorithm than this //
+ // //
+ //////////////////////////////////////////////////////////////////
+
+ // NOTE: this is the rectangle packing problem, which is NP-hard.
+ // We could go to arbitrary lengths in order to improve the
+ // efficiency of this packing. Ideally we would like to minimize
+ // wasted space and (probably) shoot for a somewhat-square layout.
+ // Because currently we're just trying to get things working,
+ // we're going to do the simplest layout possible: just line
+ // things up left-to-right.
+ int maxHeight = -1;
+ int widthSum = 0;
+
+ // Two-pass algorithm, one getting maximum height and summing
+ // widths, and one laying things out
+ for (int i = 0; i < parent.getComponentCount(); i++) {
+ Component c = ((OffscreenComponentWrapper) parent.getComponent(i)).getChild();
+ int w = c.getWidth();
+ int h = c.getHeight();
+ maxHeight = Math.max(maxHeight, h);
+ widthSum += w;
+ }
+ int curX = 0;
+ for (int i = 0; i < parent.getComponentCount(); i++) {
+ Component c = ((OffscreenComponentWrapper) parent.getComponent(i)).getChild();
+ int w = c.getWidth();
+ int h = c.getHeight();
+ Rectangle r = new Rectangle(curX, 0, w, h);
+ componentPositionsOnBackBuffer.put(c, r);
+ curX += w;
+ }
+
+ // Re-create off-screen buffer if necessary
+ int offscreenWidth = nextPowerOf2(widthSum);
+ int offscreenHeight = nextPowerOf2(maxHeight);
+ if ((offscreenBackBuffer == null) ||
+ (offscreenWidth != offscreenBackBuffer.getWidth()) ||
+ (offscreenHeight != offscreenBackBuffer.getHeight())) {
+ if (offscreenBackBuffer != null) {
+ offscreenBackBuffer.flush();
+ }
+ offscreenBackBuffer =
+ getDefaultConfiguration().createCompatibleVolatileImage(offscreenWidth,
+ offscreenHeight);
+ }
+
+ if (DEBUG) {
+ debuggingPanel.setPreferredSize(new Dimension(offscreenWidth, offscreenHeight));
+ debuggingFrame.setSize(offscreenWidth + 10, offscreenHeight + 30);
+ }
+
+ dirtyState = STATE_REDRAW;
+ }
+ }
+
+ /** Updates the image on the off-screen back buffer. This should
+ always be called before attempting to draw the child components'
+ contents on the screen. If the child components' states are
+ clean, this method does nothing. Note that this changes the
+ state bits back to clean, so subclasses should capture the
+ current state before calling the superclass implementation. */
+ public void updateOffscreenBuffer(OffscreenDesktopPane parent) {
+ if (!needsCopyBack()) {
+ // Cleanest possible state
+ return;
+ }
+
+ layoutOffscreenBuffer(parent);
+
+ boolean validated = false;
+ boolean done = false;
+ while (!done) {
+ if (needsRedraw()) {
+ boolean redrawn = false;
+
+ do {
+ // Validate it
+ int res = offscreenBackBuffer.validate(getDefaultConfiguration());
+ if (!((res == VolatileImage.IMAGE_OK) ||
+ (res == VolatileImage.IMAGE_RESTORED))) {
+ // FIXME: fail more gracefully
+ throw new RuntimeException("Unable to validate VolatileImage");
+ }
+ validated = true;
+
+ // Lay out and render components
+ final Graphics g = offscreenBackBuffer.getGraphics();
+ int curX = 0;
+ for (int i = 0; i < parent.getComponentCount(); i++) {
+ Component c = ((OffscreenComponentWrapper) parent.getComponent(i)).getChild();
+
+ if (c.isVisible()) {
+ // Ensure this component and all children have double
+ // buffering disabled to prevent incorrect rendering results.
+ // Should try to make this more efficient, but doesn't look
+ // like there's any way to listen for setDoubleBuffered
+ // changes; could however listen for hierarchy change events,
+ // which are more likely to break things, but these are
+ // expensive to deliver as well
+ switchDoubleBuffering(c, false);
+
+ // NOTE: should probably be smarter about this and only
+ // paint components which really need it (consult
+ // RepaintManager?). However, experimentation has shown
+ // that at this point the RepaintManager is already in
+ // the process of painting the child components and its
+ // notion of the dirty regions has already been cleared.
+
+ Rectangle r = (Rectangle) componentPositionsOnBackBuffer.get(c);
+ if (r == null) {
+ // May be bug or race condition; for now just skip this one
+ continue;
+ }
+ Graphics g2 = g.create();
+ if (DEBUG && VERBOSE) {
+ System.err.println("Translating Graphics to (" + r.x + "," + r.y + ")");
+ System.err.println(" Surface identifier = " + Java2D.getOGLSurfaceIdentifier(g2));
+ }
+ g2.translate(r.x, r.y);
+ c.paint(g2);
+ g2.dispose();
+ }
+ }
+ g.dispose();
+ if (!offscreenBackBuffer.contentsLost()) {
+ redrawn = true;
+ done = true;
+ }
+ } while (!redrawn);
+ }
+
+ // If we didn't need to re-layout and draw the components, we
+ // might still need to copy back their results to an OpenGL
+ // texture. Subclasses should override this method to do
+ // additional work afterward.
+
+ if (!validated) {
+ int res = offscreenBackBuffer.validate(getDefaultConfiguration());
+ if (!((res == VolatileImage.IMAGE_OK) ||
+ (res == VolatileImage.IMAGE_RESTORED))) {
+ // FIXME: fail more gracefully
+ throw new RuntimeException("Unable to validate VolatileImage");
+ }
+ if (res == VolatileImage.IMAGE_RESTORED) {
+ // The contents were blown away since the time of the last
+ // render, so force a re-render
+ setNeedsRedraw();
+ } else {
+ done = true;
+ }
+ }
+ }
+
+ dirtyState = STATE_CLEAN;
+
+ // Subclasses would do the copy back here
+
+ if (DEBUG) {
+ debuggingPanel.repaint();
+ }
+ }
+
+ public void openFrame(JInternalFrame f) {
+ if(getDesktopPaneParent(f.getDesktopIcon()) != null) {
+ getDesktopPaneParent(f.getDesktopIcon()).add(f);
+ removeIconFor(f);
+ }
+
+ setNeedsReLayout();
+ }
+
+ public void closeFrame(JInternalFrame f) {
+ boolean findNext = f.isSelected();
+ JDesktopPane c = getDesktopPaneParent(f);
+ if (findNext)
+ try { f.setSelected(false); } catch (PropertyVetoException e2) { }
+ if(c != null) {
+ c.remove(f.getParent());
+ repaintPortionOfDesktop(c, f);
+ }
+ removeIconFor(f);
+ if(f.getNormalBounds() != null)
+ f.setNormalBounds(null);
+ if(wasIcon(f))
+ setWasIcon(f, null);
+ if (findNext) activateNextFrame(c);
+
+ setNeedsReLayout();
+ }
+
+ public void maximizeFrame(JInternalFrame f) {
+ if (f.isIcon()) {
+ try {
+ // In turn calls deiconifyFrame in the desktop manager.
+ // That method will handle the maximization of the frame.
+ f.setIcon(false);
+ } catch (PropertyVetoException e2) {
+ }
+ } else {
+ f.setNormalBounds(f.getBounds());
+ Rectangle desktopBounds = getDesktopPaneParent(f).getBounds();
+ setBoundsForFrame(f, 0, 0,
+ desktopBounds.width, desktopBounds.height);
+ }
+
+ // Set the maximized frame as selected.
+ try {
+ f.setSelected(true);
+ } catch (PropertyVetoException e2) {
+ }
+
+ setNeedsReLayout();
+ }
+
+ public void minimizeFrame(JInternalFrame f) {
+ // If the frame was an icon restore it back to an icon.
+ if (f.isIcon()) {
+ iconifyFrame(f);
+ return;
+ }
+
+ if ((f.getNormalBounds()) != null) {
+ Rectangle r = f.getNormalBounds();
+ f.setNormalBounds(null);
+ try { f.setSelected(true); } catch (PropertyVetoException e2) { }
+ setBoundsForFrame(f, r.x, r.y, r.width, r.height);
+ }
+
+ setNeedsReLayout();
+ }
+
+ public void iconifyFrame(JInternalFrame f) {
+ JInternalFrame.JDesktopIcon desktopIcon;
+ Container c = getDesktopPaneParent(f);
+ JDesktopPane d = f.getDesktopPane();
+ boolean findNext = f.isSelected();
+
+ desktopIcon = f.getDesktopIcon();
+ if(!wasIcon(f)) {
+ Rectangle r = getBoundsForIconOf(f);
+ desktopIcon.setBounds(r.x, r.y, r.width, r.height);
+ setWasIcon(f, Boolean.TRUE);
+ }
+
+ if (c == null) {
+ return;
+ }
+
+ if (c instanceof JLayeredPane) {
+ JLayeredPane lp = (JLayeredPane)c;
+ int layer = lp.getLayer(f);
+ lp.putLayer(desktopIcon, layer);
+ }
+
+ // If we are maximized we already have the normal bounds recorded
+ // don't try to re-record them, otherwise we incorrectly set the
+ // normal bounds to maximized state.
+ if (!f.isMaximum()) {
+ f.setNormalBounds(f.getBounds());
+ }
+ c.remove(f);
+ c.add(desktopIcon);
+ try {
+ f.setSelected(false);
+ } catch (PropertyVetoException e2) {
+ }
+
+ // Get topmost of the remaining frames
+ if (findNext) {
+ activateNextFrame(c);
+ }
+
+ setNeedsReLayout();
+ }
+
+ protected void activateNextFrame(Container c) {
+ int i;
+ JInternalFrame nextFrame = null;
+ if (c == null) return;
+ for (i = 0; i < c.getComponentCount(); i++) {
+ if (c.getComponent(i) instanceof JInternalFrame) {
+ nextFrame = (JInternalFrame) c.getComponent(i);
+ break;
+ }
+ }
+ if (nextFrame != null) {
+ try { nextFrame.setSelected(true); }
+ catch (PropertyVetoException e2) { }
+ moveToFront(nextFrame);
+ }
+ else {
+ c.requestFocus();
+ }
+
+ // This operation will change the graphic contents of the
+ // offscreen buffer but not the positions of any of the windows
+ setNeedsCopyBack();
+ }
+
+ public void deiconifyFrame(JInternalFrame f) {
+ JInternalFrame.JDesktopIcon desktopIcon = f.getDesktopIcon();
+ Container c = getDesktopPaneParent(desktopIcon);
+ if (c != null) {
+ c.add(f);
+ // If the frame is to be restored to a maximized state make
+ // sure it still fills the whole desktop.
+ if (f.isMaximum()) {
+ Rectangle desktopBounds = c.getBounds();
+ if (f.getWidth() != desktopBounds.width ||
+ f.getHeight() != desktopBounds.height) {
+ setBoundsForFrame(f, 0, 0,
+ desktopBounds.width, desktopBounds.height);
+ }
+ }
+ removeIconFor(f);
+ if (f.isSelected()) {
+ moveToFront(f);
+ } else {
+ try {
+ f.setSelected(true);
+ } catch (PropertyVetoException e2) {
+ }
+ }
+ }
+
+ setNeedsReLayout();
+ }
+
+ public void activateFrame(JInternalFrame f) {
+ Container p = getDesktopPaneParent(f);
+ Component[] c;
+ JDesktopPane d = f.getDesktopPane();
+ JInternalFrame currentlyActiveFrame =
+ (d == null) ? null : d.getSelectedFrame();
+ // fix for bug: 4162443
+ if(p == null) {
+ // If the frame is not in parent, its icon maybe, check it
+ p = getDesktopPaneParent(f.getDesktopIcon());
+ if(p == null)
+ return;
+ }
+ // we only need to keep track of the currentActive InternalFrame, if any
+ if (currentlyActiveFrame == null){
+ if (d != null) { d.setSelectedFrame(f);}
+ } else if (currentlyActiveFrame != f) {
+ // if not the same frame as the current active
+ // we deactivate the current
+ if (currentlyActiveFrame.isSelected()) {
+ try {
+ currentlyActiveFrame.setSelected(false);
+ }
+ catch(PropertyVetoException e2) {}
+ }
+ if (d != null) { d.setSelectedFrame(f);}
+ }
+ moveToFront(f);
+
+ // This operation will change the graphic contents of the
+ // offscreen buffer but not the positions of any of the windows
+ // setNeedsCopyBack();
+ setNeedsRedraw();
+
+ repaintPortionOfDesktop(f);
+ }
+
+ public void deactivateFrame(JInternalFrame f) {
+ JDesktopPane d = f.getDesktopPane();
+ JInternalFrame currentlyActiveFrame =
+ (d == null) ? null : d.getSelectedFrame();
+ if (currentlyActiveFrame == f)
+ d.setSelectedFrame(null);
+
+ // This operation will change the graphic contents of the
+ // offscreen buffer but not the positions of any of the windows
+ setNeedsRedraw();
+
+ repaintPortionOfDesktop(f);
+ }
+
+ public void beginDraggingFrame(JComponent f) {
+ // Nothing to do any more because the DesktopPane handles this by
+ // redrawing from the off-screen buffer
+ }
+
+ public void dragFrame(JComponent f, int newX, int newY) {
+ f.setLocation(newX, newY);
+ repaintPortionOfDesktop(f);
+ }
+
+ public void endDraggingFrame(JComponent f) {
+ // NOTE: nothing to do any more because OffscreenDesktopPane
+ // subclasses handle this
+ }
+
+ public void beginResizingFrame(JComponent f, int direction) {
+ }
+
+ public void resizeFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
+ setBoundsForFrame(f, newX, newY, newWidth, newHeight);
+ repaintPortionOfDesktop(f);
+ }
+
+ public void endResizingFrame(JComponent f) {
+ repaintPortionOfDesktop(f);
+ }
+
+ public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
+ boolean didResize = (f.getWidth() != newWidth || f.getHeight() != newHeight);
+ f.setBounds(newX, newY, newWidth, newHeight);
+ if(didResize) {
+ f.validate();
+ }
+ setNeedsReLayout();
+ }
+
+ protected void removeIconFor(JInternalFrame f) {
+ JInternalFrame.JDesktopIcon di = f.getDesktopIcon();
+ JDesktopPane c = getDesktopPaneParent(di);
+ if(c != null) {
+ c.remove(di);
+ repaintPortionOfDesktop(c, di);
+ }
+ }
+
+ protected Rectangle getBoundsForIconOf(JInternalFrame f) {
+ //
+ // Get the icon for this internal frame and its preferred size
+ //
+
+ JInternalFrame.JDesktopIcon icon = f.getDesktopIcon();
+ Dimension prefSize = icon.getPreferredSize();
+ //
+ // Get the parent bounds and child components.
+ //
+
+ Container c = getDesktopPaneParent(f);
+ if (c == null) {
+ c = getDesktopPaneParent(f.getDesktopIcon());
+ }
+
+ if (c == null) {
+ /* the frame has not yet been added to the parent; how about (0,0) ?*/
+ return new Rectangle(0, 0, prefSize.width, prefSize.height);
+ }
+
+ Rectangle parentBounds = c.getBounds();
+ Component [] components = c.getComponents();
+
+
+ //
+ // Iterate through valid default icon locations and return the
+ // first one that does not intersect any other icons.
+ //
+
+ Rectangle availableRectangle = null;
+ JInternalFrame.JDesktopIcon currentIcon = null;
+
+ int x = 0;
+ int y = parentBounds.height - prefSize.height;
+ int w = prefSize.width;
+ int h = prefSize.height;
+
+ boolean found = false;
+
+ while (!found) {
+
+ availableRectangle = new Rectangle(x,y,w,h);
+
+ found = true;
+
+ for ( int i=0; i<components.length; i++ ) {
+
+ //
+ // Get the icon for this component
+ //
+
+ if ( components[i] instanceof JInternalFrame ) {
+ currentIcon = ((JInternalFrame)components[i]).getDesktopIcon();
+ }
+ else if ( components[i] instanceof JInternalFrame.JDesktopIcon ){
+ currentIcon = (JInternalFrame.JDesktopIcon)components[i];
+ } else
+ /* found a child that's neither an internal frame nor
+ an icon. I don't believe this should happen, but at
+ present it does and causes a null pointer exception.
+ Even when that gets fixed, this code protects against
+ the npe. hania */
+ continue;
+
+ //
+ // If this icon intersects the current location, get next location.
+ //
+
+ if ( !currentIcon.equals(icon) ) {
+ if ( availableRectangle.intersects(currentIcon.getBounds()) ) {
+ found = false;
+ break;
+ }
+ }
+ }
+
+ if (currentIcon == null)
+ /* didn't find any useful children above. This probably shouldn't
+ happen, but this check protects against an npe if it ever does
+ (and it's happening now) */
+ return availableRectangle;
+
+ x += currentIcon.getBounds().width;
+
+ if ( x + w > parentBounds.width ) {
+ x = 0;
+ y -= h;
+ }
+ }
+
+ return(availableRectangle);
+ }
+
+ protected void setWasIcon(JInternalFrame f, Boolean value) {
+ if (value != null) {
+ f.putClientProperty(HAS_BEEN_ICONIFIED_PROPERTY, value);
+ }
+ }
+
+ protected boolean wasIcon(JInternalFrame f) {
+ return (f.getClientProperty(HAS_BEEN_ICONIFIED_PROPERTY) == Boolean.TRUE);
+ }
+
+ protected JDesktopPane getDesktopPaneParent(Component f) {
+ Container c = f.getParent();
+ if (c == null) {
+ return null;
+ }
+ if (!(c instanceof OffscreenComponentWrapper)) {
+ throw new RuntimeException("Illegal component structure");
+ }
+ Container parent = c.getParent();
+ if (parent == null) {
+ return null;
+ }
+ if (!(parent instanceof JDesktopPane)) {
+ throw new RuntimeException("Illegal component structure");
+ }
+ return (JDesktopPane) parent;
+ }
+
+ private void moveToFront(Component f) {
+ Container c = getDesktopPaneParent(f);
+ if (c instanceof JDesktopPane) {
+ ((JDesktopPane) c).moveToFront(f.getParent());
+ }
+ }
+
+ private static int nextPowerOf2(int number) {
+ // Workaround for problems where 0 width or height are transiently
+ // seen during layout
+ if (number == 0) {
+ return 2;
+ }
+
+ if (((number-1) & number) == 0) {
+ //ex: 8 -> 0b1000; 8-1=7 -> 0b0111; 0b1000&0b0111 == 0
+ return number;
+ }
+ int power = 0;
+ while (number > 0) {
+ number = number>>1;
+ power++;
+ }
+ return (1<<power);
+ }
+
+ /** Repaints the portion of the desktop pane parent corresponding to
+ the given component. */
+ protected void repaintPortionOfDesktop(Component comp) {
+ repaintPortionOfDesktop(getDesktopPaneParent(comp), comp);
+ }
+
+ /** Repaints the portion of the passed desktop pane corresponding to
+ the given component. */
+ protected void repaintPortionOfDesktop(JDesktopPane desktop, Component comp) {
+ // Indicate to the desktop pane that a certain portion of the
+ // on-screen representation is dirty. The desktop can map these
+ // coordinates to the positions of the windows if they are
+ // different.
+ Rectangle r = comp.getBounds();
+ desktop.repaint(r.x, r.y, r.width, r.height);
+ }
+
+ /** Helper function to force the double-buffering of an entire
+ component hierarchy to the on or off state. */
+ public static void switchDoubleBuffering(Component root, boolean val) {
+ if (root instanceof JComponent) {
+ ((JComponent) root).setDoubleBuffered(val);
+ }
+ if (root instanceof Container) {
+ Container container = (Container) root;
+ for (int i = 0; i < container.getComponentCount(); i++) {
+ switchDoubleBuffering(container.getComponent(i), val);
+ }
+ }
+ }
+}