846d59dde3723fc6538fa53ee9c6c2e71beba925
[fedora-idea.git] / platform / platform-impl / src / com / intellij / ui / popup / AbstractPopup.java
blob846d59dde3723fc6538fa53ee9c6c2e71beba925
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;
143 private boolean myNativePopup;
146 AbstractPopup() {
149 AbstractPopup init(final Project project,
150 @NotNull final JComponent component,
151 @Nullable final JComponent preferredFocusedComponent,
152 final boolean requestFocus,
153 final boolean focusable,
154 final boolean forceHeavyweight,
155 final boolean movable,
156 final String dimensionServiceKey,
157 final boolean resizable,
158 @Nullable final String caption,
159 @Nullable final Computable<Boolean> callback,
160 final boolean cancelOnClickOutside,
161 @Nullable final Set<JBPopupListener> listeners,
162 final boolean useDimServiceForXYLocation,
163 InplaceButton commandButton,
164 @Nullable final IconButton cancelButton,
165 @Nullable final MouseChecker cancelOnMouseOutCallback,
166 final boolean cancelOnWindow,
167 @Nullable final ActiveIcon titleIcon,
168 final boolean cancelKeyEnabled,
169 final boolean locateBycontent,
170 final boolean placeWithinScreenBounds,
171 @Nullable final Dimension minSize,
172 float alpha,
173 @Nullable MaskProvider maskProvider,
174 boolean inStack,
175 boolean modalContext,
176 @Nullable Component[] focusOwners,
177 @Nullable String adText,
178 final boolean headerAlwaysFocusable,
179 @NotNull List<Pair<ActionListener, KeyStroke>> keyboardActions,
180 Component settingsButtons) {
182 if (requestFocus && !focusable) {
183 assert false : "Incorrect argument combination: requestFocus=" + requestFocus + " focusable=" + focusable;
186 myProject = project;
187 myComponent = component;
188 myPopupBorder = PopupBorder.Factory.create(true);
189 myShadowed = !movable && !resizable && Registry.is("ide.popup.dropShadow");
190 myContent = createContentPanel(resizable, myPopupBorder, isToDrawMacCorner());
192 myContent.add(component, BorderLayout.CENTER);
193 if (adText != null) {
194 myContent.add(HintUtil.createAdComponent(adText), BorderLayout.SOUTH);
197 myCancelKeyEnabled = cancelKeyEnabled;
198 myLocateByContent = locateBycontent;
199 myLocateWithinScreen = placeWithinScreenBounds;
200 myAlpha = alpha;
201 myMaskProvider = maskProvider;
202 myInStack = inStack;
203 myModalContext = modalContext;
204 myFocusOwners = focusOwners;
205 myHeaderAlwaysFocusable = headerAlwaysFocusable;
206 myMovable = movable;
208 ActiveIcon actualIcon = titleIcon == null ? new ActiveIcon(new EmptyIcon(0)) : titleIcon;
210 myHeaderPanel = new JPanel(new BorderLayout());
212 if (caption != null) {
213 if (caption.length() > 0) {
214 myCaption = new TitlePanel(actualIcon.getRegular(), actualIcon.getInactive());
215 ((TitlePanel)myCaption).setText(caption);
217 else {
218 myCaption = new CaptionPanel();
220 if (cancelButton != null) {
221 myCaption.setButtonComponent(new InplaceButton(cancelButton, new ActionListener() {
222 public void actionPerformed(final ActionEvent e) {
223 cancel();
225 }));
227 else if (commandButton != null) {
228 myCaption.setButtonComponent(commandButton);
231 else {
232 myCaption = new CaptionPanel();
233 myCaption.setBorder(null);
234 myCaption.setPreferredSize(new Dimension(0, 0));
237 setWindowActive(myHeaderAlwaysFocusable);
239 myHeaderPanel.add(myCaption, BorderLayout.NORTH);
240 myContent.add(myHeaderPanel, BorderLayout.NORTH);
242 myForcedHeavyweight = forceHeavyweight;
243 myResizable = resizable;
244 myPreferredFocusedComponent = preferredFocusedComponent;
245 myRequestFocus = requestFocus;
246 myFocusable = focusable;
247 myDimensionServiceKey = dimensionServiceKey;
248 myCallBack = callback;
249 myCancelOnClickOutside = cancelOnClickOutside;
250 myCancelOnMouseOutCallback = cancelOnMouseOutCallback;
251 myListeners = listeners == null ? new HashSet<JBPopupListener>() : listeners;
252 myUseDimServiceForXYLocation = useDimServiceForXYLocation;
253 myCancelOnWindow = cancelOnWindow;
254 myMinSize = minSize;
256 for (Pair<ActionListener, KeyStroke> pair : keyboardActions) {
257 myContent.registerKeyboardAction(pair.getFirst(), pair.getSecond(), JComponent.WHEN_IN_FOCUSED_WINDOW);
260 if (settingsButtons != null) {
261 myCaption.addSettingsComponent(settingsButtons);
264 return this;
267 private void setWindowActive(boolean active) {
268 boolean value = myHeaderAlwaysFocusable || active;
270 if (myCaption != null) {
271 myCaption.setActive(value);
273 myPopupBorder.setActive(value);
274 myContent.repaint();
278 @NotNull
279 protected MyContentPanel createContentPanel(final boolean resizable, PopupBorder border, boolean isToDrawMacCorner) {
280 return new MyContentPanel(resizable, border, isToDrawMacCorner, myShadowed);
283 public static boolean isToDrawMacCorner() {
284 return SystemInfo.isMac;
289 public String getDimensionServiceKey() {
290 return myDimensionServiceKey;
293 public void setDimensionServiceKey(final String dimensionServiceKey) {
294 myDimensionServiceKey = dimensionServiceKey;
297 public void showInCenterOf(@NotNull Component aContainer) {
298 final Point popupPoint = getCenterOf(aContainer, myContent);
299 show(aContainer, popupPoint.x, popupPoint.y, false);
302 public void setAdText(@NotNull final String s) {
303 myContent.add(HintUtil.createAdComponent(s, BorderFactory.createEmptyBorder(3, 5, 3, 5)), BorderLayout.SOUTH);
306 public static Point getCenterOf(final Component aContainer, final JComponent content) {
307 final JComponent component = getTargetComponent(aContainer);
309 Point containerScreenPoint = component.getVisibleRect().getLocation();
310 SwingUtilities.convertPointToScreen(containerScreenPoint, aContainer);
312 return UIUtil.getCenterPoint(new Rectangle(containerScreenPoint, component.getVisibleRect().getSize()), content.getPreferredSize());
315 public void showCenteredInCurrentWindow(@NotNull Project project) {
316 Window window = null;
318 Component focusedComponent = getWndManager().getFocusedComponent(project);
319 if (focusedComponent != null) {
320 if (focusedComponent instanceof Window) {
321 window = (Window)focusedComponent;
323 else {
324 window = SwingUtilities.getWindowAncestor(focusedComponent);
327 if (window == null) {
328 window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
331 if (window != null) {
332 showInCenterOf(window);
336 public void showUnderneathOf(@NotNull Component aComponent) {
337 show(new RelativePoint(aComponent, new Point(0, aComponent.getHeight())));
340 public void show(@NotNull RelativePoint aPoint) {
341 final Point screenPoint = aPoint.getScreenPoint();
342 show(aPoint.getComponent(), screenPoint.x, screenPoint.y, false);
345 public void showInScreenCoordinates(@NotNull Component owner, @NotNull Point point) {
346 show(owner, point.x, point.y, false);
349 public void showInBestPositionFor(@NotNull DataContext dataContext) {
350 final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
351 if (editor != null) {
352 showInBestPositionFor(editor);
354 else {
355 show(relativePointByQuickSearch(dataContext));
359 public void showInFocusCenter() {
360 final Component focused = getWndManager().getFocusedComponent(myProject);
361 if (focused != null) {
362 showInCenterOf(focused);
363 } else {
364 final JFrame frame = WindowManager.getInstance().getFrame(myProject);
365 showInCenterOf(frame.getRootPane());
369 private RelativePoint relativePointByQuickSearch(final DataContext dataContext) {
370 Rectangle dominantArea = PlatformDataKeys.DOMINANT_HINT_AREA_RECTANGLE.getData(dataContext);
372 if (dominantArea != null) {
373 final Component focusedComponent = getWndManager().getFocusedComponent(myProject);
374 Window window = SwingUtilities.windowForComponent(focusedComponent);
375 JLayeredPane layeredPane;
376 if (window instanceof JFrame) {
377 layeredPane = ((JFrame)window).getLayeredPane();
379 else if (window instanceof JDialog) {
380 layeredPane = ((JDialog)window).getLayeredPane();
382 else if (window instanceof JWindow) {
383 layeredPane = ((JWindow)window).getLayeredPane();
385 else {
386 throw new IllegalStateException("cannot find parent window: project=" + myProject + "; window=" + window);
389 return relativePointWithDominantRectangle(layeredPane, dominantArea);
392 return JBPopupFactory.getInstance().guessBestPopupLocation(dataContext);
395 public void showInBestPositionFor(@NotNull Editor editor) {
396 assert editor.getComponent().isShowing() : "Editor must be showing on the screen";
398 DataContext context = ((EditorEx)editor).getDataContext();
399 Rectangle dominantArea = PlatformDataKeys.DOMINANT_HINT_AREA_RECTANGLE.getData(context);
400 if (dominantArea != null && !myRequestFocus) {
401 final JLayeredPane layeredPane = editor.getContentComponent().getRootPane().getLayeredPane();
402 show(relativePointWithDominantRectangle(layeredPane, dominantArea));
404 else {
405 show(JBPopupFactory.getInstance().guessBestPopupLocation(editor));
409 public void addPopupListener(JBPopupListener listener) {
410 myListeners.add(listener);
413 private RelativePoint relativePointWithDominantRectangle(final JLayeredPane layeredPane, final Rectangle bounds) {
414 Dimension preferredSize = getComponent().getPreferredSize();
415 if (myDimensionServiceKey != null) {
416 final Dimension dimension = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
417 if (dimension != null) {
418 preferredSize = dimension;
421 final Point leftTopCorner = new Point(bounds.x + bounds.width, bounds.y);
422 final Point leftTopCornerScreen = (Point)leftTopCorner.clone();
423 SwingUtilities.convertPointToScreen(leftTopCornerScreen, layeredPane);
424 final RelativePoint relativePoint;
425 if (!ScreenUtil.isOutsideOnTheRightOFScreen(
426 new Rectangle(leftTopCornerScreen.x, leftTopCornerScreen.y, preferredSize.width, preferredSize.height))) {
427 relativePoint = new RelativePoint(layeredPane, leftTopCorner);
429 else {
430 if (bounds.x > preferredSize.width) {
431 relativePoint = new RelativePoint(layeredPane, new Point(bounds.x - preferredSize.width, bounds.y));
433 else {
434 setDimensionServiceKey(null); // going to cut width
435 Rectangle screen = ScreenUtil.getScreenRectangle(leftTopCornerScreen.x, leftTopCornerScreen.y);
436 final int spaceOnTheLeft = bounds.x;
437 final int spaceOnTheRight = (screen.x + screen.width) - leftTopCornerScreen.x;
438 if (spaceOnTheLeft > spaceOnTheRight) {
439 relativePoint = new RelativePoint(layeredPane, new Point(0, bounds.y));
440 myComponent.setPreferredSize(new Dimension(spaceOnTheLeft, Math.max(preferredSize.height, 200)));
442 else {
443 relativePoint = new RelativePoint(layeredPane, leftTopCorner);
444 myComponent.setPreferredSize(new Dimension(spaceOnTheRight, Math.max(preferredSize.height, 200)));
448 return relativePoint;
451 public final void cancel() {
452 cancel(null);
455 public void setRequestFocus(boolean requestFocus) {
456 myRequestFocus = requestFocus;
459 public void cancel(InputEvent e) {
460 if (isDisposed()) return;
462 if (myPopup != null) {
463 if (!canClose()) {
464 return;
466 storeDimensionSize(myContent.getSize());
467 if (myUseDimServiceForXYLocation) {
468 final JRootPane root = myComponent.getRootPane();
469 if (root != null) {
470 final Container popupWindow = root.getParent();
471 if (popupWindow != null && popupWindow.isShowing()) {
472 storeLocation(popupWindow.getLocationOnScreen());
477 if (e instanceof MouseEvent) {
478 JBAwtEventQueue.getInstance().blockNextEvents(((MouseEvent)e));
481 myPopup.hide(false);
483 if (ApplicationManagerEx.getApplicationEx() != null) {
484 StackingPopupDispatcher.getInstance().onPopupHidden(this);
487 if (myInStack) {
488 myFocusTrackback.restoreFocus();
492 disposePopup();
494 if (myListeners != null) {
495 for (JBPopupListener each : myListeners) {
496 each.onClosed(new LightweightWindowEvent(this));
501 Disposer.dispose(this, false);
505 private void disposePopup() {
506 if (myPopup != null) {
507 myPopup.hide(true);
509 myPopup = null;
512 public boolean canClose() {
513 return myCallBack == null || myCallBack.compute().booleanValue();
516 public boolean isVisible() {
517 return myPopup != null;
520 public void show(final Component owner) {
521 show(owner, -1, -1, true);
524 public void show(Component owner, int aScreenX, int aScreenY, final boolean considerForcedXY) {
525 if (ApplicationManagerEx.getApplicationEx() != null && ApplicationManager.getApplication().isHeadlessEnvironment()) return;
526 if (isDisposed()) {
527 throw new IllegalStateException("Popup was already disposed. Recreate a new instance to show again");
530 assert ApplicationManager.getApplication().isDispatchThread();
533 final boolean shouldShow = beforeShow();
534 if (!shouldShow) {
535 return;
538 prepareToShow();
540 if (myInStack) {
541 myFocusTrackback = new FocusTrackback(this, owner, true);
542 myFocusTrackback.setMustBeShown(true);
546 Dimension sizeToSet = null;
548 if (myDimensionServiceKey != null) {
549 sizeToSet = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
552 if (myForcedSize != null) {
553 sizeToSet = myForcedSize;
556 if (myMinSize == null) {
557 myMinSize = myContent.getMinimumSize();
560 if (sizeToSet == null) {
561 sizeToSet = myContent.getPreferredSize();
564 if (sizeToSet != null) {
565 sizeToSet.width = Math.max(sizeToSet.width, myMinSize.width);
566 sizeToSet.height = Math.max(sizeToSet.height, myMinSize.height);
568 myContent.setSize(sizeToSet);
569 myContent.setPreferredSize(sizeToSet);
572 Point xy = new Point(aScreenX, aScreenY);
573 boolean adjustXY = true;
574 if (myDimensionServiceKey != null) {
575 final Point storedLocation = DimensionService.getInstance().getLocation(myDimensionServiceKey, myProject);
576 if (storedLocation != null) {
577 xy = storedLocation;
578 adjustXY = false;
582 if (adjustXY) {
583 final Insets insets = myContent.getInsets();
584 if (insets != null) {
585 xy.x -= insets.left;
586 xy.y -= insets.top;
590 if (considerForcedXY && myForcedLocation != null) {
591 xy = myForcedLocation;
594 if (myLocateByContent) {
595 final Dimension captionSize = myHeaderPanel.getPreferredSize();
596 xy.y -= captionSize.height;
599 Rectangle targetBounds = new Rectangle(xy, myContent.getPreferredSize());
600 Rectangle original = new Rectangle(targetBounds);
601 if (myLocateWithinScreen) {
602 ScreenUtil.moveRectangleToFitTheScreen(targetBounds);
605 if (myMouseOutCanceller != null) {
606 myMouseOutCanceller.myEverEntered = targetBounds.equals(original);
609 myOwner = IdeFrameImpl.findNearestModalComponent(owner);
610 if (myOwner == null) {
611 myOwner = owner;
614 myRequestorComponent = owner;
616 PopupComponent.Factory factory = getFactory(myForcedHeavyweight || myResizable);
617 myNativePopup = factory.isNativePopup();
618 myPopup = factory.getPopup(myOwner, myContent, targetBounds.x, targetBounds.y);
620 if (myResizable) {
621 final JRootPane root = myContent.getRootPane();
622 final IdeGlassPaneImpl glass = new IdeGlassPaneImpl(root);
623 root.setGlassPane(glass);
625 final ResizeComponentListener resizeListener = new ResizeComponentListener(this);
626 glass.addMousePreprocessor(resizeListener, this);
627 glass.addMouseMotionPreprocessor(resizeListener, this);
630 if (myCaption != null && myMovable) {
631 final MoveComponentListener moveListener = new MoveComponentListener(myCaption) {
632 public void mousePressed(final MouseEvent e) {
633 super.mousePressed(e);
634 if (e.isConsumed()) return;
636 if (UIUtil.isCloseClick(e)) {
637 if (myCaption.isWithinPanel(e)) {
638 cancel();
643 ListenerUtil.addMouseListener(myCaption, moveListener);
644 ListenerUtil.addMouseMotionListener(myCaption, moveListener);
645 final MyContentPanel saved = myContent;
646 Disposer.register(this, new Disposable() {
647 public void dispose() {
648 ListenerUtil.removeMouseListener(saved, moveListener);
649 ListenerUtil.removeMouseMotionListener(saved, moveListener);
654 for(JBPopupListener listener: myListeners) {
655 listener.beforeShown(new LightweightWindowEvent(this));
658 myPopup.show();
660 final Window window = SwingUtilities.getWindowAncestor(myContent);
662 myWindowListener = new MyWindowListener();
663 window.addWindowListener(myWindowListener);
665 if (myFocusable) {
666 window.setFocusableWindowState(true);
667 window.setFocusable(true);
668 if (myRequestFocus) {
669 window.requestFocusInWindow();
673 myWindow = updateMaskAndAlpha(window);
675 if (myWindow instanceof JWindow) {
676 ((JWindow)myWindow).getRootPane().putClientProperty(KEY, this);
679 SwingUtilities.invokeLater(new Runnable() {
680 public void run() {
681 if (isDisposed()) return;
683 if (myRequestFocus) {
684 requestFocus();
687 if (myPreferredFocusedComponent != null && myInStack) {
688 myFocusTrackback.registerFocusComponent(myPreferredFocusedComponent);
691 afterShow();
696 private void prepareToShow() {
697 final MouseAdapter mouseAdapter = new MouseAdapter() {
698 public void mousePressed(MouseEvent e) {
699 Point point = (Point)e.getPoint().clone();
700 SwingUtilities.convertPointToScreen(point, e.getComponent());
702 final Dimension dimension = myContent.getSize();
703 dimension.height += myResizable && isToDrawMacCorner() ? ourMacCorner.getHeight(myContent) : 4;
704 dimension.width += 4;
705 Point locationOnScreen = myContent.getLocationOnScreen();
706 final Rectangle bounds = new Rectangle(new Point(locationOnScreen.x - 2, locationOnScreen.y - 2), dimension);
707 if (!bounds.contains(point)) {
708 cancel();
712 myContent.addMouseListener(mouseAdapter);
713 Disposer.register(this, new Disposable() {
714 public void dispose() {
715 myContent.removeMouseListener(mouseAdapter);
719 myContent.registerKeyboardAction(new ActionListener() {
720 public void actionPerformed(ActionEvent e) {
721 if (myCancelKeyEnabled) {
722 cancel();
725 }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
728 myContent.addKeyListener(new KeyListener() {
729 public void keyTyped(final KeyEvent e) {
730 mySpeedSearch.process(e);
733 public void keyPressed(final KeyEvent e) {
734 mySpeedSearch.process(e);
737 public void keyReleased(final KeyEvent e) {
738 mySpeedSearch.process(e);
742 if (myCancelOnMouseOutCallback != null || myCancelOnWindow) {
743 myMouseOutCanceller = new Canceller();
744 Toolkit.getDefaultToolkit().addAWTEventListener(myMouseOutCanceller, AWTEvent.MOUSE_EVENT_MASK | WindowEvent.WINDOW_ACTIVATED |
745 AWTEvent.MOUSE_MOTION_EVENT_MASK);
749 myFocusWatcher = new ChildFocusWatcher(myContent) {
750 protected void onFocusGained(final FocusEvent event) {
751 setWindowActive(true);
754 protected void onFocusLost(final FocusEvent event) {
755 setWindowActive(false);
760 mySpeedSearchPatternField = new JTextField();
761 if (SystemInfo.isMac) {
762 Font f = mySpeedSearchPatternField.getFont();
763 mySpeedSearchPatternField.setFont(f.deriveFont(f.getStyle(), f.getSize() - 2));
767 private Window updateMaskAndAlpha(Window window) {
768 if (window == null) return window;
770 final WindowManagerEx wndManager = getWndManager();
771 if (wndManager == null) return window;
773 if (!wndManager.isAlphaModeEnabled(window)) return window;
775 if (myAlpha != myLastAlpha) {
776 wndManager.setAlphaModeRatio(window, myAlpha);
777 myLastAlpha = myAlpha;
780 if (myMaskProvider != null) {
781 final Dimension size = window.getSize();
782 Shape mask = myMaskProvider.getMask(size);
783 wndManager.setWindowMask(window, mask);
786 return window;
789 private static WindowManagerEx getWndManager() {
790 return ApplicationManagerEx.getApplicationEx() != null ? WindowManagerEx.getInstanceEx() : null;
793 public boolean isDisposed() {
794 return myContent == null;
797 protected boolean beforeShow() {
798 if (ApplicationManagerEx.getApplicationEx() == null) return true;
799 StackingPopupDispatcher.getInstance().onPopupShown(this, myInStack);
800 return true;
803 protected void afterShow() {
806 protected final void requestFocus() {
807 if (!myFocusable) return;
809 if (myPreferredFocusedComponent != null) {
810 if (myProject != null) {
811 getFocusManager().requestFocus(myPreferredFocusedComponent, true);
813 else {
814 myPreferredFocusedComponent.requestFocus();
819 private IdeFocusManager getFocusManager() {
820 if (myProject != null) {
821 return IdeFocusManager.getInstance(myProject);
822 } else if (myOwner != null) {
823 return IdeFocusManager.findInstanceByComponent(myOwner);
824 } else {
825 return IdeFocusManager.findInstance();
829 private static JComponent getTargetComponent(Component aComponent) {
830 if (aComponent instanceof JComponent) {
831 return (JComponent)aComponent;
833 else if (aComponent instanceof RootPaneContainer) {
834 return ((RootPaneContainer)aComponent).getRootPane();
837 LOG.error("Cannot find target for:" + aComponent);
838 return null;
841 private PopupComponent.Factory getFactory(boolean forceHeavyweight) {
842 if (isPersistent()) {
843 return new PopupComponent.Factory.Dialog();
844 } else if (forceHeavyweight || !SystemInfo.isWindows) {
845 return new PopupComponent.Factory.AwtHeavyweight();
846 } else {
847 return new PopupComponent.Factory.AwtDefault();
851 public JComponent getContent() {
852 return myContent;
855 public void setLocation(RelativePoint p) {
856 setLocation(p, myPopup, myContent);
859 private static void setLocation(final RelativePoint p, final PopupComponent popup, Component content) {
860 if (popup == null) return;
862 final Window wnd = popup.getWindow();
863 assert wnd != null;
865 wnd.setLocation(p.getScreenPoint());
868 public void pack() {
869 final Window window = SwingUtilities.getWindowAncestor(myContent);
871 window.pack();
874 public JComponent getComponent() {
875 return myComponent;
878 public void setProject(Project project) {
879 myProject = project;
883 public void dispose() {
884 Disposer.dispose(this, false);
886 assert ApplicationManager.getApplication().isDispatchThread();
888 if (myPopup != null) {
889 cancel(myDisposeEvent);
892 if (myContent != null) {
893 myContent.removeAll();
895 myContent = null;
896 myComponent = null;
897 myFocusTrackback = null;
898 myCallBack = null;
899 myListeners = null;
901 if (myMouseOutCanceller != null) {
902 final Toolkit toolkit = Toolkit.getDefaultToolkit();
903 // it may happen, but have no idea how
904 // http://www.jetbrains.net/jira/browse/IDEADEV-21265
905 if (toolkit != null) {
906 toolkit.removeAWTEventListener(myMouseOutCanceller);
909 myMouseOutCanceller = null;
911 if (myFocusWatcher != null) {
912 myFocusWatcher.dispose();
913 myFocusWatcher = null;
916 resetWindow();
918 if (myFinalRunnable != null) {
919 getFocusManager().doWhenFocusSettlesDown(myFinalRunnable);
920 myFinalRunnable = null;
924 private void resetWindow() {
925 if (myWindow != null && getWndManager() != null) {
926 getWndManager().resetWindow(myWindow);
927 if (myWindowListener != null) {
928 myWindow.removeWindowListener(myWindowListener);
931 if (myWindow instanceof JWindow) {
932 ((JWindow)myWindow).getRootPane().putClientProperty(KEY, null);
935 myWindow = null;
936 myWindowListener = null;
940 public void storeDimensionSize(final Dimension size) {
941 if (myDimensionServiceKey != null) {
942 DimensionService.getInstance().setSize(myDimensionServiceKey, size, myProject);
946 public void storeLocation(final Point xy) {
947 if (myDimensionServiceKey != null) {
948 DimensionService.getInstance().setLocation(myDimensionServiceKey, xy, myProject);
952 public static class MyContentPanel extends JPanel {
953 private final boolean myResizable;
954 private final boolean myDrawMacCorner;
955 private final boolean myShadowed;
957 public MyContentPanel(final boolean resizable, final PopupBorder border, boolean drawMacCorner) {
958 this(resizable, border, drawMacCorner, false);
961 public MyContentPanel(final boolean resizable, final PopupBorder border, boolean drawMacCorner, boolean shadowed) {
962 super(new BorderLayout());
963 myResizable = resizable;
964 myDrawMacCorner = drawMacCorner;
965 myShadowed = shadowed;
966 if (isShadowPossible()) {
967 setOpaque(false);
968 setBorder(new EmptyBorder(POPUP_TOP_SIZE, POPUP_SIDE_SIZE, POPUP_BOTTOM_SIZE, POPUP_SIDE_SIZE) {
969 @Override
970 public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
971 border.paintBorder(c, g,
972 x + POPUP_SIDE_SIZE - 1,
973 y + POPUP_TOP_SIZE - 1,
974 width - 2 * POPUP_SIDE_SIZE + 2,
975 height - POPUP_TOP_SIZE - POPUP_BOTTOM_SIZE + 2);
978 } else {
979 setBorder(border);
983 private boolean isShadowPossible() {
984 return myShadowed && !SystemInfo.isMac && !UISettings.isRemoteDesktopConnected();
987 public void paint(Graphics g) {
988 if (isShadowPossible()) {
989 paintShadow(g);
992 super.paint(g);
994 if (myResizable && myDrawMacCorner) {
995 g.drawImage(ourMacCorner,
996 getX() + getWidth() - ourMacCorner.getWidth(this),
997 getY() + getHeight() - ourMacCorner.getHeight(this),
998 this);
1002 private void paintShadow(final Graphics g) {
1003 BufferedImage capture = null;
1004 try {
1005 final Point onScreen = getLocationOnScreen();
1006 capture = new Robot().createScreenCapture(
1007 new Rectangle(onScreen.x, onScreen.y, getWidth() + 2 * POPUP_SIDE_SIZE, getHeight() + POPUP_TOP_SIZE + POPUP_BOTTOM_SIZE));
1008 final BufferedImage shadow = ShadowBorderPainter.createPopupShadow(this, getWidth(), getHeight());
1009 ((Graphics2D)capture.getGraphics()).drawImage(shadow, null, null);
1011 catch (Exception e) {
1012 LOG.info(e);
1014 if (capture != null) g.drawImage(capture, 0, 0, null);
1018 public boolean isCancelOnClickOutside() {
1019 return myCancelOnClickOutside;
1022 private class Canceller implements AWTEventListener {
1024 private boolean myEverEntered = false;
1026 public void eventDispatched(final AWTEvent event) {
1027 if (event.getID() == WindowEvent.WINDOW_ACTIVATED) {
1028 if (myCancelOnWindow) {
1029 cancel();
1031 } else if (event.getID() == MouseEvent.MOUSE_ENTERED) {
1032 if (withinPopup(event)) {
1033 myEverEntered = true;
1035 } else if (event.getID() == MouseEvent.MOUSE_MOVED) {
1036 if (myCancelOnMouseOutCallback != null && myEverEntered && !withinPopup(event)) {
1037 if (myCancelOnMouseOutCallback.check((MouseEvent)event)) {
1038 cancel();
1044 private boolean withinPopup(final AWTEvent event) {
1045 if (!myContent.isShowing()) return false;
1047 final MouseEvent mouse = (MouseEvent)event;
1048 final Point point = mouse.getPoint();
1049 SwingUtilities.convertPointToScreen(point, mouse.getComponent());
1050 return new Rectangle(myContent.getLocationOnScreen(), myContent.getSize()).contains(point);
1054 public void setLocation(@NotNull final Point screenPoint) {
1055 if (myPopup == null) {
1056 myForcedLocation = screenPoint;
1057 } else {
1058 moveTo(myContent, screenPoint, myLocateByContent ? myHeaderPanel.getPreferredSize() : null);
1062 public static Window moveTo(JComponent content, Point screenPoint, final Dimension headerCorrectionSize) {
1063 setDefaultCursor(content);
1064 final Window wnd = SwingUtilities.getWindowAncestor(content);
1065 if (headerCorrectionSize != null) {
1066 screenPoint.y -= headerCorrectionSize.height;
1068 wnd.setLocation(screenPoint);
1069 return wnd;
1072 public void setSize(@NotNull final Dimension size) {
1073 if (myPopup == null) {
1074 myForcedSize = size;
1075 } else {
1076 updateMaskAndAlpha(setSize(myContent, size));
1080 public static Window setSize(JComponent content, final Dimension size) {
1081 final Window popupWindow = SwingUtilities.windowForComponent(content);
1082 final Point location = popupWindow.getLocation();
1083 popupWindow.setBounds(location.x, location.y, size.width, size.height);
1084 return popupWindow;
1087 public static void setDefaultCursor(JComponent content) {
1088 final Window wnd = SwingUtilities.getWindowAncestor(content);
1089 if (wnd != null) {
1090 wnd.setCursor(Cursor.getDefaultCursor());
1094 public void setCaption(String title) {
1095 if (myCaption instanceof TitlePanel) {
1096 ((TitlePanel)myCaption).setText(title);
1100 private class MyWindowListener extends WindowAdapter {
1101 public void windowClosed(final WindowEvent e) {
1102 resetWindow();
1106 public boolean isPersistent() {
1107 return !myCancelOnClickOutside && !myCancelOnWindow;
1110 public boolean isNativePopup() {
1111 return myNativePopup;
1114 public void setUiVisible(final boolean visible) {
1115 if (myPopup != null) {
1116 if (visible) {
1117 myPopup.show();
1118 final Window window = getPopupWindow();
1119 if (window != null && myRestoreWindowSize != null) {
1120 window.setSize(myRestoreWindowSize);
1121 myRestoreWindowSize = null;
1124 else {
1125 final Window window = getPopupWindow();
1126 if (window != null) {
1127 myRestoreWindowSize = window.getSize();
1128 window.setVisible(true);
1134 private Window getPopupWindow() {
1135 return myPopup.getWindow();
1138 public void setUserData(ArrayList<Object> userData) {
1139 myUserData = userData;
1142 public <T> T getUserData(final Class<T> userDataClass) {
1143 if (myUserData != null) {
1144 for(Object o: myUserData) {
1145 if (userDataClass.isInstance(o)) {
1146 //noinspection unchecked
1147 return (T) o;
1151 return null;
1154 public boolean isModalContext() {
1155 return myModalContext;
1158 public boolean isFocused() {
1159 if (myComponent != null && isFocused(new Component[] {SwingUtilities.getWindowAncestor(myComponent)}))
1160 return true;
1161 return isFocused(myFocusOwners);
1164 public static boolean isFocused(@Nullable Component[] components) {
1165 if (components == null) return false;
1167 Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
1169 if (owner == null) return false;
1170 for (Component each : components) {
1171 if (each != null && SwingUtilities.isDescendingFrom(owner, each)) return true;
1174 return false;
1177 public boolean isCancelKeyEnabled() {
1178 return myCancelKeyEnabled;
1181 @NotNull
1182 CaptionPanel getTitle() {
1183 return myCaption;
1186 private void setHeaderComponent(JComponent c) {
1187 boolean doRevalidate = false;
1188 if (myHeaderComponent != null) {
1189 myHeaderPanel.remove(myHeaderComponent);
1190 myHeaderPanel.add(myCaption, BorderLayout.NORTH);
1191 myHeaderComponent = null;
1192 doRevalidate = true;
1195 if (c != null) {
1196 myHeaderPanel.remove(myCaption);
1197 myHeaderPanel.add(c, BorderLayout.NORTH);
1198 myHeaderComponent = c;
1200 final Dimension size = myContent.getSize();
1201 if (size.height < c.getPreferredSize().height * 2) {
1202 size.height += c.getPreferredSize().height;
1203 setSize(size);
1206 doRevalidate = true;
1209 if (doRevalidate) myContent.revalidate();
1212 public void addListener(final JBPopupListener listener) {
1213 myListeners.add(listener);
1216 public void removeListener(final JBPopupListener listener) {
1217 myListeners.remove(listener);
1220 protected void onSpeedSearchPatternChanged() {
1223 public Component getOwner() {
1224 return myRequestorComponent;
1227 public void setMinimumSize(Dimension size) {
1228 myMinSize = size;
1231 public Runnable getFinalRunnable() {
1232 return myFinalRunnable;
1235 public void setFinalRunnable(Runnable finalRunnable) {
1236 myFinalRunnable = finalRunnable;