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.EventQueue;
  33 import java.awt.Graphics;
  34 import java.awt.Graphics2D;
  35 import java.awt.Rectangle;
  36 import java.awt.image.BufferedImage;
  37 import java.awt.image.DataBufferInt;
  38 
  39 import javax.swing.JLayeredPane;
  40 import javax.swing.JPanel;
  41 import javax.swing.JRootPane;
  42 import javax.swing.LayoutFocusTraversalPolicy;
  43 import javax.swing.RootPaneContainer;
  44 
  45 import sun.awt.LightweightFrame;
  46 
  47 /**
  48  * The frame serves as a lightweight container which paints its content
  49  * to an offscreen image and provides access to the image's data via the
  50  * {@link LightweightContent} interface. Note, that it may not be shown
  51  * as a standalone toplevel frame. Its purpose is to provide functionality
  52  * for lightweight embedding.
  53  * 
  54  * @author Artem Ananiev
  55  * @author Anton Tarasov
  56  */
  57 public final class JLightweightFrame extends LightweightFrame implements RootPaneContainer {
  58     
  59     private final JRootPane rootPane = new JRootPane();
  60     
  61     private LightweightContent content;
  62 
  63     private Component component;
  64     private JPanel contentPane;
  65 
  66     private BufferedImage bbImage;
  67     
  68     /**
  69      * Constructs a new, initially invisible {@code JLightweightFrame}
  70      * instance.
  71      */
  72     public JLightweightFrame() {
  73         super();
  74         add(rootPane, BorderLayout.CENTER);
  75         setBackground(new Color(0, 0, 0, 0));
  76         setFocusTraversalPolicy(new LayoutFocusTraversalPolicy());
  77     }
  78     
  79     /**
  80      * Sets the {@link LightweightContent} instance for this frame.
  81      * The {@code JComponent} object returned by the
  82      * {@link LightweightContent#getComponent()} method is immediately
  83      * added to the frame's content pane.
  84      * 
  85      * @param content the {@link LightweightContent} instance
  86      */
  87     public void setContent(LightweightContent content) {
  88         this.content = content;
  89         this.component = content.getComponent();
  90 
  91         initInterior();
  92     }
  93 
  94     @Override
  95     public Graphics getGraphics() {
  96         if (bbImage == null) return null;
  97         
  98         Graphics2D g = bbImage.createGraphics();
  99         g.setBackground(getBackground());
 100         g.setColor(getForeground());
 101         g.setFont(getFont());
 102         return g;
 103     }
 104     
 105     /**
 106      * {@inheritDoc}
 107      *
 108      * @see LightweightContent#focusGrabbed()
 109      */    
 110     @Override
 111     public void grabFocus() {
 112         if (content != null) content.focusGrabbed();
 113     }
 114     
 115     /**
 116      * {@inheritDoc}
 117      *
 118      * @see LightweightContent#focusUngrabbed()
 119      */    
 120     @Override
 121     public void ungrabFocus() {
 122         if (content != null) content.focusUngrabbed();
 123     }
 124     
 125     private void initInterior() {
 126         contentPane = new JPanel() {
 127             @Override
 128             public void paint(Graphics g) {
 129                 content.paintLock();
 130                 try {
 131                     super.paint(g);
 132                     
 133                     final Rectangle clip = g.getClipBounds() != null ?
 134                             g.getClipBounds() : new Rectangle(0, 0, contentPane.getWidth(), contentPane.getHeight());
 135 
 136                     clip.x = Math.max(0, clip.x);
 137                     clip.y = Math.max(0, clip.y);
 138                     clip.width = Math.min(contentPane.getWidth(), clip.width);
 139                     clip.height = Math.min(contentPane.getHeight(), clip.height);
 140                     
 141                     EventQueue.invokeLater(new Runnable() {
 142                         @Override
 143                         public void run() {
 144                             content.imageUpdated(clip.x, clip.y, clip.width, clip.height);
 145                         }
 146                     });
 147                 } finally {
 148                     content.paintUnlock();
 149                 }
 150             }
 151             @Override
 152             protected boolean isPaintingOrigin() {
 153                 return true;
 154             }
 155         };
 156         contentPane.setLayout(new BorderLayout());
 157         contentPane.add(component);
 158         setContentPane(contentPane);
 159     }
 160 
 161     @SuppressWarnings("deprecation")
 162     @Override public void reshape(int x, int y, int width, int height) {
 163         super.reshape(x, y, width, height);
 164         
 165         if (width == 0 || height == 0) {
 166             return;
 167         }
 168 
 169         content.paintLock();
 170         try {
 171             if ((bbImage == null) || (width != bbImage.getWidth()) || (height != bbImage.getHeight())) {
 172                 boolean createBB = true;
 173                 int newW = width;
 174                 int newH = height;
 175                 if (bbImage != null) {
 176                     int oldW = bbImage.getWidth();
 177                     int oldH = bbImage.getHeight();
 178                     if ((oldW >= newW) && (oldH >= newH)) {
 179                         createBB = false;
 180                     } else {
 181                         if (oldW >= newW) {
 182                             newW = oldW;
 183                         } else {
 184                             newW = Math.max((int)(oldW * 1.2), width);
 185                         }
 186                         if (oldH >= newH) {
 187                             newH = oldH;
 188                         } else {
 189                             newH = Math.max((int)(oldH * 1.2), height);
 190                         }
 191                     }
 192                 }
 193                 if (createBB) {
 194                     BufferedImage oldBB = bbImage;
 195                     bbImage = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB_PRE);
 196                     if (oldBB != null) {
 197                         Graphics g = bbImage.getGraphics();
 198                         try {
 199                             g.drawImage(oldBB, 0, 0, newW, newH, null);
 200                         } finally {
 201                             g.dispose();
 202                             oldBB.flush();
 203                         }
 204                     }
 205                     DataBufferInt dataBuf = (DataBufferInt)bbImage.getRaster().getDataBuffer();
 206                     content.imageBufferReset(dataBuf.getData(), 0, 0, width, height, bbImage.getWidth());
 207                 } else {
 208                     content.imageReshaped(0, 0, width, height);
 209                 }
 210             }
 211         } finally {
 212             content.paintUnlock();
 213         }
 214     }
 215 
 216     @Override
 217     public JRootPane getRootPane() {
 218         return rootPane;
 219     }
 220 
 221     @Override
 222     public void setContentPane(Container contentPane) {
 223         getRootPane().setContentPane(contentPane);
 224     }
 225 
 226     @Override
 227     public Container getContentPane() {
 228         return getRootPane().getContentPane();
 229     }
 230 
 231     @Override
 232     public void setLayeredPane(JLayeredPane layeredPane) {
 233         getRootPane().setLayeredPane(layeredPane);
 234     }
 235 
 236     @Override
 237     public JLayeredPane getLayeredPane() {
 238         return getRootPane().getLayeredPane();
 239     }
 240 
 241     @Override
 242     public void setGlassPane(Component glassPane) {
 243         getRootPane().setGlassPane(glassPane);
 244     }
 245 
 246     @Override
 247     public Component getGlassPane() {
 248         return getRootPane().getGlassPane();
 249     }
 250 }