NPE
[fedora-idea.git] / platform / platform-impl / src / com / intellij / ui / popup / AbstractPopup.java
blob9e249c7a19f191621c6d9898fc144f9eafe582a6
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.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.ui.ChildFocusWatcher;
44 import com.intellij.util.ui.EmptyIcon;
45 import com.intellij.util.ui.UIUtil;
46 import org.jetbrains.annotations.NotNull;
47 import org.jetbrains.annotations.Nullable;
49 import javax.swing.*;
50 import javax.swing.border.EmptyBorder;
51 import java.awt.*;
52 import java.awt.event.*;
53 import java.awt.image.BufferedImage;
54 import java.util.ArrayList;
55 import java.util.HashSet;
56 import java.util.List;
57 import java.util.Set;
59 import static com.intellij.openapi.ui.impl.ShadowBorderPainter.*;
61 public class AbstractPopup implements JBPopup {
62 private static final Logger LOG = Logger.getInstance("#com.intellij.ui.popup.AbstractPopup");
64 private static final Image ourMacCorner = ImageLoader.loadFromResource("/general/macCorner.png");
66 private PopupComponent myPopup;
67 private MyContentPanel myContent;
68 private JComponent myPreferredFocusedComponent;
69 private boolean myRequestFocus;
70 private boolean myFocusable;
71 private boolean myForcedHeavyweight = false;
72 private boolean myLocateWithinScreen = true;
73 private boolean myResizable = false;
74 private JPanel myHeaderPanel;
75 private CaptionPanel myCaption = null;
76 private JComponent myComponent;
77 private String myDimensionServiceKey = null;
78 private Computable<Boolean> myCallBack = null;
79 private Project myProject;
80 private boolean myCancelOnClickOutside;
81 private Set<JBPopupListener> myListeners;
82 private boolean myUseDimServiceForXYLocation;
83 private MouseChecker myCancelOnMouseOutCallback;
84 private Canceller myMouseOutCanceller;
85 private boolean myCancelOnWindow;
86 private Dimension myForcedSize;
87 private Point myForcedLocation;
88 private ChildFocusWatcher myFocusWatcher;
89 private boolean myCancelKeyEnabled;
90 private boolean myLocateByContent;
91 protected FocusTrackback myFocusTrackback;
92 private Dimension myMinSize;
93 private ArrayList<Object> myUserData;
94 private boolean myShadowed;
96 private float myAlpha = 0;
97 private float myLastAlpha = 0;
99 private MaskProvider myMaskProvider;
101 private Window myWindow;
102 private boolean myInStack;
103 private MyWindowListener myWindowListener;
105 private boolean myModalContext;
107 private Component[] myFocusOwners;
108 private PopupBorder myPopupBorder;
109 private Dimension myRestoreWindowSize;
110 protected Component myOwner;
111 protected Component myRequestorComponent;
112 private boolean myHeaderAlwaysFocusable;
113 private boolean myMovable;
114 private JComponent myHeaderComponent;
116 protected InputEvent myDisposeEvent;
118 private Runnable myFinalRunnable;
120 protected final SpeedSearch mySpeedSearch = new SpeedSearch() {
121 boolean searchFieldShown = false;
122 protected void update() {
123 mySpeedSearchPatternField.setBackground(new JTextField().getBackground());
124 onSpeedSearchPatternChanged();
125 mySpeedSearchPatternField.setText(getFilter());
126 if (isHoldingFilter() && !searchFieldShown) {
127 setHeaderComponent(mySpeedSearchPatternField);
128 searchFieldShown = true;
130 else if (!isHoldingFilter() && searchFieldShown) {
131 setHeaderComponent(null);
132 searchFieldShown = false;
136 @Override
137 public void noHits() {
138 mySpeedSearchPatternField.setBackground(LightColors.RED);
142 private JTextField mySpeedSearchPatternField;
145 AbstractPopup() {
148 AbstractPopup init(final Project project,
149 @NotNull final JComponent component,
150 @Nullable final JComponent preferredFocusedComponent,
151 final boolean requestFocus,
152 final boolean focusable,
153 final boolean forceHeavyweight,
154 final boolean movable,
155 final String dimensionServiceKey,
156 final boolean resizable,
157 @Nullable final String caption,
158 @Nullable final Computable<Boolean> callback,
159 final boolean cancelOnClickOutside,
160 @Nullable final Set<JBPopupListener> listeners,
161 final boolean useDimServiceForXYLocation,
162 InplaceButton commandButton,
163 @Nullable final IconButton cancelButton,
164 @Nullable final MouseChecker cancelOnMouseOutCallback,
165 final boolean cancelOnWindow,
166 @Nullable final ActiveIcon titleIcon,
167 final boolean cancelKeyEnabled,
168 final boolean locateBycontent,
169 final boolean placeWithinScreenBounds,
170 @Nullable final Dimension minSize,
171 float alpha,
172 @Nullable MaskProvider maskProvider,
173 boolean inStack,
174 boolean modalContext,
175 @Nullable Component[] focusOwners,
176 @Nullable String adText,
177 final boolean headerAlwaysFocusable,
178 @NotNull List<Pair<ActionListener, KeyStroke>> keyboardActions,
179 Component settingsButtons) {
181 if (requestFocus && !focusable) {
182 assert false : "Incorrect argument combination: requestFocus=" + requestFocus + " focusable=" + focusable;
185 myProject = project;
186 myComponent = component;
187 myPopupBorder = PopupBorder.Factory.create(true);
188 myShadowed = !movable && !resizable && Registry.is("ide.popup.dropShadow");
189 myContent = createContentPanel(resizable, myPopupBorder, isToDrawMacCorner());
191 myContent.add(component, BorderLayout.CENTER);
192 if (adText != null) {
193 myContent.add(HintUtil.createAdComponent(adText), BorderLayout.SOUTH);
196 myCancelKeyEnabled = cancelKeyEnabled;
197 myLocateByContent = locateBycontent;
198 myLocateWithinScreen = placeWithinScreenBounds;
199 myAlpha = alpha;
200 myMaskProvider = maskProvider;
201 myInStack = inStack;
202 myModalContext = modalContext;
203 myFocusOwners = focusOwners;
204 myHeaderAlwaysFocusable = headerAlwaysFocusable;
205 myMovable = movable;
207 ActiveIcon actualIcon = titleIcon == null ? new ActiveIcon(new EmptyIcon(0)) : titleIcon;
209 myHeaderPanel = new JPanel(new BorderLayout());
211 if (caption != null) {
212 if (caption.length() > 0) {
213 myCaption = new TitlePanel(actualIcon.getRegular(), actualIcon.getInactive());
214 ((TitlePanel)myCaption).setText(caption);
216 else {
217 myCaption = new CaptionPanel();
219 if (cancelButton != null) {
220 myCaption.setButtonComponent(new InplaceButton(cancelButton, new ActionListener() {
221 public void actionPerformed(final ActionEvent e) {
222 cancel();
224 }));
226 else if (commandButton != null) {
227 myCaption.setButtonComponent(commandButton);
230 else {
231 myCaption = new CaptionPanel();
232 myCaption.setBorder(null);
233 myCaption.setPreferredSize(new Dimension(0, 0));
236 setWindowActive(myHeaderAlwaysFocusable);
238 myHeaderPanel.add(myCaption, BorderLayout.NORTH);
239 myContent.add(myHeaderPanel, BorderLayout.NORTH);
241 myForcedHeavyweight = forceHeavyweight;
242 myResizable = resizable;
243 myPreferredFocusedComponent = preferredFocusedComponent;
244 myRequestFocus = requestFocus;
245 myFocusable = focusable;
246 myDimensionServiceKey = dimensionServiceKey;
247 myCallBack = callback;
248 myCancelOnClickOutside = cancelOnClickOutside;
249 myCancelOnMouseOutCallback = cancelOnMouseOutCallback;
250 myListeners = listeners == null ? new HashSet<JBPopupListener>() : listeners;
251 myUseDimServiceForXYLocation = useDimServiceForXYLocation;
252 myCancelOnWindow = cancelOnWindow;
253 myMinSize = minSize;
255 for (Pair<ActionListener, KeyStroke> pair : keyboardActions) {
256 myContent.registerKeyboardAction(pair.getFirst(), pair.getSecond(), JComponent.WHEN_IN_FOCUSED_WINDOW);
259 if (settingsButtons != null) {
260 myCaption.addSettingsComponent(settingsButtons);
263 return this;
266 private void setWindowActive(boolean active) {
267 boolean value = myHeaderAlwaysFocusable || active;
269 if (myCaption != null) {
270 myCaption.setActive(value);
272 myPopupBorder.setActive(value);
273 myContent.repaint();
277 @NotNull
278 protected MyContentPanel createContentPanel(final boolean resizable, PopupBorder border, boolean isToDrawMacCorner) {
279 return new MyContentPanel(resizable, border, isToDrawMacCorner, myShadowed);
282 public static boolean isToDrawMacCorner() {
283 return SystemInfo.isMac;
288 public String getDimensionServiceKey() {
289 return myDimensionServiceKey;
292 public void setDimensionServiceKey(final String dimensionServiceKey) {
293 myDimensionServiceKey = dimensionServiceKey;
296 public void showInCenterOf(@NotNull Component aContainer) {
297 final Point popupPoint = getCenterOf(aContainer, myContent);
298 show(aContainer, popupPoint.x, popupPoint.y, false);
301 public void setAdText(@NotNull final String s) {
302 myContent.add(HintUtil.createAdComponent(s, BorderFactory.createEmptyBorder(3, 5, 3, 5)), BorderLayout.SOUTH);
305 public static Point getCenterOf(final Component aContainer, final JComponent content) {
306 final JComponent component = getTargetComponent(aContainer);
308 Point containerScreenPoint = component.getVisibleRect().getLocation();
309 SwingUtilities.convertPointToScreen(containerScreenPoint, aContainer);
311 return UIUtil.getCenterPoint(new Rectangle(containerScreenPoint, component.getVisibleRect().getSize()), content.getPreferredSize());
314 public void showCenteredInCurrentWindow(@NotNull Project project) {
315 Window window = null;
317 Component focusedComponent = getWndManager().getFocusedComponent(project);
318 if (focusedComponent != null) {
319 if (focusedComponent instanceof Window) {
320 window = (Window)focusedComponent;
322 else {
323 window = SwingUtilities.getWindowAncestor(focusedComponent);
326 if (window == null) {
327 window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
330 if (window != null) {
331 showInCenterOf(window);
335 public void showUnderneathOf(@NotNull Component aComponent) {
336 show(new RelativePoint(aComponent, new Point(0, aComponent.getHeight())));
339 public void show(@NotNull RelativePoint aPoint) {
340 final Point screenPoint = aPoint.getScreenPoint();
341 show(aPoint.getComponent(), screenPoint.x, screenPoint.y, false);
344 public void showInScreenCoordinates(@NotNull Component owner, @NotNull Point point) {
345 show(owner, point.x, point.y, false);
348 public void showInBestPositionFor(@NotNull DataContext dataContext) {
349 final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
350 if (editor != null) {
351 showInBestPositionFor(editor);
353 else {
354 show(relativePointByQuickSearch(dataContext));
358 public void showInFocusCenter() {
359 final Component focused = getWndManager().getFocusedComponent(myProject);
360 if (focused != null) {
361 showInCenterOf(focused);
362 } else {
363 final JFrame frame = WindowManager.getInstance().getFrame(myProject);
364 showInCenterOf(frame.getRootPane());
368 private RelativePoint relativePointByQuickSearch(final DataContext dataContext) {
369 Rectangle dominantArea = PlatformDataKeys.DOMINANT_HINT_AREA_RECTANGLE.getData(dataContext);
371 if (dominantArea != null) {
372 final Component focusedComponent = getWndManager().getFocusedComponent(myProject);
373 Window window = SwingUtilities.windowForComponent(focusedComponent);
374 JLayeredPane layeredPane;
375 if (window instanceof JFrame) {
376 layeredPane = ((JFrame)window).getLayeredPane();
378 else if (window instanceof JDialog) {
379 layeredPane = ((JDialog)window).getLayeredPane();
381 else if (window instanceof JWindow) {
382 layeredPane = ((JWindow)window).getLayeredPane();
384 else {
385 throw new IllegalStateException("cannot find parent window: project=" + myProject + "; window=" + window);
388 return relativePointWithDominantRectangle(layeredPane, dominantArea);
391 return JBPopupFactory.getInstance().guessBestPopupLocation(dataContext);
394 public void showInBestPositionFor(@NotNull Editor editor) {
395 assert editor.getComponent().isShowing() : "Editor must be showing on the screen";
397 DataContext context = ((EditorEx)editor).getDataContext();
398 Rectangle dominantArea = PlatformDataKeys.DOMINANT_HINT_AREA_RECTANGLE.getData(context);
399 if (dominantArea != null && !myRequestFocus) {
400 final JLayeredPane layeredPane = editor.getContentComponent().getRootPane().getLayeredPane();
401 show(relativePointWithDominantRectangle(layeredPane, dominantArea));
403 else {
404 show(JBPopupFactory.getInstance().guessBestPopupLocation(editor));
408 public void addPopupListener(JBPopupListener listener) {
409 myListeners.add(listener);
412 private RelativePoint relativePointWithDominantRectangle(final JLayeredPane layeredPane, final Rectangle bounds) {
413 Dimension preferredSize = getComponent().getPreferredSize();
414 if (myDimensionServiceKey != null) {
415 final Dimension dimension = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
416 if (dimension != null) {
417 preferredSize = dimension;
420 final Point leftTopCorner = new Point(bounds.x + bounds.width, bounds.y);
421 final Point leftTopCornerScreen = (Point)leftTopCorner.clone();
422 SwingUtilities.convertPointToScreen(leftTopCornerScreen, layeredPane);
423 final RelativePoint relativePoint;
424 if (!ScreenUtil.isOutsideOnTheRightOFScreen(
425 new Rectangle(leftTopCornerScreen.x, leftTopCornerScreen.y, preferredSize.width, preferredSize.height))) {
426 relativePoint = new RelativePoint(layeredPane, leftTopCorner);
428 else {
429 if (bounds.x > preferredSize.width) {
430 relativePoint = new RelativePoint(layeredPane, new Point(bounds.x - preferredSize.width, bounds.y));
432 else {
433 setDimensionServiceKey(null); // going to cut width
434 Rectangle screen = ScreenUtil.getScreenRectangle(leftTopCornerScreen.x, leftTopCornerScreen.y);
435 final int spaceOnTheLeft = bounds.x;
436 final int spaceOnTheRight = (screen.x + screen.width) - leftTopCornerScreen.x;
437 if (spaceOnTheLeft > spaceOnTheRight) {
438 relativePoint = new RelativePoint(layeredPane, new Point(0, bounds.y));
439 myComponent.setPreferredSize(new Dimension(spaceOnTheLeft, Math.max(preferredSize.height, 200)));
441 else {
442 relativePoint = new RelativePoint(layeredPane, leftTopCorner);
443 myComponent.setPreferredSize(new Dimension(spaceOnTheRight, Math.max(preferredSize.height, 200)));
447 return relativePoint;
450 public final void cancel() {
451 cancel(null);
454 public void setRequestFocus(boolean requestFocus) {
455 myRequestFocus = requestFocus;
458 public void cancel(InputEvent e) {
459 if (isDisposed()) return;
461 if (myPopup != null) {
462 if (!canClose()) {
463 return;
465 storeDimensionSize(myContent.getSize());
466 if (myUseDimServiceForXYLocation) {
467 final JRootPane root = myComponent.getRootPane();
468 if (root != null) {
469 final Container popupWindow = root.getParent();
470 if (popupWindow != null && popupWindow.isShowing()) {
471 storeLocation(popupWindow.getLocationOnScreen());
476 if (e instanceof MouseEvent) {
477 JBAwtEventQueue.getInstance().blockNextEvents(((MouseEvent)e));
480 myPopup.hide(false);
482 if (ApplicationManagerEx.getApplicationEx() != null) {
483 StackingPopupDispatcher.getInstance().onPopupHidden(this);
486 if (myInStack) {
487 myFocusTrackback.restoreFocus();
491 disposePopup();
493 if (myListeners != null) {
494 for (JBPopupListener each : myListeners) {
495 each.onClosed(new LightweightWindowEvent(this));
500 Disposer.dispose(this, false);
504 private void disposePopup() {
505 if (myPopup != null) {
506 myPopup.hide(true);
508 myPopup = null;
511 public boolean canClose() {
512 return myCallBack == null || myCallBack.compute().booleanValue();
515 public boolean isVisible() {
516 return myPopup != null;
519 public void show(final Component owner) {
520 show(owner, -1, -1, true);
523 public void show(Component owner, int aScreenX, int aScreenY, final boolean considerForcedXY) {
524 if (ApplicationManagerEx.getApplicationEx() != null && ApplicationManager.getApplication().isHeadlessEnvironment()) return;
525 if (isDisposed()) {
526 throw new IllegalStateException("Popup was already disposed. Recreate a new instance to show again");
529 assert ApplicationManager.getApplication().isDispatchThread();
532 final boolean shouldShow = beforeShow();
533 if (!shouldShow) {
534 return;
537 prepareToShow();
539 if (myInStack) {
540 myFocusTrackback = new FocusTrackback(this, owner, true);
541 myFocusTrackback.setMustBeShown(true);
545 Dimension sizeToSet = null;
547 if (myDimensionServiceKey != null) {
548 sizeToSet = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
551 if (myForcedSize != null) {
552 sizeToSet = myForcedSize;
555 if (myMinSize == null) {
556 myMinSize = myContent.getMinimumSize();
559 if (sizeToSet == null) {
560 sizeToSet = myContent.getPreferredSize();
563 if (sizeToSet != null) {
564 sizeToSet.width = Math.max(sizeToSet.width, myMinSize.width);
565 sizeToSet.height = Math.max(sizeToSet.height, myMinSize.height);
567 myContent.setSize(sizeToSet);
568 myContent.setPreferredSize(sizeToSet);
571 Point xy = new Point(aScreenX, aScreenY);
572 boolean adjustXY = true;
573 if (myDimensionServiceKey != null) {
574 final Point storedLocation = DimensionService.getInstance().getLocation(myDimensionServiceKey, myProject);
575 if (storedLocation != null) {
576 xy = storedLocation;
577 adjustXY = false;
581 if (adjustXY) {
582 final Insets insets = myContent.getInsets();
583 if (insets != null) {
584 xy.x -= insets.left;
585 xy.y -= insets.top;
589 if (considerForcedXY && myForcedLocation != null) {
590 xy = myForcedLocation;
593 if (myLocateByContent) {
594 final Dimension captionSize = myHeaderPanel.getPreferredSize();
595 xy.y -= captionSize.height;
598 Rectangle targetBounds = new Rectangle(xy, myContent.getPreferredSize());
599 Rectangle original = new Rectangle(targetBounds);
600 if (myLocateWithinScreen) {
601 ScreenUtil.moveRectangleToFitTheScreen(targetBounds);
604 if (myMouseOutCanceller != null) {
605 myMouseOutCanceller.myEverEntered = targetBounds.equals(original);
608 myOwner = IdeFrameImpl.findNearestModalComponent(owner);
609 if (myOwner == null) {
610 myOwner = owner;
613 myRequestorComponent = owner;
615 myPopup = getFactory(myForcedHeavyweight || myResizable).getPopup(myOwner, myContent, targetBounds.x, targetBounds.y);
617 if (myResizable) {
618 final JRootPane root = myContent.getRootPane();
619 final IdeGlassPaneImpl glass = new IdeGlassPaneImpl(root);
620 root.setGlassPane(glass);
622 final ResizeComponentListener resizeListener = new ResizeComponentListener(this);
623 glass.addMousePreprocessor(resizeListener, this);
624 glass.addMouseMotionPreprocessor(resizeListener, this);
627 if (myCaption != null && myMovable) {
628 final MoveComponentListener moveListener = new MoveComponentListener(myCaption) {
629 public void mousePressed(final MouseEvent e) {
630 super.mousePressed(e);
631 if (e.isConsumed()) return;
633 if (UIUtil.isCloseClick(e)) {
634 if (myCaption.isWithinPanel(e)) {
635 cancel();
640 ListenerUtil.addMouseListener(myCaption, moveListener);
641 ListenerUtil.addMouseMotionListener(myCaption, moveListener);
642 final MyContentPanel saved = myContent;
643 Disposer.register(this, new Disposable() {
644 public void dispose() {
645 ListenerUtil.removeMouseListener(saved, moveListener);
646 ListenerUtil.removeMouseMotionListener(saved, moveListener);
651 for(JBPopupListener listener: myListeners) {
652 listener.beforeShown(new LightweightWindowEvent(this));
655 myPopup.show();
657 final Window window = SwingUtilities.getWindowAncestor(myContent);
659 myWindowListener = new MyWindowListener();
660 window.addWindowListener(myWindowListener);
662 if (myFocusable) {
663 window.setFocusableWindowState(true);
664 window.setFocusable(true);
665 if (myRequestFocus) {
666 window.requestFocusInWindow();
670 myWindow = updateMaskAndAlpha(window);
672 if (myWindow instanceof JWindow) {
673 ((JWindow)myWindow).getRootPane().putClientProperty(KEY, this);
676 SwingUtilities.invokeLater(new Runnable() {
677 public void run() {
678 if (isDisposed()) return;
680 if (myRequestFocus) {
681 requestFocus();
684 if (myPreferredFocusedComponent != null && myInStack) {
685 myFocusTrackback.registerFocusComponent(myPreferredFocusedComponent);
688 afterShow();
693 private void prepareToShow() {
694 final MouseAdapter mouseAdapter = new MouseAdapter() {
695 public void mousePressed(MouseEvent e) {
696 Point point = (Point)e.getPoint().clone();
697 SwingUtilities.convertPointToScreen(point, e.getComponent());
699 final Dimension dimension = myContent.getSize();
700 dimension.height += myResizable && isToDrawMacCorner() ? ourMacCorner.getHeight(myContent) : 4;
701 dimension.width += 4;
702 Point locationOnScreen = myContent.getLocationOnScreen();
703 final Rectangle bounds = new Rectangle(new Point(locationOnScreen.x - 2, locationOnScreen.y - 2), dimension);
704 if (!bounds.contains(point)) {
705 cancel();
709 myContent.addMouseListener(mouseAdapter);
710 Disposer.register(this, new Disposable() {
711 public void dispose() {
712 myContent.removeMouseListener(mouseAdapter);
716 myContent.registerKeyboardAction(new ActionListener() {
717 public void actionPerformed(ActionEvent e) {
718 if (myCancelKeyEnabled) {
719 cancel();
722 }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
725 myContent.addKeyListener(new KeyListener() {
726 public void keyTyped(final KeyEvent e) {
727 mySpeedSearch.process(e);
730 public void keyPressed(final KeyEvent e) {
731 mySpeedSearch.process(e);
734 public void keyReleased(final KeyEvent e) {
735 mySpeedSearch.process(e);
739 if (myCancelOnMouseOutCallback != null || myCancelOnWindow) {
740 myMouseOutCanceller = new Canceller();
741 Toolkit.getDefaultToolkit().addAWTEventListener(myMouseOutCanceller, AWTEvent.MOUSE_EVENT_MASK | WindowEvent.WINDOW_ACTIVATED |
742 AWTEvent.MOUSE_MOTION_EVENT_MASK);
746 myFocusWatcher = new ChildFocusWatcher(myContent) {
747 protected void onFocusGained(final FocusEvent event) {
748 setWindowActive(true);
751 protected void onFocusLost(final FocusEvent event) {
752 setWindowActive(false);
757 mySpeedSearchPatternField = new JTextField();
758 if (SystemInfo.isMac) {
759 Font f = mySpeedSearchPatternField.getFont();
760 mySpeedSearchPatternField.setFont(f.deriveFont(f.getStyle(), f.getSize() - 2));
764 private Window updateMaskAndAlpha(Window window) {
765 if (window == null) return window;
767 final WindowManagerEx wndManager = getWndManager();
768 if (wndManager == null) return window;
770 if (!wndManager.isAlphaModeEnabled(window)) return window;
772 if (myAlpha != myLastAlpha) {
773 wndManager.setAlphaModeRatio(window, myAlpha);
774 myLastAlpha = myAlpha;
777 if (myMaskProvider != null) {
778 final Dimension size = window.getSize();
779 Shape mask = myMaskProvider.getMask(size);
780 wndManager.setWindowMask(window, mask);
783 return window;
786 private static WindowManagerEx getWndManager() {
787 return ApplicationManagerEx.getApplicationEx() != null ? WindowManagerEx.getInstanceEx() : null;
790 public boolean isDisposed() {
791 return myContent == null;
794 protected boolean beforeShow() {
795 if (ApplicationManagerEx.getApplicationEx() == null) return true;
796 StackingPopupDispatcher.getInstance().onPopupShown(this, myInStack);
797 return true;
800 protected void afterShow() {
803 protected final void requestFocus() {
804 if (!myFocusable) return;
806 if (myPreferredFocusedComponent != null) {
807 if (myProject != null) {
808 getFocusManager().requestFocus(myPreferredFocusedComponent, true);
810 else {
811 myPreferredFocusedComponent.requestFocus();
816 private IdeFocusManager getFocusManager() {
817 return myProject == null ? null : IdeFocusManager.getInstance(myProject);
820 private static JComponent getTargetComponent(Component aComponent) {
821 if (aComponent instanceof JComponent) {
822 return (JComponent)aComponent;
824 else if (aComponent instanceof RootPaneContainer) {
825 return ((RootPaneContainer)aComponent).getRootPane();
828 LOG.error("Cannot find target for:" + aComponent);
829 return null;
832 private PopupComponent.Factory getFactory(boolean forceHeavyweight) {
833 if (isPersistent()) {
834 return new PopupComponent.Factory.Dialog();
835 } else if (forceHeavyweight || !SystemInfo.isWindows) {
836 return new PopupComponent.Factory.AwtHeavyweight();
837 } else {
838 return new PopupComponent.Factory.AwtDefault();
842 public JComponent getContent() {
843 return myContent;
846 public void setLocation(RelativePoint p) {
847 setLocation(p, myPopup, myContent);
850 private static void setLocation(final RelativePoint p, final PopupComponent popup, Component content) {
851 if (popup == null) return;
853 final Window wnd = popup.getWindow();
854 assert wnd != null;
856 wnd.pack();
857 wnd.setLocation(p.getScreenPoint());
860 public void pack() {
861 final Window window = SwingUtilities.getWindowAncestor(myContent);
863 window.pack();
866 public JComponent getComponent() {
867 return myComponent;
870 public void setProject(Project project) {
871 myProject = project;
875 public void dispose() {
876 Disposer.dispose(this, false);
878 assert ApplicationManager.getApplication().isDispatchThread();
880 if (myPopup != null) {
881 cancel(myDisposeEvent);
884 if (myContent != null) {
885 myContent.removeAll();
887 myContent = null;
888 myComponent = null;
889 myFocusTrackback = null;
890 myCallBack = null;
891 myListeners = null;
893 if (myMouseOutCanceller != null) {
894 final Toolkit toolkit = Toolkit.getDefaultToolkit();
895 // it may happen, but have no idea how
896 // http://www.jetbrains.net/jira/browse/IDEADEV-21265
897 if (toolkit != null) {
898 toolkit.removeAWTEventListener(myMouseOutCanceller);
901 myMouseOutCanceller = null;
903 if (myFocusWatcher != null) {
904 myFocusWatcher.dispose();
905 myFocusWatcher = null;
908 resetWindow();
910 if (myFinalRunnable != null) {
911 IdeFocusManager focusManager = getFocusManager();
912 if (focusManager != null) {
913 focusManager.doWhenFocusSettlesDown(myFinalRunnable);
915 else {
916 myFinalRunnable.run();
918 myFinalRunnable = null;
922 private void resetWindow() {
923 if (myWindow != null && getWndManager() != null) {
924 getWndManager().resetWindow(myWindow);
925 if (myWindowListener != null) {
926 myWindow.removeWindowListener(myWindowListener);
929 if (myWindow instanceof JWindow) {
930 ((JWindow)myWindow).getRootPane().putClientProperty(KEY, null);
933 myWindow = null;
934 myWindowListener = null;
938 public void storeDimensionSize(final Dimension size) {
939 if (myDimensionServiceKey != null) {
940 DimensionService.getInstance().setSize(myDimensionServiceKey, size, myProject);
944 public void storeLocation(final Point xy) {
945 if (myDimensionServiceKey != null) {
946 DimensionService.getInstance().setLocation(myDimensionServiceKey, xy, myProject);
950 public static class MyContentPanel extends JPanel {
951 private final boolean myResizable;
952 private final boolean myDrawMacCorner;
953 private final boolean myShadowed;
955 public MyContentPanel(final boolean resizable, final PopupBorder border, boolean drawMacCorner) {
956 this(resizable, border, drawMacCorner, false);
959 public MyContentPanel(final boolean resizable, final PopupBorder border, boolean drawMacCorner, boolean shadowed) {
960 super(new BorderLayout());
961 myResizable = resizable;
962 myDrawMacCorner = drawMacCorner;
963 myShadowed = shadowed;
964 if (isShadowPossible()) {
965 setOpaque(false);
966 setBorder(new EmptyBorder(POPUP_TOP_SIZE, POPUP_SIDE_SIZE, POPUP_BOTTOM_SIZE, POPUP_SIDE_SIZE) {
967 @Override
968 public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
969 border.paintBorder(c, g,
970 x + POPUP_SIDE_SIZE - 1,
971 y + POPUP_TOP_SIZE - 1,
972 width - 2 * POPUP_SIDE_SIZE + 2,
973 height - POPUP_TOP_SIZE - POPUP_BOTTOM_SIZE + 2);
976 } else {
977 setBorder(border);
981 private boolean isShadowPossible() {
982 return myShadowed && !SystemInfo.isMac && !UISettings.isRemoteDesktopConnected();
985 public void paint(Graphics g) {
986 if (isShadowPossible()) {
987 paintShadow(g);
990 super.paint(g);
992 if (myResizable && myDrawMacCorner) {
993 g.drawImage(ourMacCorner,
994 getX() + getWidth() - ourMacCorner.getWidth(this),
995 getY() + getHeight() - ourMacCorner.getHeight(this),
996 this);
1000 private void paintShadow(final Graphics g) {
1001 BufferedImage capture = null;
1002 try {
1003 final Point onScreen = getLocationOnScreen();
1004 capture = new Robot().createScreenCapture(
1005 new Rectangle(onScreen.x, onScreen.y, getWidth() + 2 * POPUP_SIDE_SIZE, getHeight() + POPUP_TOP_SIZE + POPUP_BOTTOM_SIZE));
1006 final BufferedImage shadow = ShadowBorderPainter.createPopupShadow(this, getWidth(), getHeight());
1007 ((Graphics2D)capture.getGraphics()).drawImage(shadow, null, null);
1009 catch (Exception e) {
1010 e.printStackTrace();
1012 if (capture != null) g.drawImage(capture, 0, 0, null);
1016 public boolean isCancelOnClickOutside() {
1017 return myCancelOnClickOutside;
1020 private class Canceller implements AWTEventListener {
1022 private boolean myEverEntered = false;
1024 public void eventDispatched(final AWTEvent event) {
1025 if (event.getID() == WindowEvent.WINDOW_ACTIVATED) {
1026 if (myCancelOnWindow) {
1027 cancel();
1029 } else if (event.getID() == MouseEvent.MOUSE_ENTERED) {
1030 if (withinPopup(event)) {
1031 myEverEntered = true;
1033 } else if (event.getID() == MouseEvent.MOUSE_MOVED) {
1034 if (myCancelOnMouseOutCallback != null && myEverEntered && !withinPopup(event)) {
1035 if (myCancelOnMouseOutCallback.check((MouseEvent)event)) {
1036 cancel();
1042 private boolean withinPopup(final AWTEvent event) {
1043 if (!myContent.isShowing()) return false;
1045 final MouseEvent mouse = (MouseEvent)event;
1046 final Point point = mouse.getPoint();
1047 SwingUtilities.convertPointToScreen(point, mouse.getComponent());
1048 return new Rectangle(myContent.getLocationOnScreen(), myContent.getSize()).contains(point);
1052 public void setLocation(@NotNull final Point screenPoint) {
1053 if (myPopup == null) {
1054 myForcedLocation = screenPoint;
1055 } else {
1056 moveTo(myContent, screenPoint, myLocateByContent ? myHeaderPanel.getPreferredSize() : null);
1060 public static Window moveTo(JComponent content, Point screenPoint, final Dimension headerCorrectionSize) {
1061 setDefaultCursor(content);
1062 final Window wnd = SwingUtilities.getWindowAncestor(content);
1063 if (headerCorrectionSize != null) {
1064 screenPoint.y -= headerCorrectionSize.height;
1066 wnd.setLocation(screenPoint);
1067 return wnd;
1070 public void setSize(@NotNull final Dimension size) {
1071 if (myPopup == null) {
1072 myForcedSize = size;
1073 } else {
1074 updateMaskAndAlpha(setSize(myContent, size));
1078 public static Window setSize(JComponent content, final Dimension size) {
1079 final Window popupWindow = SwingUtilities.windowForComponent(content);
1080 final Point location = popupWindow.getLocation();
1081 popupWindow.setBounds(location.x, location.y, size.width, size.height);
1082 return popupWindow;
1085 public static void setDefaultCursor(JComponent content) {
1086 final Window wnd = SwingUtilities.getWindowAncestor(content);
1087 if (wnd != null) {
1088 wnd.setCursor(Cursor.getDefaultCursor());
1092 public void setCaption(String title) {
1093 if (myCaption instanceof TitlePanel) {
1094 ((TitlePanel)myCaption).setText(title);
1098 private class MyWindowListener extends WindowAdapter {
1099 public void windowClosed(final WindowEvent e) {
1100 resetWindow();
1104 public boolean isPersistent() {
1105 return !myCancelOnClickOutside && !myCancelOnWindow;
1108 public void setUiVisible(final boolean visible) {
1109 if (myPopup != null) {
1110 if (visible) {
1111 myPopup.show();
1112 final Window window = getPopupWindow();
1113 if (window != null && myRestoreWindowSize != null) {
1114 window.setSize(myRestoreWindowSize);
1115 myRestoreWindowSize = null;
1118 else {
1119 final Window window = getPopupWindow();
1120 if (window != null) {
1121 myRestoreWindowSize = window.getSize();
1122 window.setVisible(true);
1128 private Window getPopupWindow() {
1129 return myPopup.getWindow();
1132 public void setUserData(ArrayList<Object> userData) {
1133 myUserData = userData;
1136 public <T> T getUserData(final Class<T> userDataClass) {
1137 if (myUserData != null) {
1138 for(Object o: myUserData) {
1139 if (userDataClass.isInstance(o)) {
1140 //noinspection unchecked
1141 return (T) o;
1145 return null;
1148 public boolean isModalContext() {
1149 return myModalContext;
1152 public boolean isFocused() {
1153 if (myComponent != null && isFocused(new Component[] {SwingUtilities.getWindowAncestor(myComponent)}))
1154 return true;
1155 return isFocused(myFocusOwners);
1158 public static boolean isFocused(@Nullable Component[] components) {
1159 if (components == null) return false;
1161 Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
1163 if (owner == null) return false;
1164 for (Component each : components) {
1165 if (each != null && SwingUtilities.isDescendingFrom(owner, each)) return true;
1168 return false;
1171 public boolean isCancelKeyEnabled() {
1172 return myCancelKeyEnabled;
1175 @NotNull
1176 CaptionPanel getTitle() {
1177 return myCaption;
1180 private void setHeaderComponent(JComponent c) {
1181 boolean doRevalidate = false;
1182 if (myHeaderComponent != null) {
1183 myHeaderPanel.remove(myHeaderComponent);
1184 myHeaderPanel.add(myCaption, BorderLayout.NORTH);
1185 myHeaderComponent = null;
1186 doRevalidate = true;
1189 if (c != null) {
1190 myHeaderPanel.remove(myCaption);
1191 myHeaderPanel.add(c, BorderLayout.NORTH);
1192 myHeaderComponent = c;
1194 final Dimension size = myContent.getSize();
1195 if (size.height < c.getPreferredSize().height * 2) {
1196 size.height += c.getPreferredSize().height;
1197 setSize(size);
1200 doRevalidate = true;
1203 if (doRevalidate) myContent.revalidate();
1206 public void addListener(final JBPopupListener listener) {
1207 myListeners.add(listener);
1210 public void removeListener(final JBPopupListener listener) {
1211 myListeners.remove(listener);
1214 protected void onSpeedSearchPatternChanged() {
1217 public Component getOwner() {
1218 return myRequestorComponent;
1221 public void setMinimumSize(Dimension size) {
1222 myMinSize = size;
1225 public Runnable getFinalRunnable() {
1226 return myFinalRunnable;
1229 public void setFinalRunnable(Runnable finalRunnable) {
1230 myFinalRunnable = finalRunnable;