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
;
50 import javax
.swing
.border
.EmptyBorder
;
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
;
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;
137 public void noHits() {
138 mySpeedSearchPatternField
.setBackground(LightColors
.RED
);
142 private JTextField mySpeedSearchPatternField
;
148 AbstractPopup
init(final Project project
,
149 @NotNull final JComponent component
,
150 @Nullable final JComponent preferredFocusedComponent
,
151 final boolean requestFocus
,
152 final boolean focusable
,
153 final boolean forceHeavyweight
,
154 final boolean movable
,
155 final String dimensionServiceKey
,
156 final boolean resizable
,
157 @Nullable final String caption
,
158 @Nullable final Computable
<Boolean
> callback
,
159 final boolean cancelOnClickOutside
,
160 @Nullable final Set
<JBPopupListener
> listeners
,
161 final boolean useDimServiceForXYLocation
,
162 InplaceButton commandButton
,
163 @Nullable final IconButton cancelButton
,
164 @Nullable final MouseChecker cancelOnMouseOutCallback
,
165 final boolean cancelOnWindow
,
166 @Nullable final ActiveIcon titleIcon
,
167 final boolean cancelKeyEnabled
,
168 final boolean locateBycontent
,
169 final boolean placeWithinScreenBounds
,
170 @Nullable final Dimension minSize
,
172 @Nullable MaskProvider maskProvider
,
174 boolean modalContext
,
175 @Nullable Component
[] focusOwners
,
176 @Nullable String adText
,
177 final boolean headerAlwaysFocusable
,
178 @NotNull List
<Pair
<ActionListener
, KeyStroke
>> keyboardActions
,
179 Component settingsButtons
) {
181 if (requestFocus
&& !focusable
) {
182 assert false : "Incorrect argument combination: requestFocus=" + requestFocus
+ " focusable=" + focusable
;
186 myComponent
= component
;
187 myPopupBorder
= PopupBorder
.Factory
.create(true);
188 myShadowed
= !movable
&& !resizable
&& Registry
.is("ide.popup.dropShadow");
189 myContent
= createContentPanel(resizable
, myPopupBorder
, isToDrawMacCorner());
191 myContent
.add(component
, BorderLayout
.CENTER
);
192 if (adText
!= null) {
193 myContent
.add(HintUtil
.createAdComponent(adText
), BorderLayout
.SOUTH
);
196 myCancelKeyEnabled
= cancelKeyEnabled
;
197 myLocateByContent
= locateBycontent
;
198 myLocateWithinScreen
= placeWithinScreenBounds
;
200 myMaskProvider
= maskProvider
;
202 myModalContext
= modalContext
;
203 myFocusOwners
= focusOwners
;
204 myHeaderAlwaysFocusable
= headerAlwaysFocusable
;
207 ActiveIcon actualIcon
= titleIcon
== null ?
new ActiveIcon(new EmptyIcon(0)) : titleIcon
;
209 myHeaderPanel
= new JPanel(new BorderLayout());
211 if (caption
!= null) {
212 if (caption
.length() > 0) {
213 myCaption
= new TitlePanel(actualIcon
.getRegular(), actualIcon
.getInactive());
214 ((TitlePanel
)myCaption
).setText(caption
);
217 myCaption
= new CaptionPanel();
219 if (cancelButton
!= null) {
220 myCaption
.setButtonComponent(new InplaceButton(cancelButton
, new ActionListener() {
221 public void actionPerformed(final ActionEvent e
) {
226 else if (commandButton
!= null) {
227 myCaption
.setButtonComponent(commandButton
);
231 myCaption
= new CaptionPanel();
232 myCaption
.setBorder(null);
233 myCaption
.setPreferredSize(new Dimension(0, 0));
236 setWindowActive(myHeaderAlwaysFocusable
);
238 myHeaderPanel
.add(myCaption
, BorderLayout
.NORTH
);
239 myContent
.add(myHeaderPanel
, BorderLayout
.NORTH
);
241 myForcedHeavyweight
= forceHeavyweight
;
242 myResizable
= resizable
;
243 myPreferredFocusedComponent
= preferredFocusedComponent
;
244 myRequestFocus
= requestFocus
;
245 myFocusable
= focusable
;
246 myDimensionServiceKey
= dimensionServiceKey
;
247 myCallBack
= callback
;
248 myCancelOnClickOutside
= cancelOnClickOutside
;
249 myCancelOnMouseOutCallback
= cancelOnMouseOutCallback
;
250 myListeners
= listeners
== null ?
new HashSet
<JBPopupListener
>() : listeners
;
251 myUseDimServiceForXYLocation
= useDimServiceForXYLocation
;
252 myCancelOnWindow
= cancelOnWindow
;
255 for (Pair
<ActionListener
, KeyStroke
> pair
: keyboardActions
) {
256 myContent
.registerKeyboardAction(pair
.getFirst(), pair
.getSecond(), JComponent
.WHEN_IN_FOCUSED_WINDOW
);
259 if (settingsButtons
!= null) {
260 myCaption
.addSettingsComponent(settingsButtons
);
266 private void setWindowActive(boolean active
) {
267 boolean value
= myHeaderAlwaysFocusable
|| active
;
269 if (myCaption
!= null) {
270 myCaption
.setActive(value
);
272 myPopupBorder
.setActive(value
);
278 protected MyContentPanel
createContentPanel(final boolean resizable
, PopupBorder border
, boolean isToDrawMacCorner
) {
279 return new MyContentPanel(resizable
, border
, isToDrawMacCorner
, myShadowed
);
282 public static boolean isToDrawMacCorner() {
283 return SystemInfo
.isMac
;
288 public String
getDimensionServiceKey() {
289 return myDimensionServiceKey
;
292 public void setDimensionServiceKey(final String dimensionServiceKey
) {
293 myDimensionServiceKey
= dimensionServiceKey
;
296 public void showInCenterOf(@NotNull Component aContainer
) {
297 final Point popupPoint
= getCenterOf(aContainer
, myContent
);
298 show(aContainer
, popupPoint
.x
, popupPoint
.y
, false);
301 public void setAdText(@NotNull final String s
) {
302 myContent
.add(HintUtil
.createAdComponent(s
, BorderFactory
.createEmptyBorder(3, 5, 3, 5)), BorderLayout
.SOUTH
);
305 public static Point
getCenterOf(final Component aContainer
, final JComponent content
) {
306 final JComponent component
= getTargetComponent(aContainer
);
308 Point containerScreenPoint
= component
.getVisibleRect().getLocation();
309 SwingUtilities
.convertPointToScreen(containerScreenPoint
, aContainer
);
311 return UIUtil
.getCenterPoint(new Rectangle(containerScreenPoint
, component
.getVisibleRect().getSize()), content
.getPreferredSize());
314 public void showCenteredInCurrentWindow(@NotNull Project project
) {
315 Window window
= null;
317 Component focusedComponent
= getWndManager().getFocusedComponent(project
);
318 if (focusedComponent
!= null) {
319 if (focusedComponent
instanceof Window
) {
320 window
= (Window
)focusedComponent
;
323 window
= SwingUtilities
.getWindowAncestor(focusedComponent
);
326 if (window
== null) {
327 window
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusedWindow();
330 if (window
!= null) {
331 showInCenterOf(window
);
335 public void showUnderneathOf(@NotNull Component aComponent
) {
336 show(new RelativePoint(aComponent
, new Point(0, aComponent
.getHeight())));
339 public void show(@NotNull RelativePoint aPoint
) {
340 final Point screenPoint
= aPoint
.getScreenPoint();
341 show(aPoint
.getComponent(), screenPoint
.x
, screenPoint
.y
, false);
344 public void showInScreenCoordinates(@NotNull Component owner
, @NotNull Point point
) {
345 show(owner
, point
.x
, point
.y
, false);
348 public void showInBestPositionFor(@NotNull DataContext dataContext
) {
349 final Editor editor
= PlatformDataKeys
.EDITOR
.getData(dataContext
);
350 if (editor
!= null) {
351 showInBestPositionFor(editor
);
354 show(relativePointByQuickSearch(dataContext
));
358 public void showInFocusCenter() {
359 final Component focused
= getWndManager().getFocusedComponent(myProject
);
360 if (focused
!= null) {
361 showInCenterOf(focused
);
363 final JFrame frame
= WindowManager
.getInstance().getFrame(myProject
);
364 showInCenterOf(frame
.getRootPane());
368 private RelativePoint
relativePointByQuickSearch(final DataContext dataContext
) {
369 Rectangle dominantArea
= PlatformDataKeys
.DOMINANT_HINT_AREA_RECTANGLE
.getData(dataContext
);
371 if (dominantArea
!= null) {
372 final Component focusedComponent
= getWndManager().getFocusedComponent(myProject
);
373 Window window
= SwingUtilities
.windowForComponent(focusedComponent
);
374 JLayeredPane layeredPane
;
375 if (window
instanceof JFrame
) {
376 layeredPane
= ((JFrame
)window
).getLayeredPane();
378 else if (window
instanceof JDialog
) {
379 layeredPane
= ((JDialog
)window
).getLayeredPane();
381 else if (window
instanceof JWindow
) {
382 layeredPane
= ((JWindow
)window
).getLayeredPane();
385 throw new IllegalStateException("cannot find parent window: project=" + myProject
+ "; window=" + window
);
388 return relativePointWithDominantRectangle(layeredPane
, dominantArea
);
391 return JBPopupFactory
.getInstance().guessBestPopupLocation(dataContext
);
394 public void showInBestPositionFor(@NotNull Editor editor
) {
395 assert editor
.getComponent().isShowing() : "Editor must be showing on the screen";
397 DataContext context
= ((EditorEx
)editor
).getDataContext();
398 Rectangle dominantArea
= PlatformDataKeys
.DOMINANT_HINT_AREA_RECTANGLE
.getData(context
);
399 if (dominantArea
!= null && !myRequestFocus
) {
400 final JLayeredPane layeredPane
= editor
.getContentComponent().getRootPane().getLayeredPane();
401 show(relativePointWithDominantRectangle(layeredPane
, dominantArea
));
404 show(JBPopupFactory
.getInstance().guessBestPopupLocation(editor
));
408 public void addPopupListener(JBPopupListener listener
) {
409 myListeners
.add(listener
);
412 private RelativePoint
relativePointWithDominantRectangle(final JLayeredPane layeredPane
, final Rectangle bounds
) {
413 Dimension preferredSize
= getComponent().getPreferredSize();
414 if (myDimensionServiceKey
!= null) {
415 final Dimension dimension
= DimensionService
.getInstance().getSize(myDimensionServiceKey
, myProject
);
416 if (dimension
!= null) {
417 preferredSize
= dimension
;
420 final Point leftTopCorner
= new Point(bounds
.x
+ bounds
.width
, bounds
.y
);
421 final Point leftTopCornerScreen
= (Point
)leftTopCorner
.clone();
422 SwingUtilities
.convertPointToScreen(leftTopCornerScreen
, layeredPane
);
423 final RelativePoint relativePoint
;
424 if (!ScreenUtil
.isOutsideOnTheRightOFScreen(
425 new Rectangle(leftTopCornerScreen
.x
, leftTopCornerScreen
.y
, preferredSize
.width
, preferredSize
.height
))) {
426 relativePoint
= new RelativePoint(layeredPane
, leftTopCorner
);
429 if (bounds
.x
> preferredSize
.width
) {
430 relativePoint
= new RelativePoint(layeredPane
, new Point(bounds
.x
- preferredSize
.width
, bounds
.y
));
433 setDimensionServiceKey(null); // going to cut width
434 Rectangle screen
= ScreenUtil
.getScreenRectangle(leftTopCornerScreen
.x
, leftTopCornerScreen
.y
);
435 final int spaceOnTheLeft
= bounds
.x
;
436 final int spaceOnTheRight
= (screen
.x
+ screen
.width
) - leftTopCornerScreen
.x
;
437 if (spaceOnTheLeft
> spaceOnTheRight
) {
438 relativePoint
= new RelativePoint(layeredPane
, new Point(0, bounds
.y
));
439 myComponent
.setPreferredSize(new Dimension(spaceOnTheLeft
, Math
.max(preferredSize
.height
, 200)));
442 relativePoint
= new RelativePoint(layeredPane
, leftTopCorner
);
443 myComponent
.setPreferredSize(new Dimension(spaceOnTheRight
, Math
.max(preferredSize
.height
, 200)));
447 return relativePoint
;
450 public final void cancel() {
454 public void setRequestFocus(boolean requestFocus
) {
455 myRequestFocus
= requestFocus
;
458 public void cancel(InputEvent e
) {
459 if (isDisposed()) return;
461 final Runnable finalRunnable
= myFinalRunnable
;
462 final IdeFocusManager focusManager
= myOwner
!= null ? IdeFocusManager
.findInstanceByComponent(myOwner
) : IdeFocusManager
.findInstance();
464 if (myPopup
!= null) {
468 storeDimensionSize(myContent
.getSize());
469 if (myUseDimServiceForXYLocation
) {
470 final JRootPane root
= myComponent
.getRootPane();
472 final Container popupWindow
= root
.getParent();
473 if (popupWindow
!= null && popupWindow
.isShowing()) {
474 storeLocation(popupWindow
.getLocationOnScreen());
479 if (e
instanceof MouseEvent
) {
480 JBAwtEventQueue
.getInstance().blockNextEvents(((MouseEvent
)e
));
485 if (ApplicationManagerEx
.getApplicationEx() != null) {
486 StackingPopupDispatcher
.getInstance().onPopupHidden(this);
490 myFocusTrackback
.restoreFocus();
496 if (myListeners
!= null) {
497 for (JBPopupListener each
: myListeners
) {
498 each
.onClosed(new LightweightWindowEvent(this));
503 Disposer
.dispose(this, false);
505 if (finalRunnable
!= null) {
506 SwingUtilities
.invokeLater(new Runnable() {
508 focusManager
.doWhenFocusSettlesDown(finalRunnable
);
515 private void disposePopup() {
516 if (myPopup
!= null) {
522 public boolean canClose() {
523 return myCallBack
== null || myCallBack
.compute().booleanValue();
526 public boolean isVisible() {
527 return myPopup
!= null;
530 public void show(final Component owner
) {
531 show(owner
, -1, -1, true);
534 public void show(Component owner
, int aScreenX
, int aScreenY
, final boolean considerForcedXY
) {
535 if (ApplicationManagerEx
.getApplicationEx() != null && ApplicationManager
.getApplication().isHeadlessEnvironment()) return;
537 throw new IllegalStateException("Popup was already disposed. Recreate a new instance to show again");
540 assert ApplicationManager
.getApplication().isDispatchThread();
543 final boolean shouldShow
= beforeShow();
551 myFocusTrackback
= new FocusTrackback(this, owner
, true);
552 myFocusTrackback
.setMustBeShown(true);
556 Dimension sizeToSet
= null;
558 if (myDimensionServiceKey
!= null) {
559 sizeToSet
= DimensionService
.getInstance().getSize(myDimensionServiceKey
, myProject
);
562 if (myForcedSize
!= null) {
563 sizeToSet
= myForcedSize
;
566 if (myMinSize
== null) {
567 myMinSize
= myContent
.getMinimumSize();
570 if (sizeToSet
== null) {
571 sizeToSet
= myContent
.getPreferredSize();
574 if (sizeToSet
!= null) {
575 sizeToSet
.width
= Math
.max(sizeToSet
.width
, myMinSize
.width
);
576 sizeToSet
.height
= Math
.max(sizeToSet
.height
, myMinSize
.height
);
578 myContent
.setSize(sizeToSet
);
579 myContent
.setPreferredSize(sizeToSet
);
582 Point xy
= new Point(aScreenX
, aScreenY
);
583 boolean adjustXY
= true;
584 if (myDimensionServiceKey
!= null) {
585 final Point storedLocation
= DimensionService
.getInstance().getLocation(myDimensionServiceKey
, myProject
);
586 if (storedLocation
!= null) {
593 final Insets insets
= myContent
.getInsets();
594 if (insets
!= null) {
600 if (considerForcedXY
&& myForcedLocation
!= null) {
601 xy
= myForcedLocation
;
604 if (myLocateByContent
) {
605 final Dimension captionSize
= myHeaderPanel
.getPreferredSize();
606 xy
.y
-= captionSize
.height
;
609 Rectangle targetBounds
= new Rectangle(xy
, myContent
.getPreferredSize());
610 Rectangle original
= new Rectangle(targetBounds
);
611 if (myLocateWithinScreen
) {
612 ScreenUtil
.moveRectangleToFitTheScreen(targetBounds
);
615 if (myMouseOutCanceller
!= null) {
616 myMouseOutCanceller
.myEverEntered
= targetBounds
.equals(original
);
619 myOwner
= IdeFrameImpl
.findNearestModalComponent(owner
);
620 if (myOwner
== null) {
624 myRequestorComponent
= owner
;
626 myPopup
= getFactory(myForcedHeavyweight
|| myResizable
).getPopup(myOwner
, myContent
, targetBounds
.x
, targetBounds
.y
);
629 final JRootPane root
= myContent
.getRootPane();
630 final IdeGlassPaneImpl glass
= new IdeGlassPaneImpl(root
);
631 root
.setGlassPane(glass
);
633 final ResizeComponentListener resizeListener
= new ResizeComponentListener(this);
634 glass
.addMousePreprocessor(resizeListener
, this);
635 glass
.addMouseMotionPreprocessor(resizeListener
, this);
638 if (myCaption
!= null && myMovable
) {
639 final MoveComponentListener moveListener
= new MoveComponentListener(myCaption
) {
640 public void mousePressed(final MouseEvent e
) {
641 super.mousePressed(e
);
642 if (e
.isConsumed()) return;
644 if (UIUtil
.isCloseClick(e
)) {
645 if (myCaption
.isWithinPanel(e
)) {
651 ListenerUtil
.addMouseListener(myCaption
, moveListener
);
652 ListenerUtil
.addMouseMotionListener(myCaption
, moveListener
);
653 final MyContentPanel saved
= myContent
;
654 Disposer
.register(this, new Disposable() {
655 public void dispose() {
656 ListenerUtil
.removeMouseListener(saved
, moveListener
);
657 ListenerUtil
.removeMouseMotionListener(saved
, moveListener
);
662 for(JBPopupListener listener
: myListeners
) {
663 listener
.beforeShown(new LightweightWindowEvent(this));
668 final Window window
= SwingUtilities
.getWindowAncestor(myContent
);
670 myWindowListener
= new MyWindowListener();
671 window
.addWindowListener(myWindowListener
);
674 window
.setFocusableWindowState(true);
675 window
.setFocusable(true);
676 if (myRequestFocus
) {
677 window
.requestFocusInWindow();
681 myWindow
= updateMaskAndAlpha(window
);
683 if (myWindow
instanceof JWindow
) {
684 ((JWindow
)myWindow
).getRootPane().putClientProperty(KEY
, this);
687 SwingUtilities
.invokeLater(new Runnable() {
689 if (isDisposed()) return;
691 if (myRequestFocus
) {
695 if (myPreferredFocusedComponent
!= null && myInStack
) {
696 myFocusTrackback
.registerFocusComponent(myPreferredFocusedComponent
);
704 private void prepareToShow() {
705 final MouseAdapter mouseAdapter
= new MouseAdapter() {
706 public void mousePressed(MouseEvent e
) {
707 Point point
= (Point
)e
.getPoint().clone();
708 SwingUtilities
.convertPointToScreen(point
, e
.getComponent());
710 final Dimension dimension
= myContent
.getSize();
711 dimension
.height
+= myResizable
&& isToDrawMacCorner() ? ourMacCorner
.getHeight(myContent
) : 4;
712 dimension
.width
+= 4;
713 Point locationOnScreen
= myContent
.getLocationOnScreen();
714 final Rectangle bounds
= new Rectangle(new Point(locationOnScreen
.x
- 2, locationOnScreen
.y
- 2), dimension
);
715 if (!bounds
.contains(point
)) {
720 myContent
.addMouseListener(mouseAdapter
);
721 Disposer
.register(this, new Disposable() {
722 public void dispose() {
723 myContent
.removeMouseListener(mouseAdapter
);
727 myContent
.registerKeyboardAction(new ActionListener() {
728 public void actionPerformed(ActionEvent e
) {
729 if (myCancelKeyEnabled
) {
733 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0), JComponent
.WHEN_IN_FOCUSED_WINDOW
);
736 myContent
.addKeyListener(new KeyListener() {
737 public void keyTyped(final KeyEvent e
) {
738 mySpeedSearch
.process(e
);
741 public void keyPressed(final KeyEvent e
) {
742 mySpeedSearch
.process(e
);
745 public void keyReleased(final KeyEvent e
) {
746 mySpeedSearch
.process(e
);
750 if (myCancelOnMouseOutCallback
!= null || myCancelOnWindow
) {
751 myMouseOutCanceller
= new Canceller();
752 Toolkit
.getDefaultToolkit().addAWTEventListener(myMouseOutCanceller
, AWTEvent
.MOUSE_EVENT_MASK
| WindowEvent
.WINDOW_ACTIVATED
|
753 AWTEvent
.MOUSE_MOTION_EVENT_MASK
);
757 myFocusWatcher
= new ChildFocusWatcher(myContent
) {
758 protected void onFocusGained(final FocusEvent event
) {
759 setWindowActive(true);
762 protected void onFocusLost(final FocusEvent event
) {
763 setWindowActive(false);
768 mySpeedSearchPatternField
= new JTextField();
769 if (SystemInfo
.isMac
) {
770 Font f
= mySpeedSearchPatternField
.getFont();
771 mySpeedSearchPatternField
.setFont(f
.deriveFont(f
.getStyle(), f
.getSize() - 2));
775 private Window
updateMaskAndAlpha(Window window
) {
776 if (window
== null) return window
;
778 final WindowManagerEx wndManager
= getWndManager();
779 if (wndManager
== null) return window
;
781 if (!wndManager
.isAlphaModeEnabled(window
)) return window
;
783 if (myAlpha
!= myLastAlpha
) {
784 wndManager
.setAlphaModeRatio(window
, myAlpha
);
785 myLastAlpha
= myAlpha
;
788 if (myMaskProvider
!= null) {
789 final Dimension size
= window
.getSize();
790 Shape mask
= myMaskProvider
.getMask(size
);
791 wndManager
.setWindowMask(window
, mask
);
797 private static WindowManagerEx
getWndManager() {
798 return ApplicationManagerEx
.getApplicationEx() != null ? WindowManagerEx
.getInstanceEx() : null;
801 public boolean isDisposed() {
802 return myContent
== null;
805 protected boolean beforeShow() {
806 if (ApplicationManagerEx
.getApplicationEx() == null) return true;
807 StackingPopupDispatcher
.getInstance().onPopupShown(this, myInStack
);
811 protected void afterShow() {
814 protected final void requestFocus() {
815 if (!myFocusable
) return;
817 if (myPreferredFocusedComponent
!= null) {
818 if (myProject
!= null) {
819 getFocusManager().requestFocus(myPreferredFocusedComponent
, true);
822 myPreferredFocusedComponent
.requestFocus();
827 private IdeFocusManager
getFocusManager() {
828 return IdeFocusManager
.getInstance(myProject
);
831 private static JComponent
getTargetComponent(Component aComponent
) {
832 if (aComponent
instanceof JComponent
) {
833 return (JComponent
)aComponent
;
835 else if (aComponent
instanceof RootPaneContainer
) {
836 return ((RootPaneContainer
)aComponent
).getRootPane();
839 LOG
.error("Cannot find target for:" + aComponent
);
843 private PopupComponent
.Factory
getFactory(boolean forceHeavyweight
) {
844 if (isPersistent()) {
845 return new PopupComponent
.Factory
.Dialog();
846 } else if (forceHeavyweight
|| !SystemInfo
.isWindows
) {
847 return new PopupComponent
.Factory
.AwtHeavyweight();
849 return new PopupComponent
.Factory
.AwtDefault();
853 public JComponent
getContent() {
857 public void setLocation(RelativePoint p
) {
858 setLocation(p
, myPopup
, myContent
);
861 private static void setLocation(final RelativePoint p
, final PopupComponent popup
, Component content
) {
862 if (popup
== null) return;
864 final Window wnd
= popup
.getWindow();
868 wnd
.setLocation(p
.getScreenPoint());
872 final Window window
= SwingUtilities
.getWindowAncestor(myContent
);
877 public JComponent
getComponent() {
881 public void setProject(Project project
) {
886 public void dispose() {
887 Disposer
.dispose(this, false);
889 assert ApplicationManager
.getApplication().isDispatchThread();
891 if (myPopup
!= null) {
892 cancel(myDisposeEvent
);
895 if (myContent
!= null) {
896 myContent
.removeAll();
900 myFocusTrackback
= null;
904 if (myMouseOutCanceller
!= null) {
905 final Toolkit toolkit
= Toolkit
.getDefaultToolkit();
906 // it may happen, but have no idea how
907 // http://www.jetbrains.net/jira/browse/IDEADEV-21265
908 if (toolkit
!= null) {
909 toolkit
.removeAWTEventListener(myMouseOutCanceller
);
912 myMouseOutCanceller
= null;
914 if (myFocusWatcher
!= null) {
915 myFocusWatcher
.dispose();
916 myFocusWatcher
= null;
923 private void resetWindow() {
924 if (myWindow
!= null && getWndManager() != null) {
925 getWndManager().resetWindow(myWindow
);
926 if (myWindowListener
!= null) {
927 myWindow
.removeWindowListener(myWindowListener
);
930 if (myWindow
instanceof JWindow
) {
931 ((JWindow
)myWindow
).getRootPane().putClientProperty(KEY
, null);
935 myWindowListener
= null;
939 public void storeDimensionSize(final Dimension size
) {
940 if (myDimensionServiceKey
!= null) {
941 DimensionService
.getInstance().setSize(myDimensionServiceKey
, size
, myProject
);
945 public void storeLocation(final Point xy
) {
946 if (myDimensionServiceKey
!= null) {
947 DimensionService
.getInstance().setLocation(myDimensionServiceKey
, xy
, myProject
);
951 public static class MyContentPanel
extends JPanel
{
952 private final boolean myResizable
;
953 private final boolean myDrawMacCorner
;
954 private final boolean myShadowed
;
956 public MyContentPanel(final boolean resizable
, final PopupBorder border
, boolean drawMacCorner
) {
957 this(resizable
, border
, drawMacCorner
, false);
960 public MyContentPanel(final boolean resizable
, final PopupBorder border
, boolean drawMacCorner
, boolean shadowed
) {
961 super(new BorderLayout());
962 myResizable
= resizable
;
963 myDrawMacCorner
= drawMacCorner
;
964 myShadowed
= shadowed
;
965 if (isShadowPossible()) {
967 setBorder(new EmptyBorder(POPUP_TOP_SIZE
, POPUP_SIDE_SIZE
, POPUP_BOTTOM_SIZE
, POPUP_SIDE_SIZE
) {
969 public void paintBorder(Component c
, Graphics g
, int x
, int y
, int width
, int height
) {
970 border
.paintBorder(c
, g
,
971 x
+ POPUP_SIDE_SIZE
- 1,
972 y
+ POPUP_TOP_SIZE
- 1,
973 width
- 2 * POPUP_SIDE_SIZE
+ 2,
974 height
- POPUP_TOP_SIZE
- POPUP_BOTTOM_SIZE
+ 2);
982 private boolean isShadowPossible() {
983 return myShadowed
&& !SystemInfo
.isMac
&& !UISettings
.isRemoteDesktopConnected();
986 public void paint(Graphics g
) {
987 if (isShadowPossible()) {
993 if (myResizable
&& myDrawMacCorner
) {
994 g
.drawImage(ourMacCorner
,
995 getX() + getWidth() - ourMacCorner
.getWidth(this),
996 getY() + getHeight() - ourMacCorner
.getHeight(this),
1001 private void paintShadow(final Graphics g
) {
1002 BufferedImage capture
= null;
1004 final Point onScreen
= getLocationOnScreen();
1005 capture
= new Robot().createScreenCapture(
1006 new Rectangle(onScreen
.x
, onScreen
.y
, getWidth() + 2 * POPUP_SIDE_SIZE
, getHeight() + POPUP_TOP_SIZE
+ POPUP_BOTTOM_SIZE
));
1007 final BufferedImage shadow
= ShadowBorderPainter
.createPopupShadow(this, getWidth(), getHeight());
1008 ((Graphics2D
)capture
.getGraphics()).drawImage(shadow
, null, null);
1010 catch (Exception e
) {
1011 e
.printStackTrace();
1013 if (capture
!= null) g
.drawImage(capture
, 0, 0, null);
1017 public boolean isCancelOnClickOutside() {
1018 return myCancelOnClickOutside
;
1021 private class Canceller
implements AWTEventListener
{
1023 private boolean myEverEntered
= false;
1025 public void eventDispatched(final AWTEvent event
) {
1026 if (event
.getID() == WindowEvent
.WINDOW_ACTIVATED
) {
1027 if (myCancelOnWindow
) {
1030 } else if (event
.getID() == MouseEvent
.MOUSE_ENTERED
) {
1031 if (withinPopup(event
)) {
1032 myEverEntered
= true;
1034 } else if (event
.getID() == MouseEvent
.MOUSE_MOVED
) {
1035 if (myCancelOnMouseOutCallback
!= null && myEverEntered
&& !withinPopup(event
)) {
1036 if (myCancelOnMouseOutCallback
.check((MouseEvent
)event
)) {
1043 private boolean withinPopup(final AWTEvent event
) {
1044 if (!myContent
.isShowing()) return false;
1046 final MouseEvent mouse
= (MouseEvent
)event
;
1047 final Point point
= mouse
.getPoint();
1048 SwingUtilities
.convertPointToScreen(point
, mouse
.getComponent());
1049 return new Rectangle(myContent
.getLocationOnScreen(), myContent
.getSize()).contains(point
);
1053 public void setLocation(@NotNull final Point screenPoint
) {
1054 if (myPopup
== null) {
1055 myForcedLocation
= screenPoint
;
1057 moveTo(myContent
, screenPoint
, myLocateByContent ? myHeaderPanel
.getPreferredSize() : null);
1061 public static Window
moveTo(JComponent content
, Point screenPoint
, final Dimension headerCorrectionSize
) {
1062 setDefaultCursor(content
);
1063 final Window wnd
= SwingUtilities
.getWindowAncestor(content
);
1064 if (headerCorrectionSize
!= null) {
1065 screenPoint
.y
-= headerCorrectionSize
.height
;
1067 wnd
.setLocation(screenPoint
);
1071 public void setSize(@NotNull final Dimension size
) {
1072 if (myPopup
== null) {
1073 myForcedSize
= size
;
1075 updateMaskAndAlpha(setSize(myContent
, size
));
1079 public static Window
setSize(JComponent content
, final Dimension size
) {
1080 final Window popupWindow
= SwingUtilities
.windowForComponent(content
);
1081 final Point location
= popupWindow
.getLocation();
1082 popupWindow
.setBounds(location
.x
, location
.y
, size
.width
, size
.height
);
1086 public static void setDefaultCursor(JComponent content
) {
1087 final Window wnd
= SwingUtilities
.getWindowAncestor(content
);
1089 wnd
.setCursor(Cursor
.getDefaultCursor());
1093 public void setCaption(String title
) {
1094 if (myCaption
instanceof TitlePanel
) {
1095 ((TitlePanel
)myCaption
).setText(title
);
1099 private class MyWindowListener
extends WindowAdapter
{
1100 public void windowClosed(final WindowEvent e
) {
1105 public boolean isPersistent() {
1106 return !myCancelOnClickOutside
&& !myCancelOnWindow
;
1109 public void setUiVisible(final boolean visible
) {
1110 if (myPopup
!= null) {
1113 final Window window
= getPopupWindow();
1114 if (window
!= null && myRestoreWindowSize
!= null) {
1115 window
.setSize(myRestoreWindowSize
);
1116 myRestoreWindowSize
= null;
1120 final Window window
= getPopupWindow();
1121 if (window
!= null) {
1122 myRestoreWindowSize
= window
.getSize();
1123 window
.setVisible(true);
1129 private Window
getPopupWindow() {
1130 return myPopup
.getWindow();
1133 public void setUserData(ArrayList
<Object
> userData
) {
1134 myUserData
= userData
;
1137 public <T
> T
getUserData(final Class
<T
> userDataClass
) {
1138 if (myUserData
!= null) {
1139 for(Object o
: myUserData
) {
1140 if (userDataClass
.isInstance(o
)) {
1141 //noinspection unchecked
1149 public boolean isModalContext() {
1150 return myModalContext
;
1153 public boolean isFocused() {
1154 if (myComponent
!= null && isFocused(new Component
[] {SwingUtilities
.getWindowAncestor(myComponent
)}))
1156 return isFocused(myFocusOwners
);
1159 public static boolean isFocused(@Nullable Component
[] components
) {
1160 if (components
== null) return false;
1162 Component owner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
1164 if (owner
== null) return false;
1165 for (Component each
: components
) {
1166 if (each
!= null && SwingUtilities
.isDescendingFrom(owner
, each
)) return true;
1172 public boolean isCancelKeyEnabled() {
1173 return myCancelKeyEnabled
;
1177 CaptionPanel
getTitle() {
1181 private void setHeaderComponent(JComponent c
) {
1182 boolean doRevalidate
= false;
1183 if (myHeaderComponent
!= null) {
1184 myHeaderPanel
.remove(myHeaderComponent
);
1185 myHeaderPanel
.add(myCaption
, BorderLayout
.NORTH
);
1186 myHeaderComponent
= null;
1187 doRevalidate
= true;
1191 myHeaderPanel
.remove(myCaption
);
1192 myHeaderPanel
.add(c
, BorderLayout
.NORTH
);
1193 myHeaderComponent
= c
;
1195 final Dimension size
= myContent
.getSize();
1196 if (size
.height
< c
.getPreferredSize().height
* 2) {
1197 size
.height
+= c
.getPreferredSize().height
;
1201 doRevalidate
= true;
1204 if (doRevalidate
) myContent
.revalidate();
1207 public void addListener(final JBPopupListener listener
) {
1208 myListeners
.add(listener
);
1211 public void removeListener(final JBPopupListener listener
) {
1212 myListeners
.remove(listener
);
1215 protected void onSpeedSearchPatternChanged() {
1218 public Component
getOwner() {
1219 return myRequestorComponent
;
1222 public void setMinimumSize(Dimension size
) {
1226 public Runnable
getFinalRunnable() {
1227 return myFinalRunnable
;
1230 public void setFinalRunnable(Runnable finalRunnable
) {
1231 myFinalRunnable
= finalRunnable
;