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