Path: blob/master/src/java.desktop/share/classes/sun/swing/JLightweightFrame.java
41153 views
/*1* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.swing;2627import java.awt.*;28import java.awt.dnd.DragGestureEvent;29import java.awt.dnd.DragGestureListener;30import java.awt.dnd.DragGestureRecognizer;31import java.awt.dnd.DragSource;32import java.awt.dnd.DropTarget;33import java.awt.dnd.InvalidDnDOperationException;34import java.awt.dnd.peer.DragSourceContextPeer;35import java.awt.event.ContainerEvent;36import java.awt.event.ContainerListener;37import java.awt.geom.AffineTransform;38import java.awt.image.BufferedImage;39import java.awt.image.DataBufferInt;40import java.beans.PropertyChangeEvent;41import java.beans.PropertyChangeListener;42import java.security.AccessController;43import javax.swing.JComponent;4445import javax.swing.JLayeredPane;46import javax.swing.JPanel;47import javax.swing.JRootPane;48import javax.swing.LayoutFocusTraversalPolicy;49import javax.swing.RepaintManager;50import javax.swing.RootPaneContainer;51import javax.swing.SwingUtilities;5253import sun.awt.AWTAccessor;54import sun.awt.DisplayChangedListener;55import sun.awt.LightweightFrame;56import sun.awt.OverrideNativeWindowHandle;57import sun.security.action.GetPropertyAction;58import sun.swing.SwingUtilities2.RepaintListener;5960/**61* The frame serves as a lightweight container which paints its content62* to an offscreen image and provides access to the image's data via the63* {@link LightweightContent} interface. Note, that it may not be shown64* as a standalone toplevel frame. Its purpose is to provide functionality65* for lightweight embedding.66*67* @author Artem Ananiev68* @author Anton Tarasov69*/70@SuppressWarnings({"removal","serial"}) // JDK-implementation class71public final class JLightweightFrame extends LightweightFrame implements RootPaneContainer {7273private final JRootPane rootPane = new JRootPane();7475private LightweightContent content;7677private Component component;78private JPanel contentPane;7980private BufferedImage bbImage;8182private volatile double scaleFactorX;83private volatile double scaleFactorY;8485/**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 the89* latter changes. {@code JLightweightFrame} passes the copy buffer array90* to the {@link LightweightContent#imageBufferReset} method. The code spot91* which synchronizes two buffers becomes the only critical section guarded92* by the lock (managed with the {@link LightweightContent#paintLock()},93* {@link LightweightContent#paintUnlock()} methods).94*/95private static boolean copyBufferEnabled;96private int[] copyBuffer;9798private PropertyChangeListener layoutSizeListener;99private RepaintListener repaintListener;100101static {102SwingAccessor.setJLightweightFrameAccessor(new SwingAccessor.JLightweightFrameAccessor() {103@Override104public void updateCursor(JLightweightFrame frame) {105frame.updateClientCursor();106}107});108copyBufferEnabled = "true".equals(AccessController.109doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true")));110}111112/**113* Constructs a new, initially invisible {@code JLightweightFrame}114* instance.115*/116public JLightweightFrame() {117super();118AffineTransform defaultTransform =119getGraphicsConfiguration().getDefaultTransform();120scaleFactorX = defaultTransform.getScaleX();121scaleFactorY = defaultTransform.getScaleY();122copyBufferEnabled = "true".equals(AccessController.123doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true")));124125add(rootPane, BorderLayout.CENTER);126setFocusTraversalPolicy(new LayoutFocusTraversalPolicy());127if (getGraphicsConfiguration().isTranslucencyCapable()) {128setBackground(new Color(0, 0, 0, 0));129}130131layoutSizeListener = new PropertyChangeListener() {132@Override133public void propertyChange(PropertyChangeEvent e) {134Dimension d = (Dimension)e.getNewValue();135136if ("preferredSize".equals(e.getPropertyName())) {137content.preferredSizeChanged(d.width, d.height);138139} else if ("maximumSize".equals(e.getPropertyName())) {140content.maximumSizeChanged(d.width, d.height);141142} else if ("minimumSize".equals(e.getPropertyName())) {143content.minimumSizeChanged(d.width, d.height);144}145}146};147148repaintListener = (JComponent c, int x, int y, int w, int h) -> {149Window jlf = SwingUtilities.getWindowAncestor(c);150if (jlf != JLightweightFrame.this) {151return;152}153Point p = SwingUtilities.convertPoint(c, x, y, jlf);154Rectangle r = new Rectangle(p.x, p.y, w, h).intersection(155new Rectangle(0, 0,156(int)Math.round(bbImage.getWidth() / scaleFactorX),157(int)Math.round(bbImage.getHeight() / scaleFactorY)));158159if (!r.isEmpty()) {160notifyImageUpdated(r.x, r.y, r.width, r.height);161}162};163164SwingAccessor.getRepaintManagerAccessor().addRepaintListener(165RepaintManager.currentManager(this), repaintListener);166}167168@Override169public void dispose() {170SwingAccessor.getRepaintManagerAccessor().removeRepaintListener(171RepaintManager.currentManager(this), repaintListener);172super.dispose();173}174175/**176* Sets the {@link LightweightContent} instance for this frame.177* The {@code JComponent} object returned by the178* {@link LightweightContent#getComponent()} method is immediately179* added to the frame's content pane.180*181* @param content the {@link LightweightContent} instance182*/183public void setContent(final LightweightContent content) {184if (content == null) {185System.err.println("JLightweightFrame.setContent: content may not be null!");186return;187}188this.content = content;189this.component = content.getComponent();190191Dimension d = this.component.getPreferredSize();192content.preferredSizeChanged(d.width, d.height);193194d = this.component.getMaximumSize();195content.maximumSizeChanged(d.width, d.height);196197d = this.component.getMinimumSize();198content.minimumSizeChanged(d.width, d.height);199200initInterior();201}202203@Override204public Graphics getGraphics() {205if (bbImage == null) return null;206207Graphics2D g = bbImage.createGraphics();208g.setBackground(getBackground());209g.setColor(getForeground());210g.setFont(getFont());211g.scale(scaleFactorX, scaleFactorY);212return g;213}214215/**216* {@inheritDoc}217*218* @see LightweightContent#focusGrabbed()219*/220@Override221public void grabFocus() {222if (content != null) content.focusGrabbed();223}224225/**226* {@inheritDoc}227*228* @see LightweightContent#focusUngrabbed()229*/230@Override231public void ungrabFocus() {232if (content != null) content.focusUngrabbed();233}234235@Override236@SuppressWarnings("deprecation")237public int getScaleFactor() {238return (int)scaleFactorX;239}240241@Override242public double getScaleFactorX() {243return scaleFactorX;244}245246@Override247public double getScaleFactorY() {248return scaleFactorY;249}250251@Override252@SuppressWarnings("deprecation")253public void notifyDisplayChanged(final int scaleFactor) {254notifyDisplayChanged(scaleFactor, scaleFactor);255}256257@Override258public void notifyDisplayChanged(final double scaleFactorX,259final double scaleFactorY) {260if (Double.compare(scaleFactorX, this.scaleFactorX) != 0 ||261Double.compare(scaleFactorY, this.scaleFactorY) != 0) {262if (!copyBufferEnabled) content.paintLock();263try {264if (bbImage != null) {265resizeBuffer(getWidth(), getHeight(), scaleFactorX,266scaleFactorY);267}268} finally {269if (!copyBufferEnabled) content.paintUnlock();270}271this.scaleFactorX = scaleFactorX;272this.scaleFactorY = scaleFactorY;273274if(isVisible()) {275final Object peer =276AWTAccessor.getComponentAccessor().getPeer(this);277if (peer instanceof DisplayChangedListener) {278((DisplayChangedListener) peer).displayChanged();279}280repaint();281}282}283}284285@Override286public void addNotify() {287super.addNotify();288final Object peer = AWTAccessor.getComponentAccessor().getPeer(this);289if (peer instanceof DisplayChangedListener) {290((DisplayChangedListener) peer).displayChanged();291}292}293294private void syncCopyBuffer(boolean reset, int x, int y, int w, int h,295double scaleX, double scaleY) {296content.paintLock();297try {298int[] srcBuffer = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData();299if (reset) {300copyBuffer = new int[srcBuffer.length];301}302int linestride = bbImage.getWidth();303304int startX = (int)Math.floor(x * scaleX);305int startY = (int)Math.floor(y * scaleY);306int width = (int)Math.ceil((x + w) * scaleX) - startX;307int height = (int)Math.ceil((y + h) * scaleY) - startY;308if (startX + width > linestride) {309width = linestride - startX;310}311if (startY + height > bbImage.getHeight()) {312height = bbImage.getHeight() - startY;313}314315for (int i = 0; i < height; i++) {316int from = (startY + i) * linestride + startX;317System.arraycopy(srcBuffer, from, copyBuffer, from, width);318}319} finally {320content.paintUnlock();321}322}323324private void notifyImageUpdated(int x, int y, int width, int height) {325if (copyBufferEnabled) {326syncCopyBuffer(false, x, y, width, height, scaleFactorX,327scaleFactorY);328}329content.imageUpdated(x, y, width, height);330}331332@SuppressWarnings("serial") // anonymous class inside333private void initInterior() {334contentPane = new JPanel() {335@Override336public void paint(Graphics g) {337if (!copyBufferEnabled) {338content.paintLock();339}340try {341super.paint(g);342343final Rectangle clip = g.getClipBounds() != null ?344g.getClipBounds() :345new Rectangle(0, 0, contentPane.getWidth(), contentPane.getHeight());346347clip.x = Math.max(0, clip.x);348clip.y = Math.max(0, clip.y);349clip.width = Math.min(contentPane.getWidth(), clip.width);350clip.height = Math.min(contentPane.getHeight(), clip.height);351352EventQueue.invokeLater(new Runnable() {353@Override354public void run() {355Rectangle c = contentPane.getBounds().intersection(clip);356notifyImageUpdated(c.x, c.y, c.width, c.height);357}358});359} finally {360if (!copyBufferEnabled) {361content.paintUnlock();362}363}364}365@Override366protected boolean isPaintingOrigin() {367return true;368}369};370contentPane.setLayout(new BorderLayout());371contentPane.add(component);372if ("true".equals(AccessController.373doPrivileged(new GetPropertyAction("swing.jlf.contentPaneTransparent", "false"))))374{375contentPane.setOpaque(false);376}377setContentPane(contentPane);378379contentPane.addContainerListener(new ContainerListener() {380@Override381public void componentAdded(ContainerEvent e) {382Component c = JLightweightFrame.this.component;383if (e.getChild() == c) {384c.addPropertyChangeListener("preferredSize", layoutSizeListener);385c.addPropertyChangeListener("maximumSize", layoutSizeListener);386c.addPropertyChangeListener("minimumSize", layoutSizeListener);387}388}389@Override390public void componentRemoved(ContainerEvent e) {391Component c = JLightweightFrame.this.component;392if (e.getChild() == c) {393c.removePropertyChangeListener(layoutSizeListener);394}395}396});397}398399@SuppressWarnings("deprecation")400@Override public void reshape(int x, int y, int width, int height) {401super.reshape(x, y, width, height);402403if (width == 0 || height == 0) {404return;405}406if (!copyBufferEnabled) {407content.paintLock();408}409try {410boolean createBB = (bbImage == null);411int newW = width;412int newH = height;413if (bbImage != null) {414int imgWidth = (int)Math.round(bbImage.getWidth() /415scaleFactorX);416int imgHeight = (int)Math.round(bbImage.getHeight() /417scaleFactorY);418if (width != imgWidth || height != imgHeight) {419createBB = true;420if (bbImage != null) {421int oldW = imgWidth;422int oldH = imgHeight;423if ((oldW >= newW) && (oldH >= newH)) {424createBB = false;425} else {426if (oldW >= newW) {427newW = oldW;428} else {429newW = Math.max((int)(oldW * 1.2), width);430}431if (oldH >= newH) {432newH = oldH;433} else {434newH = Math.max((int)(oldH * 1.2), height);435}436}437}438}439}440if (createBB) {441resizeBuffer(newW, newH, scaleFactorX, scaleFactorY);442return;443}444content.imageReshaped(0, 0, width, height);445446} finally {447if (!copyBufferEnabled) {448content.paintUnlock();449}450}451}452453private void resizeBuffer(int width, int height, double newScaleFactorX,454double newScaleFactorY) {455bbImage = new BufferedImage((int)Math.round(width * newScaleFactorX),456(int)Math.round(height * newScaleFactorY),457BufferedImage.TYPE_INT_ARGB_PRE);458int[] pixels= ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData();459if (copyBufferEnabled) {460syncCopyBuffer(true, 0, 0, width, height, newScaleFactorX,461newScaleFactorY);462pixels = copyBuffer;463}464content.imageBufferReset(pixels, 0, 0, width, height,465bbImage.getWidth(), newScaleFactorX, newScaleFactorY);466}467468@Override469public JRootPane getRootPane() {470return rootPane;471}472473@Override474public void setContentPane(Container contentPane) {475getRootPane().setContentPane(contentPane);476}477478@Override479public Container getContentPane() {480return getRootPane().getContentPane();481}482483@Override484public void setLayeredPane(JLayeredPane layeredPane) {485getRootPane().setLayeredPane(layeredPane);486}487488@Override489public JLayeredPane getLayeredPane() {490return getRootPane().getLayeredPane();491}492493@Override494public void setGlassPane(Component glassPane) {495getRootPane().setGlassPane(glassPane);496}497498@Override499public Component getGlassPane() {500return getRootPane().getGlassPane();501}502503504/*505* Notifies client toolkit that it should change a cursor.506*507* Called from the peer via SwingAccessor, because the508* Component.updateCursorImmediately method is final509* and could not be overridden.510*/511private void updateClientCursor() {512PointerInfo pointerInfo = MouseInfo.getPointerInfo();513if (pointerInfo == null) {514/*515* This can happen when multiple graphics device cannot decide516* which graphics device contains the current mouse position517* or on systems without a mouse518*/519return;520}521Point p = pointerInfo.getLocation();522SwingUtilities.convertPointFromScreen(p, this);523Component target = SwingUtilities.getDeepestComponentAt(this, p.x, p.y);524if (target != null) {525content.setCursor(target.getCursor());526}527}528529//Called by reflection by SwingNode530public void overrideNativeWindowHandle(long handle, Runnable closeWindow) {531final Object peer = AWTAccessor.getComponentAccessor().getPeer(this);532if (peer instanceof OverrideNativeWindowHandle) {533((OverrideNativeWindowHandle) peer).overrideWindowHandle(handle);534}535if (closeWindow != null) {536closeWindow.run();537}538}539540541public <T extends DragGestureRecognizer> T createDragGestureRecognizer(542Class<T> abstractRecognizerClass,543DragSource ds, Component c, int srcActions,544DragGestureListener dgl)545{546return content == null ? null : content.createDragGestureRecognizer(547abstractRecognizerClass, ds, c, srcActions, dgl);548}549550public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {551return content == null ? null : content.createDragSourceContextPeer(dge);552}553554public void addDropTarget(DropTarget dt) {555if (content == null) return;556content.addDropTarget(dt);557}558559public void removeDropTarget(DropTarget dt) {560if (content == null) return;561content.removeDropTarget(dt);562}563}564565566