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 if (myPopup
!= null) {
465 storeDimensionSize(myContent
.getSize());
466 if (myUseDimServiceForXYLocation
) {
467 final JRootPane root
= myComponent
.getRootPane();
469 final Container popupWindow
= root
.getParent();
470 if (popupWindow
!= null && popupWindow
.isShowing()) {
471 storeLocation(popupWindow
.getLocationOnScreen());
476 if (e
instanceof MouseEvent
) {
477 JBAwtEventQueue
.getInstance().blockNextEvents(((MouseEvent
)e
));
482 if (ApplicationManagerEx
.getApplicationEx() != null) {
483 StackingPopupDispatcher
.getInstance().onPopupHidden(this);
487 myFocusTrackback
.restoreFocus();
493 if (myListeners
!= null) {
494 for (JBPopupListener each
: myListeners
) {
495 each
.onClosed(new LightweightWindowEvent(this));
500 Disposer
.dispose(this, false);
504 private void disposePopup() {
505 if (myPopup
!= null) {
511 public boolean canClose() {
512 return myCallBack
== null || myCallBack
.compute().booleanValue();
515 public boolean isVisible() {
516 return myPopup
!= null;
519 public void show(final Component owner
) {
520 show(owner
, -1, -1, true);
523 public void show(Component owner
, int aScreenX
, int aScreenY
, final boolean considerForcedXY
) {
524 if (ApplicationManagerEx
.getApplicationEx() != null && ApplicationManager
.getApplication().isHeadlessEnvironment()) return;
526 throw new IllegalStateException("Popup was already disposed. Recreate a new instance to show again");
529 assert ApplicationManager
.getApplication().isDispatchThread();
532 final boolean shouldShow
= beforeShow();
540 myFocusTrackback
= new FocusTrackback(this, owner
, true);
541 myFocusTrackback
.setMustBeShown(true);
545 Dimension sizeToSet
= null;
547 if (myDimensionServiceKey
!= null) {
548 sizeToSet
= DimensionService
.getInstance().getSize(myDimensionServiceKey
, myProject
);
551 if (myForcedSize
!= null) {
552 sizeToSet
= myForcedSize
;
555 if (myMinSize
== null) {
556 myMinSize
= myContent
.getMinimumSize();
559 if (sizeToSet
== null) {
560 sizeToSet
= myContent
.getPreferredSize();
563 if (sizeToSet
!= null) {
564 sizeToSet
.width
= Math
.max(sizeToSet
.width
, myMinSize
.width
);
565 sizeToSet
.height
= Math
.max(sizeToSet
.height
, myMinSize
.height
);
567 myContent
.setSize(sizeToSet
);
568 myContent
.setPreferredSize(sizeToSet
);
571 Point xy
= new Point(aScreenX
, aScreenY
);
572 boolean adjustXY
= true;
573 if (myDimensionServiceKey
!= null) {
574 final Point storedLocation
= DimensionService
.getInstance().getLocation(myDimensionServiceKey
, myProject
);
575 if (storedLocation
!= null) {
582 final Insets insets
= myContent
.getInsets();
583 if (insets
!= null) {
589 if (considerForcedXY
&& myForcedLocation
!= null) {
590 xy
= myForcedLocation
;
593 if (myLocateByContent
) {
594 final Dimension captionSize
= myHeaderPanel
.getPreferredSize();
595 xy
.y
-= captionSize
.height
;
598 Rectangle targetBounds
= new Rectangle(xy
, myContent
.getPreferredSize());
599 Rectangle original
= new Rectangle(targetBounds
);
600 if (myLocateWithinScreen
) {
601 ScreenUtil
.moveRectangleToFitTheScreen(targetBounds
);
604 if (myMouseOutCanceller
!= null) {
605 myMouseOutCanceller
.myEverEntered
= targetBounds
.equals(original
);
608 myOwner
= IdeFrameImpl
.findNearestModalComponent(owner
);
609 if (myOwner
== null) {
613 myRequestorComponent
= owner
;
615 myPopup
= getFactory(myForcedHeavyweight
|| myResizable
).getPopup(myOwner
, myContent
, targetBounds
.x
, targetBounds
.y
);
618 final JRootPane root
= myContent
.getRootPane();
619 final IdeGlassPaneImpl glass
= new IdeGlassPaneImpl(root
);
620 root
.setGlassPane(glass
);
622 final ResizeComponentListener resizeListener
= new ResizeComponentListener(this);
623 glass
.addMousePreprocessor(resizeListener
, this);
624 glass
.addMouseMotionPreprocessor(resizeListener
, this);
627 if (myCaption
!= null && myMovable
) {
628 final MoveComponentListener moveListener
= new MoveComponentListener(myCaption
) {
629 public void mousePressed(final MouseEvent e
) {
630 super.mousePressed(e
);
631 if (e
.isConsumed()) return;
633 if (UIUtil
.isCloseClick(e
)) {
634 if (myCaption
.isWithinPanel(e
)) {
640 ListenerUtil
.addMouseListener(myCaption
, moveListener
);
641 ListenerUtil
.addMouseMotionListener(myCaption
, moveListener
);
642 final MyContentPanel saved
= myContent
;
643 Disposer
.register(this, new Disposable() {
644 public void dispose() {
645 ListenerUtil
.removeMouseListener(saved
, moveListener
);
646 ListenerUtil
.removeMouseMotionListener(saved
, moveListener
);
651 for(JBPopupListener listener
: myListeners
) {
652 listener
.beforeShown(new LightweightWindowEvent(this));
657 final Window window
= SwingUtilities
.getWindowAncestor(myContent
);
659 myWindowListener
= new MyWindowListener();
660 window
.addWindowListener(myWindowListener
);
663 window
.setFocusableWindowState(true);
664 window
.setFocusable(true);
665 if (myRequestFocus
) {
666 window
.requestFocusInWindow();
670 myWindow
= updateMaskAndAlpha(window
);
672 if (myWindow
instanceof JWindow
) {
673 ((JWindow
)myWindow
).getRootPane().putClientProperty(KEY
, this);
676 SwingUtilities
.invokeLater(new Runnable() {
678 if (isDisposed()) return;
680 if (myRequestFocus
) {
684 if (myPreferredFocusedComponent
!= null && myInStack
) {
685 myFocusTrackback
.registerFocusComponent(myPreferredFocusedComponent
);
693 private void prepareToShow() {
694 final MouseAdapter mouseAdapter
= new MouseAdapter() {
695 public void mousePressed(MouseEvent e
) {
696 Point point
= (Point
)e
.getPoint().clone();
697 SwingUtilities
.convertPointToScreen(point
, e
.getComponent());
699 final Dimension dimension
= myContent
.getSize();
700 dimension
.height
+= myResizable
&& isToDrawMacCorner() ? ourMacCorner
.getHeight(myContent
) : 4;
701 dimension
.width
+= 4;
702 Point locationOnScreen
= myContent
.getLocationOnScreen();
703 final Rectangle bounds
= new Rectangle(new Point(locationOnScreen
.x
- 2, locationOnScreen
.y
- 2), dimension
);
704 if (!bounds
.contains(point
)) {
709 myContent
.addMouseListener(mouseAdapter
);
710 Disposer
.register(this, new Disposable() {
711 public void dispose() {
712 myContent
.removeMouseListener(mouseAdapter
);
716 myContent
.registerKeyboardAction(new ActionListener() {
717 public void actionPerformed(ActionEvent e
) {
718 if (myCancelKeyEnabled
) {
722 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0), JComponent
.WHEN_IN_FOCUSED_WINDOW
);
725 myContent
.addKeyListener(new KeyListener() {
726 public void keyTyped(final KeyEvent e
) {
727 mySpeedSearch
.process(e
);
730 public void keyPressed(final KeyEvent e
) {
731 mySpeedSearch
.process(e
);
734 public void keyReleased(final KeyEvent e
) {
735 mySpeedSearch
.process(e
);
739 if (myCancelOnMouseOutCallback
!= null || myCancelOnWindow
) {
740 myMouseOutCanceller
= new Canceller();
741 Toolkit
.getDefaultToolkit().addAWTEventListener(myMouseOutCanceller
, AWTEvent
.MOUSE_EVENT_MASK
| WindowEvent
.WINDOW_ACTIVATED
|
742 AWTEvent
.MOUSE_MOTION_EVENT_MASK
);
746 myFocusWatcher
= new ChildFocusWatcher(myContent
) {
747 protected void onFocusGained(final FocusEvent event
) {
748 setWindowActive(true);
751 protected void onFocusLost(final FocusEvent event
) {
752 setWindowActive(false);
757 mySpeedSearchPatternField
= new JTextField();
758 if (SystemInfo
.isMac
) {
759 Font f
= mySpeedSearchPatternField
.getFont();
760 mySpeedSearchPatternField
.setFont(f
.deriveFont(f
.getStyle(), f
.getSize() - 2));
764 private Window
updateMaskAndAlpha(Window window
) {
765 if (window
== null) return window
;
767 final WindowManagerEx wndManager
= getWndManager();
768 if (wndManager
== null) return window
;
770 if (!wndManager
.isAlphaModeEnabled(window
)) return window
;
772 if (myAlpha
!= myLastAlpha
) {
773 wndManager
.setAlphaModeRatio(window
, myAlpha
);
774 myLastAlpha
= myAlpha
;
777 if (myMaskProvider
!= null) {
778 final Dimension size
= window
.getSize();
779 Shape mask
= myMaskProvider
.getMask(size
);
780 wndManager
.setWindowMask(window
, mask
);
786 private static WindowManagerEx
getWndManager() {
787 return ApplicationManagerEx
.getApplicationEx() != null ? WindowManagerEx
.getInstanceEx() : null;
790 public boolean isDisposed() {
791 return myContent
== null;
794 protected boolean beforeShow() {
795 if (ApplicationManagerEx
.getApplicationEx() == null) return true;
796 StackingPopupDispatcher
.getInstance().onPopupShown(this, myInStack
);
800 protected void afterShow() {
803 protected final void requestFocus() {
804 if (!myFocusable
) return;
806 if (myPreferredFocusedComponent
!= null) {
807 if (myProject
!= null) {
808 getFocusManager().requestFocus(myPreferredFocusedComponent
, true);
811 myPreferredFocusedComponent
.requestFocus();
816 private IdeFocusManager
getFocusManager() {
817 return myProject
== null ?
null : IdeFocusManager
.getInstance(myProject
);
820 private static JComponent
getTargetComponent(Component aComponent
) {
821 if (aComponent
instanceof JComponent
) {
822 return (JComponent
)aComponent
;
824 else if (aComponent
instanceof RootPaneContainer
) {
825 return ((RootPaneContainer
)aComponent
).getRootPane();
828 LOG
.error("Cannot find target for:" + aComponent
);
832 private PopupComponent
.Factory
getFactory(boolean forceHeavyweight
) {
833 if (isPersistent()) {
834 return new PopupComponent
.Factory
.Dialog();
835 } else if (forceHeavyweight
|| !SystemInfo
.isWindows
) {
836 return new PopupComponent
.Factory
.AwtHeavyweight();
838 return new PopupComponent
.Factory
.AwtDefault();
842 public JComponent
getContent() {
846 public void setLocation(RelativePoint p
) {
847 setLocation(p
, myPopup
, myContent
);
850 private static void setLocation(final RelativePoint p
, final PopupComponent popup
, Component content
) {
851 if (popup
== null) return;
853 final Window wnd
= popup
.getWindow();
857 wnd
.setLocation(p
.getScreenPoint());
861 final Window window
= SwingUtilities
.getWindowAncestor(myContent
);
866 public JComponent
getComponent() {
870 public void setProject(Project project
) {
875 public void dispose() {
876 Disposer
.dispose(this, false);
878 assert ApplicationManager
.getApplication().isDispatchThread();
880 if (myPopup
!= null) {
881 cancel(myDisposeEvent
);
884 if (myContent
!= null) {
885 myContent
.removeAll();
889 myFocusTrackback
= null;
893 if (myMouseOutCanceller
!= null) {
894 final Toolkit toolkit
= Toolkit
.getDefaultToolkit();
895 // it may happen, but have no idea how
896 // http://www.jetbrains.net/jira/browse/IDEADEV-21265
897 if (toolkit
!= null) {
898 toolkit
.removeAWTEventListener(myMouseOutCanceller
);
901 myMouseOutCanceller
= null;
903 if (myFocusWatcher
!= null) {
904 myFocusWatcher
.dispose();
905 myFocusWatcher
= null;
910 if (myFinalRunnable
!= null) {
911 IdeFocusManager focusManager
= getFocusManager();
912 if (focusManager
!= null) {
913 focusManager
.doWhenFocusSettlesDown(myFinalRunnable
);
916 myFinalRunnable
.run();
918 myFinalRunnable
= null;
922 private void resetWindow() {
923 if (myWindow
!= null && getWndManager() != null) {
924 getWndManager().resetWindow(myWindow
);
925 if (myWindowListener
!= null) {
926 myWindow
.removeWindowListener(myWindowListener
);
929 if (myWindow
instanceof JWindow
) {
930 ((JWindow
)myWindow
).getRootPane().putClientProperty(KEY
, null);
934 myWindowListener
= null;
938 public void storeDimensionSize(final Dimension size
) {
939 if (myDimensionServiceKey
!= null) {
940 DimensionService
.getInstance().setSize(myDimensionServiceKey
, size
, myProject
);
944 public void storeLocation(final Point xy
) {
945 if (myDimensionServiceKey
!= null) {
946 DimensionService
.getInstance().setLocation(myDimensionServiceKey
, xy
, myProject
);
950 public static class MyContentPanel
extends JPanel
{
951 private final boolean myResizable
;
952 private final boolean myDrawMacCorner
;
953 private final boolean myShadowed
;
955 public MyContentPanel(final boolean resizable
, final PopupBorder border
, boolean drawMacCorner
) {
956 this(resizable
, border
, drawMacCorner
, false);
959 public MyContentPanel(final boolean resizable
, final PopupBorder border
, boolean drawMacCorner
, boolean shadowed
) {
960 super(new BorderLayout());
961 myResizable
= resizable
;
962 myDrawMacCorner
= drawMacCorner
;
963 myShadowed
= shadowed
;
964 if (isShadowPossible()) {
966 setBorder(new EmptyBorder(POPUP_TOP_SIZE
, POPUP_SIDE_SIZE
, POPUP_BOTTOM_SIZE
, POPUP_SIDE_SIZE
) {
968 public void paintBorder(Component c
, Graphics g
, int x
, int y
, int width
, int height
) {
969 border
.paintBorder(c
, g
,
970 x
+ POPUP_SIDE_SIZE
- 1,
971 y
+ POPUP_TOP_SIZE
- 1,
972 width
- 2 * POPUP_SIDE_SIZE
+ 2,
973 height
- POPUP_TOP_SIZE
- POPUP_BOTTOM_SIZE
+ 2);
981 private boolean isShadowPossible() {
982 return myShadowed
&& !SystemInfo
.isMac
&& !UISettings
.isRemoteDesktopConnected();
985 public void paint(Graphics g
) {
986 if (isShadowPossible()) {
992 if (myResizable
&& myDrawMacCorner
) {
993 g
.drawImage(ourMacCorner
,
994 getX() + getWidth() - ourMacCorner
.getWidth(this),
995 getY() + getHeight() - ourMacCorner
.getHeight(this),
1000 private void paintShadow(final Graphics g
) {
1001 BufferedImage capture
= null;
1003 final Point onScreen
= getLocationOnScreen();
1004 capture
= new Robot().createScreenCapture(
1005 new Rectangle(onScreen
.x
, onScreen
.y
, getWidth() + 2 * POPUP_SIDE_SIZE
, getHeight() + POPUP_TOP_SIZE
+ POPUP_BOTTOM_SIZE
));
1006 final BufferedImage shadow
= ShadowBorderPainter
.createPopupShadow(this, getWidth(), getHeight());
1007 ((Graphics2D
)capture
.getGraphics()).drawImage(shadow
, null, null);
1009 catch (Exception e
) {
1010 e
.printStackTrace();
1012 if (capture
!= null) g
.drawImage(capture
, 0, 0, null);
1016 public boolean isCancelOnClickOutside() {
1017 return myCancelOnClickOutside
;
1020 private class Canceller
implements AWTEventListener
{
1022 private boolean myEverEntered
= false;
1024 public void eventDispatched(final AWTEvent event
) {
1025 if (event
.getID() == WindowEvent
.WINDOW_ACTIVATED
) {
1026 if (myCancelOnWindow
) {
1029 } else if (event
.getID() == MouseEvent
.MOUSE_ENTERED
) {
1030 if (withinPopup(event
)) {
1031 myEverEntered
= true;
1033 } else if (event
.getID() == MouseEvent
.MOUSE_MOVED
) {
1034 if (myCancelOnMouseOutCallback
!= null && myEverEntered
&& !withinPopup(event
)) {
1035 if (myCancelOnMouseOutCallback
.check((MouseEvent
)event
)) {
1042 private boolean withinPopup(final AWTEvent event
) {
1043 if (!myContent
.isShowing()) return false;
1045 final MouseEvent mouse
= (MouseEvent
)event
;
1046 final Point point
= mouse
.getPoint();
1047 SwingUtilities
.convertPointToScreen(point
, mouse
.getComponent());
1048 return new Rectangle(myContent
.getLocationOnScreen(), myContent
.getSize()).contains(point
);
1052 public void setLocation(@NotNull final Point screenPoint
) {
1053 if (myPopup
== null) {
1054 myForcedLocation
= screenPoint
;
1056 moveTo(myContent
, screenPoint
, myLocateByContent ? myHeaderPanel
.getPreferredSize() : null);
1060 public static Window
moveTo(JComponent content
, Point screenPoint
, final Dimension headerCorrectionSize
) {
1061 setDefaultCursor(content
);
1062 final Window wnd
= SwingUtilities
.getWindowAncestor(content
);
1063 if (headerCorrectionSize
!= null) {
1064 screenPoint
.y
-= headerCorrectionSize
.height
;
1066 wnd
.setLocation(screenPoint
);
1070 public void setSize(@NotNull final Dimension size
) {
1071 if (myPopup
== null) {
1072 myForcedSize
= size
;
1074 updateMaskAndAlpha(setSize(myContent
, size
));
1078 public static Window
setSize(JComponent content
, final Dimension size
) {
1079 final Window popupWindow
= SwingUtilities
.windowForComponent(content
);
1080 final Point location
= popupWindow
.getLocation();
1081 popupWindow
.setBounds(location
.x
, location
.y
, size
.width
, size
.height
);
1085 public static void setDefaultCursor(JComponent content
) {
1086 final Window wnd
= SwingUtilities
.getWindowAncestor(content
);
1088 wnd
.setCursor(Cursor
.getDefaultCursor());
1092 public void setCaption(String title
) {
1093 if (myCaption
instanceof TitlePanel
) {
1094 ((TitlePanel
)myCaption
).setText(title
);
1098 private class MyWindowListener
extends WindowAdapter
{
1099 public void windowClosed(final WindowEvent e
) {
1104 public boolean isPersistent() {
1105 return !myCancelOnClickOutside
&& !myCancelOnWindow
;
1108 public void setUiVisible(final boolean visible
) {
1109 if (myPopup
!= null) {
1112 final Window window
= getPopupWindow();
1113 if (window
!= null && myRestoreWindowSize
!= null) {
1114 window
.setSize(myRestoreWindowSize
);
1115 myRestoreWindowSize
= null;
1119 final Window window
= getPopupWindow();
1120 if (window
!= null) {
1121 myRestoreWindowSize
= window
.getSize();
1122 window
.setVisible(true);
1128 private Window
getPopupWindow() {
1129 return myPopup
.getWindow();
1132 public void setUserData(ArrayList
<Object
> userData
) {
1133 myUserData
= userData
;
1136 public <T
> T
getUserData(final Class
<T
> userDataClass
) {
1137 if (myUserData
!= null) {
1138 for(Object o
: myUserData
) {
1139 if (userDataClass
.isInstance(o
)) {
1140 //noinspection unchecked
1148 public boolean isModalContext() {
1149 return myModalContext
;
1152 public boolean isFocused() {
1153 if (myComponent
!= null && isFocused(new Component
[] {SwingUtilities
.getWindowAncestor(myComponent
)}))
1155 return isFocused(myFocusOwners
);
1158 public static boolean isFocused(@Nullable Component
[] components
) {
1159 if (components
== null) return false;
1161 Component owner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
1163 if (owner
== null) return false;
1164 for (Component each
: components
) {
1165 if (each
!= null && SwingUtilities
.isDescendingFrom(owner
, each
)) return true;
1171 public boolean isCancelKeyEnabled() {
1172 return myCancelKeyEnabled
;
1176 CaptionPanel
getTitle() {
1180 private void setHeaderComponent(JComponent c
) {
1181 boolean doRevalidate
= false;
1182 if (myHeaderComponent
!= null) {
1183 myHeaderPanel
.remove(myHeaderComponent
);
1184 myHeaderPanel
.add(myCaption
, BorderLayout
.NORTH
);
1185 myHeaderComponent
= null;
1186 doRevalidate
= true;
1190 myHeaderPanel
.remove(myCaption
);
1191 myHeaderPanel
.add(c
, BorderLayout
.NORTH
);
1192 myHeaderComponent
= c
;
1194 final Dimension size
= myContent
.getSize();
1195 if (size
.height
< c
.getPreferredSize().height
* 2) {
1196 size
.height
+= c
.getPreferredSize().height
;
1200 doRevalidate
= true;
1203 if (doRevalidate
) myContent
.revalidate();
1206 public void addListener(final JBPopupListener listener
) {
1207 myListeners
.add(listener
);
1210 public void removeListener(final JBPopupListener listener
) {
1211 myListeners
.remove(listener
);
1214 protected void onSpeedSearchPatternChanged() {
1217 public Component
getOwner() {
1218 return myRequestorComponent
;
1221 public void setMinimumSize(Dimension size
) {
1225 public Runnable
getFinalRunnable() {
1226 return myFinalRunnable
;
1229 public void setFinalRunnable(Runnable finalRunnable
) {
1230 myFinalRunnable
= finalRunnable
;