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