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;
97 private float myAlpha = 0;
98 private float myLastAlpha = 0;
100 private MaskProvider myMaskProvider;
102 private Window myWindow;
103 private boolean myInStack;
104 private MyWindowListener myWindowListener;
106 private boolean myModalContext;
108 private Component[] myFocusOwners;
109 private PopupBorder myPopupBorder;
110 private Dimension myRestoreWindowSize;
111 protected Component myOwner;
112 protected Component myRequestorComponent;
113 private boolean myHeaderAlwaysFocusable;
114 private boolean myMovable;
115 private JComponent myHeaderComponent;
117 protected InputEvent myDisposeEvent;
119 private Runnable myFinalRunnable;
121 protected boolean myOk;
123 protected final SpeedSearch mySpeedSearch = new SpeedSearch() {
124 boolean searchFieldShown = false;
125 protected void update() {
126 mySpeedSearchPatternField.setBackground(new JTextField().getBackground());
127 onSpeedSearchPatternChanged();
128 mySpeedSearchPatternField.setText(getFilter());
129 if (isHoldingFilter() && !searchFieldShown) {
130 setHeaderComponent(mySpeedSearchPatternField);
131 searchFieldShown = true;
133 else if (!isHoldingFilter() && searchFieldShown) {
134 setHeaderComponent(null);
135 searchFieldShown = false;
140 public void noHits() {
141 mySpeedSearchPatternField.setBackground(LightColors.RED);
145 private JTextField mySpeedSearchPatternField;
146 private boolean myNativePopup;
147 private boolean myMayBeParent;
153 AbstractPopup init(final Project project,
154 @NotNull final JComponent component,
155 @Nullable final JComponent preferredFocusedComponent,
156 final boolean requestFocus,
157 final boolean focusable,
158 final boolean forceHeavyweight,
159 final boolean movable,
160 final String dimensionServiceKey,
161 final boolean resizable,
162 @Nullable final String caption,
163 @Nullable final Computable<Boolean> callback,
164 final boolean cancelOnClickOutside,
165 @Nullable final Set<JBPopupListener> listeners,
166 final boolean useDimServiceForXYLocation,
167 InplaceButton commandButton,
168 @Nullable final IconButton cancelButton,
169 @Nullable final MouseChecker cancelOnMouseOutCallback,
170 final boolean cancelOnWindow,
171 @Nullable final ActiveIcon titleIcon,
172 final boolean cancelKeyEnabled,
173 final boolean locateBycontent,
174 final boolean placeWithinScreenBounds,
175 @Nullable final Dimension minSize,
177 @Nullable MaskProvider maskProvider,
179 boolean modalContext,
180 @Nullable Component[] focusOwners,
181 @Nullable String adText,
182 final boolean headerAlwaysFocusable,
183 @NotNull List<Pair<ActionListener, KeyStroke>> keyboardActions,
184 Component settingsButtons,
185 @Nullable final Processor<JBPopup> pinCallback,
186 boolean mayBeParent) {
188 if (requestFocus && !focusable) {
189 assert false : "Incorrect argument combination: requestFocus=" + requestFocus + " focusable=" + focusable;
193 myComponent = component;
194 myPopupBorder = PopupBorder.Factory.create(true);
195 myShadowed = !movable && !resizable && Registry.is("ide.popup.dropShadow");
196 myContent = createContentPanel(resizable, myPopupBorder, isToDrawMacCorner());
197 myMayBeParent = mayBeParent;
199 myContent.add(component, BorderLayout.CENTER);
200 if (adText != null) {
201 myContent.add(HintUtil.createAdComponent(adText), BorderLayout.SOUTH);
204 myCancelKeyEnabled = cancelKeyEnabled;
205 myLocateByContent = locateBycontent;
206 myLocateWithinScreen = placeWithinScreenBounds;
208 myMaskProvider = maskProvider;
210 myModalContext = modalContext;
211 myFocusOwners = focusOwners;
212 myHeaderAlwaysFocusable = headerAlwaysFocusable;
215 ActiveIcon actualIcon = titleIcon == null ? new ActiveIcon(new EmptyIcon(0)) : titleIcon;
217 myHeaderPanel = new JPanel(new BorderLayout());
219 if (caption != null) {
220 if (caption.length() > 0) {
221 myCaption = new TitlePanel(actualIcon.getRegular(), actualIcon.getInactive());
222 ((TitlePanel)myCaption).setText(caption);
225 myCaption = new CaptionPanel();
228 if (pinCallback != null) {
229 myCaption.setButtonComponent(new InplaceButton(new IconButton("Pin", IconLoader.getIcon("/general/autohideOff.png"),
230 IconLoader.getIcon("/general/autohideOff.png"),
231 IconLoader.getIcon("/general/autohideOffInactive.png")), new ActionListener() {
232 public void actionPerformed(final ActionEvent e) {
233 pinCallback.process(AbstractPopup.this);
236 } else if (cancelButton != null) {
237 myCaption.setButtonComponent(new InplaceButton(cancelButton, new ActionListener() {
238 public void actionPerformed(final ActionEvent e) {
243 else if (commandButton != null) {
244 myCaption.setButtonComponent(commandButton);
248 myCaption = new CaptionPanel();
249 myCaption.setBorder(null);
250 myCaption.setPreferredSize(new Dimension(0, 0));
253 setWindowActive(myHeaderAlwaysFocusable);
255 myHeaderPanel.add(myCaption, BorderLayout.NORTH);
256 myContent.add(myHeaderPanel, BorderLayout.NORTH);
258 myForcedHeavyweight = forceHeavyweight;
259 myResizable = resizable;
260 myPreferredFocusedComponent = preferredFocusedComponent;
261 myRequestFocus = requestFocus;
262 myFocusable = focusable;
263 myDimensionServiceKey = dimensionServiceKey;
264 myCallBack = callback;
265 myCancelOnClickOutside = cancelOnClickOutside;
266 myCancelOnMouseOutCallback = cancelOnMouseOutCallback;
267 myListeners = listeners == null ? new HashSet<JBPopupListener>() : listeners;
268 myUseDimServiceForXYLocation = useDimServiceForXYLocation;
269 myCancelOnWindow = cancelOnWindow;
272 for (Pair<ActionListener, KeyStroke> pair : keyboardActions) {
273 myContent.registerKeyboardAction(pair.getFirst(), pair.getSecond(), JComponent.WHEN_IN_FOCUSED_WINDOW);
276 if (settingsButtons != null) {
277 myCaption.addSettingsComponent(settingsButtons);
283 private void setWindowActive(boolean active) {
284 boolean value = myHeaderAlwaysFocusable || active;
286 if (myCaption != null) {
287 myCaption.setActive(value);
289 myPopupBorder.setActive(value);
295 protected MyContentPanel createContentPanel(final boolean resizable, PopupBorder border, boolean isToDrawMacCorner) {
296 return new MyContentPanel(resizable, border, isToDrawMacCorner, myShadowed);
299 public static boolean isToDrawMacCorner() {
300 return SystemInfo.isMac;
305 public String getDimensionServiceKey() {
306 return myDimensionServiceKey;
309 public void setDimensionServiceKey(final String dimensionServiceKey) {
310 myDimensionServiceKey = dimensionServiceKey;
313 public void showInCenterOf(@NotNull Component aContainer) {
314 final Point popupPoint = getCenterOf(aContainer, myContent);
315 show(aContainer, popupPoint.x, popupPoint.y, false);
318 public void setAdText(@NotNull final String s) {
319 myContent.add(HintUtil.createAdComponent(s, BorderFactory.createEmptyBorder(3, 5, 3, 5)), BorderLayout.SOUTH);
322 public static Point getCenterOf(final Component aContainer, final JComponent content) {
323 final JComponent component = getTargetComponent(aContainer);
325 Point containerScreenPoint = component.getVisibleRect().getLocation();
326 SwingUtilities.convertPointToScreen(containerScreenPoint, aContainer);
328 return UIUtil.getCenterPoint(new Rectangle(containerScreenPoint, component.getVisibleRect().getSize()), content.getPreferredSize());
331 public void showCenteredInCurrentWindow(@NotNull Project project) {
332 Window window = null;
334 Component focusedComponent = getWndManager().getFocusedComponent(project);
335 if (focusedComponent != null) {
336 Component parent = UIUtil.findUltimateParent(focusedComponent);
337 if (parent instanceof Window) {
338 window = (Window)parent;
341 if (window == null) {
342 window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
345 if (window != null) {
346 showInCenterOf(window);
350 public void showUnderneathOf(@NotNull Component aComponent) {
351 show(new RelativePoint(aComponent, new Point(0, aComponent.getHeight())));
354 public void show(@NotNull RelativePoint aPoint) {
355 final Point screenPoint = aPoint.getScreenPoint();
356 show(aPoint.getComponent(), screenPoint.x, screenPoint.y, false);
359 public void showInScreenCoordinates(@NotNull Component owner, @NotNull Point point) {
360 show(owner, point.x, point.y, false);
363 public void showInBestPositionFor(@NotNull DataContext dataContext) {
364 final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
365 if (editor != null) {
366 showInBestPositionFor(editor);
369 show(relativePointByQuickSearch(dataContext));
373 public void showInFocusCenter() {
374 final Component focused = getWndManager().getFocusedComponent(myProject);
375 if (focused != null) {
376 showInCenterOf(focused);
378 final JFrame frame = WindowManager.getInstance().getFrame(myProject);
379 showInCenterOf(frame.getRootPane());
383 private RelativePoint relativePointByQuickSearch(final DataContext dataContext) {
384 Rectangle dominantArea = PlatformDataKeys.DOMINANT_HINT_AREA_RECTANGLE.getData(dataContext);
386 if (dominantArea != null) {
387 final Component focusedComponent = getWndManager().getFocusedComponent(myProject);
388 Window window = SwingUtilities.windowForComponent(focusedComponent);
389 JLayeredPane layeredPane;
390 if (window instanceof JFrame) {
391 layeredPane = ((JFrame)window).getLayeredPane();
393 else if (window instanceof JDialog) {
394 layeredPane = ((JDialog)window).getLayeredPane();
396 else if (window instanceof JWindow) {
397 layeredPane = ((JWindow)window).getLayeredPane();
400 throw new IllegalStateException("cannot find parent window: project=" + myProject + "; window=" + window);
403 return relativePointWithDominantRectangle(layeredPane, dominantArea);
406 return JBPopupFactory.getInstance().guessBestPopupLocation(dataContext);
409 public void showInBestPositionFor(@NotNull Editor editor) {
410 assert editor.getComponent().isShowing() : "Editor must be showing on the screen";
412 DataContext context = ((EditorEx)editor).getDataContext();
413 Rectangle dominantArea = PlatformDataKeys.DOMINANT_HINT_AREA_RECTANGLE.getData(context);
414 if (dominantArea != null && !myRequestFocus) {
415 final JLayeredPane layeredPane = editor.getContentComponent().getRootPane().getLayeredPane();
416 show(relativePointWithDominantRectangle(layeredPane, dominantArea));
419 show(JBPopupFactory.getInstance().guessBestPopupLocation(editor));
423 public void addPopupListener(JBPopupListener listener) {
424 myListeners.add(listener);
427 private RelativePoint relativePointWithDominantRectangle(final JLayeredPane layeredPane, final Rectangle bounds) {
428 Dimension preferredSize = getComponent().getPreferredSize();
429 if (myDimensionServiceKey != null) {
430 final Dimension dimension = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
431 if (dimension != null) {
432 preferredSize = dimension;
435 final Point leftTopCorner = new Point(bounds.x + bounds.width, bounds.y);
436 final Point leftTopCornerScreen = (Point)leftTopCorner.clone();
437 SwingUtilities.convertPointToScreen(leftTopCornerScreen, layeredPane);
438 final RelativePoint relativePoint;
439 if (!ScreenUtil.isOutsideOnTheRightOFScreen(
440 new Rectangle(leftTopCornerScreen.x, leftTopCornerScreen.y, preferredSize.width, preferredSize.height))) {
441 relativePoint = new RelativePoint(layeredPane, leftTopCorner);
444 if (bounds.x > preferredSize.width) {
445 relativePoint = new RelativePoint(layeredPane, new Point(bounds.x - preferredSize.width, bounds.y));
448 setDimensionServiceKey(null); // going to cut width
449 Rectangle screen = ScreenUtil.getScreenRectangle(leftTopCornerScreen.x, leftTopCornerScreen.y);
450 final int spaceOnTheLeft = bounds.x;
451 final int spaceOnTheRight = (screen.x + screen.width) - leftTopCornerScreen.x;
452 if (spaceOnTheLeft > spaceOnTheRight) {
453 relativePoint = new RelativePoint(layeredPane, new Point(0, bounds.y));
454 myComponent.setPreferredSize(new Dimension(spaceOnTheLeft, Math.max(preferredSize.height, 200)));
457 relativePoint = new RelativePoint(layeredPane, leftTopCorner);
458 myComponent.setPreferredSize(new Dimension(spaceOnTheRight, Math.max(preferredSize.height, 200)));
462 return relativePoint;
465 public final void closeOk(@Nullable InputEvent e) {
470 public final void cancel() {
474 public void setRequestFocus(boolean requestFocus) {
475 myRequestFocus = requestFocus;
478 public void cancel(InputEvent e) {
479 if (isDisposed()) return;
481 if (myPopup != null) {
485 storeDimensionSize(myContent.getSize());
486 if (myUseDimServiceForXYLocation) {
487 final JRootPane root = myComponent.getRootPane();
489 final Container popupWindow = root.getParent();
490 if (popupWindow != null && popupWindow.isShowing()) {
491 storeLocation(popupWindow.getLocationOnScreen());
496 if (e instanceof MouseEvent) {
497 JBAwtEventQueue.getInstance().blockNextEvents(((MouseEvent)e));
502 if (ApplicationManagerEx.getApplicationEx() != null) {
503 StackingPopupDispatcher.getInstance().onPopupHidden(this);
507 myFocusTrackback.restoreFocus();
513 if (myListeners != null) {
514 for (JBPopupListener each : myListeners) {
515 each.onClosed(new LightweightWindowEvent(this, myOk));
520 Disposer.dispose(this, false);
524 private void disposePopup() {
525 if (myPopup != null) {
531 public boolean canClose() {
532 return myCallBack == null || myCallBack.compute().booleanValue();
535 public boolean isVisible() {
536 return myPopup != null;
539 public void show(final Component owner) {
540 show(owner, -1, -1, true);
543 public void show(Component owner, int aScreenX, int aScreenY, final boolean considerForcedXY) {
544 if (ApplicationManagerEx.getApplicationEx() != null && ApplicationManager.getApplication().isHeadlessEnvironment()) return;
546 throw new IllegalStateException("Popup was already disposed. Recreate a new instance to show again");
549 assert ApplicationManager.getApplication().isDispatchThread();
552 final boolean shouldShow = beforeShow();
560 myFocusTrackback = new FocusTrackback(this, owner, true);
561 myFocusTrackback.setMustBeShown(true);
565 Dimension sizeToSet = null;
567 if (myDimensionServiceKey != null) {
568 sizeToSet = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
571 if (myForcedSize != null) {
572 sizeToSet = myForcedSize;
575 if (myMinSize == null) {
576 myMinSize = myContent.getMinimumSize();
579 if (sizeToSet == null) {
580 sizeToSet = myContent.getPreferredSize();
583 if (sizeToSet != null) {
584 sizeToSet.width = Math.max(sizeToSet.width, myMinSize.width);
585 sizeToSet.height = Math.max(sizeToSet.height, myMinSize.height);
587 myContent.setSize(sizeToSet);
588 myContent.setPreferredSize(sizeToSet);
591 Point xy = new Point(aScreenX, aScreenY);
592 boolean adjustXY = true;
593 if (myDimensionServiceKey != null) {
594 final Point storedLocation = DimensionService.getInstance().getLocation(myDimensionServiceKey, myProject);
595 if (storedLocation != null) {
602 final Insets insets = myContent.getInsets();
603 if (insets != null) {
609 if (considerForcedXY && myForcedLocation != null) {
610 xy = myForcedLocation;
613 if (myLocateByContent) {
614 final Dimension captionSize = myHeaderPanel.getPreferredSize();
615 xy.y -= captionSize.height;
618 Rectangle targetBounds = new Rectangle(xy, myContent.getPreferredSize());
619 Rectangle original = new Rectangle(targetBounds);
620 if (myLocateWithinScreen) {
621 ScreenUtil.moveRectangleToFitTheScreen(targetBounds);
624 if (myMouseOutCanceller != null) {
625 myMouseOutCanceller.myEverEntered = targetBounds.equals(original);
628 myOwner = IdeFrameImpl.findNearestModalComponent(owner);
629 if (myOwner == null) {
633 myRequestorComponent = owner;
635 PopupComponent.Factory factory = getFactory(myForcedHeavyweight || myResizable);
636 myNativePopup = factory.isNativePopup();
637 myPopup = factory.getPopup(myOwner, myContent, targetBounds.x, targetBounds.y);
640 final JRootPane root = myContent.getRootPane();
641 final IdeGlassPaneImpl glass = new IdeGlassPaneImpl(root);
642 root.setGlassPane(glass);
644 final ResizeComponentListener resizeListener = new ResizeComponentListener(this);
645 glass.addMousePreprocessor(resizeListener, this);
646 glass.addMouseMotionPreprocessor(resizeListener, this);
649 if (myCaption != null && myMovable) {
650 final MoveComponentListener moveListener = new MoveComponentListener(myCaption) {
651 public void mousePressed(final MouseEvent e) {
652 super.mousePressed(e);
653 if (e.isConsumed()) return;
655 if (UIUtil.isCloseClick(e)) {
656 if (myCaption.isWithinPanel(e)) {
662 ListenerUtil.addMouseListener(myCaption, moveListener);
663 ListenerUtil.addMouseMotionListener(myCaption, moveListener);
664 final MyContentPanel saved = myContent;
665 Disposer.register(this, new Disposable() {
666 public void dispose() {
667 ListenerUtil.removeMouseListener(saved, moveListener);
668 ListenerUtil.removeMouseMotionListener(saved, moveListener);
673 for(JBPopupListener listener: myListeners) {
674 listener.beforeShown(new LightweightWindowEvent(this));
679 final Window window = SwingUtilities.getWindowAncestor(myContent);
681 myWindowListener = new MyWindowListener();
682 window.addWindowListener(myWindowListener);
685 window.setFocusableWindowState(true);
686 window.setFocusable(true);
687 if (myRequestFocus) {
688 window.requestFocusInWindow();
692 myWindow = updateMaskAndAlpha(window);
694 if (myWindow instanceof JWindow) {
695 ((JWindow)myWindow).getRootPane().putClientProperty(KEY, this);
698 if (myWindow != null) {
699 if (!myMayBeParent) {
700 WindowManager.getInstance().doNotSuggestAsParent(myWindow);
704 SwingUtilities.invokeLater(new Runnable() {
706 if (isDisposed()) return;
708 if (myRequestFocus) {
712 if (myPreferredFocusedComponent != null && myInStack) {
713 myFocusTrackback.registerFocusComponent(myPreferredFocusedComponent);
721 private void prepareToShow() {
722 final MouseAdapter mouseAdapter = new MouseAdapter() {
723 public void mousePressed(MouseEvent e) {
724 Point point = (Point)e.getPoint().clone();
725 SwingUtilities.convertPointToScreen(point, e.getComponent());
727 final Dimension dimension = myContent.getSize();
728 dimension.height += myResizable && isToDrawMacCorner() ? ourMacCorner.getHeight(myContent) : 4;
729 dimension.width += 4;
730 Point locationOnScreen = myContent.getLocationOnScreen();
731 final Rectangle bounds = new Rectangle(new Point(locationOnScreen.x - 2, locationOnScreen.y - 2), dimension);
732 if (!bounds.contains(point)) {
737 myContent.addMouseListener(mouseAdapter);
738 Disposer.register(this, new Disposable() {
739 public void dispose() {
740 myContent.removeMouseListener(mouseAdapter);
744 myContent.registerKeyboardAction(new ActionListener() {
745 public void actionPerformed(ActionEvent e) {
746 if (myCancelKeyEnabled) {
750 }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
753 myContent.addKeyListener(new KeyListener() {
754 public void keyTyped(final KeyEvent e) {
755 mySpeedSearch.process(e);
758 public void keyPressed(final KeyEvent e) {
759 mySpeedSearch.process(e);
762 public void keyReleased(final KeyEvent e) {
763 mySpeedSearch.process(e);
767 if (myCancelOnMouseOutCallback != null || myCancelOnWindow) {
768 myMouseOutCanceller = new Canceller();
769 Toolkit.getDefaultToolkit().addAWTEventListener(myMouseOutCanceller, AWTEvent.MOUSE_EVENT_MASK | WindowEvent.WINDOW_ACTIVATED |
770 AWTEvent.MOUSE_MOTION_EVENT_MASK);
774 myFocusWatcher = new ChildFocusWatcher(myContent) {
775 protected void onFocusGained(final FocusEvent event) {
776 setWindowActive(true);
779 protected void onFocusLost(final FocusEvent event) {
780 setWindowActive(false);
785 mySpeedSearchPatternField = new JTextField();
786 if (SystemInfo.isMac) {
787 Font f = mySpeedSearchPatternField.getFont();
788 mySpeedSearchPatternField.setFont(f.deriveFont(f.getStyle(), f.getSize() - 2));
792 private Window updateMaskAndAlpha(Window window) {
793 if (window == null) return window;
795 final WindowManagerEx wndManager = getWndManager();
796 if (wndManager == null) return window;
798 if (!wndManager.isAlphaModeEnabled(window)) return window;
800 if (myAlpha != myLastAlpha) {
801 wndManager.setAlphaModeRatio(window, myAlpha);
802 myLastAlpha = myAlpha;
805 if (myMaskProvider != null) {
806 final Dimension size = window.getSize();
807 Shape mask = myMaskProvider.getMask(size);
808 wndManager.setWindowMask(window, mask);
814 private static WindowManagerEx getWndManager() {
815 return ApplicationManagerEx.getApplicationEx() != null ? WindowManagerEx.getInstanceEx() : null;
818 public boolean isDisposed() {
819 return myContent == null;
822 protected boolean beforeShow() {
823 if (ApplicationManagerEx.getApplicationEx() == null) return true;
824 StackingPopupDispatcher.getInstance().onPopupShown(this, myInStack);
828 protected void afterShow() {
831 protected final void requestFocus() {
832 if (!myFocusable) return;
834 if (myPreferredFocusedComponent != null) {
835 if (myProject != null) {
836 getFocusManager().requestFocus(myPreferredFocusedComponent, true);
839 myPreferredFocusedComponent.requestFocus();
844 private IdeFocusManager getFocusManager() {
845 if (myProject != null) {
846 return IdeFocusManager.getInstance(myProject);
847 } else if (myOwner != null) {
848 return IdeFocusManager.findInstanceByComponent(myOwner);
850 return IdeFocusManager.findInstance();
854 private static JComponent getTargetComponent(Component aComponent) {
855 if (aComponent instanceof JComponent) {
856 return (JComponent)aComponent;
858 else if (aComponent instanceof RootPaneContainer) {
859 return ((RootPaneContainer)aComponent).getRootPane();
862 LOG.error("Cannot find target for:" + aComponent);
866 private PopupComponent.Factory getFactory(boolean forceHeavyweight) {
867 if (isPersistent()) {
868 return new PopupComponent.Factory.Dialog();
869 } else if (forceHeavyweight || !SystemInfo.isWindows) {
870 return new PopupComponent.Factory.AwtHeavyweight();
872 return new PopupComponent.Factory.AwtDefault();
876 public JComponent getContent() {
880 public void setLocation(RelativePoint p) {
881 setLocation(p, myPopup, myContent);
884 private static void setLocation(final RelativePoint p, final PopupComponent popup, Component content) {
885 if (popup == null) return;
887 final Window wnd = popup.getWindow();
890 wnd.setLocation(p.getScreenPoint());
894 final Window window = SwingUtilities.getWindowAncestor(myContent);
899 public JComponent getComponent() {
903 public void setProject(Project project) {
908 public void dispose() {
909 Disposer.dispose(this, false);
911 assert ApplicationManager.getApplication().isDispatchThread();
913 if (myPopup != null) {
914 cancel(myDisposeEvent);
917 if (myContent != null) {
918 myContent.removeAll();
922 myFocusTrackback = null;
926 if (myMouseOutCanceller != null) {
927 final Toolkit toolkit = Toolkit.getDefaultToolkit();
928 // it may happen, but have no idea how
929 // http://www.jetbrains.net/jira/browse/IDEADEV-21265
930 if (toolkit != null) {
931 toolkit.removeAWTEventListener(myMouseOutCanceller);
934 myMouseOutCanceller = null;
936 if (myFocusWatcher != null) {
937 myFocusWatcher.dispose();
938 myFocusWatcher = null;
943 if (myFinalRunnable != null) {
944 getFocusManager().doWhenFocusSettlesDown(myFinalRunnable);
945 myFinalRunnable = null;
949 private void resetWindow() {
950 if (myWindow != null && getWndManager() != null) {
951 getWndManager().resetWindow(myWindow);
952 if (myWindowListener != null) {
953 myWindow.removeWindowListener(myWindowListener);
956 if (myWindow instanceof JWindow) {
957 ((JWindow)myWindow).getRootPane().putClientProperty(KEY, null);
961 myWindowListener = null;
965 public void storeDimensionSize(final Dimension size) {
966 if (myDimensionServiceKey != null) {
967 DimensionService.getInstance().setSize(myDimensionServiceKey, size, myProject);
971 public void storeLocation(final Point xy) {
972 if (myDimensionServiceKey != null) {
973 DimensionService.getInstance().setLocation(myDimensionServiceKey, xy, myProject);
977 public static class MyContentPanel extends JPanel {
978 private final boolean myResizable;
979 private final boolean myDrawMacCorner;
980 private final boolean myShadowed;
982 public MyContentPanel(final boolean resizable, final PopupBorder border, boolean drawMacCorner) {
983 this(resizable, border, drawMacCorner, false);
986 public MyContentPanel(final boolean resizable, final PopupBorder border, boolean drawMacCorner, boolean shadowed) {
987 super(new BorderLayout());
988 myResizable = resizable;
989 myDrawMacCorner = drawMacCorner;
990 myShadowed = shadowed;
991 if (isShadowPossible()) {
993 setBorder(new EmptyBorder(POPUP_TOP_SIZE, POPUP_SIDE_SIZE, POPUP_BOTTOM_SIZE, POPUP_SIDE_SIZE) {
995 public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
996 border.paintBorder(c, g,
997 x + POPUP_SIDE_SIZE - 1,
998 y + POPUP_TOP_SIZE - 1,
999 width - 2 * POPUP_SIDE_SIZE + 2,
1000 height - POPUP_TOP_SIZE - POPUP_BOTTOM_SIZE + 2);
1008 private boolean isShadowPossible() {
1009 return myShadowed && !SystemInfo.isMac && !UISettings.isRemoteDesktopConnected();
1012 public void paint(Graphics g) {
1013 if (isShadowPossible()) {
1019 if (myResizable && myDrawMacCorner) {
1020 g.drawImage(ourMacCorner,
1021 getX() + getWidth() - ourMacCorner.getWidth(this),
1022 getY() + getHeight() - ourMacCorner.getHeight(this),
1027 private void paintShadow(final Graphics g) {
1028 BufferedImage capture = null;
1030 final Point onScreen = getLocationOnScreen();
1031 capture = new Robot().createScreenCapture(
1032 new Rectangle(onScreen.x, onScreen.y, getWidth() + 2 * POPUP_SIDE_SIZE, getHeight() + POPUP_TOP_SIZE + POPUP_BOTTOM_SIZE));
1033 final BufferedImage shadow = ShadowBorderPainter.createPopupShadow(this, getWidth(), getHeight());
1034 ((Graphics2D)capture.getGraphics()).drawImage(shadow, null, null);
1036 catch (Exception e) {
1039 if (capture != null) g.drawImage(capture, 0, 0, null);
1043 public boolean isCancelOnClickOutside() {
1044 return myCancelOnClickOutside;
1047 private class Canceller implements AWTEventListener {
1049 private boolean myEverEntered = false;
1051 public void eventDispatched(final AWTEvent event) {
1052 if (event.getID() == WindowEvent.WINDOW_ACTIVATED) {
1053 if (myCancelOnWindow) {
1056 } else if (event.getID() == MouseEvent.MOUSE_ENTERED) {
1057 if (withinPopup(event)) {
1058 myEverEntered = true;
1060 } else if (event.getID() == MouseEvent.MOUSE_MOVED) {
1061 if (myCancelOnMouseOutCallback != null && myEverEntered && !withinPopup(event)) {
1062 if (myCancelOnMouseOutCallback.check((MouseEvent)event)) {
1069 private boolean withinPopup(final AWTEvent event) {
1070 if (!myContent.isShowing()) return false;
1072 final MouseEvent mouse = (MouseEvent)event;
1073 final Point point = mouse.getPoint();
1074 SwingUtilities.convertPointToScreen(point, mouse.getComponent());
1075 return new Rectangle(myContent.getLocationOnScreen(), myContent.getSize()).contains(point);
1079 public void setLocation(@NotNull final Point screenPoint) {
1080 if (myPopup == null) {
1081 myForcedLocation = screenPoint;
1083 moveTo(myContent, screenPoint, myLocateByContent ? myHeaderPanel.getPreferredSize() : null);
1087 public static Window moveTo(JComponent content, Point screenPoint, final Dimension headerCorrectionSize) {
1088 setDefaultCursor(content);
1089 final Window wnd = SwingUtilities.getWindowAncestor(content);
1090 if (headerCorrectionSize != null) {
1091 screenPoint.y -= headerCorrectionSize.height;
1093 wnd.setLocation(screenPoint);
1097 public void setSize(@NotNull final Dimension size) {
1098 if (myPopup == null) {
1099 myForcedSize = size;
1101 updateMaskAndAlpha(setSize(myContent, size));
1105 public static Window setSize(JComponent content, final Dimension size) {
1106 final Window popupWindow = SwingUtilities.windowForComponent(content);
1107 final Point location = popupWindow.getLocation();
1108 popupWindow.setBounds(location.x, location.y, size.width, size.height);
1112 public static void setDefaultCursor(JComponent content) {
1113 final Window wnd = SwingUtilities.getWindowAncestor(content);
1115 wnd.setCursor(Cursor.getDefaultCursor());
1119 public void setCaption(String title) {
1120 if (myCaption instanceof TitlePanel) {
1121 ((TitlePanel)myCaption).setText(title);
1125 private class MyWindowListener extends WindowAdapter {
1126 public void windowClosed(final WindowEvent e) {
1131 public boolean isPersistent() {
1132 return !myCancelOnClickOutside && !myCancelOnWindow;
1135 public boolean isNativePopup() {
1136 return myNativePopup;
1139 public void setUiVisible(final boolean visible) {
1140 if (myPopup != null) {
1143 final Window window = getPopupWindow();
1144 if (window != null && myRestoreWindowSize != null) {
1145 window.setSize(myRestoreWindowSize);
1146 myRestoreWindowSize = null;
1150 final Window window = getPopupWindow();
1151 if (window != null) {
1152 myRestoreWindowSize = window.getSize();
1153 window.setVisible(true);
1159 private Window getPopupWindow() {
1160 return myPopup.getWindow();
1163 public void setUserData(ArrayList<Object> userData) {
1164 myUserData = userData;
1167 public <T> T getUserData(final Class<T> userDataClass) {
1168 if (myUserData != null) {
1169 for(Object o: myUserData) {
1170 if (userDataClass.isInstance(o)) {
1171 //noinspection unchecked
1179 public boolean isModalContext() {
1180 return myModalContext;
1183 public boolean isFocused() {
1184 if (myComponent != null && isFocused(new Component[] {SwingUtilities.getWindowAncestor(myComponent)}))
1186 return isFocused(myFocusOwners);
1189 public static boolean isFocused(@Nullable Component[] components) {
1190 if (components == null) return false;
1192 Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
1194 if (owner == null) return false;
1195 for (Component each : components) {
1196 if (each != null && SwingUtilities.isDescendingFrom(owner, each)) return true;
1202 public boolean isCancelKeyEnabled() {
1203 return myCancelKeyEnabled;
1207 CaptionPanel getTitle() {
1211 private void setHeaderComponent(JComponent c) {
1212 boolean doRevalidate = false;
1213 if (myHeaderComponent != null) {
1214 myHeaderPanel.remove(myHeaderComponent);
1215 myHeaderPanel.add(myCaption, BorderLayout.NORTH);
1216 myHeaderComponent = null;
1217 doRevalidate = true;
1221 myHeaderPanel.remove(myCaption);
1222 myHeaderPanel.add(c, BorderLayout.NORTH);
1223 myHeaderComponent = c;
1225 final Dimension size = myContent.getSize();
1226 if (size.height < c.getPreferredSize().height * 2) {
1227 size.height += c.getPreferredSize().height;
1231 doRevalidate = true;
1234 if (doRevalidate) myContent.revalidate();
1237 public void addListener(final JBPopupListener listener) {
1238 myListeners.add(listener);
1241 public void removeListener(final JBPopupListener listener) {
1242 myListeners.remove(listener);
1245 protected void onSpeedSearchPatternChanged() {
1248 public Component getOwner() {
1249 return myRequestorComponent;
1252 public void setMinimumSize(Dimension size) {
1256 public Runnable getFinalRunnable() {
1257 return myFinalRunnable;
1260 public void setFinalRunnable(Runnable finalRunnable) {
1261 myFinalRunnable = finalRunnable;
1264 public void setOk(boolean ok) {