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
;
51 import javax
.swing
.border
.EmptyBorder
;
53 import java
.awt
.event
.*;
54 import java
.awt
.image
.BufferedImage
;
55 import java
.util
.ArrayList
;
56 import java
.util
.HashSet
;
57 import java
.util
.List
;
60 import static com
.intellij
.openapi
.ui
.impl
.ShadowBorderPainter
.*;
62 public class AbstractPopup
implements JBPopup
{
63 private static final Logger LOG
= Logger
.getInstance("#com.intellij.ui.popup.AbstractPopup");
65 private static final Image ourMacCorner
= ImageLoader
.loadFromResource("/general/macCorner.png");
67 private PopupComponent myPopup
;
68 private MyContentPanel myContent
;
69 private JComponent myPreferredFocusedComponent
;
70 private boolean myRequestFocus
;
71 private boolean myFocusable
;
72 private boolean myForcedHeavyweight
= false;
73 private boolean myLocateWithinScreen
= true;
74 private boolean myResizable
= false;
75 private JPanel myHeaderPanel
;
76 private CaptionPanel myCaption
= null;
77 private JComponent myComponent
;
78 private String myDimensionServiceKey
= null;
79 private Computable
<Boolean
> myCallBack
= null;
80 private Project myProject
;
81 private boolean myCancelOnClickOutside
;
82 private Set
<JBPopupListener
> myListeners
;
83 private boolean myUseDimServiceForXYLocation
;
84 private MouseChecker myCancelOnMouseOutCallback
;
85 private Canceller myMouseOutCanceller
;
86 private boolean myCancelOnWindow
;
87 private Dimension myForcedSize
;
88 private Point myForcedLocation
;
89 private ChildFocusWatcher myFocusWatcher
;
90 private boolean myCancelKeyEnabled
;
91 private boolean myLocateByContent
;
92 protected FocusTrackback myFocusTrackback
;
93 private Dimension myMinSize
;
94 private ArrayList
<Object
> myUserData
;
95 private boolean myShadowed
;
97 private float myAlpha
= 0;
98 private float myLastAlpha
= 0;
100 private MaskProvider myMaskProvider
;
102 private Window myWindow
;
103 private boolean myInStack
;
104 private MyWindowListener myWindowListener
;
106 private boolean myModalContext
;
108 private Component
[] myFocusOwners
;
109 private PopupBorder myPopupBorder
;
110 private Dimension myRestoreWindowSize
;
111 protected Component myOwner
;
112 protected Component myRequestorComponent
;
113 private boolean myHeaderAlwaysFocusable
;
114 private boolean myMovable
;
115 private JComponent myHeaderComponent
;
117 protected InputEvent myDisposeEvent
;
119 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;
136 public void noHits() {
137 mySpeedSearchPatternField
.setBackground(LightColors
.RED
);
141 private JTextField mySpeedSearchPatternField
;
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
,
171 @Nullable MaskProvider maskProvider
,
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
;
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
;
199 myMaskProvider
= maskProvider
;
201 myModalContext
= modalContext
;
202 myFocusOwners
= focusOwners
;
203 myHeaderAlwaysFocusable
= headerAlwaysFocusable
;
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
);
216 myCaption
= new CaptionPanel();
218 if (cancelButton
!= null) {
219 myCaption
.setButtonComponent(new InplaceButton(cancelButton
, new ActionListener() {
220 public void actionPerformed(final ActionEvent e
) {
225 else if (commandButton
!= null) {
226 myCaption
.setButtonComponent(commandButton
);
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
;
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
);
265 private void setWindowActive(boolean active
) {
266 boolean value
= myHeaderAlwaysFocusable
|| active
;
268 if (myCaption
!= null) {
269 myCaption
.setActive(value
);
271 myPopupBorder
.setActive(value
);
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
;
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
);
353 show(relativePointByQuickSearch(dataContext
));
357 public void showInFocusCenter() {
358 final Component focused
= getWndManager().getFocusedComponent(myProject
);
359 if (focused
!= null) {
360 showInCenterOf(focused
);
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();
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
));
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
);
425 if (bounds
.x
> preferredSize
.width
) {
426 relativePoint
= new RelativePoint(layeredPane
, new Point(bounds
.x
- preferredSize
.width
, bounds
.y
));
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)));
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() {
450 public void setRequestFocus(boolean requestFocus
) {
451 myRequestFocus
= requestFocus
;
454 public void cancel(InputEvent e
) {
455 if (isDisposed()) return;
457 if (myPopup
!= null) {
461 storeDimensionSize(myContent
.getSize());
462 if (myUseDimServiceForXYLocation
) {
463 final JRootPane root
= myComponent
.getRootPane();
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
));
478 if (ApplicationManagerEx
.getApplicationEx() != null) {
479 StackingPopupDispatcher
.getInstance().onPopupHidden(this);
483 myFocusTrackback
.restoreFocus();
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) {
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;
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();
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) {
578 final Insets insets
= myContent
.getInsets();
579 if (insets
!= null) {
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) {
609 myRequestorComponent
= owner
;
611 myPopup
= getFactory(myForcedHeavyweight
|| myResizable
).getPopup(myOwner
, myContent
, targetBounds
.x
, targetBounds
.y
);
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
)) {
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));
653 final Window window
= SwingUtilities
.getWindowAncestor(myContent
);
655 myWindowListener
= new MyWindowListener();
656 window
.addWindowListener(myWindowListener
);
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() {
674 if (isDisposed()) return;
676 if (myRequestFocus
) {
680 if (myPreferredFocusedComponent
!= null && myInStack
) {
681 myFocusTrackback
.registerFocusComponent(myPreferredFocusedComponent
);
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
)) {
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
) {
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
);
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
);
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);
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
);
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();
834 return new PopupComponent
.Factory
.AwtDefault();
838 public JComponent
getContent() {
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();
853 wnd
.setLocation(p
.getScreenPoint());
857 final Window window
= SwingUtilities
.getWindowAncestor(myContent
);
862 public JComponent
getComponent() {
866 public void setProject(Project 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();
885 myFocusTrackback
= 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;
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);
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()) {
952 setBorder(new EmptyBorder(POPUP_TOP_SIZE
, POPUP_SIDE_SIZE
, POPUP_BOTTOM_SIZE
, POPUP_SIDE_SIZE
) {
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);
967 private boolean isShadowPossible() {
968 return myShadowed
&& !SystemInfo
.isMac
&& !UISettings
.isRemoteDesktopConnected();
971 public void paint(Graphics g
) {
972 if (isShadowPossible()) {
978 if (myResizable
&& myDrawMacCorner
) {
979 g
.drawImage(ourMacCorner
,
980 getX() + getWidth() - ourMacCorner
.getWidth(this),
981 getY() + getHeight() - ourMacCorner
.getHeight(this),
986 private void paintShadow(final Graphics g
) {
987 BufferedImage capture
= null;
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
) {
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
) {
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
)) {
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
;
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
);
1056 public void setSize(@NotNull final Dimension size
) {
1057 if (myPopup
== null) {
1058 myForcedSize
= size
;
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
);
1071 public static void setDefaultCursor(JComponent content
) {
1072 final Window wnd
= SwingUtilities
.getWindowAncestor(content
);
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
) {
1090 public boolean isPersistent() {
1091 return !myCancelOnClickOutside
&& !myCancelOnWindow
;
1094 public void setUiVisible(final boolean visible
) {
1095 if (myPopup
!= null) {
1098 final Window window
= getPopupWindow();
1099 if (window
!= null && myRestoreWindowSize
!= null) {
1100 window
.setSize(myRestoreWindowSize
);
1101 myRestoreWindowSize
= null;
1105 final Window window
= getPopupWindow();
1106 if (window
!= null) {
1107 myRestoreWindowSize
= window
.getSize();
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
1134 public boolean isModalContext() {
1135 return myModalContext
;
1138 public boolean isFocused() {
1139 if (myComponent
!= null && isFocused(new Component
[] {SwingUtilities
.getWindowAncestor(myComponent
)}))
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;
1157 public boolean isCancelKeyEnabled() {
1158 return myCancelKeyEnabled
;
1162 CaptionPanel
getTitle() {
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;
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
;
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
) {