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.popup;
18 import com.intellij.codeInsight.hint.HintUtil;
19 import com.intellij.ide.ui.UISettings;
20 import com.intellij.openapi.Disposable;
21 import com.intellij.openapi.actionSystem.DataContext;
22 import com.intellij.openapi.actionSystem.JBAwtEventQueue;
23 import com.intellij.openapi.actionSystem.PlatformDataKeys;
24 import com.intellij.openapi.application.ApplicationManager;
25 import com.intellij.openapi.application.ex.ApplicationManagerEx;
26 import com.intellij.openapi.diagnostic.Logger;
27 import com.intellij.openapi.editor.Editor;
28 import com.intellij.openapi.editor.ex.EditorEx;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.ui.impl.ShadowBorderPainter;
31 import com.intellij.openapi.ui.popup.*;
32 import com.intellij.openapi.util.*;
33 import com.intellij.openapi.util.registry.Registry;
34 import com.intellij.openapi.wm.IdeFocusManager;
35 import com.intellij.openapi.wm.WindowManager;
36 import com.intellij.openapi.wm.ex.WindowManagerEx;
37 import com.intellij.openapi.wm.impl.IdeFrameImpl;
38 import com.intellij.openapi.wm.impl.IdeGlassPaneImpl;
39 import com.intellij.ui.*;
40 import com.intellij.ui.awt.RelativePoint;
41 import com.intellij.ui.speedSearch.SpeedSearch;
42 import com.intellij.util.ImageLoader;
43 import com.intellij.util.Processor;
44 import com.intellij.util.ui.ChildFocusWatcher;
45 import com.intellij.util.ui.EmptyIcon;
46 import com.intellij.util.ui.UIUtil;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
51 import javax.swing.border.EmptyBorder;
53 import java.awt.event.*;
54 import java.awt.image.BufferedImage;
55 import java.util.ArrayList;
56 import java.util.HashSet;
57 import java.util.List;
60 import static com.intellij.openapi.ui.impl.ShadowBorderPainter.*;
62 public class AbstractPopup implements JBPopup {
63 private static final Logger LOG = Logger.getInstance("#com.intellij.ui.popup.AbstractPopup");
65 private static final Image ourMacCorner = ImageLoader.loadFromResource("/general/macCorner.png");
67 private PopupComponent myPopup;
68 private MyContentPanel myContent;
69 private JComponent myPreferredFocusedComponent;
70 private boolean myRequestFocus;
71 private boolean myFocusable;
72 private boolean myForcedHeavyweight = false;
73 private boolean myLocateWithinScreen = true;
74 private boolean myResizable = false;
75 private JPanel myHeaderPanel;
76 private CaptionPanel myCaption = null;
77 private JComponent myComponent;
78 private String myDimensionServiceKey = null;
79 private Computable<Boolean> myCallBack = null;
80 private Project myProject;
81 private boolean myCancelOnClickOutside;
82 private Set<JBPopupListener> myListeners;
83 private boolean myUseDimServiceForXYLocation;
84 private MouseChecker myCancelOnMouseOutCallback;
85 private Canceller myMouseOutCanceller;
86 private boolean myCancelOnWindow;
87 private Dimension myForcedSize;
88 private Point myForcedLocation;
89 private ChildFocusWatcher myFocusWatcher;
90 private boolean myCancelKeyEnabled;
91 private boolean myLocateByContent;
92 protected FocusTrackback myFocusTrackback;
93 private Dimension myMinSize;
94 private ArrayList<Object> myUserData;
95 private boolean myShadowed;
96 private boolean myPaintShadow;
98 private float myAlpha = 0;
99 private float myLastAlpha = 0;
101 private MaskProvider myMaskProvider;
103 private Window myWindow;
104 private boolean myInStack;
105 private MyWindowListener myWindowListener;
107 private boolean myModalContext;
109 private Component[] myFocusOwners;
110 private PopupBorder myPopupBorder;
111 private Dimension myRestoreWindowSize;
112 protected Component myOwner;
113 protected Component myRequestorComponent;
114 private boolean myHeaderAlwaysFocusable;
115 private boolean myMovable;
116 private JComponent myHeaderComponent;
118 protected InputEvent myDisposeEvent;
120 private Runnable myFinalRunnable;
122 protected boolean myOk;
124 protected final SpeedSearch mySpeedSearch = new SpeedSearch() {
125 boolean searchFieldShown = false;
127 protected void update() {
128 mySpeedSearchPatternField.setBackground(new JTextField().getBackground());
129 onSpeedSearchPatternChanged();
130 mySpeedSearchPatternField.setText(getFilter());
131 if (isHoldingFilter() && !searchFieldShown) {
132 setHeaderComponent(mySpeedSearchPatternField);
133 searchFieldShown = true;
135 else if (!isHoldingFilter() && searchFieldShown) {
136 setHeaderComponent(null);
137 searchFieldShown = false;
142 public void noHits() {
143 mySpeedSearchPatternField.setBackground(LightColors.RED);
147 private JTextField mySpeedSearchPatternField;
148 private boolean myNativePopup;
149 private boolean myMayBeParent;
155 AbstractPopup init(final Project project,
156 @NotNull final JComponent component,
157 @Nullable final JComponent preferredFocusedComponent,
158 final boolean requestFocus,
159 final boolean focusable,
160 final boolean forceHeavyweight,
161 final boolean movable,
162 final String dimensionServiceKey,
163 final boolean resizable,
164 @Nullable final String caption,
165 @Nullable final Computable<Boolean> callback,
166 final boolean cancelOnClickOutside,
167 @Nullable final Set<JBPopupListener> listeners,
168 final boolean useDimServiceForXYLocation,
169 InplaceButton commandButton,
170 @Nullable final IconButton cancelButton,
171 @Nullable final MouseChecker cancelOnMouseOutCallback,
172 final boolean cancelOnWindow,
173 @Nullable final ActiveIcon titleIcon,
174 final boolean cancelKeyEnabled,
175 final boolean locateBycontent,
176 final boolean placeWithinScreenBounds,
177 @Nullable final Dimension minSize,
179 @Nullable MaskProvider maskProvider,
181 boolean modalContext,
182 @Nullable Component[] focusOwners,
183 @Nullable String adText,
184 final boolean headerAlwaysFocusable,
185 @NotNull List<Pair<ActionListener, KeyStroke>> keyboardActions,
186 Component settingsButtons,
187 @Nullable final Processor<JBPopup> pinCallback,
189 boolean showShadow) {
191 if (requestFocus && !focusable) {
192 assert false : "Incorrect argument combination: requestFocus=" + requestFocus + " focusable=" + focusable;
196 myComponent = component;
197 myPopupBorder = PopupBorder.Factory.create(true);
198 myShadowed = showShadow;
199 myPaintShadow = showShadow && !SystemInfo.isMac && !movable && !resizable && Registry.is("ide.popup.dropShadow");
200 myContent = createContentPanel(resizable, myPopupBorder, isToDrawMacCorner());
201 myMayBeParent = mayBeParent;
203 myContent.add(component, BorderLayout.CENTER);
204 if (adText != null) {
205 myContent.add(HintUtil.createAdComponent(adText), BorderLayout.SOUTH);
208 myCancelKeyEnabled = cancelKeyEnabled;
209 myLocateByContent = locateBycontent;
210 myLocateWithinScreen = placeWithinScreenBounds;
212 myMaskProvider = maskProvider;
214 myModalContext = modalContext;
215 myFocusOwners = focusOwners;
216 myHeaderAlwaysFocusable = headerAlwaysFocusable;
219 ActiveIcon actualIcon = titleIcon == null ? new ActiveIcon(new EmptyIcon(0)) : titleIcon;
221 myHeaderPanel = new JPanel(new BorderLayout());
223 if (caption != null) {
224 if (caption.length() > 0) {
225 myCaption = new TitlePanel(actualIcon.getRegular(), actualIcon.getInactive());
226 ((TitlePanel)myCaption).setText(caption);
229 myCaption = new CaptionPanel();
232 if (pinCallback != null) {
233 myCaption.setButtonComponent(new InplaceButton(new IconButton("Pin", IconLoader.getIcon("/general/autohideOff.png"),
234 IconLoader.getIcon("/general/autohideOff.png"),
235 IconLoader.getIcon("/general/autohideOffInactive.png")),
236 new ActionListener() {
237 public void actionPerformed(final ActionEvent e) {
238 pinCallback.process(AbstractPopup.this);
242 else if (cancelButton != null) {
243 myCaption.setButtonComponent(new InplaceButton(cancelButton, new ActionListener() {
244 public void actionPerformed(final ActionEvent e) {
249 else if (commandButton != null) {
250 myCaption.setButtonComponent(commandButton);
254 myCaption = new CaptionPanel();
255 myCaption.setBorder(null);
256 myCaption.setPreferredSize(new Dimension(0, 0));
259 setWindowActive(myHeaderAlwaysFocusable);
261 myHeaderPanel.add(myCaption, BorderLayout.NORTH);
262 myContent.add(myHeaderPanel, BorderLayout.NORTH);
264 myForcedHeavyweight = forceHeavyweight;
265 myResizable = resizable;
266 myPreferredFocusedComponent = preferredFocusedComponent;
267 myRequestFocus = requestFocus;
268 myFocusable = focusable;
269 myDimensionServiceKey = dimensionServiceKey;
270 myCallBack = callback;
271 myCancelOnClickOutside = cancelOnClickOutside;
272 myCancelOnMouseOutCallback = cancelOnMouseOutCallback;
273 myListeners = listeners == null ? new HashSet<JBPopupListener>() : listeners;
274 myUseDimServiceForXYLocation = useDimServiceForXYLocation;
275 myCancelOnWindow = cancelOnWindow;
278 for (Pair<ActionListener, KeyStroke> pair : keyboardActions) {
279 myContent.registerKeyboardAction(pair.getFirst(), pair.getSecond(), JComponent.WHEN_IN_FOCUSED_WINDOW);
282 if (settingsButtons != null) {
283 myCaption.addSettingsComponent(settingsButtons);
289 private void setWindowActive(boolean active) {
290 boolean value = myHeaderAlwaysFocusable || active;
292 if (myCaption != null) {
293 myCaption.setActive(value);
295 myPopupBorder.setActive(value);
301 protected MyContentPanel createContentPanel(final boolean resizable, PopupBorder border, boolean isToDrawMacCorner) {
302 return new MyContentPanel(resizable, border, isToDrawMacCorner, myPaintShadow);
305 public static boolean isToDrawMacCorner() {
306 return SystemInfo.isMac;
310 public String getDimensionServiceKey() {
311 return myDimensionServiceKey;
314 public void setDimensionServiceKey(final String dimensionServiceKey) {
315 myDimensionServiceKey = dimensionServiceKey;
318 public void showInCenterOf(@NotNull Component aContainer) {
319 final Point popupPoint = getCenterOf(aContainer, myContent);
320 show(aContainer, popupPoint.x, popupPoint.y, false);
323 public void setAdText(@NotNull final String s) {
324 myContent.add(HintUtil.createAdComponent(s, BorderFactory.createEmptyBorder(3, 5, 3, 5)), BorderLayout.SOUTH);
327 public static Point getCenterOf(final Component aContainer, final JComponent content) {
328 final JComponent component = getTargetComponent(aContainer);
330 Point containerScreenPoint = component.getVisibleRect().getLocation();
331 SwingUtilities.convertPointToScreen(containerScreenPoint, aContainer);
333 return UIUtil.getCenterPoint(new Rectangle(containerScreenPoint, component.getVisibleRect().getSize()), content.getPreferredSize());
336 public void showCenteredInCurrentWindow(@NotNull Project project) {
337 Window window = null;
339 Component focusedComponent = getWndManager().getFocusedComponent(project);
340 if (focusedComponent != null) {
341 Component parent = UIUtil.findUltimateParent(focusedComponent);
342 if (parent instanceof Window) {
343 window = (Window)parent;
346 if (window == null) {
347 window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
350 if (window != null) {
351 showInCenterOf(window);
355 public void showUnderneathOf(@NotNull Component aComponent) {
356 show(new RelativePoint(aComponent, new Point(0, aComponent.getHeight())));
359 public void show(@NotNull RelativePoint aPoint) {
360 final Point screenPoint = aPoint.getScreenPoint();
361 show(aPoint.getComponent(), screenPoint.x, screenPoint.y, false);
364 public void showInScreenCoordinates(@NotNull Component owner, @NotNull Point point) {
365 show(owner, point.x, point.y, false);
368 public void showInBestPositionFor(@NotNull DataContext dataContext) {
369 final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
370 if (editor != null) {
371 showInBestPositionFor(editor);
374 show(relativePointByQuickSearch(dataContext));
378 public void showInFocusCenter() {
379 final Component focused = getWndManager().getFocusedComponent(myProject);
380 if (focused != null) {
381 showInCenterOf(focused);
384 final JFrame frame = WindowManager.getInstance().getFrame(myProject);
385 showInCenterOf(frame.getRootPane());
389 private RelativePoint relativePointByQuickSearch(final DataContext dataContext) {
390 Rectangle dominantArea = PlatformDataKeys.DOMINANT_HINT_AREA_RECTANGLE.getData(dataContext);
392 if (dominantArea != null) {
393 final Component focusedComponent = getWndManager().getFocusedComponent(myProject);
394 Window window = SwingUtilities.windowForComponent(focusedComponent);
395 JLayeredPane layeredPane;
396 if (window instanceof JFrame) {
397 layeredPane = ((JFrame)window).getLayeredPane();
399 else if (window instanceof JDialog) {
400 layeredPane = ((JDialog)window).getLayeredPane();
402 else if (window instanceof JWindow) {
403 layeredPane = ((JWindow)window).getLayeredPane();
406 throw new IllegalStateException("cannot find parent window: project=" + myProject + "; window=" + window);
409 return relativePointWithDominantRectangle(layeredPane, dominantArea);
412 return JBPopupFactory.getInstance().guessBestPopupLocation(dataContext);
415 public void showInBestPositionFor(@NotNull Editor editor) {
416 assert editor.getComponent().isShowing() : "Editor must be showing on the screen";
418 DataContext context = ((EditorEx)editor).getDataContext();
419 Rectangle dominantArea = PlatformDataKeys.DOMINANT_HINT_AREA_RECTANGLE.getData(context);
420 if (dominantArea != null && !myRequestFocus) {
421 final JLayeredPane layeredPane = editor.getContentComponent().getRootPane().getLayeredPane();
422 show(relativePointWithDominantRectangle(layeredPane, dominantArea));
425 show(JBPopupFactory.getInstance().guessBestPopupLocation(editor));
429 public void addPopupListener(JBPopupListener listener) {
430 myListeners.add(listener);
433 private RelativePoint relativePointWithDominantRectangle(final JLayeredPane layeredPane, final Rectangle bounds) {
434 Dimension preferredSize = getComponent().getPreferredSize();
435 if (myDimensionServiceKey != null) {
436 final Dimension dimension = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
437 if (dimension != null) {
438 preferredSize = dimension;
441 final Point leftTopCorner = new Point(bounds.x + bounds.width, bounds.y);
442 final Point leftTopCornerScreen = (Point)leftTopCorner.clone();
443 SwingUtilities.convertPointToScreen(leftTopCornerScreen, layeredPane);
444 final RelativePoint relativePoint;
445 if (!ScreenUtil.isOutsideOnTheRightOFScreen(
446 new Rectangle(leftTopCornerScreen.x, leftTopCornerScreen.y, preferredSize.width, preferredSize.height))) {
447 relativePoint = new RelativePoint(layeredPane, leftTopCorner);
450 if (bounds.x > preferredSize.width) {
451 relativePoint = new RelativePoint(layeredPane, new Point(bounds.x - preferredSize.width, bounds.y));
454 setDimensionServiceKey(null); // going to cut width
455 Rectangle screen = ScreenUtil.getScreenRectangle(leftTopCornerScreen.x, leftTopCornerScreen.y);
456 final int spaceOnTheLeft = bounds.x;
457 final int spaceOnTheRight = (screen.x + screen.width) - leftTopCornerScreen.x;
458 if (spaceOnTheLeft > spaceOnTheRight) {
459 relativePoint = new RelativePoint(layeredPane, new Point(0, bounds.y));
460 myComponent.setPreferredSize(new Dimension(spaceOnTheLeft, Math.max(preferredSize.height, 200)));
463 relativePoint = new RelativePoint(layeredPane, leftTopCorner);
464 myComponent.setPreferredSize(new Dimension(spaceOnTheRight, Math.max(preferredSize.height, 200)));
468 return relativePoint;
471 public final void closeOk(@Nullable InputEvent e) {
476 public final void cancel() {
480 public void setRequestFocus(boolean requestFocus) {
481 myRequestFocus = requestFocus;
484 public void cancel(InputEvent e) {
485 if (isDisposed()) return;
487 if (myPopup != null) {
491 storeDimensionSize(myContent.getSize());
492 if (myUseDimServiceForXYLocation) {
493 final JRootPane root = myComponent.getRootPane();
495 final Container popupWindow = root.getParent();
496 if (popupWindow != null && popupWindow.isShowing()) {
497 storeLocation(popupWindow.getLocationOnScreen());
502 if (e instanceof MouseEvent) {
503 JBAwtEventQueue.getInstance().blockNextEvents(((MouseEvent)e));
508 if (ApplicationManagerEx.getApplicationEx() != null) {
509 StackingPopupDispatcher.getInstance().onPopupHidden(this);
513 myFocusTrackback.restoreFocus();
519 if (myListeners != null) {
520 for (JBPopupListener each : myListeners) {
521 each.onClosed(new LightweightWindowEvent(this, myOk));
526 Disposer.dispose(this, false);
530 private void disposePopup() {
531 if (myPopup != null) {
537 public boolean canClose() {
538 return myCallBack == null || myCallBack.compute().booleanValue();
541 public boolean isVisible() {
542 return myPopup != null;
545 public void show(final Component owner) {
546 show(owner, -1, -1, true);
549 public void show(Component owner, int aScreenX, int aScreenY, final boolean considerForcedXY) {
550 if (ApplicationManagerEx.getApplicationEx() != null && ApplicationManager.getApplication().isHeadlessEnvironment()) return;
552 throw new IllegalStateException("Popup was already disposed. Recreate a new instance to show again");
555 assert ApplicationManager.getApplication().isDispatchThread();
558 final boolean shouldShow = beforeShow();
566 myFocusTrackback = new FocusTrackback(this, owner, true);
567 myFocusTrackback.setMustBeShown(true);
571 Dimension sizeToSet = null;
573 if (myDimensionServiceKey != null) {
574 sizeToSet = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
577 if (myForcedSize != null) {
578 sizeToSet = myForcedSize;
581 if (myMinSize == null) {
582 myMinSize = myContent.getMinimumSize();
585 if (sizeToSet == null) {
586 sizeToSet = myContent.getPreferredSize();
589 if (sizeToSet != null) {
590 sizeToSet.width = Math.max(sizeToSet.width, myMinSize.width);
591 sizeToSet.height = Math.max(sizeToSet.height, myMinSize.height);
593 myContent.setSize(sizeToSet);
594 myContent.setPreferredSize(sizeToSet);
597 Point xy = new Point(aScreenX, aScreenY);
598 boolean adjustXY = true;
599 if (myDimensionServiceKey != null) {
600 final Point storedLocation = DimensionService.getInstance().getLocation(myDimensionServiceKey, myProject);
601 if (storedLocation != null) {
608 final Insets insets = myContent.getInsets();
609 if (insets != null) {
615 if (considerForcedXY && myForcedLocation != null) {
616 xy = myForcedLocation;
619 if (myLocateByContent) {
620 final Dimension captionSize = myHeaderPanel.getPreferredSize();
621 xy.y -= captionSize.height;
624 Rectangle targetBounds = new Rectangle(xy, myContent.getPreferredSize());
625 Rectangle original = new Rectangle(targetBounds);
626 if (myLocateWithinScreen) {
627 ScreenUtil.moveRectangleToFitTheScreen(targetBounds);
630 if (myMouseOutCanceller != null) {
631 myMouseOutCanceller.myEverEntered = targetBounds.equals(original);
634 myOwner = IdeFrameImpl.findNearestModalComponent(owner);
635 if (myOwner == null) {
639 myRequestorComponent = owner;
641 PopupComponent.Factory factory = getFactory(myForcedHeavyweight || myResizable);
642 myNativePopup = factory.isNativePopup();
643 myPopup = factory.getPopup(myOwner, myContent, targetBounds.x, targetBounds.y);
646 final JRootPane root = myContent.getRootPane();
647 final IdeGlassPaneImpl glass = new IdeGlassPaneImpl(root);
648 root.setGlassPane(glass);
650 final ResizeComponentListener resizeListener = new ResizeComponentListener(this);
651 glass.addMousePreprocessor(resizeListener, this);
652 glass.addMouseMotionPreprocessor(resizeListener, this);
655 if (myCaption != null && myMovable) {
656 final MoveComponentListener moveListener = new MoveComponentListener(myCaption) {
657 public void mousePressed(final MouseEvent e) {
658 super.mousePressed(e);
659 if (e.isConsumed()) return;
661 if (UIUtil.isCloseClick(e)) {
662 if (myCaption.isWithinPanel(e)) {
668 ListenerUtil.addMouseListener(myCaption, moveListener);
669 ListenerUtil.addMouseMotionListener(myCaption, moveListener);
670 final MyContentPanel saved = myContent;
671 Disposer.register(this, new Disposable() {
672 public void dispose() {
673 ListenerUtil.removeMouseListener(saved, moveListener);
674 ListenerUtil.removeMouseMotionListener(saved, moveListener);
679 for (JBPopupListener listener : myListeners) {
680 listener.beforeShown(new LightweightWindowEvent(this));
683 Window w = myPopup.getWindow();
685 WindowManagerEx.WindowShadowMode mode =
686 myShadowed ? WindowManagerEx.WindowShadowMode.NORMAL : WindowManagerEx.WindowShadowMode.DISABLED;
687 WindowManagerEx.getInstanceEx().setWindowShadow(myWindow, mode);
690 myPopup.setRequestFocus(myRequestFocus);
693 final Window window = SwingUtilities.getWindowAncestor(myContent);
695 myWindowListener = new MyWindowListener();
696 window.addWindowListener(myWindowListener);
699 window.setFocusableWindowState(true);
700 window.setFocusable(true);
701 if (myRequestFocus) {
702 window.requestFocusInWindow();
706 myWindow = updateMaskAndAlpha(window);
708 if (myWindow instanceof JWindow) {
709 ((JWindow)myWindow).getRootPane().putClientProperty(KEY, this);
712 if (myWindow != null) {
713 // dialogwrapper-based popups do this internally through peer,
714 // for other popups like jdialog-based we should exclude them manually, but
715 // we still have to be able to use IdeFrame as parent
716 if (!myMayBeParent && !(myWindow instanceof Frame)) {
717 WindowManager.getInstance().doNotSuggestAsParent(myWindow);
721 SwingUtilities.invokeLater(new Runnable() {
723 if (isDisposed()) return;
725 if (myRequestFocus) {
729 if (myPreferredFocusedComponent != null && myInStack) {
730 myFocusTrackback.registerFocusComponent(myPreferredFocusedComponent);
738 private void prepareToShow() {
739 final MouseAdapter mouseAdapter = new MouseAdapter() {
740 public void mousePressed(MouseEvent e) {
741 Point point = (Point)e.getPoint().clone();
742 SwingUtilities.convertPointToScreen(point, e.getComponent());
744 final Dimension dimension = myContent.getSize();
745 dimension.height += myResizable && isToDrawMacCorner() ? ourMacCorner.getHeight(myContent) : 4;
746 dimension.width += 4;
747 Point locationOnScreen = myContent.getLocationOnScreen();
748 final Rectangle bounds = new Rectangle(new Point(locationOnScreen.x - 2, locationOnScreen.y - 2), dimension);
749 if (!bounds.contains(point)) {
754 myContent.addMouseListener(mouseAdapter);
755 Disposer.register(this, new Disposable() {
756 public void dispose() {
757 myContent.removeMouseListener(mouseAdapter);
761 myContent.registerKeyboardAction(new ActionListener() {
762 public void actionPerformed(ActionEvent e) {
763 if (myCancelKeyEnabled) {
767 }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
770 myContent.addKeyListener(new KeyListener() {
771 public void keyTyped(final KeyEvent e) {
772 mySpeedSearch.process(e);
775 public void keyPressed(final KeyEvent e) {
776 mySpeedSearch.process(e);
779 public void keyReleased(final KeyEvent e) {
780 mySpeedSearch.process(e);
784 if (myCancelOnMouseOutCallback != null || myCancelOnWindow) {
785 myMouseOutCanceller = new Canceller();
786 Toolkit.getDefaultToolkit().addAWTEventListener(myMouseOutCanceller, AWTEvent.MOUSE_EVENT_MASK | WindowEvent.WINDOW_ACTIVATED |
787 AWTEvent.MOUSE_MOTION_EVENT_MASK);
791 myFocusWatcher = new ChildFocusWatcher(myContent) {
792 protected void onFocusGained(final FocusEvent event) {
793 setWindowActive(true);
796 protected void onFocusLost(final FocusEvent event) {
797 setWindowActive(false);
802 mySpeedSearchPatternField = new JTextField();
803 if (SystemInfo.isMac) {
804 Font f = mySpeedSearchPatternField.getFont();
805 mySpeedSearchPatternField.setFont(f.deriveFont(f.getStyle(), f.getSize() - 2));
809 private Window updateMaskAndAlpha(Window window) {
810 if (window == null) return window;
812 final WindowManagerEx wndManager = getWndManager();
813 if (wndManager == null) return window;
815 if (!wndManager.isAlphaModeEnabled(window)) return window;
817 if (myAlpha != myLastAlpha) {
818 wndManager.setAlphaModeRatio(window, myAlpha);
819 myLastAlpha = myAlpha;
822 if (myMaskProvider != null) {
823 final Dimension size = window.getSize();
824 Shape mask = myMaskProvider.getMask(size);
825 wndManager.setWindowMask(window, mask);
831 private static WindowManagerEx getWndManager() {
832 return ApplicationManagerEx.getApplicationEx() != null ? WindowManagerEx.getInstanceEx() : null;
835 public boolean isDisposed() {
836 return myContent == null;
839 protected boolean beforeShow() {
840 if (ApplicationManagerEx.getApplicationEx() == null) return true;
841 StackingPopupDispatcher.getInstance().onPopupShown(this, myInStack);
845 protected void afterShow() {
848 protected final void requestFocus() {
849 if (!myFocusable) return;
851 if (myPreferredFocusedComponent != null) {
852 if (myProject != null) {
853 getFocusManager().requestFocus(myPreferredFocusedComponent, true);
856 myPreferredFocusedComponent.requestFocus();
861 private IdeFocusManager getFocusManager() {
862 if (myProject != null) {
863 return IdeFocusManager.getInstance(myProject);
865 else if (myOwner != null) {
866 return IdeFocusManager.findInstanceByComponent(myOwner);
869 return IdeFocusManager.findInstance();
873 private static JComponent getTargetComponent(Component aComponent) {
874 if (aComponent instanceof JComponent) {
875 return (JComponent)aComponent;
877 else if (aComponent instanceof RootPaneContainer) {
878 return ((RootPaneContainer)aComponent).getRootPane();
881 LOG.error("Cannot find target for:" + aComponent);
885 private PopupComponent.Factory getFactory(boolean forceHeavyweight) {
886 if (isPersistent()) {
887 return new PopupComponent.Factory.Dialog();
889 else if (forceHeavyweight || !SystemInfo.isWindows) {
890 return new PopupComponent.Factory.AwtHeavyweight();
893 return new PopupComponent.Factory.AwtDefault();
897 public JComponent getContent() {
901 public void setLocation(RelativePoint p) {
902 setLocation(p, myPopup, myContent);
905 private static void setLocation(final RelativePoint p, final PopupComponent popup, Component content) {
906 if (popup == null) return;
908 final Window wnd = popup.getWindow();
911 wnd.setLocation(p.getScreenPoint());
915 final Window window = SwingUtilities.getWindowAncestor(myContent);
920 public JComponent getComponent() {
924 public void setProject(Project project) {
929 public void dispose() {
930 Disposer.dispose(this, false);
932 assert ApplicationManager.getApplication().isDispatchThread();
934 if (myPopup != null) {
935 cancel(myDisposeEvent);
938 if (myContent != null) {
939 myContent.removeAll();
943 myFocusTrackback = null;
947 if (myMouseOutCanceller != null) {
948 final Toolkit toolkit = Toolkit.getDefaultToolkit();
949 // it may happen, but have no idea how
950 // http://www.jetbrains.net/jira/browse/IDEADEV-21265
951 if (toolkit != null) {
952 toolkit.removeAWTEventListener(myMouseOutCanceller);
955 myMouseOutCanceller = null;
957 if (myFocusWatcher != null) {
958 myFocusWatcher.dispose();
959 myFocusWatcher = null;
964 if (myFinalRunnable != null) {
965 getFocusManager().doWhenFocusSettlesDown(myFinalRunnable);
966 myFinalRunnable = null;
970 private void resetWindow() {
971 if (myWindow != null && getWndManager() != null) {
972 getWndManager().resetWindow(myWindow);
973 if (myWindowListener != null) {
974 myWindow.removeWindowListener(myWindowListener);
977 if (myWindow instanceof JWindow) {
978 ((JWindow)myWindow).getRootPane().putClientProperty(KEY, null);
982 myWindowListener = null;
986 public void storeDimensionSize(final Dimension size) {
987 if (myDimensionServiceKey != null) {
988 DimensionService.getInstance().setSize(myDimensionServiceKey, size, myProject);
992 public void storeLocation(final Point xy) {
993 if (myDimensionServiceKey != null) {
994 DimensionService.getInstance().setLocation(myDimensionServiceKey, xy, myProject);
998 public static class MyContentPanel extends JPanel {
999 private final boolean myResizable;
1000 private final boolean myDrawMacCorner;
1001 private final boolean myPaintShadow;
1003 public MyContentPanel(final boolean resizable, final PopupBorder border, boolean drawMacCorner) {
1004 this(resizable, border, drawMacCorner, false);
1007 public MyContentPanel(final boolean resizable, final PopupBorder border, boolean drawMacCorner, boolean shadowed) {
1008 super(new BorderLayout());
1009 myResizable = resizable;
1010 myDrawMacCorner = drawMacCorner;
1011 myPaintShadow = shadowed && !UISettings.isRemoteDesktopConnected();
1012 if (myPaintShadow) {
1014 setBorder(new EmptyBorder(POPUP_TOP_SIZE, POPUP_SIDE_SIZE, POPUP_BOTTOM_SIZE, POPUP_SIDE_SIZE) {
1016 public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
1017 border.paintBorder(c, g,
1018 x + POPUP_SIDE_SIZE - 1,
1019 y + POPUP_TOP_SIZE - 1,
1020 width - 2 * POPUP_SIDE_SIZE + 2,
1021 height - POPUP_TOP_SIZE - POPUP_BOTTOM_SIZE + 2);
1030 public void paint(Graphics g) {
1031 if (myPaintShadow) {
1037 if (myResizable && myDrawMacCorner) {
1038 g.drawImage(ourMacCorner,
1039 getX() + getWidth() - ourMacCorner.getWidth(this),
1040 getY() + getHeight() - ourMacCorner.getHeight(this),
1045 private void paintShadow(final Graphics g) {
1046 BufferedImage capture = null;
1048 final Point onScreen = getLocationOnScreen();
1049 capture = new Robot().createScreenCapture(
1050 new Rectangle(onScreen.x, onScreen.y, getWidth() + 2 * POPUP_SIDE_SIZE, getHeight() + POPUP_TOP_SIZE + POPUP_BOTTOM_SIZE));
1051 final BufferedImage shadow = ShadowBorderPainter.createPopupShadow(this, getWidth(), getHeight());
1052 ((Graphics2D)capture.getGraphics()).drawImage(shadow, null, null);
1054 catch (Exception e) {
1057 if (capture != null) g.drawImage(capture, 0, 0, null);
1061 public boolean isCancelOnClickOutside() {
1062 return myCancelOnClickOutside;
1065 private class Canceller implements AWTEventListener {
1067 private boolean myEverEntered = false;
1069 public void eventDispatched(final AWTEvent event) {
1070 if (event.getID() == WindowEvent.WINDOW_ACTIVATED) {
1071 if (myCancelOnWindow) {
1075 else if (event.getID() == MouseEvent.MOUSE_ENTERED) {
1076 if (withinPopup(event)) {
1077 myEverEntered = true;
1080 else if (event.getID() == MouseEvent.MOUSE_MOVED) {
1081 if (myCancelOnMouseOutCallback != null && myEverEntered && !withinPopup(event)) {
1082 if (myCancelOnMouseOutCallback.check((MouseEvent)event)) {
1089 private boolean withinPopup(final AWTEvent event) {
1090 if (!myContent.isShowing()) return false;
1092 final MouseEvent mouse = (MouseEvent)event;
1093 final Point point = mouse.getPoint();
1094 SwingUtilities.convertPointToScreen(point, mouse.getComponent());
1095 return new Rectangle(myContent.getLocationOnScreen(), myContent.getSize()).contains(point);
1099 public void setLocation(@NotNull final Point screenPoint) {
1100 if (myPopup == null) {
1101 myForcedLocation = screenPoint;
1104 moveTo(myContent, screenPoint, myLocateByContent ? myHeaderPanel.getPreferredSize() : null);
1108 public static Window moveTo(JComponent content, Point screenPoint, final Dimension headerCorrectionSize) {
1109 setDefaultCursor(content);
1110 final Window wnd = SwingUtilities.getWindowAncestor(content);
1111 if (headerCorrectionSize != null) {
1112 screenPoint.y -= headerCorrectionSize.height;
1114 wnd.setLocation(screenPoint);
1118 public void setSize(@NotNull final Dimension size) {
1119 if (myPopup == null) {
1120 myForcedSize = size;
1123 updateMaskAndAlpha(setSize(myContent, size));
1128 public Dimension getSize() {
1129 if (myPopup != null) {
1130 final Window popupWindow = SwingUtilities.windowForComponent(myContent);
1131 return popupWindow.getSize();
1133 return myForcedSize;
1138 public void moveToFitScreen() {
1139 if (myPopup == null) return;
1141 final Window popupWindow = SwingUtilities.windowForComponent(myContent);
1142 Rectangle bounds = popupWindow.getBounds();
1144 ScreenUtil.moveRectangleToFitTheScreen(bounds);
1145 setLocation(bounds.getLocation());
1146 setSize(bounds.getSize());
1150 public static Window setSize(JComponent content, final Dimension size) {
1151 final Window popupWindow = SwingUtilities.windowForComponent(content);
1152 final Point location = popupWindow.getLocation();
1153 popupWindow.setBounds(location.x, location.y, size.width, size.height);
1157 public static void setDefaultCursor(JComponent content) {
1158 final Window wnd = SwingUtilities.getWindowAncestor(content);
1160 wnd.setCursor(Cursor.getDefaultCursor());
1164 public void setCaption(String title) {
1165 if (myCaption instanceof TitlePanel) {
1166 ((TitlePanel)myCaption).setText(title);
1170 private class MyWindowListener extends WindowAdapter {
1171 public void windowClosed(final WindowEvent e) {
1176 public boolean isPersistent() {
1177 return !myCancelOnClickOutside && !myCancelOnWindow;
1180 public boolean isNativePopup() {
1181 return myNativePopup;
1184 public void setUiVisible(final boolean visible) {
1185 if (myPopup != null) {
1188 final Window window = getPopupWindow();
1189 if (window != null && myRestoreWindowSize != null) {
1190 window.setSize(myRestoreWindowSize);
1191 myRestoreWindowSize = null;
1195 final Window window = getPopupWindow();
1196 if (window != null) {
1197 myRestoreWindowSize = window.getSize();
1198 window.setVisible(true);
1204 private Window getPopupWindow() {
1205 return myPopup.getWindow();
1208 public void setUserData(ArrayList<Object> userData) {
1209 myUserData = userData;
1212 public <T> T getUserData(final Class<T> userDataClass) {
1213 if (myUserData != null) {
1214 for (Object o : myUserData) {
1215 if (userDataClass.isInstance(o)) {
1216 //noinspection unchecked
1224 public boolean isModalContext() {
1225 return myModalContext;
1228 public boolean isFocused() {
1229 if (myComponent != null && isFocused(new Component[]{SwingUtilities.getWindowAncestor(myComponent)})) {
1232 return isFocused(myFocusOwners);
1235 public static boolean isFocused(@Nullable Component[] components) {
1236 if (components == null) return false;
1238 Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
1240 if (owner == null) return false;
1241 for (Component each : components) {
1242 if (each != null && SwingUtilities.isDescendingFrom(owner, each)) return true;
1248 public boolean isCancelKeyEnabled() {
1249 return myCancelKeyEnabled;
1253 CaptionPanel getTitle() {
1257 private void setHeaderComponent(JComponent c) {
1258 boolean doRevalidate = false;
1259 if (myHeaderComponent != null) {
1260 myHeaderPanel.remove(myHeaderComponent);
1261 myHeaderPanel.add(myCaption, BorderLayout.NORTH);
1262 myHeaderComponent = null;
1263 doRevalidate = true;
1267 myHeaderPanel.remove(myCaption);
1268 myHeaderPanel.add(c, BorderLayout.NORTH);
1269 myHeaderComponent = c;
1271 final Dimension size = myContent.getSize();
1272 if (size.height < c.getPreferredSize().height * 2) {
1273 size.height += c.getPreferredSize().height;
1277 doRevalidate = true;
1280 if (doRevalidate) myContent.revalidate();
1283 public void addListener(final JBPopupListener listener) {
1284 myListeners.add(listener);
1287 public void removeListener(final JBPopupListener listener) {
1288 myListeners.remove(listener);
1291 protected void onSpeedSearchPatternChanged() {
1294 public Component getOwner() {
1295 return myRequestorComponent;
1298 public void setMinimumSize(Dimension size) {
1302 public Runnable getFinalRunnable() {
1303 return myFinalRunnable;
1306 public void setFinalRunnable(Runnable finalRunnable) {
1307 myFinalRunnable = finalRunnable;
1310 public void setOk(boolean ok) {