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