Merge branch 'master' of git@git.labs.intellij.net:idea/community into tool-window
[fedora-idea.git] / platform / platform-impl / src / com / intellij / ui / LightweightHint.java
blobfee011278f7f73a96499b127db8b1a42e36db1f7
1 /*
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;
27 import javax.swing.*;
28 import javax.swing.event.EventListenerList;
29 import java.awt.*;
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) {
66 myTitle = title;
69 public boolean isSelectingHint() {
70 return mySelectingHint;
73 public void setSelectingHint(final boolean selectingHint) {
74 mySelectingHint = selectingHint;
77 /**
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 && (myForceLightweightPopup || fitsLayeredPane(layeredPane, myComponent, new RelativePoint(parentComponent, new Point(x, y))))) {
93 final Dimension preferredSize = myComponent.getPreferredSize();
94 final Point layeredPanePoint = SwingUtilities.convertPoint(parentComponent, x, y, layeredPane);
96 myComponent.setBounds(layeredPanePoint.x, layeredPanePoint.y, preferredSize.width, preferredSize.height);
98 layeredPane.add(myComponent, Integer.valueOf(250 + layeredPane.getComponentCount()));
100 myComponent.validate();
101 myComponent.repaint();
103 else {
104 myIsRealPopup = true;
105 myPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(myComponent, null)
106 .setRequestFocus(false)
107 .setResizable(myForceShowAsPopup)
108 .setMovable(myForceShowAsPopup)
109 .setTitle(myTitle)
110 .createPopup();
111 myPopup.show(new RelativePoint(myParentComponent, new Point(x, y)));
115 private static boolean fitsLayeredPane(JLayeredPane pane, JComponent component, RelativePoint desiredLocation) {
116 final Rectangle lpRect = new Rectangle(pane.getLocationOnScreen().x, pane.getLocationOnScreen().y, pane.getWidth(), pane.getHeight());
117 Rectangle componentRect = new Rectangle(desiredLocation.getScreenPoint().x,
118 desiredLocation.getScreenPoint().y,
119 component.getPreferredSize().width,
120 component.getPreferredSize().height);
121 return lpRect.contains(componentRect);
124 private void fireHintHidden() {
125 final EventListener[] listeners = myListenerList.getListeners(HintListener.class);
126 for (EventListener listener : listeners) {
127 ((HintListener)listener).hintHidden(new EventObject(this));
132 * Sets location of the hint in the layered pane coordinate system.
134 public final void setLocation(final int x, final int y) {
135 if (myIsRealPopup) {
136 if (myPopup == null) return;
137 ((AbstractPopup)myPopup).setLocation(new RelativePoint(myParentComponent, new Point(x, y)));
139 else {
140 myComponent.setLocation(x, y);
142 final Dimension preferredSize = myComponent.getPreferredSize();
143 if (!preferredSize.equals(myComponent.getSize())) {
144 final JLayeredPane layeredPane = myParentComponent.getRootPane().getLayeredPane();
145 final Point layeredPanePoint = SwingUtilities.convertPoint(myParentComponent, x, y, layeredPane);
146 myComponent.setBounds(layeredPanePoint.x, layeredPanePoint.y, preferredSize.width, preferredSize.height);
149 myComponent.validate();
150 myComponent.repaint();
155 * x and y are in layered pane coordinate system
157 public final void setBounds(final int x, final int y, final int width, final int height) {
158 if (myIsRealPopup) {
159 setLocation(x, y);
161 else {
162 myComponent.setBounds(x, y, width, height);
163 myComponent.setLocation(x, y);
164 myComponent.validate();
165 myComponent.repaint();
170 * @return bounds of hint component in the layered pane.
172 public final Rectangle getBounds() {
173 return myComponent.getBounds();
176 public boolean isVisible() {
177 return myIsRealPopup ? myPopup != null : myComponent.isShowing();
180 public void hide() {
181 if (isVisible()) {
182 if (myIsRealPopup) {
183 myPopup.cancel();
184 myPopup = null;
186 else {
187 final JRootPane rootPane = myComponent.getRootPane();
188 if (rootPane != null) {
189 final Rectangle bounds = myComponent.getBounds();
190 final JLayeredPane layeredPane = rootPane.getLayeredPane();
192 try {
193 if(myFocusBackComponent != null){
194 LayoutFocusTraversalPolicyExt.setOverridenDefaultComponent(myFocusBackComponent);
196 layeredPane.remove(myComponent);
198 finally {
199 LayoutFocusTraversalPolicyExt.setOverridenDefaultComponent(null);
202 layeredPane.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
206 if (myEscListener != null) {
207 myComponent.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
209 fireHintHidden();
212 public final JComponent getComponent() {
213 return myComponent;
216 public final void addHintListener(final HintListener listener) {
217 myListenerList.add(HintListener.class, listener);
220 public final void removeHintListener(final HintListener listener) {
221 myListenerList.remove(HintListener.class, listener);
224 private final class MyEscListener implements ActionListener {
225 public final void actionPerformed(final ActionEvent e) {
226 hide();
230 @Override
231 public String toString() {
232 return getComponent().toString();