blob: a967245c9224c8c512670c91aec2538fda9240ac [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ui.popup;
import com.intellij.codeInsight.hint.HintUtil;
import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.UiActivity;
import com.intellij.ide.UiActivityMonitor;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.*;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.wm.*;
import com.intellij.openapi.wm.ex.WindowManagerEx;
import com.intellij.openapi.wm.impl.IdeFrameImpl;
import com.intellij.openapi.wm.impl.IdeGlassPaneImpl;
import com.intellij.ui.*;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.speedSearch.SpeedSearch;
import com.intellij.util.Alarm;
import com.intellij.util.BooleanFunction;
import com.intellij.util.Processor;
import com.intellij.util.ui.ChildFocusWatcher;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class AbstractPopup implements JBPopup {
public static final String SHOW_HINTS = "ShowHints";
private static final Logger LOG = Logger.getInstance("#com.intellij.ui.popup.AbstractPopup");
private static final Object SUPPRESS_MAC_CORNER = new Object();
// X server sometimes focuses focusable popups upon appearance, ignoring the fact that we didn't ask to focus them (IDEA-94683)
private static final boolean X_WINDOW_FOCUS_BUG = SystemInfo.isXWindow;
private PopupComponent myPopup;
private MyContentPanel myContent;
private JComponent myPreferredFocusedComponent;
private boolean myRequestFocus;
private boolean myFocusable;
private boolean myForcedHeavyweight;
private boolean myLocateWithinScreen;
private boolean myResizable = false;
private JPanel myHeaderPanel;
private CaptionPanel myCaption = null;
private JComponent myComponent;
private String myDimensionServiceKey = null;
private Computable<Boolean> myCallBack = null;
private Project myProject;
private boolean myCancelOnClickOutside;
private Set<JBPopupListener> myListeners;
private boolean myUseDimServiceForXYLocation;
private MouseChecker myCancelOnMouseOutCallback;
private Canceller myMouseOutCanceller;
private boolean myCancelOnWindow;
private boolean myCancelOnWindowDeactivation = true;
private Dimension myForcedSize;
private Point myForcedLocation;
private boolean myCancelKeyEnabled;
private boolean myLocateByContent;
protected FocusTrackback myFocusTrackback;
private Dimension myMinSize;
private List<Object> myUserData;
private boolean myShadowed;
private float myAlpha = 0;
private float myLastAlpha = 0;
private MaskProvider myMaskProvider;
private Window myWindow;
private boolean myInStack;
private MyWindowListener myWindowListener;
private boolean myModalContext;
private Component[] myFocusOwners;
private PopupBorder myPopupBorder;
private Dimension myRestoreWindowSize;
protected Component myOwner;
protected Component myRequestorComponent;
private boolean myHeaderAlwaysFocusable;
private boolean myMovable;
private JComponent myHeaderComponent;
protected InputEvent myDisposeEvent;
private Runnable myFinalRunnable;
@Nullable private BooleanFunction<KeyEvent> myKeyEventHandler;
protected boolean myOk;
protected final SpeedSearch mySpeedSearch = new SpeedSearch() {
boolean searchFieldShown = false;
@Override
public void update() {
mySpeedSearchPatternField.setBackground(new JTextField().getBackground());
onSpeedSearchPatternChanged();
mySpeedSearchPatternField.setText(getFilter());
if (isHoldingFilter() && !searchFieldShown) {
setHeaderComponent(mySpeedSearchPatternField);
searchFieldShown = true;
}
else if (!isHoldingFilter() && searchFieldShown) {
setHeaderComponent(null);
searchFieldShown = false;
}
}
@Override
public void noHits() {
mySpeedSearchPatternField.setBackground(LightColors.RED);
}
};
private JTextField mySpeedSearchPatternField;
private boolean myNativePopup;
private boolean myMayBeParent;
private AbstractPopup.SpeedSearchKeyListener mySearchKeyListener;
private JLabel myAdComponent;
private boolean myDisposed;
private UiActivity myActivityKey;
private Disposable myProjectDisposable;
private volatile State myState = State.NEW;
private enum State {NEW, INIT, SHOWING, SHOWN, CANCEL, DISPOSE}
private void debugState(String message, State... states) {
if (LOG.isDebugEnabled()) {
LOG.debug(hashCode() + " - " + message);
if (!ApplicationManager.getApplication().isDispatchThread()) {
LOG.debug("unexpected thread");
}
for (State state : states) {
if (state == myState) {
return;
}
}
LOG.debug(new IllegalStateException("myState=" + myState));
}
}
AbstractPopup() { }
AbstractPopup init(Project project,
@NotNull JComponent component,
@Nullable JComponent preferredFocusedComponent,
boolean requestFocus,
boolean focusable,
boolean movable,
String dimensionServiceKey,
boolean resizable,
@Nullable String caption,
@Nullable Computable<Boolean> callback,
boolean cancelOnClickOutside,
@Nullable Set<JBPopupListener> listeners,
boolean useDimServiceForXYLocation,
ActiveComponent commandButton,
@Nullable IconButton cancelButton,
@Nullable MouseChecker cancelOnMouseOutCallback,
boolean cancelOnWindow,
@Nullable ActiveIcon titleIcon,
boolean cancelKeyEnabled,
boolean locateByContent,
boolean placeWithinScreenBounds,
@Nullable Dimension minSize,
float alpha,
@Nullable MaskProvider maskProvider,
boolean inStack,
boolean modalContext,
@Nullable Component[] focusOwners,
@Nullable String adText,
int adTextAlignment,
boolean headerAlwaysFocusable,
@NotNull List<Pair<ActionListener, KeyStroke>> keyboardActions,
Component settingsButtons,
@Nullable final Processor<JBPopup> pinCallback,
boolean mayBeParent,
boolean showShadow,
boolean showBorder,
boolean cancelOnWindowDeactivation,
@Nullable BooleanFunction<KeyEvent> keyEventHandler)
{
if (requestFocus && !focusable) {
assert false : "Incorrect argument combination: requestFocus=true focusable=false";
}
myActivityKey = new UiActivity.Focus("Popup:" + this);
myProject = project;
myComponent = component;
myPopupBorder = showBorder ? PopupBorder.Factory.create(true, showShadow) : PopupBorder.Factory.createEmpty();
myShadowed = showShadow;
myContent = createContentPanel(resizable, myPopupBorder, isToDrawMacCorner() && resizable);
myMayBeParent = mayBeParent;
myCancelOnWindowDeactivation = cancelOnWindowDeactivation;
myContent.add(component, BorderLayout.CENTER);
if (adText != null) {
setAdText(adText, adTextAlignment);
}
myCancelKeyEnabled = cancelKeyEnabled;
myLocateByContent = locateByContent;
myLocateWithinScreen = placeWithinScreenBounds;
myAlpha = alpha;
myMaskProvider = maskProvider;
myInStack = inStack;
myModalContext = modalContext;
myFocusOwners = focusOwners;
myHeaderAlwaysFocusable = headerAlwaysFocusable;
myMovable = movable;
ActiveIcon actualIcon = titleIcon == null ? new ActiveIcon(EmptyIcon.ICON_0) : titleIcon;
myHeaderPanel = new JPanel(new BorderLayout());
if (caption != null) {
if (!caption.isEmpty()) {
myCaption = new TitlePanel(actualIcon.getRegular(), actualIcon.getInactive());
((TitlePanel)myCaption).setText(caption);
}
else {
myCaption = new CaptionPanel();
}
if (pinCallback != null) {
myCaption.setButtonComponent(new InplaceButton(
new IconButton("Pin", AllIcons.General.AutohideOff, AllIcons.General.AutohideOff, AllIcons.General.AutohideOffInactive),
new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
pinCallback.process(AbstractPopup.this);
}
}
));
}
else if (cancelButton != null) {
myCaption.setButtonComponent(new InplaceButton(cancelButton, new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
cancel();
}
}));
}
else if (commandButton != null) {
myCaption.setButtonComponent(commandButton);
}
}
else {
myCaption = new CaptionPanel();
myCaption.setBorder(null);
myCaption.setPreferredSize(new Dimension(0, 0));
}
setWindowActive(myHeaderAlwaysFocusable);
myHeaderPanel.add(myCaption, BorderLayout.NORTH);
myContent.add(myHeaderPanel, BorderLayout.NORTH);
myForcedHeavyweight = true;
myResizable = resizable;
myPreferredFocusedComponent = preferredFocusedComponent;
myRequestFocus = requestFocus;
myFocusable = focusable;
myDimensionServiceKey = dimensionServiceKey;
myCallBack = callback;
myCancelOnClickOutside = cancelOnClickOutside;
myCancelOnMouseOutCallback = cancelOnMouseOutCallback;
myListeners = listeners == null ? new HashSet<JBPopupListener>() : listeners;
myUseDimServiceForXYLocation = useDimServiceForXYLocation;
myCancelOnWindow = cancelOnWindow;
myMinSize = minSize;
for (Pair<ActionListener, KeyStroke> pair : keyboardActions) {
myContent.registerKeyboardAction(pair.getFirst(), pair.getSecond(), JComponent.WHEN_IN_FOCUSED_WINDOW);
}
if (settingsButtons != null) {
myCaption.addSettingsComponent(settingsButtons);
}
myKeyEventHandler = keyEventHandler;
debugState("popup initialized", State.NEW);
myState = State.INIT;
return this;
}
private void setWindowActive(boolean active) {
boolean value = myHeaderAlwaysFocusable || active;
if (myCaption != null) {
myCaption.setActive(value);
}
myPopupBorder.setActive(value);
myContent.repaint();
}
@NotNull
protected MyContentPanel createContentPanel(final boolean resizable, PopupBorder border, boolean isToDrawMacCorner) {
return new MyContentPanel(resizable, border, isToDrawMacCorner);
}
public boolean isToDrawMacCorner() {
if (!SystemInfo.isMac || myComponent.getComponentCount() <= 0) {
return false;
}
if (myComponent.getComponentCount() > 0) {
Component component = myComponent.getComponent(0);
if (component instanceof JComponent && Boolean.TRUE.equals(((JComponent)component).getClientProperty(SUPPRESS_MAC_CORNER))) {
return false;
}
}
return true;
}
public void setShowHints(boolean show) {
final Window ancestor = getContentWindow(myComponent);
if (ancestor instanceof RootPaneContainer) {
final JRootPane rootPane = ((RootPaneContainer)ancestor).getRootPane();
if (rootPane != null) {
rootPane.putClientProperty(SHOW_HINTS, Boolean.valueOf(show));
}
}
}
public static void suppressMacCornerFor(JComponent popupComponent) {
popupComponent.putClientProperty(SUPPRESS_MAC_CORNER, Boolean.TRUE);
}
public String getDimensionServiceKey() {
return myDimensionServiceKey;
}
public void setDimensionServiceKey(@Nullable final String dimensionServiceKey) {
myDimensionServiceKey = dimensionServiceKey;
}
@Override
public void showInCenterOf(@NotNull Component aContainer) {
final Point popupPoint = getCenterOf(aContainer, myContent);
show(aContainer, popupPoint.x, popupPoint.y, false);
}
public void setAdText(@NotNull final String s) {
setAdText(s, SwingConstants.LEFT);
}
@Override
public void setAdText(@NotNull final String s, int alignment) {
if (myAdComponent == null) {
myAdComponent = HintUtil.createAdComponent(s, BorderFactory.createEmptyBorder(1, 5, 1, 5), alignment);
JPanel wrapper = new JPanel(new BorderLayout()) {
@Override
protected void paintComponent(Graphics g) {
g.setColor(Gray._135);
g.drawLine(0, 0, getWidth(), 0);
super.paintComponent(g);
}
};
wrapper.setOpaque(false);
wrapper.setBorder(new EmptyBorder(1, 0, 0, 0));
wrapper.add(myAdComponent, BorderLayout.CENTER);
myContent.add(wrapper, BorderLayout.SOUTH);
pack(false, true);
} else {
myAdComponent.setText(s);
myAdComponent.setHorizontalAlignment(alignment);
}
}
public static Point getCenterOf(final Component aContainer, final JComponent content) {
final JComponent component = getTargetComponent(aContainer);
Point containerScreenPoint = component.getVisibleRect().getLocation();
SwingUtilities.convertPointToScreen(containerScreenPoint, aContainer);
return UIUtil.getCenterPoint(new Rectangle(containerScreenPoint, component.getVisibleRect().getSize()), content.getPreferredSize());
}
@Override
public void showCenteredInCurrentWindow(@NotNull Project project) {
Window window = null;
Component focusedComponent = getWndManager().getFocusedComponent(project);
if (focusedComponent != null) {
Component parent = UIUtil.findUltimateParent(focusedComponent);
if (parent instanceof Window) {
window = (Window)parent;
}
}
if (window == null) {
window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
}
if (window != null) {
showInCenterOf(window);
}
}
@Override
public void showUnderneathOf(@NotNull Component aComponent) {
show(new RelativePoint(aComponent, new Point(0, aComponent.getHeight())));
}
@Override
public void show(@NotNull RelativePoint aPoint) {
final Point screenPoint = aPoint.getScreenPoint();
show(aPoint.getComponent(), screenPoint.x, screenPoint.y, false);
}
@Override
public void showInScreenCoordinates(@NotNull Component owner, @NotNull Point point) {
show(owner, point.x, point.y, false);
}
@Override
public void showInBestPositionFor(@NotNull DataContext dataContext) {
final Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
if (editor != null) {
showInBestPositionFor(editor);
}
else {
show(relativePointByQuickSearch(dataContext));
}
}
@Override
public void showInFocusCenter() {
final Component focused = getWndManager().getFocusedComponent(myProject);
if (focused != null) {
showInCenterOf(focused);
}
else {
final WindowManager manager = WindowManager.getInstance();
final JFrame frame = myProject != null ? manager.getFrame(myProject) : manager.findVisibleFrame();
showInCenterOf(frame.getRootPane());
}
}
private RelativePoint relativePointByQuickSearch(final DataContext dataContext) {
Rectangle dominantArea = PlatformDataKeys.DOMINANT_HINT_AREA_RECTANGLE.getData(dataContext);
if (dominantArea != null) {
final Component focusedComponent = getWndManager().getFocusedComponent(myProject);
if (focusedComponent != null) {
Window window = SwingUtilities.windowForComponent(focusedComponent);
JLayeredPane layeredPane;
if (window instanceof JFrame) {
layeredPane = ((JFrame)window).getLayeredPane();
}
else if (window instanceof JDialog) {
layeredPane = ((JDialog)window).getLayeredPane();
}
else if (window instanceof JWindow) {
layeredPane = ((JWindow)window).getLayeredPane();
}
else {
throw new IllegalStateException("cannot find parent window: project=" + myProject + "; window=" + window);
}
return relativePointWithDominantRectangle(layeredPane, dominantArea);
}
}
return JBPopupFactory.getInstance().guessBestPopupLocation(dataContext);
}
@Override
public void showInBestPositionFor(@NotNull Editor editor) {
assert editor.getComponent().isShowing() : "Editor must be showing on the screen";
DataContext context = ((EditorEx)editor).getDataContext();
Rectangle dominantArea = PlatformDataKeys.DOMINANT_HINT_AREA_RECTANGLE.getData(context);
if (dominantArea != null && !myRequestFocus) {
final JLayeredPane layeredPane = editor.getContentComponent().getRootPane().getLayeredPane();
show(relativePointWithDominantRectangle(layeredPane, dominantArea));
}
else {
show(guessBestPopupLocation(editor));
}
}
@NotNull
private RelativePoint guessBestPopupLocation(@NotNull Editor editor) {
RelativePoint preferredLocation = JBPopupFactory.getInstance().guessBestPopupLocation(editor);
if (myDimensionServiceKey == null) {
return preferredLocation;
}
Dimension preferredSize = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
if (preferredSize == null) {
return preferredLocation;
}
Rectangle preferredBounds = new Rectangle(preferredLocation.getScreenPoint(), preferredSize);
Rectangle adjustedBounds = new Rectangle(preferredBounds);
ScreenUtil.moveRectangleToFitTheScreen(adjustedBounds);
if (preferredBounds.y - adjustedBounds.y <= 0) {
return preferredLocation;
}
int adjustedY = preferredBounds.y - editor.getLineHeight() * 3 / 2 - preferredSize.height;
return adjustedY >= 0 ? RelativePoint.fromScreen(new Point(preferredBounds.x, adjustedY)) : preferredLocation;
}
public void addPopupListener(JBPopupListener listener) {
myListeners.add(listener);
}
private RelativePoint relativePointWithDominantRectangle(final JLayeredPane layeredPane, final Rectangle bounds) {
Dimension preferredSize = getComponent().getPreferredSize();
if (myDimensionServiceKey != null) {
final Dimension dimension = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
if (dimension != null) {
preferredSize = dimension;
}
}
final Point leftTopCorner = new Point(bounds.x + bounds.width, bounds.y);
final Point leftTopCornerScreen = (Point)leftTopCorner.clone();
SwingUtilities.convertPointToScreen(leftTopCornerScreen, layeredPane);
final RelativePoint relativePoint;
if (!ScreenUtil.isOutsideOnTheRightOFScreen(
new Rectangle(leftTopCornerScreen.x, leftTopCornerScreen.y, preferredSize.width, preferredSize.height))) {
relativePoint = new RelativePoint(layeredPane, leftTopCorner);
}
else {
if (bounds.x > preferredSize.width) {
relativePoint = new RelativePoint(layeredPane, new Point(bounds.x - preferredSize.width, bounds.y));
}
else {
setDimensionServiceKey(null); // going to cut width
Rectangle screen = ScreenUtil.getScreenRectangle(leftTopCornerScreen.x, leftTopCornerScreen.y);
final int spaceOnTheLeft = bounds.x;
final int spaceOnTheRight = screen.x + screen.width - leftTopCornerScreen.x;
if (spaceOnTheLeft > spaceOnTheRight) {
relativePoint = new RelativePoint(layeredPane, new Point(0, bounds.y));
myComponent.setPreferredSize(new Dimension(spaceOnTheLeft, Math.max(preferredSize.height, 200)));
}
else {
relativePoint = new RelativePoint(layeredPane, leftTopCorner);
myComponent.setPreferredSize(new Dimension(spaceOnTheRight, Math.max(preferredSize.height, 200)));
}
}
}
return relativePoint;
}
@Override
public final void closeOk(@Nullable InputEvent e) {
setOk(true);
cancel(e);
}
@Override
public final void cancel() {
cancel(null);
}
@Override
public void setRequestFocus(boolean requestFocus) {
myRequestFocus = requestFocus;
}
@Override
public void cancel(InputEvent e) {
if (myState == State.CANCEL || myState == State.DISPOSE) {
return;
}
debugState("cancel popup", State.SHOWN);
myState = State.CANCEL;
if (isDisposed()) return;
if (myPopup != null) {
if (!canClose()) {
debugState("cannot cancel popup", State.CANCEL);
myState = State.SHOWN;
return;
}
storeDimensionSize(myContent.getSize());
if (myUseDimServiceForXYLocation) {
final JRootPane root = myComponent.getRootPane();
if (root != null) {
final Container popupWindow = root.getParent();
if (popupWindow != null && popupWindow.isShowing()) {
storeLocation(popupWindow.getLocationOnScreen());
}
}
}
if (e instanceof MouseEvent) {
IdeEventQueue.getInstance().blockNextEvents((MouseEvent)e);
}
myPopup.hide(false);
if (ApplicationManagerEx.getApplicationEx() != null) {
StackingPopupDispatcher.getInstance().onPopupHidden(this);
}
if (myInStack) {
if (myFocusTrackback != null) {
myFocusTrackback.setForcedRestore(!myOk && myFocusable);
myFocusTrackback.restoreFocus();
}
else if (LOG.isDebugEnabled()) {
LOG.debug("cancel before show @ " + Thread.currentThread());
}
}
disposePopup();
if (myListeners != null) {
for (JBPopupListener each : myListeners) {
each.onClosed(new LightweightWindowEvent(this, myOk));
}
}
}
Disposer.dispose(this, false);
if (myProjectDisposable != null) {
Disposer.dispose(myProjectDisposable);
}
}
public FocusTrackback getFocusTrackback() {
return myFocusTrackback;
}
private void disposePopup() {
if (myPopup != null) {
myPopup.hide(true);
}
myPopup = null;
}
@Override
public boolean canClose() {
return myCallBack == null || myCallBack.compute().booleanValue();
}
@Override
public boolean isVisible() {
return myPopup != null;
}
@Override
public void show(final Component owner) {
show(owner, -1, -1, true);
}
public void show(Component owner, int aScreenX, int aScreenY, final boolean considerForcedXY) {
if (ApplicationManagerEx.getApplicationEx() != null && ApplicationManager.getApplication().isHeadlessEnvironment()) return;
if (isDisposed()) {
throw new IllegalStateException("Popup was already disposed. Recreate a new instance to show again");
}
assert ApplicationManager.getApplication().isDispatchThread();
debugState("show popup", State.INIT);
myState = State.SHOWING;
installWindowHook(this);
installProjectDisposer();
addActivity();
final Component prevOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
final boolean shouldShow = beforeShow();
if (!shouldShow) {
removeActivity();
debugState("rejected to show popup", State.SHOWING);
myState = State.INIT;
return;
}
prepareToShow();
if (myInStack) {
myFocusTrackback = new FocusTrackback(this, owner, true);
myFocusTrackback.setMustBeShown(true);
}
Dimension sizeToSet = null;
if (myDimensionServiceKey != null) {
sizeToSet = DimensionService.getInstance().getSize(myDimensionServiceKey, myProject);
}
if (myForcedSize != null) {
sizeToSet = myForcedSize;
}
if (sizeToSet != null) {
sizeToSet.width = Math.max(sizeToSet.width, myContent.getMinimumSize().width);
sizeToSet.height = Math.max(sizeToSet.height, myContent.getMinimumSize().height);
myContent.setSize(sizeToSet);
myContent.setPreferredSize(sizeToSet);
}
Point xy = new Point(aScreenX, aScreenY);
boolean adjustXY = true;
if (myDimensionServiceKey != null) {
final Point storedLocation = DimensionService.getInstance().getLocation(myDimensionServiceKey, myProject);
if (storedLocation != null) {
xy = storedLocation;
adjustXY = false;
}
}
if (adjustXY) {
final Insets insets = myContent.getInsets();
if (insets != null) {
xy.x -= insets.left;
xy.y -= insets.top;
}
}
if (considerForcedXY && myForcedLocation != null) {
xy = myForcedLocation;
}
if (myLocateByContent) {
final Dimension captionSize = myHeaderPanel.getPreferredSize();
xy.y -= captionSize.height;
}
Rectangle targetBounds = new Rectangle(xy, myContent.getPreferredSize());
Insets insets = myPopupBorder.getBorderInsets(myContent);
if (insets != null) {
targetBounds.x += insets.left;
targetBounds.y += insets.top;
}
Rectangle original = new Rectangle(targetBounds);
if (myLocateWithinScreen) {
if (myMovable) {
ScreenUtil.moveRectangleToFitTheScreen(targetBounds);
}
}
if (myMouseOutCanceller != null) {
myMouseOutCanceller.myEverEntered = targetBounds.equals(original);
}
myOwner = IdeFrameImpl.findNearestModalComponent(owner);
if (myOwner == null) {
myOwner = owner;
}
myRequestorComponent = owner;
boolean forcedDialog = myMayBeParent
|| SystemInfo.isMac && !(myOwner instanceof IdeFrame) && myOwner != null && myOwner.isShowing();
PopupComponent.Factory factory = getFactory(myForcedHeavyweight || myResizable, forcedDialog);
myNativePopup = factory.isNativePopup();
Component popupOwner = myOwner;
if (popupOwner instanceof RootPaneContainer && !(popupOwner instanceof IdeFrame && !Registry.is("popup.fix.ide.frame.owner"))) {
// JDK uses cached heavyweight popup for a window ancestor
RootPaneContainer root = (RootPaneContainer)popupOwner;
popupOwner = root.getRootPane();
LOG.debug("popup owner fixed for JDK cache");
}
if (LOG.isDebugEnabled()) {
LOG.debug("expected preferred size: " + myContent.getPreferredSize());
}
myPopup = factory.getPopup(popupOwner, myContent, targetBounds.x, targetBounds.y, this);
if (LOG.isDebugEnabled()) {
LOG.debug(" actual preferred size: " + myContent.getPreferredSize());
}
if ((targetBounds.width != myContent.getWidth()) || (targetBounds.height != myContent.getHeight())) {
// JDK uses cached heavyweight popup that is not initialized properly
LOG.debug("the expected size is not equal to the actual size");
Window popup = myPopup.getWindow();
if (popup != null) {
popup.setSize(targetBounds.width, targetBounds.height);
if (myContent.getParent().getComponentCount() != 1) {
LOG.debug("unexpected count of components in heavy-weight popup");
}
}
else {
LOG.debug("cannot fix size for non-heavy-weight popup");
}
}
if (myResizable) {
final JRootPane root = myContent.getRootPane();
final IdeGlassPaneImpl glass = new IdeGlassPaneImpl(root);
root.setGlassPane(glass);
final ResizeComponentListener resizeListener = new ResizeComponentListener(this, glass);
glass.addMousePreprocessor(resizeListener, this);
glass.addMouseMotionPreprocessor(resizeListener, this);
}
if (myCaption != null && myMovable) {
final MoveComponentListener moveListener = new MoveComponentListener(myCaption) {
@Override
public void mousePressed(final MouseEvent e) {
super.mousePressed(e);
if (e.isConsumed()) return;
if (UIUtil.isCloseClick(e)) {
if (myCaption.isWithinPanel(e)) {
cancel();
}
}
}
};
ListenerUtil.addMouseListener(myCaption, moveListener);
ListenerUtil.addMouseMotionListener(myCaption, moveListener);
final MyContentPanel saved = myContent;
Disposer.register(this, new Disposable() {
@Override
public void dispose() {
ListenerUtil.removeMouseListener(saved, moveListener);
ListenerUtil.removeMouseMotionListener(saved, moveListener);
}
});
}
for (JBPopupListener listener : myListeners) {
listener.beforeShown(new LightweightWindowEvent(this));
}
myPopup.setRequestFocus(myRequestFocus);
myPopup.show();
final Window window = getContentWindow(myContent);
myWindow = window;
myWindowListener = new MyWindowListener();
window.addWindowListener(myWindowListener);
if (myFocusable) {
window.setFocusableWindowState(true);
window.setFocusable(true);
}
if (myWindow != null) {
// dialogwrapper-based popups do this internally through peer,
// for other popups like jdialog-based we should exclude them manually, but
// we still have to be able to use IdeFrame as parent
if (!myMayBeParent && !(myWindow instanceof Frame)) {
WindowManager.getInstance().doNotSuggestAsParent(myWindow);
}
}
setMinimumSize(myMinSize);
final Runnable afterShow = new Runnable() {
@Override
public void run() {
if (myPreferredFocusedComponent != null && myInStack && myFocusable) {
myFocusTrackback.registerFocusComponent(myPreferredFocusedComponent);
}
removeActivity();
afterShow();
}
};
if (myRequestFocus) {
getFocusManager().requestFocus(new FocusCommand() {
@NotNull
@Override
public ActionCallback run() {
if (isDisposed()) {
removeActivity();
return new ActionCallback.Done();
}
_requestFocus();
final ActionCallback result = new ActionCallback();
final Runnable afterShowRunnable = new Runnable() {
@Override
public void run() {
afterShow.run();
result.setDone();
}
};
if (myNativePopup) {
final FocusRequestor furtherRequestor = getFocusManager().getFurtherRequestor();
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isDisposed()) {
result.setRejected();
return;
}
furtherRequestor.requestFocus(new FocusCommand() {
@NotNull
@Override
public ActionCallback run() {
if (isDisposed()) {
return new ActionCallback.Rejected();
}
_requestFocus();
afterShowRunnable.run();
return new ActionCallback.Done();
}
}, true).notify(result).doWhenProcessed(new Runnable() {
@Override
public void run() {
removeActivity();
}
});
}
});
} else {
afterShowRunnable.run();
}
return result;
}
}, true).doWhenRejected(new Runnable() {
@Override
public void run() {
afterShow.run();
}
});
} else {
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isDisposed()) {
removeActivity();
return;
}
if (X_WINDOW_FOCUS_BUG && !myRequestFocus && prevOwner != null &&
Registry.is("actionSystem.xWindow.remove.focus.from.nonFocusable.popups")) {
new Alarm().addRequest(new Runnable() {
@Override
public void run() {
if (isFocused()) {
IdeFocusManager.getInstance(myProject).requestFocus(prevOwner, false);
}
}
}, Registry.intValue("actionSystem.xWindow.remove.focus.from.nonFocusable.popups.delay"));
}
afterShow.run();
}
});
}
debugState("popup shown", State.SHOWING);
myState = State.SHOWN;
}
public void focusPreferredComponent() {
_requestFocus();
}
private void installProjectDisposer() {
final Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (c != null) {
final DataContext context = DataManager.getInstance().getDataContext(c);
final Project project = CommonDataKeys.PROJECT.getData(context);
if (project != null) {
myProjectDisposable = new Disposable() {
@Override
public void dispose() {
if (!AbstractPopup.this.isDisposed()) {
Disposer.dispose(AbstractPopup.this);
}
}
};
Disposer.register(project, myProjectDisposable);
}
}
}
//Sometimes just after popup was shown the WINDOW_ACTIVATED cancels it
private static void installWindowHook(final AbstractPopup popup) {
if (popup.myCancelOnWindow) {
popup.myCancelOnWindow = false;
new Alarm(popup).addRequest(new Runnable() {
@Override
public void run() {
popup.myCancelOnWindow = true;
}
}, 100);
}
}
private void addActivity() {
UiActivityMonitor.getInstance().addActivity(myActivityKey);
}
private void removeActivity() {
UiActivityMonitor.getInstance().removeActivity(myActivityKey);
}
private void prepareToShow() {
final MouseAdapter mouseAdapter = new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
Point point = (Point)e.getPoint().clone();
SwingUtilities.convertPointToScreen(point, e.getComponent());
final Dimension dimension = myContent.getSize();
dimension.height += myResizable && isToDrawMacCorner() ? AllIcons.General.MacCorner.getIconHeight() : 4;
dimension.width += 4;
Point locationOnScreen = myContent.getLocationOnScreen();
final Rectangle bounds = new Rectangle(new Point(locationOnScreen.x - 2, locationOnScreen.y - 2), dimension);
if (!bounds.contains(point)) {
cancel();
}
}
};
myContent.addMouseListener(mouseAdapter);
Disposer.register(this, new Disposable() {
@Override
public void dispose() {
myContent.removeMouseListener(mouseAdapter);
}
});
myContent.registerKeyboardAction(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (myCancelKeyEnabled) {
cancel();
}
}
}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
mySearchKeyListener = new SpeedSearchKeyListener();
myContent.addKeyListener(mySearchKeyListener);
if (myCancelOnMouseOutCallback != null || myCancelOnWindow) {
myMouseOutCanceller = new Canceller();
Toolkit.getDefaultToolkit().addAWTEventListener(myMouseOutCanceller, AWTEvent.MOUSE_EVENT_MASK | WindowEvent.WINDOW_ACTIVATED |
AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
ChildFocusWatcher focusWatcher = new ChildFocusWatcher(myContent) {
@Override
protected void onFocusGained(final FocusEvent event) {
setWindowActive(true);
}
@Override
protected void onFocusLost(final FocusEvent event) {
setWindowActive(false);
}
};
Disposer.register(this, focusWatcher);
mySpeedSearchPatternField = new JTextField();
if (SystemInfo.isMac) {
Font f = mySpeedSearchPatternField.getFont();
mySpeedSearchPatternField.setFont(f.deriveFont(f.getStyle(), f.getSize() - 2));
}
}
private Window updateMaskAndAlpha(Window window) {
if (window == null) return null;
final WindowManagerEx wndManager = getWndManager();
if (wndManager == null) return window;
if (!wndManager.isAlphaModeEnabled(window)) return window;
if (myAlpha != myLastAlpha) {
wndManager.setAlphaModeRatio(window, myAlpha);
myLastAlpha = myAlpha;
}
if (myMaskProvider != null) {
final Dimension size = window.getSize();
Shape mask = myMaskProvider.getMask(size);
wndManager.setWindowMask(window, mask);
}
WindowManagerEx.WindowShadowMode mode =
myShadowed ? WindowManagerEx.WindowShadowMode.NORMAL : WindowManagerEx.WindowShadowMode.DISABLED;
WindowManagerEx.getInstanceEx().setWindowShadow(window, mode);
return window;
}
private static WindowManagerEx getWndManager() {
return ApplicationManagerEx.getApplicationEx() != null ? WindowManagerEx.getInstanceEx() : null;
}
@Override
public boolean isDisposed() {
return myContent == null;
}
protected boolean beforeShow() {
if (ApplicationManagerEx.getApplicationEx() == null) return true;
StackingPopupDispatcher.getInstance().onPopupShown(this, myInStack);
return true;
}
protected void afterShow() {
}
protected final boolean requestFocus() {
if (!myFocusable) return false;
getFocusManager().requestFocus(new FocusCommand() {
@NotNull
@Override
public ActionCallback run() {
_requestFocus();
return new ActionCallback.Done();
}
}, true);
return true;
}
private void _requestFocus() {
if (!myFocusable) return;
if (myPreferredFocusedComponent != null) {
myPreferredFocusedComponent.requestFocus();
}
}
private IdeFocusManager getFocusManager() {
if (myProject != null) {
return IdeFocusManager.getInstance(myProject);
}
if (myOwner != null) {
return IdeFocusManager.findInstanceByComponent(myOwner);
}
return IdeFocusManager.findInstance();
}
private static JComponent getTargetComponent(Component aComponent) {
if (aComponent instanceof JComponent) {
return (JComponent)aComponent;
}
if (aComponent instanceof RootPaneContainer) {
return ((RootPaneContainer)aComponent).getRootPane();
}
LOG.error("Cannot find target for:" + aComponent);
return null;
}
private PopupComponent.Factory getFactory(boolean forceHeavyweight, boolean forceDialog) {
if (Registry.is("allow.dialog.based.popups")) {
boolean noFocus = !myFocusable || !myRequestFocus;
boolean cannotBeDialog = noFocus && SystemInfo.isXWindow;
if (!cannotBeDialog && (isPersistent() || forceDialog)) {
return new PopupComponent.Factory.Dialog();
}
}
if (forceHeavyweight) {
return new PopupComponent.Factory.AwtHeavyweight();
}
return new PopupComponent.Factory.AwtDefault();
}
@Override
public JComponent getContent() {
return myContent;
}
public void setLocation(RelativePoint p) {
setLocation(p, myPopup);
}
private static void setLocation(final RelativePoint p, final PopupComponent popup) {
if (popup == null) return;
final Window wnd = popup.getWindow();
assert wnd != null;
wnd.setLocation(p.getScreenPoint());
}
@Override
public void pack(boolean width, boolean height) {
if (!isVisible() || !width && !height) return;
Dimension size = getSize();
Dimension prefSize = myContent.computePreferredSize();
if (width) {
size.width = prefSize.width;
}
if (height) {
size.height = prefSize.height;
}
size = computeWindowSize(size);
final Window window = getContentWindow(myContent);
if (window != null) {
window.setSize(size);
}
}
public void pack() {
final Window window = getContentWindow(myContent);
if (window != null) {
window.pack();
}
}
public JComponent getComponent() {
return myComponent;
}
public void setProject(Project project) {
myProject = project;
}
@Override
public void dispose() {
if (myState == State.SHOWN) {
LOG.debug("shown popup must be cancelled");
cancel();
}
if (myState == State.DISPOSE) {
return;
}
debugState("dispose popup", State.INIT, State.CANCEL);
myState = State.DISPOSE;
if (myDisposed) {
return;
}
myDisposed = true;
if (LOG.isDebugEnabled()) {
LOG.debug("start disposing " + myContent);
}
Disposer.dispose(this, false);
ApplicationManager.getApplication().assertIsDispatchThread();
if (myPopup != null) {
cancel(myDisposeEvent);
}
if (myContent != null) {
myContent.removeAll();
myContent.removeKeyListener(mySearchKeyListener);
}
myContent = null;
myPreferredFocusedComponent = null;
myComponent = null;
myFocusTrackback = null;
myCallBack = null;
myListeners = null;
if (myMouseOutCanceller != null) {
final Toolkit toolkit = Toolkit.getDefaultToolkit();
// it may happen, but have no idea how
// http://www.jetbrains.net/jira/browse/IDEADEV-21265
if (toolkit != null) {
toolkit.removeAWTEventListener(myMouseOutCanceller);
}
}
myMouseOutCanceller = null;
resetWindow();
if (myFinalRunnable != null) {
final ActionCallback typeAheadDone = new ActionCallback();
Runnable runFinal = new Runnable() {
@Override
public void run() {
//noinspection SSBasedInspection
SwingUtilities.invokeLater(typeAheadDone.createSetDoneRunnable());
myFinalRunnable.run();
myFinalRunnable = null;
}
};
IdeFocusManager.getInstance(myProject).typeAheadUntil(typeAheadDone);
getFocusManager().doWhenFocusSettlesDown(runFinal);
}
if (LOG.isDebugEnabled()) {
LOG.debug("stop disposing content");
}
}
private void resetWindow() {
if (myWindow != null && getWndManager() != null) {
getWndManager().resetWindow(myWindow);
if (myWindowListener != null) {
myWindow.removeWindowListener(myWindowListener);
}
if (myWindow instanceof JWindow) {
((JWindow)myWindow).getRootPane().putClientProperty(KEY, null);
}
myWindow = null;
myWindowListener = null;
}
}
public void storeDimensionSize(final Dimension size) {
if (myDimensionServiceKey != null) {
DimensionService.getInstance().setSize(myDimensionServiceKey, size, myProject);
}
}
public void storeLocation(final Point xy) {
if (myDimensionServiceKey != null) {
DimensionService.getInstance().setLocation(myDimensionServiceKey, xy, myProject);
}
}
public static class MyContentPanel extends JPanel implements DataProvider {
private final boolean myResizable;
private final boolean myDrawMacCorner;
@Nullable private DataProvider myDataProvider;
public MyContentPanel(final boolean resizable, final PopupBorder border, boolean drawMacCorner) {
super(new BorderLayout());
myResizable = resizable;
myDrawMacCorner = drawMacCorner;
setBorder(border);
}
@Override
public void paint(Graphics g) {
super.paint(g);
if (myResizable && myDrawMacCorner) {
AllIcons.General.MacCorner.paintIcon(this, g,
getX() + getWidth() - AllIcons.General.MacCorner.getIconWidth(),
getY() + getHeight() - AllIcons.General.MacCorner.getIconHeight());
}
}
public Dimension computePreferredSize() {
if (isPreferredSizeSet()) {
Dimension setSize = getPreferredSize();
setPreferredSize(null);
Dimension result = getPreferredSize();
setPreferredSize(setSize);
return result;
}
return getPreferredSize();
}
@Nullable
@Override
public Object getData(@NonNls String dataId) {
return myDataProvider != null ? myDataProvider.getData(dataId) : null;
}
public void setDataProvider(@Nullable DataProvider dataProvider) {
myDataProvider = dataProvider;
}
}
public boolean isCancelOnClickOutside() {
return myCancelOnClickOutside;
}
public boolean isCancelOnWindowDeactivation() {
return myCancelOnWindowDeactivation;
}
private class Canceller implements AWTEventListener {
private boolean myEverEntered = false;
@Override
public void eventDispatched(final AWTEvent event) {
if (event.getID() == WindowEvent.WINDOW_ACTIVATED) {
if (myCancelOnWindow && myPopup != null && !myPopup.isPopupWindow(((WindowEvent)event).getWindow())) {
cancel();
}
}
else if (event.getID() == MouseEvent.MOUSE_ENTERED) {
if (withinPopup(event)) {
myEverEntered = true;
}
}
else if (event.getID() == MouseEvent.MOUSE_MOVED) {
if (myCancelOnMouseOutCallback != null && myEverEntered && !withinPopup(event)) {
if (myCancelOnMouseOutCallback.check((MouseEvent)event)) {
cancel();
}
}
}
}
private boolean withinPopup(final AWTEvent event) {
if (!myContent.isShowing()) return false;
final MouseEvent mouse = (MouseEvent)event;
final Point point = mouse.getPoint();
SwingUtilities.convertPointToScreen(point, mouse.getComponent());
return new Rectangle(myContent.getLocationOnScreen(), myContent.getSize()).contains(point);
}
}
@Override
public void setLocation(@NotNull final Point screenPoint) {
if (myPopup == null) {
myForcedLocation = screenPoint;
}
else {
moveTo(myContent, screenPoint, myLocateByContent ? myHeaderPanel.getPreferredSize() : null);
}
}
public static Window moveTo(JComponent content, Point screenPoint, final Dimension headerCorrectionSize) {
final Window wnd = getContentWindow(content);
if (wnd != null) {
wnd.setCursor(Cursor.getDefaultCursor());
if (headerCorrectionSize != null) {
screenPoint.y -= headerCorrectionSize.height;
}
wnd.setLocation(screenPoint);
}
return wnd;
}
private static Window getContentWindow(Component content) {
Window window = SwingUtilities.getWindowAncestor(content);
if (window == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("no window ancestor for " + content);
}
}
return window;
}
@Override
public Point getLocationOnScreen() {
Dimension headerCorrectionSize = myLocateByContent ? myHeaderPanel.getPreferredSize() : null;
Point screenPoint = myContent.getLocationOnScreen();
if (headerCorrectionSize != null) {
screenPoint.y -= headerCorrectionSize.height;
}
return screenPoint;
}
@Override
public void setSize(@NotNull final Dimension size) {
setSize(size, true);
}
private void setSize(Dimension size, boolean adjustByContent) {
Dimension toSet = size;
if (myPopup == null) {
myForcedSize = toSet;
}
else {
if (adjustByContent) {
toSet = computeWindowSize(toSet);
}
updateMaskAndAlpha(setSize(myContent, toSet));
}
}
private Dimension computeWindowSize(Dimension size) {
if (myAdComponent != null && myAdComponent.isShowing()) {
size.height += myAdComponent.getPreferredSize().height + 1;
}
return size;
}
@Override
public Dimension getSize() {
if (myPopup != null) {
final Window popupWindow = getContentWindow(myContent);
return (popupWindow == null) ? myForcedSize : popupWindow.getSize();
} else {
return myForcedSize;
}
}
@Override
public void moveToFitScreen() {
if (myPopup == null) return;
final Window popupWindow = getContentWindow(myContent);
if (popupWindow == null) return;
Rectangle bounds = popupWindow.getBounds();
ScreenUtil.moveRectangleToFitTheScreen(bounds);
setLocation(bounds.getLocation());
setSize(bounds.getSize(), false);
}
public static Window setSize(JComponent content, final Dimension size) {
final Window popupWindow = getContentWindow(content);
if (popupWindow == null) return null;
Insets insets = content.getInsets();
if (insets != null) {
size.width += insets.left + insets.right;
size.height += insets.top + insets.bottom;
}
content.setPreferredSize(size);
popupWindow.pack();
return popupWindow;
}
public static void setDefaultCursor(JComponent content) {
final Window wnd = getContentWindow(content);
if (wnd != null) {
wnd.setCursor(Cursor.getDefaultCursor());
}
}
public void setCaption(String title) {
if (myCaption instanceof TitlePanel) {
((TitlePanel)myCaption).setText(title);
}
}
private class MyWindowListener extends WindowAdapter {
@Override
public void windowOpened(WindowEvent e) {
updateMaskAndAlpha(myWindow);
}
@Override
public void windowClosing(final WindowEvent e) {
resetWindow();
cancel();
}
}
@Override
public boolean isPersistent() {
return !myCancelOnClickOutside && !myCancelOnWindow;
}
@Override
public boolean isNativePopup() {
return myNativePopup;
}
@Override
public void setUiVisible(final boolean visible) {
if (myPopup != null) {
if (visible) {
myPopup.show();
final Window window = getPopupWindow();
if (window != null && myRestoreWindowSize != null) {
window.setSize(myRestoreWindowSize);
myRestoreWindowSize = null;
}
}
else {
final Window window = getPopupWindow();
if (window != null) {
myRestoreWindowSize = window.getSize();
window.setVisible(true);
}
}
}
}
public Window getPopupWindow() {
return myPopup.getWindow();
}
public void setUserData(List<Object> userData) {
myUserData = userData;
}
@Override
public <T> T getUserData(final Class<T> userDataClass) {
if (myUserData != null) {
for (Object o : myUserData) {
if (userDataClass.isInstance(o)) {
@SuppressWarnings("unchecked") T t = (T)o;
return t;
}
}
}
return null;
}
@Override
public boolean isModalContext() {
return myModalContext;
}
@Override
public boolean isFocused() {
if (myComponent != null && isFocused(new Component[]{SwingUtilities.getWindowAncestor(myComponent)})) {
return true;
}
return isFocused(myFocusOwners);
}
public static boolean isFocused(@Nullable Component[] components) {
if (components == null) return false;
Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (owner == null) return false;
Window wnd;
if (owner instanceof Window) {
wnd = (Window)owner;
}
else {
wnd = SwingUtilities.getWindowAncestor(owner);
}
for (Component each : components) {
if (each != null && SwingUtilities.isDescendingFrom(owner, each)) {
Window eachWindow = each instanceof Window ? (Window)each : SwingUtilities.getWindowAncestor(each);
if (eachWindow == wnd) {
return true;
}
}
}
return false;
}
@Override
public boolean isCancelKeyEnabled() {
return myCancelKeyEnabled;
}
@NotNull
CaptionPanel getTitle() {
return myCaption;
}
private void setHeaderComponent(JComponent c) {
boolean doRevalidate = false;
if (myHeaderComponent != null) {
myHeaderPanel.remove(myHeaderComponent);
myHeaderPanel.add(myCaption, BorderLayout.NORTH);
myHeaderComponent = null;
doRevalidate = true;
}
if (c != null) {
myHeaderPanel.remove(myCaption);
myHeaderPanel.add(c, BorderLayout.NORTH);
myHeaderComponent = c;
final Dimension size = myContent.getSize();
if (size.height < c.getPreferredSize().height * 2) {
size.height += c.getPreferredSize().height;
setSize(size);
}
doRevalidate = true;
}
if (doRevalidate) myContent.revalidate();
}
public void setWarning(@NotNull String text) {
JBLabel label = new JBLabel(text, UIUtil.getBalloonWarningIcon(), SwingConstants.CENTER);
label.setOpaque(true);
Color color = HintUtil.INFORMATION_COLOR;
label.setBackground(color);
label.setBorder(BorderFactory.createLineBorder(color, 3));
myHeaderPanel.add(label, BorderLayout.SOUTH);
}
@Override
public void addListener(final JBPopupListener listener) {
myListeners.add(listener);
}
@Override
public void removeListener(final JBPopupListener listener) {
myListeners.remove(listener);
}
protected void onSpeedSearchPatternChanged() {
}
@Override
public Component getOwner() {
return myRequestorComponent;
}
@Override
public void setMinimumSize(Dimension size) {
//todo: consider changing only the caption panel minimum size
Dimension sizeFromHeader = myHeaderPanel.getPreferredSize();
if (sizeFromHeader == null) {
sizeFromHeader = myHeaderPanel.getMinimumSize();
}
if (sizeFromHeader == null) {
int minimumSize = myWindow.getGraphics().getFontMetrics(myHeaderPanel.getFont()).getHeight();
sizeFromHeader = new Dimension(minimumSize, minimumSize);
}
if (size == null) {
myMinSize = sizeFromHeader;
} else {
final int width = Math.max(size.width, sizeFromHeader.width);
final int height = Math.max(size.height, sizeFromHeader.height);
myMinSize = new Dimension(width, height);
}
if (myWindow != null) {
myWindow.setMinimumSize(myMinSize);
}
}
@Override
public void setFinalRunnable(Runnable finalRunnable) {
myFinalRunnable = finalRunnable;
}
public void setOk(boolean ok) {
myOk = ok;
}
@Override
public void setDataProvider(@NotNull DataProvider dataProvider) {
if (myContent != null) {
myContent.setDataProvider(dataProvider);
}
}
@Override
public boolean dispatchKeyEvent(@NotNull KeyEvent e) {
BooleanFunction<KeyEvent> handler = myKeyEventHandler;
if (handler != null) {
return handler.fun(e);
}
else {
if (isCloseRequest(e) && myCancelKeyEnabled) {
cancel(e);
return true;
}
}
return false;
}
private class SpeedSearchKeyListener implements KeyListener {
@Override
public void keyTyped(final KeyEvent e) {
mySpeedSearch.process(e);
}
@Override
public void keyPressed(final KeyEvent e) {
mySpeedSearch.process(e);
}
@Override
public void keyReleased(final KeyEvent e) {
mySpeedSearch.process(e);
}
}
@NotNull
public Dimension getHeaderPreferredSize() {
return myHeaderPanel.getPreferredSize();
}
@NotNull
public Dimension getFooterPreferredSize() {
return myAdComponent == null ? new Dimension(0,0) : myAdComponent.getPreferredSize();
}
public static boolean isCloseRequest(KeyEvent e) {
return e != null && e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_ESCAPE && e.getModifiers() == 0;
}
}