1 /* 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.swing; 27 28 import java.awt.BorderLayout; 29 import java.awt.Color; 30 import java.awt.Component; 31 import java.awt.Container; 32 import java.awt.Dimension; 33 import java.awt.EventQueue; 34 import java.awt.Graphics; 35 import java.awt.Graphics2D; 36 import java.awt.Rectangle; 37 import java.awt.event.ComponentEvent; 38 import java.awt.event.ComponentListener; 39 import java.awt.event.ContainerAdapter; 40 import java.awt.event.ContainerEvent; 41 import java.awt.event.ContainerListener; 42 import java.awt.image.BufferedImage; 43 import java.awt.image.DataBufferInt; 44 import java.beans.PropertyChangeEvent; 45 import java.beans.PropertyChangeListener; 46 import java.security.AccessController; 47 48 import javax.swing.JLayeredPane; 49 import javax.swing.JPanel; 50 import javax.swing.JRootPane; 51 import javax.swing.LayoutFocusTraversalPolicy; 52 import javax.swing.RootPaneContainer; 53 54 import sun.awt.LightweightFrame; 55 import sun.security.action.GetPropertyAction; 56 57 /** 58 * The frame serves as a lightweight container which paints its content 59 * to an offscreen image and provides access to the image's data via the 60 * {@link LightweightContent} interface. Note, that it may not be shown 61 * as a standalone toplevel frame. Its purpose is to provide functionality 62 * for lightweight embedding. 63 * 64 * @author Artem Ananiev 65 * @author Anton Tarasov 66 */ 67 public final class JLightweightFrame extends LightweightFrame implements RootPaneContainer { 68 69 private final JRootPane rootPane = new JRootPane(); 70 71 private LightweightContent content; 72 73 private Component component; 74 private JPanel contentPane; 75 76 private BufferedImage bbImage; 77 78 /** 79 * {@code copyBufferEnabled}, true by default, defines the following strategy. 80 * A duplicating (copy) buffer is created for the original pixel buffer. 81 * The copy buffer is synchronized with the original buffer every time the 82 * latter changes. {@code JLightweightFrame} passes the copy buffer array 83 * to the {@link LightweightContent#imageBufferReset} method. The code spot 84 * which synchronizes two buffers becomes the only critical section guarded 85 * by the lock (managed with the {@link LightweightContent#paintLock()}, 86 * {@link LightweightContent#paintUnlock()} methods). 87 */ 88 private boolean copyBufferEnabled; 89 private int[] copyBuffer; 90 91 private PropertyChangeListener layoutSizeListener; 92 93 /** 94 * Constructs a new, initially invisible {@code JLightweightFrame} 95 * instance. 96 */ 97 public JLightweightFrame() { 98 super(); 99 copyBufferEnabled = "true".equals(AccessController. 100 doPrivileged(new GetPropertyAction("jlf.copyBufferEnabled", "true"))); 101 102 add(rootPane, BorderLayout.CENTER); 103 setFocusTraversalPolicy(new LayoutFocusTraversalPolicy()); 104 if (getGraphicsConfiguration().isTranslucencyCapable()) { 105 setBackground(new Color(0, 0, 0, 0)); 106 } 107 } 108 109 /** 110 * Sets the {@link LightweightContent} instance for this frame. 111 * The {@code JComponent} object returned by the 112 * {@link LightweightContent#getComponent()} method is immediately 113 * added to the frame's content pane. 114 * 115 * @param content the {@link LightweightContent} instance 116 */ 117 public void setContent(final LightweightContent content) { 118 if (content == null) { 119 System.err.println("JLightweightFrame.setContent: content may not be null!"); 120 return; 121 } 122 this.content = content; 123 this.component = content.getComponent(); 124 125 layoutSizeListener = new PropertyChangeListener() { 126 @Override 127 public void propertyChange(PropertyChangeEvent e) { 128 Dimension d = (Dimension)e.getNewValue(); 129 130 if ("preferredSize".equals(e.getPropertyName())) { 131 content.preferredSizeChanged(d.width, d.height); 132 133 } else if ("maximumSize".equals(e.getPropertyName())) { 134 content.maximumSizeChanged(d.width, d.height); 135 136 } else if ("minimumSize".equals(e.getPropertyName())) { 137 content.minimumSizeChanged(d.width, d.height); 138 } 139 } 140 }; 141 this.component.addPropertyChangeListener("preferredSize", layoutSizeListener); 142 Dimension d = this.component.getPreferredSize(); 143 content.preferredSizeChanged(d.width, d.height); 144 145 this.component.addPropertyChangeListener("maximumSize", layoutSizeListener); 146 d = this.component.getMaximumSize(); 147 content.maximumSizeChanged(d.width, d.height); 148 149 this.component.addPropertyChangeListener("minimumSize", layoutSizeListener); 150 d = this.component.getMinimumSize(); 151 content.minimumSizeChanged(d.width, d.height); 152 153 initInterior(); 154 } 155 156 @Override 157 public Graphics getGraphics() { 158 if (bbImage == null) return null; 159 160 Graphics2D g = bbImage.createGraphics(); 161 g.setBackground(getBackground()); 162 g.setColor(getForeground()); 163 g.setFont(getFont()); 164 return g; 165 } 166 167 /** 168 * {@inheritDoc} 169 * 170 * @see LightweightContent#focusGrabbed() 171 */ 172 @Override 173 public void grabFocus() { 174 if (content != null) content.focusGrabbed(); 175 } 176 177 /** 178 * {@inheritDoc} 179 * 180 * @see LightweightContent#focusUngrabbed() 181 */ 182 @Override 183 public void ungrabFocus() { 184 if (content != null) content.focusUngrabbed(); 185 } 186 187 private void syncCopyBuffer(boolean reset, int x, int y, int w, int h) { 188 content.paintLock(); 189 try { 190 int[] srcBuffer = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData(); 191 if (reset) { 192 copyBuffer = new int[srcBuffer.length]; 193 } 194 int linestride = bbImage.getWidth(); 195 196 for (int i=0; i<h; i++) { 197 int from = (y + i) * linestride + x; 198 System.arraycopy(srcBuffer, from, copyBuffer, from, w); 199 } 200 } finally { 201 content.paintUnlock(); 202 } 203 } 204 205 private void initInterior() { 206 contentPane = new JPanel() { 207 @Override 208 public void paint(Graphics g) { 209 if (!copyBufferEnabled) { 210 content.paintLock(); 211 } 212 try { 213 super.paint(g); 214 215 final Rectangle clip = g.getClipBounds() != null ? 216 g.getClipBounds() : 217 new Rectangle(0, 0, contentPane.getWidth(), contentPane.getHeight()); 218 219 clip.x = Math.max(0, clip.x); 220 clip.y = Math.max(0, clip.y); 221 clip.width = Math.min(contentPane.getWidth(), clip.width); 222 clip.height = Math.min(contentPane.getHeight(), clip.height); 223 224 EventQueue.invokeLater(new Runnable() { 225 @Override 226 public void run() { 227 if (copyBufferEnabled) { 228 syncCopyBuffer(false, clip.x, clip.y, clip.width, clip.height); 229 } 230 content.imageUpdated(clip.x, clip.y, clip.width, clip.height); 231 } 232 }); 233 } finally { 234 if (!copyBufferEnabled) { 235 content.paintUnlock(); 236 } 237 } 238 } 239 @Override 240 protected boolean isPaintingOrigin() { 241 return true; 242 } 243 }; 244 contentPane.setLayout(new BorderLayout()); 245 contentPane.add(component); 246 setContentPane(contentPane); 247 248 contentPane.addContainerListener(new ContainerAdapter() { 249 @Override 250 public void componentRemoved(ContainerEvent e) { 251 Component c = JLightweightFrame.this.component; 252 if (e.getChild() == c) { 253 c.removePropertyChangeListener(layoutSizeListener); 254 } 255 } 256 }); 257 } 258 259 @SuppressWarnings("deprecation") 260 @Override public void reshape(int x, int y, int width, int height) { 261 super.reshape(x, y, width, height); 262 263 if (width == 0 || height == 0) { 264 return; 265 } 266 if (!copyBufferEnabled) { 267 content.paintLock(); 268 } 269 try { 270 if ((bbImage == null) || (width != bbImage.getWidth()) || (height != bbImage.getHeight())) { 271 boolean createBB = true; 272 int newW = width; 273 int newH = height; 274 if (bbImage != null) { 275 int oldW = bbImage.getWidth(); 276 int oldH = bbImage.getHeight(); 277 if ((oldW >= newW) && (oldH >= newH)) { 278 createBB = false; 279 } else { 280 if (oldW >= newW) { 281 newW = oldW; 282 } else { 283 newW = Math.max((int)(oldW * 1.2), width); 284 } 285 if (oldH >= newH) { 286 newH = oldH; 287 } else { 288 newH = Math.max((int)(oldH * 1.2), height); 289 } 290 } 291 } 292 if (createBB) { 293 BufferedImage oldBB = bbImage; 294 bbImage = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB_PRE); 295 if (oldBB != null) { 296 Graphics g = bbImage.getGraphics(); 297 try { 298 g.drawImage(oldBB, 0, 0, newW, newH, null); 299 } finally { 300 g.dispose(); 301 oldBB.flush(); 302 } 303 } 304 int[] pixels = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData(); 305 if (copyBufferEnabled) { 306 syncCopyBuffer(true, 0, 0, width, height); 307 pixels = copyBuffer; 308 } 309 content.imageBufferReset(pixels, 0, 0, width, height, bbImage.getWidth()); 310 return; 311 } 312 } 313 content.imageReshaped(0, 0, width, height); 314 315 } finally { 316 if (!copyBufferEnabled) { 317 content.paintUnlock(); 318 } 319 } 320 } 321 322 @Override 323 public JRootPane getRootPane() { 324 return rootPane; 325 } 326 327 @Override 328 public void setContentPane(Container contentPane) { 329 getRootPane().setContentPane(contentPane); 330 } 331 332 @Override 333 public Container getContentPane() { 334 return getRootPane().getContentPane(); 335 } 336 337 @Override 338 public void setLayeredPane(JLayeredPane layeredPane) { 339 getRootPane().setLayeredPane(layeredPane); 340 } 341 342 @Override 343 public JLayeredPane getLayeredPane() { 344 return getRootPane().getLayeredPane(); 345 } 346 347 @Override 348 public void setGlassPane(Component glassPane) { 349 getRootPane().setGlassPane(glassPane); 350 } 351 352 @Override 353 public Component getGlassPane() { 354 return getRootPane().getGlassPane(); 355 } 356 }