2 * Copyright 2000-2009 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;
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.ui.popup.JBPopup;
20 import com.intellij.openapi.ui.popup.JBPopupFactory;
21 import com.intellij.openapi.util.UserDataHolderBase;
22 import com.intellij.openapi.wm.ex.LayoutFocusTraversalPolicyExt;
23 import com.intellij.ui.awt.RelativePoint;
24 import com.intellij.ui.popup.AbstractPopup;
25 import org.jetbrains.annotations.NotNull;
28 import javax.swing.event.EventListenerList;
30 import java.awt.event.ActionEvent;
31 import java.awt.event.ActionListener;
32 import java.awt.event.KeyEvent;
33 import java.util.EventListener;
34 import java.util.EventObject;
36 public class LightweightHint extends UserDataHolderBase implements Hint {
37 private static final Logger LOG = Logger.getInstance("#com.intellij.ui.LightweightHint");
39 private final JComponent myComponent;
40 private JComponent myFocusBackComponent;
41 private final EventListenerList myListenerList = new EventListenerList();
42 private MyEscListener myEscListener;
43 private JBPopup myPopup;
44 private JComponent myParentComponent;
45 private boolean myIsRealPopup = false;
46 private boolean myForceLightweightPopup = false;
47 private boolean mySelectingHint;
49 private boolean myForceShowAsPopup = false;
50 private String myTitle = null;
51 private boolean myCancelOnClickOutside;
52 private boolean myCancelOnOtherWindowOpen;
53 private boolean myResizable;
55 public LightweightHint(@NotNull final JComponent component) {
56 myComponent = component;
59 public void setForceLightweightPopup(final boolean forceLightweightPopup) {
60 myForceLightweightPopup = forceLightweightPopup;
64 public void setForceShowAsPopup(final boolean forceShowAsPopup) {
65 myForceShowAsPopup = forceShowAsPopup;
68 public void setTitle(final String title) {
72 public boolean isSelectingHint() {
73 return mySelectingHint;
76 public void setSelectingHint(final boolean selectingHint) {
77 mySelectingHint = selectingHint;
80 public void setCancelOnClickOutside(final boolean b) {
81 myCancelOnClickOutside = b;
84 public void setCancelOnOtherWindowOpen(final boolean b) {
85 myCancelOnOtherWindowOpen = b;
88 public void setResizable(final boolean b) {
93 * Shows the hint in the layered pane. Coordinates <code>x</code> and <code>y</code>
94 * are in <code>parentComponent</code> coordinate system. Note that the component
95 * appears on 250 layer.
97 public void show(@NotNull final JComponent parentComponent, final int x, final int y, final JComponent focusBackComponent) {
98 myParentComponent = parentComponent;
100 myFocusBackComponent = focusBackComponent;
102 LOG.assertTrue(myParentComponent.isShowing());
103 myEscListener = new MyEscListener();
104 myComponent.registerKeyboardAction(myEscListener, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
105 JComponent.WHEN_IN_FOCUSED_WINDOW);
106 final JLayeredPane layeredPane = parentComponent.getRootPane().getLayeredPane();
107 if (!myForceShowAsPopup &&
108 (myForceLightweightPopup || fitsLayeredPane(layeredPane, myComponent, new RelativePoint(parentComponent, new Point(x, y))))) {
110 final Dimension preferredSize = myComponent.getPreferredSize();
111 final Point layeredPanePoint = SwingUtilities.convertPoint(parentComponent, x, y, layeredPane);
113 myComponent.setBounds(layeredPanePoint.x, layeredPanePoint.y, preferredSize.width, preferredSize.height);
114 layeredPane.add(myComponent, JLayeredPane.POPUP_LAYER);
116 myComponent.validate();
117 myComponent.repaint();
120 myIsRealPopup = true;
121 myPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(myComponent, null)
122 .setRequestFocus(false)
123 .setResizable(myResizable)
124 .setMovable(myTitle != null)
126 .setShowShadow(false)
127 .setCancelKeyEnabled(false)
128 .setCancelOnClickOutside(myCancelOnClickOutside)
129 .setCancelOnOtherWindowOpen(myCancelOnOtherWindowOpen)
133 myPopup.show(new RelativePoint(myParentComponent, new Point(x, y)));
137 protected void beforeShow() {
141 private static boolean fitsLayeredPane(JLayeredPane pane, JComponent component, RelativePoint desiredLocation) {
142 final Rectangle lpRect = new Rectangle(pane.getLocationOnScreen().x, pane.getLocationOnScreen().y, pane.getWidth(), pane.getHeight());
143 Rectangle componentRect = new Rectangle(desiredLocation.getScreenPoint().x,
144 desiredLocation.getScreenPoint().y,
145 component.getPreferredSize().width,
146 component.getPreferredSize().height);
147 return lpRect.contains(componentRect);
150 private void fireHintHidden() {
151 final EventListener[] listeners = myListenerList.getListeners(HintListener.class);
152 for (EventListener listener : listeners) {
153 ((HintListener)listener).hintHidden(new EventObject(this));
158 * @return bounds of hint component in the layered pane.
160 public final Rectangle getBounds() {
161 return myComponent.getBounds();
164 public boolean isVisible() {
165 return myIsRealPopup ? myPopup != null : myComponent.isShowing();
168 protected final boolean isRealPopup() {
169 return myIsRealPopup;
179 final JRootPane rootPane = myComponent.getRootPane();
180 if (rootPane != null) {
181 final Rectangle bounds = myComponent.getBounds();
182 final JLayeredPane layeredPane = rootPane.getLayeredPane();
185 if (myFocusBackComponent != null) {
186 LayoutFocusTraversalPolicyExt.setOverridenDefaultComponent(myFocusBackComponent);
188 layeredPane.remove(myComponent);
191 LayoutFocusTraversalPolicyExt.setOverridenDefaultComponent(null);
194 layeredPane.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
198 if (myEscListener != null) {
199 myComponent.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
205 public void updateBounds() {
206 updateBounds(-1, -1, false);
210 public void updateBounds(int x, int y) {
211 updateBounds(x, y, true);
214 private void updateBounds(int x, int y, boolean updateLocation) {
216 if (myPopup == null) return;
217 if (updateLocation) ((AbstractPopup)myPopup).setLocation(new RelativePoint(myParentComponent, new Point(x, y)));
218 myPopup.setSize(myComponent.getPreferredSize());
221 if (updateLocation) {
222 JLayeredPane layeredPane = myParentComponent.getRootPane().getLayeredPane();
223 myComponent.setLocation(SwingUtilities.convertPoint(myParentComponent, x, y, layeredPane));
226 Dimension preferredSize = myComponent.getPreferredSize();
227 myComponent.setSize(preferredSize.width, preferredSize.height);
229 myComponent.validate();
230 myComponent.repaint();
234 public final JComponent getComponent() {
238 public final void addHintListener(final HintListener listener) {
239 myListenerList.add(HintListener.class, listener);
242 public final void removeHintListener(final HintListener listener) {
243 myListenerList.remove(HintListener.class, listener);
246 private final class MyEscListener implements ActionListener {
247 public final void actionPerformed(final ActionEvent e) {
253 public String toString() {
254 return getComponent().toString();