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;
52 public LightweightHint(@NotNull final JComponent component) {
53 myComponent = component;
56 public void setForceLightweightPopup(final boolean forceLightweightPopup) {
57 myForceLightweightPopup = forceLightweightPopup;
61 public void setForceShowAsPopup(final boolean forceShowAsPopup) {
62 myForceShowAsPopup = forceShowAsPopup;
65 public void setTitle(final String title) {
69 public boolean isSelectingHint() {
70 return mySelectingHint;
73 public void setSelectingHint(final boolean selectingHint) {
74 mySelectingHint = selectingHint;
78 * Shows the hint in the layered pane. Coordinates <code>x</code> and <code>y</code>
79 * are in <code>parentComponent</code> coordinate system. Note that the component
80 * appears on 250 layer.
82 public void show(@NotNull final JComponent parentComponent, final int x, final int y, final JComponent focusBackComponent) {
83 myParentComponent = parentComponent;
85 myFocusBackComponent = focusBackComponent;
87 LOG.assertTrue(myParentComponent.isShowing());
88 myEscListener = new MyEscListener();
89 myComponent.registerKeyboardAction(myEscListener, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
90 JComponent.WHEN_IN_FOCUSED_WINDOW);
91 final JLayeredPane layeredPane = parentComponent.getRootPane().getLayeredPane();
92 if (!myForceShowAsPopup &&
93 (myForceLightweightPopup || fitsLayeredPane(layeredPane, myComponent, new RelativePoint(parentComponent, new Point(x, y))))) {
95 final Dimension preferredSize = myComponent.getPreferredSize();
96 final Point layeredPanePoint = SwingUtilities.convertPoint(parentComponent, x, y, layeredPane);
98 myComponent.setBounds(layeredPanePoint.x, layeredPanePoint.y, preferredSize.width, preferredSize.height);
99 layeredPane.add(myComponent, JLayeredPane.POPUP_LAYER);
101 myComponent.validate();
102 myComponent.repaint();
105 myIsRealPopup = true;
106 myPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(myComponent, null)
107 .setRequestFocus(false)
108 .setResizable(myForceShowAsPopup)
109 .setMovable(myForceShowAsPopup)
111 .setShowShadow(false)
112 .setCancelKeyEnabled(false)
113 .setCancelOnClickOutside(false)
114 .setCancelOnOtherWindowOpen(false)
118 myPopup.show(new RelativePoint(myParentComponent, new Point(x, y)));
122 protected void beforeShow() {
126 private static boolean fitsLayeredPane(JLayeredPane pane, JComponent component, RelativePoint desiredLocation) {
127 final Rectangle lpRect = new Rectangle(pane.getLocationOnScreen().x, pane.getLocationOnScreen().y, pane.getWidth(), pane.getHeight());
128 Rectangle componentRect = new Rectangle(desiredLocation.getScreenPoint().x,
129 desiredLocation.getScreenPoint().y,
130 component.getPreferredSize().width,
131 component.getPreferredSize().height);
132 return lpRect.contains(componentRect);
135 private void fireHintHidden() {
136 final EventListener[] listeners = myListenerList.getListeners(HintListener.class);
137 for (EventListener listener : listeners) {
138 ((HintListener)listener).hintHidden(new EventObject(this));
143 * @return bounds of hint component in the layered pane.
145 public final Rectangle getBounds() {
146 return myComponent.getBounds();
149 public boolean isVisible() {
150 return myIsRealPopup ? myPopup != null : myComponent.isShowing();
153 protected final boolean isRealPopup() {
154 return myIsRealPopup;
164 final JRootPane rootPane = myComponent.getRootPane();
165 if (rootPane != null) {
166 final Rectangle bounds = myComponent.getBounds();
167 final JLayeredPane layeredPane = rootPane.getLayeredPane();
170 if (myFocusBackComponent != null) {
171 LayoutFocusTraversalPolicyExt.setOverridenDefaultComponent(myFocusBackComponent);
173 layeredPane.remove(myComponent);
176 LayoutFocusTraversalPolicyExt.setOverridenDefaultComponent(null);
179 layeredPane.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
183 if (myEscListener != null) {
184 myComponent.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
190 public void updateBounds() {
191 updateBounds(-1, -1, false);
195 public void updateBounds(int x, int y) {
196 updateBounds(x, y, true);
199 private void updateBounds(int x, int y, boolean updateLocation) {
201 if (myPopup == null) return;
202 if (updateLocation) ((AbstractPopup)myPopup).setLocation(new RelativePoint(myParentComponent, new Point(x, y)));
203 myPopup.setSize(myComponent.getPreferredSize());
206 if (updateLocation) {
207 JLayeredPane layeredPane = myParentComponent.getRootPane().getLayeredPane();
208 myComponent.setLocation(SwingUtilities.convertPoint(myParentComponent, x, y, layeredPane));
211 Dimension preferredSize = myComponent.getPreferredSize();
212 myComponent.setSize(preferredSize.width, preferredSize.height);
214 myComponent.validate();
215 myComponent.repaint();
219 public final JComponent getComponent() {
223 public final void addHintListener(final HintListener listener) {
224 myListenerList.add(HintListener.class, listener);
227 public final void removeHintListener(final HintListener listener) {
228 myListenerList.remove(HintListener.class, listener);
231 private final class MyEscListener implements ActionListener {
232 public final void actionPerformed(final ActionEvent e) {
238 public String toString() {
239 return getComponent().toString();