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