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.MouseInfo; 37 import java.awt.Point; 38 import java.awt.Rectangle; 39 import java.awt.event.ContainerEvent; 40 import java.awt.event.ContainerListener; 41 import java.awt.image.BufferedImage; 42 import java.awt.image.DataBufferInt; 43 import java.beans.PropertyChangeEvent; 44 import java.beans.PropertyChangeListener; 45 import java.security.AccessController; 46 47 import javax.swing.JLayeredPane; 48 import javax.swing.JPanel; 49 import javax.swing.JRootPane; 50 import javax.swing.LayoutFocusTraversalPolicy; 51 import javax.swing.RootPaneContainer; 52 import javax.swing.SwingUtilities; 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 static { 94 SwingAccessor.setJLightweightFrameAccessor(new SwingAccessor.JLightweightFrameAccessor() { 95 @Override 96 public void updateCursor(JLightweightFrame frame) { 97 frame.updateClientCursor(); 98 } 99 }); 100 } 101 102 /** 103 * Constructs a new, initially invisible {@code JLightweightFrame} 104 * instance. 105 */ 106 public JLightweightFrame() { 107 super(); 108 copyBufferEnabled = "true".equals(AccessController. 109 doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true"))); 110 111 add(rootPane, BorderLayout.CENTER); 112 setFocusTraversalPolicy(new LayoutFocusTraversalPolicy()); 113 if (getGraphicsConfiguration().isTranslucencyCapable()) { 114 setBackground(new Color(0, 0, 0, 0)); 115 } 116 117 layoutSizeListener = new PropertyChangeListener() { 118 @Override 119 public void propertyChange(PropertyChangeEvent e) { 120 Dimension d = (Dimension)e.getNewValue(); 121 122 if ("preferredSize".equals(e.getPropertyName())) { 123 content.preferredSizeChanged(d.width, d.height); 124 125 } else if ("maximumSize".equals(e.getPropertyName())) { 126 content.maximumSizeChanged(d.width, d.height); 127 128 } else if ("minimumSize".equals(e.getPropertyName())) { 129 content.minimumSizeChanged(d.width, d.height); 130 } 131 } 132 }; 133 } 134 135 /** 136 * Sets the {@link LightweightContent} instance for this frame. 137 * The {@code JComponent} object returned by the 138 * {@link LightweightContent#getComponent()} method is immediately 139 * added to the frame's content pane. 140 * 141 * @param content the {@link LightweightContent} instance 142 */ 143 public void setContent(final LightweightContent content) { 144 if (content == null) { 145 System.err.println("JLightweightFrame.setContent: content may not be null!"); 146 return; 147 } 148 this.content = content; 149 this.component = content.getComponent(); 150 151 Dimension d = this.component.getPreferredSize(); 152 content.preferredSizeChanged(d.width, d.height); 153 154 d = this.component.getMaximumSize(); 155 content.maximumSizeChanged(d.width, d.height); 156 157 d = this.component.getMinimumSize(); 158 content.minimumSizeChanged(d.width, d.height); 159 160 initInterior(); 161 } 162 163 @Override 164 public Graphics getGraphics() { 165 if (bbImage == null) return null; 166 167 Graphics2D g = bbImage.createGraphics(); 168 g.setBackground(getBackground()); 169 g.setColor(getForeground()); 170 g.setFont(getFont()); 171 return g; 172 } 173 174 /** 175 * {@inheritDoc} 176 * 177 * @see LightweightContent#focusGrabbed() 178 */ 179 @Override 180 public void grabFocus() { 181 if (content != null) content.focusGrabbed(); 182 } 183 184 /** 185 * {@inheritDoc} 186 * 187 * @see LightweightContent#focusUngrabbed() 188 */ 189 @Override 190 public void ungrabFocus() { 191 if (content != null) content.focusUngrabbed(); 192 } 193 194 private void syncCopyBuffer(boolean reset, int x, int y, int w, int h) { 195 content.paintLock(); 196 try { 197 int[] srcBuffer = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData(); 198 if (reset) { 199 copyBuffer = new int[srcBuffer.length]; 200 } 201 int linestride = bbImage.getWidth(); 202 203 for (int i=0; i<h; i++) { 204 int from = (y + i) * linestride + x; 205 System.arraycopy(srcBuffer, from, copyBuffer, from, w); 206 } 207 } finally { 208 content.paintUnlock(); 209 } 210 } 211 212 private void initInterior() { 213 contentPane = new JPanel() { 214 @Override 215 public void paint(Graphics g) { 216 if (!copyBufferEnabled) { 217 content.paintLock(); 218 } 219 try { 220 super.paint(g); 221 222 final Rectangle clip = g.getClipBounds() != null ? 223 g.getClipBounds() : 224 new Rectangle(0, 0, contentPane.getWidth(), contentPane.getHeight()); 225 226 clip.x = Math.max(0, clip.x); 227 clip.y = Math.max(0, clip.y); 228 clip.width = Math.min(contentPane.getWidth(), clip.width); 229 clip.height = Math.min(contentPane.getHeight(), clip.height); 230 231 EventQueue.invokeLater(new Runnable() { 232 @Override 233 public void run() { 234 if (copyBufferEnabled) { 235 syncCopyBuffer(false, clip.x, clip.y, clip.width, clip.height); 236 } 237 content.imageUpdated(clip.x, clip.y, clip.width, clip.height); 238 } 239 }); 240 } finally { 241 if (!copyBufferEnabled) { 242 content.paintUnlock(); 243 } 244 } 245 } 246 @Override 247 protected boolean isPaintingOrigin() { 248 return true; 249 } 250 }; 251 contentPane.setLayout(new BorderLayout()); 252 contentPane.add(component); 253 if ("true".equals(AccessController. 254 doPrivileged(new GetPropertyAction("swing.jlf.contentPaneTransparent", "false")))) 255 { 256 contentPane.setOpaque(false); 257 } 258 setContentPane(contentPane); 259 260 contentPane.addContainerListener(new ContainerListener() { 261 @Override 262 public void componentAdded(ContainerEvent e) { 263 Component c = JLightweightFrame.this.component; 264 if (e.getChild() == c) { 265 c.addPropertyChangeListener("preferredSize", layoutSizeListener); 266 c.addPropertyChangeListener("maximumSize", layoutSizeListener); 267 c.addPropertyChangeListener("minimumSize", layoutSizeListener); 268 } 269 } 270 @Override 271 public void componentRemoved(ContainerEvent e) { 272 Component c = JLightweightFrame.this.component; 273 if (e.getChild() == c) { 274 c.removePropertyChangeListener(layoutSizeListener); 275 } 276 } 277 }); 278 } 279 280 @SuppressWarnings("deprecation") 281 @Override public void reshape(int x, int y, int width, int height) { 282 super.reshape(x, y, width, height); 283 284 if (width == 0 || height == 0) { 285 return; 286 } 287 if (!copyBufferEnabled) { 288 content.paintLock(); 289 } 290 try { 291 if ((bbImage == null) || (width != bbImage.getWidth()) || (height != bbImage.getHeight())) { 292 boolean createBB = true; 293 int newW = width; 294 int newH = height; 295 if (bbImage != null) { 296 int oldW = bbImage.getWidth(); 297 int oldH = bbImage.getHeight(); 298 if ((oldW >= newW) && (oldH >= newH)) { 299 createBB = false; 300 } else { 301 if (oldW >= newW) { 302 newW = oldW; 303 } else { 304 newW = Math.max((int)(oldW * 1.2), width); 305 } 306 if (oldH >= newH) { 307 newH = oldH; 308 } else { 309 newH = Math.max((int)(oldH * 1.2), height); 310 } 311 } 312 } 313 if (createBB) { 314 BufferedImage oldBB = bbImage; 315 bbImage = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB_PRE); 316 if (oldBB != null) { 317 Graphics g = bbImage.getGraphics(); 318 try { 319 g.drawImage(oldBB, 0, 0, newW, newH, null); 320 } finally { 321 g.dispose(); 322 oldBB.flush(); 323 } 324 } 325 int[] pixels = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData(); 326 if (copyBufferEnabled) { 327 syncCopyBuffer(true, 0, 0, width, height); 328 pixels = copyBuffer; 329 } 330 content.imageBufferReset(pixels, 0, 0, width, height, bbImage.getWidth()); 331 return; 332 } 333 } 334 content.imageReshaped(0, 0, width, height); 335 336 } finally { 337 if (!copyBufferEnabled) { 338 content.paintUnlock(); 339 } 340 } 341 } 342 343 @Override 344 public JRootPane getRootPane() { 345 return rootPane; 346 } 347 348 @Override 349 public void setContentPane(Container contentPane) { 350 getRootPane().setContentPane(contentPane); 351 } 352 353 @Override 354 public Container getContentPane() { 355 return getRootPane().getContentPane(); 356 } 357 358 @Override 359 public void setLayeredPane(JLayeredPane layeredPane) { 360 getRootPane().setLayeredPane(layeredPane); 361 } 362 363 @Override 364 public JLayeredPane getLayeredPane() { 365 return getRootPane().getLayeredPane(); 366 } 367 368 @Override 369 public void setGlassPane(Component glassPane) { 370 getRootPane().setGlassPane(glassPane); 371 } 372 373 @Override 374 public Component getGlassPane() { 375 return getRootPane().getGlassPane(); 376 } 377 378 379 /* 380 * Notifies client toolkit that it should change a cursor. 381 * 382 * Called from the peer via SwingAccessor, because the 383 * Component.updateCursorImmediately method is final 384 * and could not be overridden. 385 */ 386 private void updateClientCursor() { 387 Point p = MouseInfo.getPointerInfo().getLocation(); 388 SwingUtilities.convertPointFromScreen(p, this); 389 Component target = SwingUtilities.getDeepestComponentAt(this, p.x, p.y); 390 if (target != null) { 391 content.setCursor(target.getCursor()); 392 } 393 } 394 }