2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.ui.popup;
18 import com.intellij.util.ui.UIUtil;
19 import org.jetbrains.annotations.NotNull;
23 import java.util.ArrayDeque;
26 * @author Sergey Malenkov
28 public class MovablePopup {
29 private static final Object CACHE = new Object();
30 private final Component myOwner;
31 private final Component myContent;
32 private Rectangle myViewBounds;
33 private Container myView;
34 private boolean myAlwaysOnTop;
35 private boolean myHeavyWeight;
36 private boolean myWindowFocusable;
37 private boolean myWindowShadow;
40 * @param owner a component to which this popup belongs
41 * @param content a component to show within this popup
43 public MovablePopup(@NotNull Component owner, @NotNull Component content) {
46 myViewBounds = new Rectangle(content.getPreferredSize());
51 * Sets whether this popup should be always on top.
52 * This property is used by heavy weight popups only.
54 public void setAlwaysOnTop(boolean value) {
55 if (myAlwaysOnTop != value) {
56 myAlwaysOnTop = value;
57 disposeAndUpdate(true);
61 private static void setAlwaysOnTop(@NotNull Window window, boolean value) {
62 if (value != window.isAlwaysOnTop()) {
64 window.setAlwaysOnTop(value);
66 catch (Exception ignored) {
72 * Sets whether this popup should be a separate window.
73 * A light weight popups are painted on the layered pane.
75 public void setHeavyWeight(boolean value) {
76 if (myHeavyWeight != value) {
77 myHeavyWeight = value;
78 disposeAndUpdate(true);
83 * Sets whether this popup should grab a focus.
84 * This property is used by heavy weight popups only.
86 public void setWindowFocusable(boolean value) {
87 if (myWindowFocusable != value) {
88 myWindowFocusable = value;
89 disposeAndUpdate(true);
93 private static void setWindowFocusable(@NotNull Window window, boolean value) {
94 if (value != window.getFocusableWindowState()) {
95 window.setFocusableWindowState(value);
100 * Sets whether this popup should have a shadow.
101 * This property is used by heavy weight popups only.
103 public void setWindowShadow(boolean value) {
104 if (myWindowShadow != value) {
105 myWindowShadow = value;
106 disposeAndUpdate(true);
110 private static void setWindowShadow(@NotNull Window window, boolean value) {
111 JRootPane root = getRootPane(window);
113 root.putClientProperty("Window.shadow", value);
117 public void setBounds(@NotNull Rectangle bounds) {
118 setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
121 public void setBounds(int x, int y, int width, int height) {
122 if (myViewBounds != null) {
123 myViewBounds.setBounds(x, y, width, height);
126 setBounds(new Point(x, y), new Dimension(width, height));
130 public void setLocation(@NotNull Point location) {
131 setLocation(location.x, location.y);
134 public void setLocation(int x, int y) {
135 if (myViewBounds != null) {
136 myViewBounds.setLocation(x, y);
139 setBounds(new Point(x, y), null);
143 public void setSize(@NotNull Dimension size) {
144 setSize(size.width, size.height);
147 public void setSize(int width, int height) {
148 if (myViewBounds != null) {
149 myViewBounds.setSize(width, height);
152 setBounds(null, new Dimension(width, height));
156 public void setVisible(boolean visible) {
157 if (!visible && myView != null) {
158 disposeAndUpdate(false);
160 else if (visible && myView == null) {
161 Window owner = UIUtil.getWindow(myOwner);
164 Window view = pop(owner);
166 view = new JWindow(owner);
167 view.setType(Window.Type.POPUP);
169 setAlwaysOnTop(view, myAlwaysOnTop);
170 setWindowFocusable(view, myWindowFocusable);
171 setWindowShadow(view, myWindowShadow);
174 else if (owner instanceof RootPaneContainer) {
175 JLayeredPane parent = ((RootPaneContainer)owner).getLayeredPane();
176 if (parent != null) {
177 JPanel view = new JPanel(new BorderLayout());
178 view.setVisible(false);
179 parent.add(view, JLayeredPane.POPUP_LAYER, 0);
184 if (myView != null) {
185 myView.add(myContent);
186 Component parent = myView instanceof Window ? null : myView.getParent();
187 if (parent != null) {
188 Point location = myViewBounds.getLocation();
189 SwingUtilities.convertPointFromScreen(location, parent);
190 myViewBounds.setLocation(location);
192 myView.setBackground(UIUtil.getLabelBackground());
193 myView.setBounds(myViewBounds);
194 myView.setVisible(true);
201 * Determines whether this popup should be visible.
203 public boolean isVisible() {
204 return myView != null && myView.isVisible();
207 private void disposeAndUpdate(boolean update) {
208 if (myView != null) {
209 boolean visible = myView.isVisible();
210 myView.setVisible(false);
211 Container container = myContent.getParent();
212 if (container != null) {
213 container.remove(myContent);
215 if (myView instanceof Window) {
216 myViewBounds = myView.getBounds();
217 Window window = (Window)myView;
218 if (!push(UIUtil.getWindow(myOwner), window)) {
223 Container parent = myView.getParent();
224 if (parent == null) {
225 myViewBounds = new Rectangle(myContent.getPreferredSize());
228 myViewBounds = new Rectangle(myView.getBounds());
229 parent.remove(myView);
230 Point point = new Point(myViewBounds.x, myViewBounds.y);
231 SwingUtilities.convertPointToScreen(point, parent);
232 myViewBounds.x = point.x;
233 myViewBounds.y = point.y;
237 if (update && visible) {
243 private void setBounds(Point location, Dimension size) {
244 if (myView != null) {
246 size = myView.getSize();
248 if (location == null) {
249 location = myView.getLocation();
252 Component parent = myView instanceof Window ? null : myView.getParent();
253 if (parent != null) {
254 SwingUtilities.convertPointFromScreen(location, parent);
257 myView.setBounds(location.x, location.y, size.width, size.height);
258 if (myView.isVisible()) {
266 private static JRootPane getRootPane(Window window) {
267 if (window instanceof RootPaneContainer) {
268 RootPaneContainer container = (RootPaneContainer)window;
269 return container.getRootPane();
274 private static Window pop(Window owner) {
275 JRootPane root = getRootPane(owner);
277 synchronized (CACHE) {
278 @SuppressWarnings("unchecked")
279 ArrayDeque<Window> cache = (ArrayDeque<Window>)root.getClientProperty(CACHE);
280 if (cache != null && !cache.isEmpty()) {
288 private static boolean push(Window owner, Window window) {
289 JRootPane root = getRootPane(owner);
291 synchronized (CACHE) {
292 @SuppressWarnings("unchecked")
293 ArrayDeque<Window> cache = (ArrayDeque<Window>)root.getClientProperty(CACHE);
295 cache = new ArrayDeque<>();
296 root.putClientProperty(CACHE, cache);