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
.Processor
;
44 import com
.intellij
.util
.ui
.ChildFocusWatcher
;
45 import com
.intellij
.util
.ui
.EmptyIcon
;
46 import com
.intellij
.util
.ui
.UIUtil
;
47 import org
.jetbrains
.annotations
.NotNull
;
48 import org
.jetbrains
.annotations
.Nullable
;
51 import javax
.swing
.border
.EmptyBorder
;
53 import java
.awt
.event
.*;
54 import java
.awt
.image
.BufferedImage
;
55 import java
.util
.ArrayList
;
56 import java
.util
.HashSet
;
57 import java
.util
.List
;
60 import static com
.intellij
.openapi
.ui
.impl
.ShadowBorderPainter
.*;
62 public class AbstractPopup
implements JBPopup
{
63 private static final Logger LOG
= Logger
.getInstance("#com.intellij.ui.popup.AbstractPopup");
65 private static final Image ourMacCorner
= ImageLoader
.loadFromResource("/general/macCorner.png");
67 private PopupComponent myPopup
;
68 private MyContentPanel myContent
;
69 private JComponent myPreferredFocusedComponent
;
70 private boolean myRequestFocus
;
71 private boolean myFocusable
;
72 private boolean myForcedHeavyweight
= false;
73 private boolean myLocateWithinScreen
= true;
74 private boolean myResizable
= false;
75 private JPanel myHeaderPanel
;
76 private CaptionPanel myCaption
= null;
77 private JComponent myComponent
;
78 private String myDimensionServiceKey
= null;
79 private Computable
<Boolean
> myCallBack
= null;
80 private Project myProject
;
81 private boolean myCancelOnClickOutside
;
82 private Set
<JBPopupListener
> myListeners
;
83 private boolean myUseDimServiceForXYLocation
;
84 private MouseChecker myCancelOnMouseOutCallback
;
85 private Canceller myMouseOutCanceller
;
86 private boolean myCancelOnWindow
;
87 private Dimension myForcedSize
;
88 private Point myForcedLocation
;
89 private ChildFocusWatcher myFocusWatcher
;
90 private boolean myCancelKeyEnabled
;
91 private boolean myLocateByContent
;
92 protected FocusTrackback myFocusTrackback
;
93 private Dimension myMinSize
;
94 private ArrayList
<Object
> myUserData
;
95 private boolean myShadowed
;
97 private float myAlpha
= 0;
98 private float myLastAlpha
= 0;
100 private MaskProvider myMaskProvider
;
102 private Window myWindow
;
103 private boolean myInStack
;
104 private MyWindowListener myWindowListener
;
106 private boolean myModalContext
;
108 private Component
[] myFocusOwners
;
109 private PopupBorder myPopupBorder
;
110 private Dimension myRestoreWindowSize
;
111 protected Component myOwner
;
112 protected Component myRequestorComponent
;
113 private boolean myHeaderAlwaysFocusable
;
114 private boolean myMovable
;
115 private JComponent myHeaderComponent
;
117 protected InputEvent myDisposeEvent
;
119 private Runnable myFinalRunnable
;
121 protected final SpeedSearch mySpeedSearch
= new SpeedSearch() {
122 boolean searchFieldShown
= false;
123 protected void update() {
124 mySpeedSearchPatternField
.setBackground(new JTextField().getBackground());
125 onSpeedSearchPatternChanged();
126 mySpeedSearchPatternField
.setText(getFilter());
127 if (isHoldingFilter() && !searchFieldShown
) {
128 setHeaderComponent(mySpeedSearchPatternField
);
129 searchFieldShown
= true;
131 else if (!isHoldingFilter() && searchFieldShown
) {
132 setHeaderComponent(null);
133 searchFieldShown
= false;
138 public void noHits() {
139 mySpeedSearchPatternField
.setBackground(LightColors
.RED
);
143 private JTextField mySpeedSearchPatternField
;
144 private boolean myNativePopup
;
150 AbstractPopup
init(final Project project
,
151 @NotNull final JComponent component
,
152 @Nullable final JComponent preferredFocusedComponent
,
153 final boolean requestFocus
,
154 final boolean focusable
,
155 final boolean forceHeavyweight
,
156 final boolean movable
,
157 final String dimensionServiceKey
,
158 final boolean resizable
,
159 @Nullable final String caption
,
160 @Nullable final Computable
<Boolean
> callback
,
161 final boolean cancelOnClickOutside
,
162 @Nullable final Set
<JBPopupListener
> listeners
,
163 final boolean useDimServiceForXYLocation
,
164 InplaceButton commandButton
,
165 @Nullable final IconButton cancelButton
,
166 @Nullable final MouseChecker cancelOnMouseOutCallback
,
167 final boolean cancelOnWindow
,
168 @Nullable final ActiveIcon titleIcon
,
169 final boolean cancelKeyEnabled
,
170 final boolean locateBycontent
,
171 final boolean placeWithinScreenBounds
,
172 @Nullable final Dimension minSize
,
174 @Nullable MaskProvider maskProvider
,
176 boolean modalContext
,
177 @Nullable Component
[] focusOwners
,
178 @Nullable String adText
,
179 final boolean headerAlwaysFocusable
,
180 @NotNull List
<Pair
<ActionListener
, KeyStroke
>> keyboardActions
,
181 Component settingsButtons
,
182 @Nullable final Processor
<JBPopup
> pinCallback
) {
184 if (requestFocus
&& !focusable
) {
185 assert false : "Incorrect argument combination: requestFocus=" + requestFocus
+ " focusable=" + focusable
;
189 myComponent
= component
;
190 myPopupBorder
= PopupBorder
.Factory
.create(true);
191 myShadowed
= !movable
&& !resizable
&& Registry
.is("ide.popup.dropShadow");
192 myContent
= createContentPanel(resizable
, myPopupBorder
, isToDrawMacCorner());
194 myContent
.add(component
, BorderLayout
.CENTER
);
195 if (adText
!= null) {
196 myContent
.add(HintUtil
.createAdComponent(adText
), BorderLayout
.SOUTH
);
199 myCancelKeyEnabled
= cancelKeyEnabled
;
200 myLocateByContent
= locateBycontent
;
201 myLocateWithinScreen
= placeWithinScreenBounds
;
203 myMaskProvider
= maskProvider
;
205 myModalContext
= modalContext
;
206 myFocusOwners
= focusOwners
;
207 myHeaderAlwaysFocusable
= headerAlwaysFocusable
;
210 ActiveIcon actualIcon
= titleIcon
== null ?
new ActiveIcon(new EmptyIcon(0)) : titleIcon
;
212 myHeaderPanel
= new JPanel(new BorderLayout());
214 if (caption
!= null) {
215 if (caption
.length() > 0) {
216 myCaption
= new TitlePanel(actualIcon
.getRegular(), actualIcon
.getInactive());
217 ((TitlePanel
)myCaption
).setText(caption
);
220 myCaption
= new CaptionPanel();
223 if (pinCallback
!= null) {
224 myCaption
.setButtonComponent(new InplaceButton(new IconButton("Pin", IconLoader
.getIcon("/general/autohideOff.png"),
225 IconLoader
.getIcon("/general/autohideOff.png"),
226 IconLoader
.getIcon("/general/autohideOffInactive.png")), new ActionListener() {
227 public void actionPerformed(final ActionEvent e
) {
228 pinCallback
.process(AbstractPopup
.this);
231 } else if (cancelButton
!= null) {
232 myCaption
.setButtonComponent(new InplaceButton(cancelButton
, new ActionListener() {
233 public void actionPerformed(final ActionEvent e
) {
238 else if (commandButton
!= null) {
239 myCaption
.setButtonComponent(commandButton
);
243 myCaption
= new CaptionPanel();
244 myCaption
.setBorder(null);
245 myCaption
.setPreferredSize(new Dimension(0, 0));
248 setWindowActive(myHeaderAlwaysFocusable
);
250 myHeaderPanel
.add(myCaption
, BorderLayout
.NORTH
);
251 myContent
.add(myHeaderPanel
, BorderLayout
.NORTH
);
253 myForcedHeavyweight
= forceHeavyweight
;
254 myResizable
= resizable
;
255 myPreferredFocusedComponent
= preferredFocusedComponent
;
256 myRequestFocus
= requestFocus
;
257 myFocusable
= focusable
;
258 myDimensionServiceKey
= dimensionServiceKey
;
259 myCallBack
= callback
;
260 myCancelOnClickOutside
= cancelOnClickOutside
;
261 myCancelOnMouseOutCallback
= cancelOnMouseOutCallback
;
262 myListeners
= listeners
== null ?
new HashSet
<JBPopupListener
>() : listeners
;
263 myUseDimServiceForXYLocation
= useDimServiceForXYLocation
;
264 myCancelOnWindow
= cancelOnWindow
;
267 for (Pair
<ActionListener
, KeyStroke
> pair
: keyboardActions
) {
268 myContent
.registerKeyboardAction(pair
.getFirst(), pair
.getSecond(), JComponent
.WHEN_IN_FOCUSED_WINDOW
);
271 if (settingsButtons
!= null) {
272 myCaption
.addSettingsComponent(settingsButtons
);
278 private void setWindowActive(boolean active
) {
279 boolean value
= myHeaderAlwaysFocusable
|| active
;
281 if (myCaption
!= null) {
282 myCaption
.setActive(value
);
284 myPopupBorder
.setActive(value
);
290 protected MyContentPanel
createContentPanel(final boolean resizable
, PopupBorder border
, boolean isToDrawMacCorner
) {
291 return new MyContentPanel(resizable
, border
, isToDrawMacCorner
, myShadowed
);
294 public static boolean isToDrawMacCorner() {
295 return SystemInfo
.isMac
;
300 public String
getDimensionServiceKey() {
301 return myDimensionServiceKey
;
304 public void setDimensionServiceKey(final String dimensionServiceKey
) {
305 myDimensionServiceKey
= dimensionServiceKey
;
308 public void showInCenterOf(@NotNull Component aContainer
) {
309 final Point popupPoint
= getCenterOf(aContainer
, myContent
);
310 show(aContainer
, popupPoint
.x
, popupPoint
.y
, false);
313 public void setAdText(@NotNull final String s
) {
314 myContent
.add(HintUtil
.createAdComponent(s
, BorderFactory
.createEmptyBorder(3, 5, 3, 5)), BorderLayout
.SOUTH
);
317 public static Point
getCenterOf(final Component aContainer
, final JComponent content
) {
318 final JComponent component
= getTargetComponent(aContainer
);
320 Point containerScreenPoint
= component
.getVisibleRect().getLocation();
321 SwingUtilities
.convertPointToScreen(containerScreenPoint
, aContainer
);
323 return UIUtil
.getCenterPoint(new Rectangle(containerScreenPoint
, component
.getVisibleRect().getSize()), content
.getPreferredSize());
326 public void showCenteredInCurrentWindow(@NotNull Project project
) {
327 Window window
= null;
329 Component focusedComponent
= getWndManager().getFocusedComponent(project
);
330 if (focusedComponent
!= null) {
331 if (focusedComponent
instanceof Window
) {
332 window
= (Window
)focusedComponent
;
335 window
= SwingUtilities
.getWindowAncestor(focusedComponent
);
338 if (window
== null) {
339 window
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusedWindow();
342 if (window
!= null) {
343 showInCenterOf(window
);
347 public void showUnderneathOf(@NotNull Component aComponent
) {
348 show(new RelativePoint(aComponent
, new Point(0, aComponent
.getHeight())));
351 public void show(@NotNull RelativePoint aPoint
) {
352 final Point screenPoint
= aPoint
.getScreenPoint();
353 show(aPoint
.getComponent(), screenPoint
.x
, screenPoint
.y
, false);
356 public void showInScreenCoordinates(@NotNull Component owner
, @NotNull Point point
) {
357 show(owner
, point
.x
, point
.y
, false);
360 public void showInBestPositionFor(@NotNull DataContext dataContext
) {
361 final Editor editor
= PlatformDataKeys
.EDITOR
.getData(dataContext
);
362 if (editor
!= null) {
363 showInBestPositionFor(editor
);
366 show(relativePointByQuickSearch(dataContext
));
370 public void showInFocusCenter() {
371 final Component focused
= getWndManager().getFocusedComponent(myProject
);
372 if (focused
!= null) {
373 showInCenterOf(focused
);
375 final JFrame frame
= WindowManager
.getInstance().getFrame(myProject
);
376 showInCenterOf(frame
.getRootPane());
380 private RelativePoint
relativePointByQuickSearch(final DataContext dataContext
) {
381 Rectangle dominantArea
= PlatformDataKeys
.DOMINANT_HINT_AREA_RECTANGLE
.getData(dataContext
);
383 if (dominantArea
!= null) {
384 final Component focusedComponent
= getWndManager().getFocusedComponent(myProject
);
385 Window window
= SwingUtilities
.windowForComponent(focusedComponent
);
386 JLayeredPane layeredPane
;
387 if (window
instanceof JFrame
) {
388 layeredPane
= ((JFrame
)window
).getLayeredPane();
390 else if (window
instanceof JDialog
) {
391 layeredPane
= ((JDialog
)window
).getLayeredPane();
393 else if (window
instanceof JWindow
) {
394 layeredPane
= ((JWindow
)window
).getLayeredPane();
397 throw new IllegalStateException("cannot find parent window: project=" + myProject
+ "; window=" + window
);
400 return relativePointWithDominantRectangle(layeredPane
, dominantArea
);
403 return JBPopupFactory
.getInstance().guessBestPopupLocation(dataContext
);
406 public void showInBestPositionFor(@NotNull Editor editor
) {
407 assert editor
.getComponent().isShowing() : "Editor must be showing on the screen";
409 DataContext context
= ((EditorEx
)editor
).getDataContext();
410 Rectangle dominantArea
= PlatformDataKeys
.DOMINANT_HINT_AREA_RECTANGLE
.getData(context
);
411 if (dominantArea
!= null && !myRequestFocus
) {
412 final JLayeredPane layeredPane
= editor
.getContentComponent().getRootPane().getLayeredPane();
413 show(relativePointWithDominantRectangle(layeredPane
, dominantArea
));
416 show(JBPopupFactory
.getInstance().guessBestPopupLocation(editor
));
420 public void addPopupListener(JBPopupListener listener
) {
421 myListeners
.add(listener
);
424 private RelativePoint
relativePointWithDominantRectangle(final JLayeredPane layeredPane
, final Rectangle bounds
) {
425 Dimension preferredSize
= getComponent().getPreferredSize();
426 if (myDimensionServiceKey
!= null) {
427 final Dimension dimension
= DimensionService
.getInstance().getSize(myDimensionServiceKey
, myProject
);
428 if (dimension
!= null) {
429 preferredSize
= dimension
;
432 final Point leftTopCorner
= new Point(bounds
.x
+ bounds
.width
, bounds
.y
);
433 final Point leftTopCornerScreen
= (Point
)leftTopCorner
.clone();
434 SwingUtilities
.convertPointToScreen(leftTopCornerScreen
, layeredPane
);
435 final RelativePoint relativePoint
;
436 if (!ScreenUtil
.isOutsideOnTheRightOFScreen(
437 new Rectangle(leftTopCornerScreen
.x
, leftTopCornerScreen
.y
, preferredSize
.width
, preferredSize
.height
))) {
438 relativePoint
= new RelativePoint(layeredPane
, leftTopCorner
);
441 if (bounds
.x
> preferredSize
.width
) {
442 relativePoint
= new RelativePoint(layeredPane
, new Point(bounds
.x
- preferredSize
.width
, bounds
.y
));
445 setDimensionServiceKey(null); // going to cut width
446 Rectangle screen
= ScreenUtil
.getScreenRectangle(leftTopCornerScreen
.x
, leftTopCornerScreen
.y
);
447 final int spaceOnTheLeft
= bounds
.x
;
448 final int spaceOnTheRight
= (screen
.x
+ screen
.width
) - leftTopCornerScreen
.x
;
449 if (spaceOnTheLeft
> spaceOnTheRight
) {
450 relativePoint
= new RelativePoint(layeredPane
, new Point(0, bounds
.y
));
451 myComponent
.setPreferredSize(new Dimension(spaceOnTheLeft
, Math
.max(preferredSize
.height
, 200)));
454 relativePoint
= new RelativePoint(layeredPane
, leftTopCorner
);
455 myComponent
.setPreferredSize(new Dimension(spaceOnTheRight
, Math
.max(preferredSize
.height
, 200)));
459 return relativePoint
;
462 public final void cancel() {
466 public void setRequestFocus(boolean requestFocus
) {
467 myRequestFocus
= requestFocus
;
470 public void cancel(InputEvent e
) {
471 if (isDisposed()) return;
473 if (myPopup
!= null) {
477 storeDimensionSize(myContent
.getSize());
478 if (myUseDimServiceForXYLocation
) {
479 final JRootPane root
= myComponent
.getRootPane();
481 final Container popupWindow
= root
.getParent();
482 if (popupWindow
!= null && popupWindow
.isShowing()) {
483 storeLocation(popupWindow
.getLocationOnScreen());
488 if (e
instanceof MouseEvent
) {
489 JBAwtEventQueue
.getInstance().blockNextEvents(((MouseEvent
)e
));
494 if (ApplicationManagerEx
.getApplicationEx() != null) {
495 StackingPopupDispatcher
.getInstance().onPopupHidden(this);
499 myFocusTrackback
.restoreFocus();
505 if (myListeners
!= null) {
506 for (JBPopupListener each
: myListeners
) {
507 each
.onClosed(new LightweightWindowEvent(this));
512 Disposer
.dispose(this, false);
516 private void disposePopup() {
517 if (myPopup
!= null) {
523 public boolean canClose() {
524 return myCallBack
== null || myCallBack
.compute().booleanValue();
527 public boolean isVisible() {
528 return myPopup
!= null;
531 public void show(final Component owner
) {
532 show(owner
, -1, -1, true);
535 public void show(Component owner
, int aScreenX
, int aScreenY
, final boolean considerForcedXY
) {
536 if (ApplicationManagerEx
.getApplicationEx() != null && ApplicationManager
.getApplication().isHeadlessEnvironment()) return;
538 throw new IllegalStateException("Popup was already disposed. Recreate a new instance to show again");
541 assert ApplicationManager
.getApplication().isDispatchThread();
544 final boolean shouldShow
= beforeShow();
552 myFocusTrackback
= new FocusTrackback(this, owner
, true);
553 myFocusTrackback
.setMustBeShown(true);
557 Dimension sizeToSet
= null;
559 if (myDimensionServiceKey
!= null) {
560 sizeToSet
= DimensionService
.getInstance().getSize(myDimensionServiceKey
, myProject
);
563 if (myForcedSize
!= null) {
564 sizeToSet
= myForcedSize
;
567 if (myMinSize
== null) {
568 myMinSize
= myContent
.getMinimumSize();
571 if (sizeToSet
== null) {
572 sizeToSet
= myContent
.getPreferredSize();
575 if (sizeToSet
!= null) {
576 sizeToSet
.width
= Math
.max(sizeToSet
.width
, myMinSize
.width
);
577 sizeToSet
.height
= Math
.max(sizeToSet
.height
, myMinSize
.height
);
579 myContent
.setSize(sizeToSet
);
580 myContent
.setPreferredSize(sizeToSet
);
583 Point xy
= new Point(aScreenX
, aScreenY
);
584 boolean adjustXY
= true;
585 if (myDimensionServiceKey
!= null) {
586 final Point storedLocation
= DimensionService
.getInstance().getLocation(myDimensionServiceKey
, myProject
);
587 if (storedLocation
!= null) {
594 final Insets insets
= myContent
.getInsets();
595 if (insets
!= null) {
601 if (considerForcedXY
&& myForcedLocation
!= null) {
602 xy
= myForcedLocation
;
605 if (myLocateByContent
) {
606 final Dimension captionSize
= myHeaderPanel
.getPreferredSize();
607 xy
.y
-= captionSize
.height
;
610 Rectangle targetBounds
= new Rectangle(xy
, myContent
.getPreferredSize());
611 Rectangle original
= new Rectangle(targetBounds
);
612 if (myLocateWithinScreen
) {
613 ScreenUtil
.moveRectangleToFitTheScreen(targetBounds
);
616 if (myMouseOutCanceller
!= null) {
617 myMouseOutCanceller
.myEverEntered
= targetBounds
.equals(original
);
620 myOwner
= IdeFrameImpl
.findNearestModalComponent(owner
);
621 if (myOwner
== null) {
625 myRequestorComponent
= owner
;
627 PopupComponent
.Factory factory
= getFactory(myForcedHeavyweight
|| myResizable
);
628 myNativePopup
= factory
.isNativePopup();
629 myPopup
= factory
.getPopup(myOwner
, myContent
, targetBounds
.x
, targetBounds
.y
);
632 final JRootPane root
= myContent
.getRootPane();
633 final IdeGlassPaneImpl glass
= new IdeGlassPaneImpl(root
);
634 root
.setGlassPane(glass
);
636 final ResizeComponentListener resizeListener
= new ResizeComponentListener(this);
637 glass
.addMousePreprocessor(resizeListener
, this);
638 glass
.addMouseMotionPreprocessor(resizeListener
, this);
641 if (myCaption
!= null && myMovable
) {
642 final MoveComponentListener moveListener
= new MoveComponentListener(myCaption
) {
643 public void mousePressed(final MouseEvent e
) {
644 super.mousePressed(e
);
645 if (e
.isConsumed()) return;
647 if (UIUtil
.isCloseClick(e
)) {
648 if (myCaption
.isWithinPanel(e
)) {
654 ListenerUtil
.addMouseListener(myCaption
, moveListener
);
655 ListenerUtil
.addMouseMotionListener(myCaption
, moveListener
);
656 final MyContentPanel saved
= myContent
;
657 Disposer
.register(this, new Disposable() {
658 public void dispose() {
659 ListenerUtil
.removeMouseListener(saved
, moveListener
);
660 ListenerUtil
.removeMouseMotionListener(saved
, moveListener
);
665 for(JBPopupListener listener
: myListeners
) {
666 listener
.beforeShown(new LightweightWindowEvent(this));
671 final Window window
= SwingUtilities
.getWindowAncestor(myContent
);
673 myWindowListener
= new MyWindowListener();
674 window
.addWindowListener(myWindowListener
);
677 window
.setFocusableWindowState(true);
678 window
.setFocusable(true);
679 if (myRequestFocus
) {
680 window
.requestFocusInWindow();
684 myWindow
= updateMaskAndAlpha(window
);
686 if (myWindow
instanceof JWindow
) {
687 ((JWindow
)myWindow
).getRootPane().putClientProperty(KEY
, this);
690 SwingUtilities
.invokeLater(new Runnable() {
692 if (isDisposed()) return;
694 if (myRequestFocus
) {
698 if (myPreferredFocusedComponent
!= null && myInStack
) {
699 myFocusTrackback
.registerFocusComponent(myPreferredFocusedComponent
);
707 private void prepareToShow() {
708 final MouseAdapter mouseAdapter
= new MouseAdapter() {
709 public void mousePressed(MouseEvent e
) {
710 Point point
= (Point
)e
.getPoint().clone();
711 SwingUtilities
.convertPointToScreen(point
, e
.getComponent());
713 final Dimension dimension
= myContent
.getSize();
714 dimension
.height
+= myResizable
&& isToDrawMacCorner() ? ourMacCorner
.getHeight(myContent
) : 4;
715 dimension
.width
+= 4;
716 Point locationOnScreen
= myContent
.getLocationOnScreen();
717 final Rectangle bounds
= new Rectangle(new Point(locationOnScreen
.x
- 2, locationOnScreen
.y
- 2), dimension
);
718 if (!bounds
.contains(point
)) {
723 myContent
.addMouseListener(mouseAdapter
);
724 Disposer
.register(this, new Disposable() {
725 public void dispose() {
726 myContent
.removeMouseListener(mouseAdapter
);
730 myContent
.registerKeyboardAction(new ActionListener() {
731 public void actionPerformed(ActionEvent e
) {
732 if (myCancelKeyEnabled
) {
736 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0), JComponent
.WHEN_IN_FOCUSED_WINDOW
);
739 myContent
.addKeyListener(new KeyListener() {
740 public void keyTyped(final KeyEvent e
) {
741 mySpeedSearch
.process(e
);
744 public void keyPressed(final KeyEvent e
) {
745 mySpeedSearch
.process(e
);
748 public void keyReleased(final KeyEvent e
) {
749 mySpeedSearch
.process(e
);
753 if (myCancelOnMouseOutCallback
!= null || myCancelOnWindow
) {
754 myMouseOutCanceller
= new Canceller();
755 Toolkit
.getDefaultToolkit().addAWTEventListener(myMouseOutCanceller
, AWTEvent
.MOUSE_EVENT_MASK
| WindowEvent
.WINDOW_ACTIVATED
|
756 AWTEvent
.MOUSE_MOTION_EVENT_MASK
);
760 myFocusWatcher
= new ChildFocusWatcher(myContent
) {
761 protected void onFocusGained(final FocusEvent event
) {
762 setWindowActive(true);
765 protected void onFocusLost(final FocusEvent event
) {
766 setWindowActive(false);
771 mySpeedSearchPatternField
= new JTextField();
772 if (SystemInfo
.isMac
) {
773 Font f
= mySpeedSearchPatternField
.getFont();
774 mySpeedSearchPatternField
.setFont(f
.deriveFont(f
.getStyle(), f
.getSize() - 2));
778 private Window
updateMaskAndAlpha(Window window
) {
779 if (window
== null) return window
;
781 final WindowManagerEx wndManager
= getWndManager();
782 if (wndManager
== null) return window
;
784 if (!wndManager
.isAlphaModeEnabled(window
)) return window
;
786 if (myAlpha
!= myLastAlpha
) {
787 wndManager
.setAlphaModeRatio(window
, myAlpha
);
788 myLastAlpha
= myAlpha
;
791 if (myMaskProvider
!= null) {
792 final Dimension size
= window
.getSize();
793 Shape mask
= myMaskProvider
.getMask(size
);
794 wndManager
.setWindowMask(window
, mask
);
800 private static WindowManagerEx
getWndManager() {
801 return ApplicationManagerEx
.getApplicationEx() != null ? WindowManagerEx
.getInstanceEx() : null;
804 public boolean isDisposed() {
805 return myContent
== null;
808 protected boolean beforeShow() {
809 if (ApplicationManagerEx
.getApplicationEx() == null) return true;
810 StackingPopupDispatcher
.getInstance().onPopupShown(this, myInStack
);
814 protected void afterShow() {
817 protected final void requestFocus() {
818 if (!myFocusable
) return;
820 if (myPreferredFocusedComponent
!= null) {
821 if (myProject
!= null) {
822 getFocusManager().requestFocus(myPreferredFocusedComponent
, true);
825 myPreferredFocusedComponent
.requestFocus();
830 private IdeFocusManager
getFocusManager() {
831 if (myProject
!= null) {
832 return IdeFocusManager
.getInstance(myProject
);
833 } else if (myOwner
!= null) {
834 return IdeFocusManager
.findInstanceByComponent(myOwner
);
836 return IdeFocusManager
.findInstance();
840 private static JComponent
getTargetComponent(Component aComponent
) {
841 if (aComponent
instanceof JComponent
) {
842 return (JComponent
)aComponent
;
844 else if (aComponent
instanceof RootPaneContainer
) {
845 return ((RootPaneContainer
)aComponent
).getRootPane();
848 LOG
.error("Cannot find target for:" + aComponent
);
852 private PopupComponent
.Factory
getFactory(boolean forceHeavyweight
) {
853 if (isPersistent()) {
854 return new PopupComponent
.Factory
.Dialog();
855 } else if (forceHeavyweight
|| !SystemInfo
.isWindows
) {
856 return new PopupComponent
.Factory
.AwtHeavyweight();
858 return new PopupComponent
.Factory
.AwtDefault();
862 public JComponent
getContent() {
866 public void setLocation(RelativePoint p
) {
867 setLocation(p
, myPopup
, myContent
);
870 private static void setLocation(final RelativePoint p
, final PopupComponent popup
, Component content
) {
871 if (popup
== null) return;
873 final Window wnd
= popup
.getWindow();
876 wnd
.setLocation(p
.getScreenPoint());
880 final Window window
= SwingUtilities
.getWindowAncestor(myContent
);
885 public JComponent
getComponent() {
889 public void setProject(Project project
) {
894 public void dispose() {
895 Disposer
.dispose(this, false);
897 assert ApplicationManager
.getApplication().isDispatchThread();
899 if (myPopup
!= null) {
900 cancel(myDisposeEvent
);
903 if (myContent
!= null) {
904 myContent
.removeAll();
908 myFocusTrackback
= null;
912 if (myMouseOutCanceller
!= null) {
913 final Toolkit toolkit
= Toolkit
.getDefaultToolkit();
914 // it may happen, but have no idea how
915 // http://www.jetbrains.net/jira/browse/IDEADEV-21265
916 if (toolkit
!= null) {
917 toolkit
.removeAWTEventListener(myMouseOutCanceller
);
920 myMouseOutCanceller
= null;
922 if (myFocusWatcher
!= null) {
923 myFocusWatcher
.dispose();
924 myFocusWatcher
= null;
929 if (myFinalRunnable
!= null) {
930 getFocusManager().doWhenFocusSettlesDown(myFinalRunnable
);
931 myFinalRunnable
= null;
935 private void resetWindow() {
936 if (myWindow
!= null && getWndManager() != null) {
937 getWndManager().resetWindow(myWindow
);
938 if (myWindowListener
!= null) {
939 myWindow
.removeWindowListener(myWindowListener
);
942 if (myWindow
instanceof JWindow
) {
943 ((JWindow
)myWindow
).getRootPane().putClientProperty(KEY
, null);
947 myWindowListener
= null;
951 public void storeDimensionSize(final Dimension size
) {
952 if (myDimensionServiceKey
!= null) {
953 DimensionService
.getInstance().setSize(myDimensionServiceKey
, size
, myProject
);
957 public void storeLocation(final Point xy
) {
958 if (myDimensionServiceKey
!= null) {
959 DimensionService
.getInstance().setLocation(myDimensionServiceKey
, xy
, myProject
);
963 public static class MyContentPanel
extends JPanel
{
964 private final boolean myResizable
;
965 private final boolean myDrawMacCorner
;
966 private final boolean myShadowed
;
968 public MyContentPanel(final boolean resizable
, final PopupBorder border
, boolean drawMacCorner
) {
969 this(resizable
, border
, drawMacCorner
, false);
972 public MyContentPanel(final boolean resizable
, final PopupBorder border
, boolean drawMacCorner
, boolean shadowed
) {
973 super(new BorderLayout());
974 myResizable
= resizable
;
975 myDrawMacCorner
= drawMacCorner
;
976 myShadowed
= shadowed
;
977 if (isShadowPossible()) {
979 setBorder(new EmptyBorder(POPUP_TOP_SIZE
, POPUP_SIDE_SIZE
, POPUP_BOTTOM_SIZE
, POPUP_SIDE_SIZE
) {
981 public void paintBorder(Component c
, Graphics g
, int x
, int y
, int width
, int height
) {
982 border
.paintBorder(c
, g
,
983 x
+ POPUP_SIDE_SIZE
- 1,
984 y
+ POPUP_TOP_SIZE
- 1,
985 width
- 2 * POPUP_SIDE_SIZE
+ 2,
986 height
- POPUP_TOP_SIZE
- POPUP_BOTTOM_SIZE
+ 2);
994 private boolean isShadowPossible() {
995 return myShadowed
&& !SystemInfo
.isMac
&& !UISettings
.isRemoteDesktopConnected();
998 public void paint(Graphics g
) {
999 if (isShadowPossible()) {
1005 if (myResizable
&& myDrawMacCorner
) {
1006 g
.drawImage(ourMacCorner
,
1007 getX() + getWidth() - ourMacCorner
.getWidth(this),
1008 getY() + getHeight() - ourMacCorner
.getHeight(this),
1013 private void paintShadow(final Graphics g
) {
1014 BufferedImage capture
= null;
1016 final Point onScreen
= getLocationOnScreen();
1017 capture
= new Robot().createScreenCapture(
1018 new Rectangle(onScreen
.x
, onScreen
.y
, getWidth() + 2 * POPUP_SIDE_SIZE
, getHeight() + POPUP_TOP_SIZE
+ POPUP_BOTTOM_SIZE
));
1019 final BufferedImage shadow
= ShadowBorderPainter
.createPopupShadow(this, getWidth(), getHeight());
1020 ((Graphics2D
)capture
.getGraphics()).drawImage(shadow
, null, null);
1022 catch (Exception e
) {
1025 if (capture
!= null) g
.drawImage(capture
, 0, 0, null);
1029 public boolean isCancelOnClickOutside() {
1030 return myCancelOnClickOutside
;
1033 private class Canceller
implements AWTEventListener
{
1035 private boolean myEverEntered
= false;
1037 public void eventDispatched(final AWTEvent event
) {
1038 if (event
.getID() == WindowEvent
.WINDOW_ACTIVATED
) {
1039 if (myCancelOnWindow
) {
1042 } else if (event
.getID() == MouseEvent
.MOUSE_ENTERED
) {
1043 if (withinPopup(event
)) {
1044 myEverEntered
= true;
1046 } else if (event
.getID() == MouseEvent
.MOUSE_MOVED
) {
1047 if (myCancelOnMouseOutCallback
!= null && myEverEntered
&& !withinPopup(event
)) {
1048 if (myCancelOnMouseOutCallback
.check((MouseEvent
)event
)) {
1055 private boolean withinPopup(final AWTEvent event
) {
1056 if (!myContent
.isShowing()) return false;
1058 final MouseEvent mouse
= (MouseEvent
)event
;
1059 final Point point
= mouse
.getPoint();
1060 SwingUtilities
.convertPointToScreen(point
, mouse
.getComponent());
1061 return new Rectangle(myContent
.getLocationOnScreen(), myContent
.getSize()).contains(point
);
1065 public void setLocation(@NotNull final Point screenPoint
) {
1066 if (myPopup
== null) {
1067 myForcedLocation
= screenPoint
;
1069 moveTo(myContent
, screenPoint
, myLocateByContent ? myHeaderPanel
.getPreferredSize() : null);
1073 public static Window
moveTo(JComponent content
, Point screenPoint
, final Dimension headerCorrectionSize
) {
1074 setDefaultCursor(content
);
1075 final Window wnd
= SwingUtilities
.getWindowAncestor(content
);
1076 if (headerCorrectionSize
!= null) {
1077 screenPoint
.y
-= headerCorrectionSize
.height
;
1079 wnd
.setLocation(screenPoint
);
1083 public void setSize(@NotNull final Dimension size
) {
1084 if (myPopup
== null) {
1085 myForcedSize
= size
;
1087 updateMaskAndAlpha(setSize(myContent
, size
));
1091 public static Window
setSize(JComponent content
, final Dimension size
) {
1092 final Window popupWindow
= SwingUtilities
.windowForComponent(content
);
1093 final Point location
= popupWindow
.getLocation();
1094 popupWindow
.setBounds(location
.x
, location
.y
, size
.width
, size
.height
);
1098 public static void setDefaultCursor(JComponent content
) {
1099 final Window wnd
= SwingUtilities
.getWindowAncestor(content
);
1101 wnd
.setCursor(Cursor
.getDefaultCursor());
1105 public void setCaption(String title
) {
1106 if (myCaption
instanceof TitlePanel
) {
1107 ((TitlePanel
)myCaption
).setText(title
);
1111 private class MyWindowListener
extends WindowAdapter
{
1112 public void windowClosed(final WindowEvent e
) {
1117 public boolean isPersistent() {
1118 return !myCancelOnClickOutside
&& !myCancelOnWindow
;
1121 public boolean isNativePopup() {
1122 return myNativePopup
;
1125 public void setUiVisible(final boolean visible
) {
1126 if (myPopup
!= null) {
1129 final Window window
= getPopupWindow();
1130 if (window
!= null && myRestoreWindowSize
!= null) {
1131 window
.setSize(myRestoreWindowSize
);
1132 myRestoreWindowSize
= null;
1136 final Window window
= getPopupWindow();
1137 if (window
!= null) {
1138 myRestoreWindowSize
= window
.getSize();
1139 window
.setVisible(true);
1145 private Window
getPopupWindow() {
1146 return myPopup
.getWindow();
1149 public void setUserData(ArrayList
<Object
> userData
) {
1150 myUserData
= userData
;
1153 public <T
> T
getUserData(final Class
<T
> userDataClass
) {
1154 if (myUserData
!= null) {
1155 for(Object o
: myUserData
) {
1156 if (userDataClass
.isInstance(o
)) {
1157 //noinspection unchecked
1165 public boolean isModalContext() {
1166 return myModalContext
;
1169 public boolean isFocused() {
1170 if (myComponent
!= null && isFocused(new Component
[] {SwingUtilities
.getWindowAncestor(myComponent
)}))
1172 return isFocused(myFocusOwners
);
1175 public static boolean isFocused(@Nullable Component
[] components
) {
1176 if (components
== null) return false;
1178 Component owner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
1180 if (owner
== null) return false;
1181 for (Component each
: components
) {
1182 if (each
!= null && SwingUtilities
.isDescendingFrom(owner
, each
)) return true;
1188 public boolean isCancelKeyEnabled() {
1189 return myCancelKeyEnabled
;
1193 CaptionPanel
getTitle() {
1197 private void setHeaderComponent(JComponent c
) {
1198 boolean doRevalidate
= false;
1199 if (myHeaderComponent
!= null) {
1200 myHeaderPanel
.remove(myHeaderComponent
);
1201 myHeaderPanel
.add(myCaption
, BorderLayout
.NORTH
);
1202 myHeaderComponent
= null;
1203 doRevalidate
= true;
1207 myHeaderPanel
.remove(myCaption
);
1208 myHeaderPanel
.add(c
, BorderLayout
.NORTH
);
1209 myHeaderComponent
= c
;
1211 final Dimension size
= myContent
.getSize();
1212 if (size
.height
< c
.getPreferredSize().height
* 2) {
1213 size
.height
+= c
.getPreferredSize().height
;
1217 doRevalidate
= true;
1220 if (doRevalidate
) myContent
.revalidate();
1223 public void addListener(final JBPopupListener listener
) {
1224 myListeners
.add(listener
);
1227 public void removeListener(final JBPopupListener listener
) {
1228 myListeners
.remove(listener
);
1231 protected void onSpeedSearchPatternChanged() {
1234 public Component
getOwner() {
1235 return myRequestorComponent
;
1238 public void setMinimumSize(Dimension size
) {
1242 public Runnable
getFinalRunnable() {
1243 return myFinalRunnable
;
1246 public void setFinalRunnable(Runnable finalRunnable
) {
1247 myFinalRunnable
= finalRunnable
;