IDEADEV-41476 Edit HTML fragment: Vanished when resized
[fedora-idea.git] / platform / platform-impl / src / com / intellij / ui / popup / AbstractPopup.java
blob9cb93a07d28e031239ebc835334918507b396067
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.DataConstants;
22 import com.intellij.openapi.actionSystem.DataContext;
23 import com.intellij.openapi.actionSystem.JBAwtEventQueue;
24 import com.intellij.openapi.actionSystem.PlatformDataKeys;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.application.ex.ApplicationManagerEx;
27 import com.intellij.openapi.diagnostic.Logger;
28 import com.intellij.openapi.editor.Editor;
29 import com.intellij.openapi.editor.ex.EditorEx;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.ui.impl.ShadowBorderPainter;
32 import com.intellij.openapi.ui.popup.*;
33 import com.intellij.openapi.util.*;
34 import com.intellij.openapi.util.registry.Registry;
35 import com.intellij.openapi.wm.IdeFocusManager;
36 import com.intellij.openapi.wm.WindowManager;
37 import com.intellij.openapi.wm.ex.WindowManagerEx;
38 import com.intellij.openapi.wm.impl.IdeFrameImpl;
39 import com.intellij.openapi.wm.impl.IdeGlassPaneImpl;
40 import com.intellij.ui.*;
41 import com.intellij.ui.awt.RelativePoint;
42 import com.intellij.ui.speedSearch.SpeedSearch;
43 import com.intellij.util.ImageLoader;
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;
50 import javax.swing.*;
51 import javax.swing.border.EmptyBorder;
52 import java.awt.*;
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;
58 import java.util.Set;
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 protected final SpeedSearch mySpeedSearch = new SpeedSearch() {
120 boolean searchFieldShown = false;
121 protected void update() {
122 mySpeedSearchPatternField.setBackground(new JTextField().getBackground());
123 onSpeedSearchPatternChanged();
124 mySpeedSearchPatternField.setText(getFilter());
125 if (isHoldingFilter() && !searchFieldShown) {
126 setHeaderComponent(mySpeedSearchPatternField);
127 searchFieldShown = true;
129 else if (!isHoldingFilter() && searchFieldShown) {
130 setHeaderComponent(null);
131 searchFieldShown = false;
135 @Override
136 public void noHits() {
137 mySpeedSearchPatternField.setBackground(LightColors.RED);
141 private JTextField mySpeedSearchPatternField;
144 AbstractPopup() {
147 AbstractPopup init(final Project project,
148 @NotNull final JComponent component,
149 @Nullable final JComponent preferredFocusedComponent,
150 final boolean requestFocus,
151 final boolean focusable,
152 final boolean forceHeavyweight,
153 final boolean movable,
154 final String dimensionServiceKey,
155 final boolean resizable,
156 @Nullable final String caption,
157 @Nullable final Computable<Boolean> callback,
158 final boolean cancelOnClickOutside,
159 @Nullable final Set<JBPopupListener> listeners,
160 final boolean useDimServiceForXYLocation,
161 InplaceButton commandButton,
162 @Nullable final IconButton cancelButton,
163 @Nullable final MouseChecker cancelOnMouseOutCallback,
164 final boolean cancelOnWindow,
165 @Nullable final ActiveIcon titleIcon,
166 final boolean cancelKeyEnabled,
167 final boolean locateBycontent,
168 final boolean placeWithinScreenBounds,
169 @Nullable final Dimension minSize,
170 float alpha,
171 @Nullable MaskProvider maskProvider,
172 boolean inStack,
173 boolean modalContext,
174 @Nullable Component[] focusOwners,
175 @Nullable String adText,
176 final boolean headerAlwaysFocusable,
177 @NotNull List<Pair<ActionListener, KeyStroke>> keyboardActions,
178 Component settingsButtons) {
180 if (requestFocus && !focusable) {
181 assert false : "Incorrect argument combination: requestFocus=" + requestFocus + " focusable=" + focusable;
184 myProject = project;
185 myComponent = component;
186 myPopupBorder = PopupBorder.Factory.create(true);
187 myShadowed = !movable && !resizable && Registry.is("ide.popup.dropShadow");
188 myContent = createContentPanel(resizable, myPopupBorder, isToDrawMacCorner());
190 myContent.add(component, BorderLayout.CENTER);
191 if (adText != null) {
192 myContent.add(HintUtil.createAdComponent(adText), BorderLayout.SOUTH);
195 myCancelKeyEnabled = cancelKeyEnabled;
196 myLocateByContent = locateBycontent;
197 myLocateWithinScreen = placeWithinScreenBounds;
198 myAlpha = alpha;
199 myMaskProvider = maskProvider;
200 myInStack = inStack;
201 myModalContext = modalContext;
202 myFocusOwners = focusOwners;
203 myHeaderAlwaysFocusable = headerAlwaysFocusable;
204 myMovable = movable;
206 ActiveIcon actualIcon = titleIcon == null ? new ActiveIcon(new EmptyIcon(0)) : titleIcon;
208 myHeaderPanel = new JPanel(new BorderLayout());
210 if (caption != null) {
211 if (caption.length() > 0) {
212 myCaption = new TitlePanel(actualIcon.getRegular(), actualIcon.getInactive());
213 ((TitlePanel)myCaption).setText(caption);
215 else {
216 myCaption = new CaptionPanel();
218 if (cancelButton != null) {
219 myCaption.setButtonComponent(new InplaceButton(cancelButton, new ActionListener() {
220 public void actionPerformed(final ActionEvent e) {
221 cancel();
223 }));
225 else if (commandButton != null) {
226 myCaption.setButtonComponent(commandButton);
229 else {
230 myCaption = new CaptionPanel();
231 myCaption.setBorder(null);
232 myCaption.setPreferredSize(new Dimension(0, 0));
235 setWindowActive(myHeaderAlwaysFocusable);
237 myHeaderPanel.add(myCaption, BorderLayout.NORTH);
238 myContent.add(myHeaderPanel, BorderLayout.NORTH);
240 myForcedHeavyweight = forceHeavyweight;
241 myResizable = resizable;
242 myPreferredFocusedComponent = preferredFocusedComponent;
243 myRequestFocus = requestFocus;
244 myFocusable = focusable;
245 myDimensionServiceKey = dimensionServiceKey;
246 myCallBack = callback;
247 myCancelOnClickOutside = cancelOnClickOutside;
248 myCancelOnMouseOutCallback = cancelOnMouseOutCallback;
249 myListeners = listeners == null ? new HashSet<JBPopupListener>() : listeners;
250 myUseDimServiceForXYLocation = useDimServiceForXYLocation;
251 myCancelOnWindow = cancelOnWindow;
252 myMinSize = minSize;
254 for (Pair<ActionListener, KeyStroke> pair : keyboardActions) {
255 myContent.registerKeyboardAction(pair.getFirst(), pair.getSecond(), JComponent.WHEN_IN_FOCUSED_WINDOW);
258 if (settingsButtons != null) {
259 myCaption.addSettingsComponent(settingsButtons);
262 return this;
265 private void setWindowActive(boolean active) {
266 boolean value = myHeaderAlwaysFocusable || active;
268 if (myCaption != null) {
269 myCaption.setActive(value);
271 myPopupBorder.setActive(value);
272 myContent.repaint();
276 @NotNull
277 protected MyContentPanel createContentPanel(final boolean resizable, PopupBorder border, boolean isToDrawMacCorner) {
278 return new MyContentPanel(resizable, border, isToDrawMacCorner, myShadowed);
281 public static boolean isToDrawMacCorner() {
282 return SystemInfo.isMac;
287 public String getDimensionServiceKey() {
288 return myDimensionServiceKey;
291 public void setDimensionServiceKey(final String dimensionServiceKey) {
292 myDimensionServiceKey = dimensionServiceKey;
295 public void showInCenterOf(@NotNull Component aContainer) {
296 final Point popupPoint = getCenterOf(aContainer, myContent);
297 show(aContainer, popupPoint.x, popupPoint.y, false);
300 public void setAdText(@NotNull final String s) {
301 myContent.add(HintUtil.createAdComponent(s, BorderFactory.createEmptyBorder(3, 5, 3, 5)), BorderLayout.SOUTH);
304 public static Point getCenterOf(final Component aContainer, final JComponent content) {
305 final JComponent component = getTargetComponent(aContainer);
307 Point containerScreenPoint = component.getVisibleRect().getLocation();
308 SwingUtilities.convertPointToScreen(containerScreenPoint, aContainer);
310 return UIUtil.getCenterPoint(new Rectangle(containerScreenPoint, component.getVisibleRect().getSize()), content.getPreferredSize());
313 public void showCenteredInCurrentWindow(@NotNull Project project) {
314 Window window = null;
316 Component focusedComponent = getWndManager().getFocusedComponent(project);
317 if (focusedComponent != null) {
318 if (focusedComponent instanceof Window) {
319 window = (Window)focusedComponent;
321 else {
322 window = SwingUtilities.getWindowAncestor(focusedComponent);
325 if (window == null) {
326 window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
329 if (window != null) {
330 showInCenterOf(window);
334 public void showUnderneathOf(@NotNull Component aComponent) {
335 show(new RelativePoint(aComponent, new Point(0, aComponent.getHeight())));
338 public void show(@NotNull RelativePoint aPoint) {
339 final Point screenPoint = aPoint.getScreenPoint();
340 show(aPoint.getComponent(), screenPoint.x, screenPoint.y, false);
343 public void showInScreenCoordinates(@NotNull Component owner, @NotNull Point point) {
344 show(owner, point.x, point.y, false);
347 public void showInBestPositionFor(@NotNull DataContext dataContext) {
348 final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
349 if (editor != null) {
350 showInBestPositionFor(editor);
352 else {
353 show(relativePointByQuickSearch(dataContext));
357 public void showInFocusCenter() {
358 final Component focused = getWndManager().getFocusedComponent(myProject);
359 if (focused != null) {
360 showInCenterOf(focused);
361 } else {
362 final JFrame frame = WindowManager.getInstance().getFrame(myProject);
363 showInCenterOf(frame.getRootPane());
367 private RelativePoint relativePointByQuickSearch(final DataContext dataContext) {
368 Rectangle dominantArea = (Rectangle)dataContext.getData(DataConstants.DOMINANT_HINT_AREA_RECTANGLE);
370 if (dominantArea != null) {
371 final Component focusedComponent = getWndManager().getFocusedComponent(myProject);
372 Window window = SwingUtilities.windowForComponent(focusedComponent);
373 JLayeredPane layeredPane;
374 if (window instanceof JFrame) {
375 layeredPane = ((JFrame)window).getLayeredPane();
377 else if (window instanceof JDialog) {
378 layeredPane = ((JDialog)window).getLayeredPane();
380 else {
381 throw new IllegalStateException("cannot find parent window: project=" + myProject + "; window=" + window);
384 return relativePointWithDominantRectangle(layeredPane, dominantArea);
387 return JBPopupFactory.getInstance().guessBestPopupLocation(dataContext);
390 public void showInBestPositionFor(@NotNull Editor editor) {
391 assert editor.getComponent().isShowing() : "Editor must be showing on the screen";
393 DataContext context = ((EditorEx)editor).getDataContext();
394 Rectangle dominantArea = (Rectangle)context.getData(DataConstants.DOMINANT_HINT_AREA_RECTANGLE);
395 if (dominantArea != null && !myRequestFocus) {
396 final JLayeredPane layeredPane = editor.getContentComponent().getRootPane().getLayeredPane();
397 show(relativePointWithDominantRectangle(layeredPane, dominantArea));
399 else {
400 show(JBPopupFactory.getInstance().guessBestPopupLocation(editor));
404 public void addPopupListener(JBPopupListener listener) {
405 myListeners.add(listener);
408 private RelativePoint relativePointWithDominantRectangle(final JLayeredPane layeredPane, final Rectangle bounds) {
409 Dimension preferredSize = getComponent().getPreferredSize();
410 if (myDimensionServiceKey != null) {
411 final Dimension dimension = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
412 if (dimension != null) {
413 preferredSize = dimension;
416 final Point leftTopCorner = new Point(bounds.x + bounds.width, bounds.y);
417 final Point leftTopCornerScreen = (Point)leftTopCorner.clone();
418 SwingUtilities.convertPointToScreen(leftTopCornerScreen, layeredPane);
419 final RelativePoint relativePoint;
420 if (!ScreenUtil.isOutsideOnTheRightOFScreen(
421 new Rectangle(leftTopCornerScreen.x, leftTopCornerScreen.y, preferredSize.width, preferredSize.height))) {
422 relativePoint = new RelativePoint(layeredPane, leftTopCorner);
424 else {
425 if (bounds.x > preferredSize.width) {
426 relativePoint = new RelativePoint(layeredPane, new Point(bounds.x - preferredSize.width, bounds.y));
428 else {
429 setDimensionServiceKey(null); // going to cut width
430 Rectangle screen = ScreenUtil.getScreenRectangle(leftTopCornerScreen.x, leftTopCornerScreen.y);
431 final int spaceOnTheLeft = bounds.x;
432 final int spaceOnTheRight = (screen.x + screen.width) - leftTopCornerScreen.x;
433 if (spaceOnTheLeft > spaceOnTheRight) {
434 relativePoint = new RelativePoint(layeredPane, new Point(0, bounds.y));
435 myComponent.setPreferredSize(new Dimension(spaceOnTheLeft, Math.max(preferredSize.height, 200)));
437 else {
438 relativePoint = new RelativePoint(layeredPane, leftTopCorner);
439 myComponent.setPreferredSize(new Dimension(spaceOnTheRight, Math.max(preferredSize.height, 200)));
443 return relativePoint;
446 public final void cancel() {
447 cancel(null);
450 public void setRequestFocus(boolean requestFocus) {
451 myRequestFocus = requestFocus;
454 public void cancel(InputEvent e) {
455 if (isDisposed()) return;
457 if (myPopup != null) {
458 if (!canClose()) {
459 return;
461 storeDimensionSize(myContent.getSize());
462 if (myUseDimServiceForXYLocation) {
463 final JRootPane root = myComponent.getRootPane();
464 if (root != null) {
465 final Container popupWindow = root.getParent();
466 if (popupWindow != null && popupWindow.isShowing()) {
467 storeLocation(popupWindow.getLocationOnScreen());
472 if (e instanceof MouseEvent) {
473 JBAwtEventQueue.getInstance().blockNextEvents(((MouseEvent)e));
476 myPopup.hide(false);
478 if (ApplicationManagerEx.getApplicationEx() != null) {
479 StackingPopupDispatcher.getInstance().onPopupHidden(this);
482 if (myInStack) {
483 myFocusTrackback.restoreFocus();
487 disposePopup();
489 if (myListeners != null) {
490 for (JBPopupListener each : myListeners) {
491 each.onClosed(new LightweightWindowEvent(this));
496 Disposer.dispose(this, false);
500 private void disposePopup() {
501 if (myPopup != null) {
502 myPopup.hide(true);
504 myPopup = null;
507 public boolean canClose() {
508 return myCallBack == null || myCallBack.compute().booleanValue();
511 public boolean isVisible() {
512 return myPopup != null;
515 public void show(final Component owner) {
516 show(owner, -1, -1, true);
519 public void show(Component owner, int aScreenX, int aScreenY, final boolean considerForcedXY) {
520 if (ApplicationManagerEx.getApplicationEx() != null && ApplicationManager.getApplication().isHeadlessEnvironment()) return;
521 if (isDisposed()) {
522 throw new IllegalStateException("Popup was already disposed. Recreate a new instance to show again");
525 assert ApplicationManager.getApplication().isDispatchThread();
528 final boolean shouldShow = beforeShow();
529 if (!shouldShow) {
530 return;
533 prepareToShow();
535 if (myInStack) {
536 myFocusTrackback = new FocusTrackback(this, owner, true);
537 myFocusTrackback.setMustBeShown(true);
541 Dimension sizeToSet = null;
543 if (myDimensionServiceKey != null) {
544 sizeToSet = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
547 if (myForcedSize != null) {
548 sizeToSet = myForcedSize;
551 if (myMinSize == null) {
552 myMinSize = myContent.getMinimumSize();
555 if (sizeToSet == null) {
556 sizeToSet = myContent.getPreferredSize();
559 if (sizeToSet != null) {
560 sizeToSet.width = Math.max(sizeToSet.width, myMinSize.width);
561 sizeToSet.height = Math.max(sizeToSet.height, myMinSize.height);
563 myContent.setSize(sizeToSet);
564 myContent.setPreferredSize(sizeToSet);
567 Point xy = new Point(aScreenX, aScreenY);
568 boolean adjustXY = true;
569 if (myDimensionServiceKey != null) {
570 final Point storedLocation = DimensionService.getInstance().getLocation(myDimensionServiceKey, myProject);
571 if (storedLocation != null) {
572 xy = storedLocation;
573 adjustXY = false;
577 if (adjustXY) {
578 final Insets insets = myContent.getInsets();
579 if (insets != null) {
580 xy.x -= insets.left;
581 xy.y -= insets.top;
585 if (considerForcedXY && myForcedLocation != null) {
586 xy = myForcedLocation;
589 if (myLocateByContent) {
590 final Dimension captionSize = myHeaderPanel.getPreferredSize();
591 xy.y -= captionSize.height;
594 Rectangle targetBounds = new Rectangle(xy, myContent.getPreferredSize());
595 Rectangle original = new Rectangle(targetBounds);
596 if (myLocateWithinScreen) {
597 ScreenUtil.moveRectangleToFitTheScreen(targetBounds);
600 if (myMouseOutCanceller != null) {
601 myMouseOutCanceller.myEverEntered = targetBounds.equals(original);
604 myOwner = IdeFrameImpl.findNearestModalComponent(owner);
605 if (myOwner == null) {
606 myOwner = owner;
609 myRequestorComponent = owner;
611 myPopup = getFactory(myForcedHeavyweight || myResizable).getPopup(myOwner, myContent, targetBounds.x, targetBounds.y);
613 if (myResizable) {
614 final JRootPane root = myContent.getRootPane();
615 final IdeGlassPaneImpl glass = new IdeGlassPaneImpl(root);
616 root.setGlassPane(glass);
618 final ResizeComponentListener resizeListener = new ResizeComponentListener(this);
619 glass.addMousePreprocessor(resizeListener, this);
620 glass.addMouseMotionPreprocessor(resizeListener, this);
623 if (myCaption != null && myMovable) {
624 final MoveComponentListener moveListener = new MoveComponentListener(myCaption) {
625 public void mousePressed(final MouseEvent e) {
626 super.mousePressed(e);
627 if (e.isConsumed()) return;
629 if (UIUtil.isCloseClick(e)) {
630 if (myCaption.isWithinPanel(e)) {
631 cancel();
636 ListenerUtil.addMouseListener(myCaption, moveListener);
637 ListenerUtil.addMouseMotionListener(myCaption, moveListener);
638 final MyContentPanel saved = myContent;
639 Disposer.register(this, new Disposable() {
640 public void dispose() {
641 ListenerUtil.removeMouseListener(saved, moveListener);
642 ListenerUtil.removeMouseMotionListener(saved, moveListener);
647 for(JBPopupListener listener: myListeners) {
648 listener.beforeShown(new LightweightWindowEvent(this));
651 myPopup.show();
653 final Window window = SwingUtilities.getWindowAncestor(myContent);
655 myWindowListener = new MyWindowListener();
656 window.addWindowListener(myWindowListener);
658 if (myFocusable) {
659 window.setFocusableWindowState(true);
660 window.setFocusable(true);
661 if (myRequestFocus) {
662 window.requestFocusInWindow();
666 myWindow = updateMaskAndAlpha(window);
668 if (myWindow instanceof JWindow) {
669 ((JWindow)myWindow).getRootPane().putClientProperty(KEY, this);
672 SwingUtilities.invokeLater(new Runnable() {
673 public void run() {
674 if (isDisposed()) return;
676 if (myRequestFocus) {
677 requestFocus();
680 if (myPreferredFocusedComponent != null && myInStack) {
681 myFocusTrackback.registerFocusComponent(myPreferredFocusedComponent);
684 afterShow();
689 private void prepareToShow() {
690 final MouseAdapter mouseAdapter = new MouseAdapter() {
691 public void mousePressed(MouseEvent e) {
692 Point point = (Point)e.getPoint().clone();
693 SwingUtilities.convertPointToScreen(point, e.getComponent());
695 final Dimension dimension = myContent.getSize();
696 dimension.height += myResizable && isToDrawMacCorner() ? ourMacCorner.getHeight(myContent) : 4;
697 dimension.width += 4;
698 Point locationOnScreen = myContent.getLocationOnScreen();
699 final Rectangle bounds = new Rectangle(new Point(locationOnScreen.x - 2, locationOnScreen.y - 2), dimension);
700 if (!bounds.contains(point)) {
701 cancel();
705 myContent.addMouseListener(mouseAdapter);
706 Disposer.register(this, new Disposable() {
707 public void dispose() {
708 myContent.removeMouseListener(mouseAdapter);
712 myContent.registerKeyboardAction(new ActionListener() {
713 public void actionPerformed(ActionEvent e) {
714 if (myCancelKeyEnabled) {
715 cancel();
718 }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
721 myContent.addKeyListener(new KeyListener() {
722 public void keyTyped(final KeyEvent e) {
723 mySpeedSearch.process(e);
726 public void keyPressed(final KeyEvent e) {
727 mySpeedSearch.process(e);
730 public void keyReleased(final KeyEvent e) {
731 mySpeedSearch.process(e);
735 if (myCancelOnMouseOutCallback != null || myCancelOnWindow) {
736 myMouseOutCanceller = new Canceller();
737 Toolkit.getDefaultToolkit().addAWTEventListener(myMouseOutCanceller, AWTEvent.MOUSE_EVENT_MASK | WindowEvent.WINDOW_ACTIVATED |
738 AWTEvent.MOUSE_MOTION_EVENT_MASK);
742 myFocusWatcher = new ChildFocusWatcher(myContent) {
743 protected void onFocusGained(final FocusEvent event) {
744 setWindowActive(true);
747 protected void onFocusLost(final FocusEvent event) {
748 setWindowActive(false);
753 mySpeedSearchPatternField = new JTextField();
754 if (SystemInfo.isMac) {
755 Font f = mySpeedSearchPatternField.getFont();
756 mySpeedSearchPatternField.setFont(f.deriveFont(f.getStyle(), f.getSize() - 2));
760 private Window updateMaskAndAlpha(Window window) {
761 if (window == null) return window;
763 final WindowManagerEx wndManager = getWndManager();
764 if (wndManager == null) return window;
766 if (!wndManager.isAlphaModeEnabled(window)) return window;
768 if (myAlpha != myLastAlpha) {
769 wndManager.setAlphaModeRatio(window, myAlpha);
770 myLastAlpha = myAlpha;
773 if (myMaskProvider != null) {
774 final Dimension size = window.getSize();
775 Shape mask = myMaskProvider.getMask(size);
776 wndManager.setWindowMask(window, mask);
779 return window;
782 private static WindowManagerEx getWndManager() {
783 return ApplicationManagerEx.getApplicationEx() != null ? WindowManagerEx.getInstanceEx() : null;
786 public boolean isDisposed() {
787 return myContent == null;
790 protected boolean beforeShow() {
791 if (ApplicationManagerEx.getApplicationEx() == null) return true;
792 StackingPopupDispatcher.getInstance().onPopupShown(this, myInStack);
793 return true;
796 protected void afterShow() {
799 protected final void requestFocus() {
800 if (!myFocusable) return;
802 if (myPreferredFocusedComponent != null) {
803 if (myProject != null) {
804 getFocusManager().requestFocus(myPreferredFocusedComponent, true);
806 else {
807 myPreferredFocusedComponent.requestFocus();
812 private IdeFocusManager getFocusManager() {
813 return IdeFocusManager.getInstance(myProject);
816 private static JComponent getTargetComponent(Component aComponent) {
817 if (aComponent instanceof JComponent) {
818 return (JComponent)aComponent;
820 else if (aComponent instanceof RootPaneContainer) {
821 return ((RootPaneContainer)aComponent).getRootPane();
824 LOG.error("Cannot find target for:" + aComponent);
825 return null;
828 private PopupComponent.Factory getFactory(boolean forceHeavyweight) {
829 if (isPersistent()) {
830 return new PopupComponent.Factory.Dialog();
831 } else if (forceHeavyweight || !SystemInfo.isWindows) {
832 return new PopupComponent.Factory.AwtHeavyweight();
833 } else {
834 return new PopupComponent.Factory.AwtDefault();
838 public JComponent getContent() {
839 return myContent;
842 public void setLocation(RelativePoint p) {
843 setLocation(p, myPopup, myContent);
846 private static void setLocation(final RelativePoint p, final PopupComponent popup, Component content) {
847 if (popup == null) return;
849 final Window wnd = popup.getWindow();
850 assert wnd != null;
852 wnd.pack();
853 wnd.setLocation(p.getScreenPoint());
856 public void pack() {
857 final Window window = SwingUtilities.getWindowAncestor(myContent);
859 window.pack();
862 public JComponent getComponent() {
863 return myComponent;
866 public void setProject(Project project) {
867 myProject = project;
871 public void dispose() {
872 Disposer.dispose(this, false);
874 assert ApplicationManager.getApplication().isDispatchThread();
876 if (myPopup != null) {
877 cancel(myDisposeEvent);
880 if (myContent != null) {
881 myContent.removeAll();
883 myContent = null;
884 myComponent = null;
885 myFocusTrackback = null;
886 myCallBack = null;
887 myListeners = null;
889 if (myMouseOutCanceller != null) {
890 final Toolkit toolkit = Toolkit.getDefaultToolkit();
891 // it may happen, but have no idea how
892 // http://www.jetbrains.net/jira/browse/IDEADEV-21265
893 if (toolkit != null) {
894 toolkit.removeAWTEventListener(myMouseOutCanceller);
897 myMouseOutCanceller = null;
899 if (myFocusWatcher != null) {
900 myFocusWatcher.dispose();
901 myFocusWatcher = null;
904 resetWindow();
908 private void resetWindow() {
909 if (myWindow != null && getWndManager() != null) {
910 getWndManager().resetWindow(myWindow);
911 if (myWindowListener != null) {
912 myWindow.removeWindowListener(myWindowListener);
915 if (myWindow instanceof JWindow) {
916 ((JWindow)myWindow).getRootPane().putClientProperty(KEY, null);
919 myWindow = null;
920 myWindowListener = null;
924 public void storeDimensionSize(final Dimension size) {
925 if (myDimensionServiceKey != null) {
926 DimensionService.getInstance().setSize(myDimensionServiceKey, size, myProject);
930 public void storeLocation(final Point xy) {
931 if (myDimensionServiceKey != null) {
932 DimensionService.getInstance().setLocation(myDimensionServiceKey, xy, myProject);
936 public static class MyContentPanel extends JPanel {
937 private final boolean myResizable;
938 private final boolean myDrawMacCorner;
939 private final boolean myShadowed;
941 public MyContentPanel(final boolean resizable, final PopupBorder border, boolean drawMacCorner) {
942 this(resizable, border, drawMacCorner, false);
945 public MyContentPanel(final boolean resizable, final PopupBorder border, boolean drawMacCorner, boolean shadowed) {
946 super(new BorderLayout());
947 myResizable = resizable;
948 myDrawMacCorner = drawMacCorner;
949 myShadowed = shadowed;
950 if (isShadowPossible()) {
951 setOpaque(false);
952 setBorder(new EmptyBorder(POPUP_TOP_SIZE, POPUP_SIDE_SIZE, POPUP_BOTTOM_SIZE, POPUP_SIDE_SIZE) {
953 @Override
954 public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
955 border.paintBorder(c, g,
956 x + POPUP_SIDE_SIZE - 1,
957 y + POPUP_TOP_SIZE - 1,
958 width - 2 * POPUP_SIDE_SIZE + 2,
959 height - POPUP_TOP_SIZE - POPUP_BOTTOM_SIZE + 2);
962 } else {
963 setBorder(border);
967 private boolean isShadowPossible() {
968 return myShadowed && !SystemInfo.isMac && !UISettings.isRemoteDesktopConnected();
971 public void paint(Graphics g) {
972 if (isShadowPossible()) {
973 paintShadow(g);
976 super.paint(g);
978 if (myResizable && myDrawMacCorner) {
979 g.drawImage(ourMacCorner,
980 getX() + getWidth() - ourMacCorner.getWidth(this),
981 getY() + getHeight() - ourMacCorner.getHeight(this),
982 this);
986 private void paintShadow(final Graphics g) {
987 BufferedImage capture = null;
988 try {
989 final Point onScreen = getLocationOnScreen();
990 capture = new Robot().createScreenCapture(
991 new Rectangle(onScreen.x, onScreen.y, getWidth() + 2 * POPUP_SIDE_SIZE, getHeight() + POPUP_TOP_SIZE + POPUP_BOTTOM_SIZE));
992 final BufferedImage shadow = ShadowBorderPainter.createPopupShadow(this, getWidth(), getHeight());
993 ((Graphics2D)capture.getGraphics()).drawImage(shadow, null, null);
995 catch (Exception e) {
996 e.printStackTrace();
998 if (capture != null) g.drawImage(capture, 0, 0, null);
1002 public boolean isCancelOnClickOutside() {
1003 return myCancelOnClickOutside;
1006 private class Canceller implements AWTEventListener {
1008 private boolean myEverEntered = false;
1010 public void eventDispatched(final AWTEvent event) {
1011 if (event.getID() == WindowEvent.WINDOW_ACTIVATED) {
1012 if (myCancelOnWindow) {
1013 cancel();
1015 } else if (event.getID() == MouseEvent.MOUSE_ENTERED) {
1016 if (withinPopup(event)) {
1017 myEverEntered = true;
1019 } else if (event.getID() == MouseEvent.MOUSE_MOVED) {
1020 if (myCancelOnMouseOutCallback != null && myEverEntered && !withinPopup(event)) {
1021 if (myCancelOnMouseOutCallback.check((MouseEvent)event)) {
1022 cancel();
1028 private boolean withinPopup(final AWTEvent event) {
1029 if (!myContent.isShowing()) return false;
1031 final MouseEvent mouse = (MouseEvent)event;
1032 final Point point = mouse.getPoint();
1033 SwingUtilities.convertPointToScreen(point, mouse.getComponent());
1034 return new Rectangle(myContent.getLocationOnScreen(), myContent.getSize()).contains(point);
1038 public void setLocation(@NotNull final Point screenPoint) {
1039 if (myPopup == null) {
1040 myForcedLocation = screenPoint;
1041 } else {
1042 moveTo(myContent, screenPoint, myLocateByContent ? myHeaderPanel.getPreferredSize() : null);
1046 public static Window moveTo(JComponent content, Point screenPoint, final Dimension headerCorrectionSize) {
1047 setDefaultCursor(content);
1048 final Window wnd = SwingUtilities.getWindowAncestor(content);
1049 if (headerCorrectionSize != null) {
1050 screenPoint.y -= headerCorrectionSize.height;
1052 wnd.setLocation(screenPoint);
1053 return wnd;
1056 public void setSize(@NotNull final Dimension size) {
1057 if (myPopup == null) {
1058 myForcedSize = size;
1059 } else {
1060 updateMaskAndAlpha(setSize(myContent, size));
1064 public static Window setSize(JComponent content, final Dimension size) {
1065 final Window popupWindow = SwingUtilities.windowForComponent(content);
1066 final Point location = popupWindow.getLocation();
1067 popupWindow.setBounds(location.x, location.y, size.width, size.height);
1068 return popupWindow;
1071 public static void setDefaultCursor(JComponent content) {
1072 final Window wnd = SwingUtilities.getWindowAncestor(content);
1073 if (wnd != null) {
1074 wnd.setCursor(Cursor.getDefaultCursor());
1078 public void setCaption(String title) {
1079 if (myCaption instanceof TitlePanel) {
1080 ((TitlePanel)myCaption).setText(title);
1084 private class MyWindowListener extends WindowAdapter {
1085 public void windowClosed(final WindowEvent e) {
1086 resetWindow();
1090 public boolean isPersistent() {
1091 return !myCancelOnClickOutside && !myCancelOnWindow;
1094 public void setUiVisible(final boolean visible) {
1095 if (myPopup != null) {
1096 if (visible) {
1097 myPopup.show();
1098 final Window window = getPopupWindow();
1099 if (window != null && myRestoreWindowSize != null) {
1100 window.setSize(myRestoreWindowSize);
1101 myRestoreWindowSize = null;
1104 else {
1105 final Window window = getPopupWindow();
1106 if (window != null) {
1107 myRestoreWindowSize = window.getSize();
1108 window.hide();
1114 private Window getPopupWindow() {
1115 return myPopup.getWindow();
1118 public void setUserData(ArrayList<Object> userData) {
1119 myUserData = userData;
1122 public <T> T getUserData(final Class<T> userDataClass) {
1123 if (myUserData != null) {
1124 for(Object o: myUserData) {
1125 if (userDataClass.isInstance(o)) {
1126 //noinspection unchecked
1127 return (T) o;
1131 return null;
1134 public boolean isModalContext() {
1135 return myModalContext;
1138 public boolean isFocused() {
1139 if (myComponent != null && isFocused(new Component[] {SwingUtilities.getWindowAncestor(myComponent)}))
1140 return true;
1141 return isFocused(myFocusOwners);
1144 public static boolean isFocused(@Nullable Component[] components) {
1145 if (components == null) return false;
1147 Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
1149 if (owner == null) return false;
1150 for (Component each : components) {
1151 if (each != null && SwingUtilities.isDescendingFrom(owner, each)) return true;
1154 return false;
1157 public boolean isCancelKeyEnabled() {
1158 return myCancelKeyEnabled;
1161 @NotNull
1162 CaptionPanel getTitle() {
1163 return myCaption;
1166 private void setHeaderComponent(JComponent c) {
1167 boolean doRevalidate = false;
1168 if (myHeaderComponent != null) {
1169 myHeaderPanel.remove(myHeaderComponent);
1170 myHeaderPanel.add(myCaption, BorderLayout.NORTH);
1171 myHeaderComponent = null;
1172 doRevalidate = true;
1175 if (c != null) {
1176 myHeaderPanel.remove(myCaption);
1177 myHeaderPanel.add(c, BorderLayout.NORTH);
1178 myHeaderComponent = c;
1180 final Dimension size = myContent.getSize();
1181 if (size.height < c.getPreferredSize().height * 2) {
1182 size.height += c.getPreferredSize().height;
1183 setSize(size);
1186 doRevalidate = true;
1189 if (doRevalidate) myContent.revalidate();
1192 public void addListener(final JBPopupListener listener) {
1193 myListeners.add(listener);
1196 public void removeListener(final JBPopupListener listener) {
1197 myListeners.remove(listener);
1200 protected void onSpeedSearchPatternChanged() {
1203 public Component getOwner() {
1204 return myRequestorComponent;
1207 public void setMinimumSize(Dimension size) {
1208 myMinSize = size;