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