Merge branch 'master' of git@git.labs.intellij.net:idea/community
authorKirill Kalishev <kirill.kalishev@jetbrains.com>
Wed, 9 Jun 2010 05:44:24 +0000 (09:44 +0400)
committerKirill Kalishev <kirill.kalishev@jetbrains.com>
Wed, 9 Jun 2010 05:44:24 +0000 (09:44 +0400)
14 files changed:
platform/lang-impl/src/com/intellij/execution/actions/ChooseRunConfigurationAction.java
platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java
platform/platform-api/src/com/intellij/openapi/ui/popup/Balloon.java
platform/platform-api/src/com/intellij/openapi/ui/popup/JBPopup.java
platform/platform-api/src/com/intellij/openapi/ui/popup/JBPopupFactory.java
platform/platform-api/src/com/intellij/openapi/ui/popup/LightweightWindowEvent.java
platform/platform-api/src/com/intellij/openapi/ui/popup/PopupChooserBuilder.java
platform/platform-api/src/com/intellij/util/ui/PositionTracker.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java
platform/platform-impl/src/com/intellij/ui/BalloonImpl.java
platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java
platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java
platform/platform-impl/src/com/intellij/ui/popup/list/ListPopupImpl.java
platform/platform-impl/src/com/intellij/ui/popup/tree/TreePopupImpl.java

index 4826fc67de619c44d18e29aa89211ee4dc1815c0..7f4c439b930df2cdba5e47d4c2a7c156e0a5f6d7 100644 (file)
@@ -275,7 +275,7 @@ public class ChooseRunConfigurationAction extends AnAction {
                 execute((ItemWrapper)item, executor);
               }
             });
-            listPopup.cancel();
+            listPopup.closeOk(null);
           }
         }
       }
index 802acf30fc22aae3516f4198b1b0b37aa48d0ca0..c1b9c98767c1d5c642b0f9668c84ff079487bc6e 100644 (file)
@@ -17,6 +17,7 @@
 package com.intellij.ide.util.gotoByName;
 
 import com.intellij.Patches;
+import com.intellij.codeInsight.hint.HintManager;
 import com.intellij.ide.IdeBundle;
 import com.intellij.ide.actions.CopyReferenceAction;
 import com.intellij.ide.ui.UISettings;
@@ -31,6 +32,8 @@ import com.intellij.openapi.progress.ProcessCanceledException;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.popup.JBPopup;
 import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.JBPopupListener;
+import com.intellij.openapi.ui.popup.LightweightWindowEvent;
 import com.intellij.openapi.util.ActionCallback;
 import com.intellij.openapi.util.Comparing;
 import com.intellij.openapi.util.Pair;
@@ -345,7 +348,17 @@ public abstract class ChooseByNameBase{
         public void focusLost(final FocusEvent e) {
           myHideAlarm.addRequest(new Runnable() {
             public void run() {
-              if (!JBPopupFactory.getInstance().isChildPopupFocused(e.getComponent())) {
+              JBPopup popup = JBPopupFactory.getInstance().getChildFocusedPopup(e.getComponent());
+              if (popup != null) {
+                popup.addListener(new JBPopupListener.Adapter() {
+                  @Override
+                  public void onClosed(LightweightWindowEvent event) {
+                    if (event.isOk()) {
+                      hideHint();
+                    }
+                  }
+                });
+              } else {
                 hideHint();
               }
             }
index d9d57238c64e7944f1efb2cb4d5762ef8005f6fa..555766e1a0ddbc34a261236091a686e62c85714a 100644 (file)
@@ -17,12 +17,15 @@ package com.intellij.openapi.ui.popup;
 
 import com.intellij.openapi.Disposable;
 import com.intellij.ui.awt.RelativePoint;
+import com.intellij.util.ui.PositionTracker;
 
 import javax.swing.*;
 import java.awt.*;
 
 public interface Balloon extends Disposable {
 
+  void show(PositionTracker<Balloon> tracker, Position preferredPosition);
+
   void show(RelativePoint target, Position prefferedPosition);
 
   void show(JLayeredPane pane);
index 21bf971bc40b08bc0a3f21678e99e1a2479728ff..b9dea20e77ee7fae6739e317b850e6a3effcedba 100644 (file)
@@ -101,7 +101,13 @@ public interface JBPopup extends Disposable, LightweightWindow {
   void showCenteredInCurrentWindow(@NotNull Project project);
 
   /**
-   * Cancels the popup (as if Esc was pressed).
+   * Hides popup as if Enter was pressed or or any other "accept" action
+   */
+
+  void closeOk(@Nullable InputEvent e);
+
+  /**
+   * Cancels the popup as if Esc was pressed or any other "cancel" action
    */
   void cancel();
 
index af065cddbc2024abb485aee4e36e4840ac9ac760..92ae7a192e83def38c3b2126899be4b395069cf0 100644 (file)
@@ -107,12 +107,16 @@ public abstract class JBPopupFactory {
   public abstract RelativePoint guessBestPopupLocation(JComponent component);
 
   public boolean isChildPopupFocused(@Nullable Component parent) {
-    if (parent == null) return false;
+    return getChildFocusedPopup(parent) != null;
+  }
+
+  public JBPopup getChildFocusedPopup(@Nullable Component parent) {
+    if (parent == null) return null;
     List<JBPopup> popups = getChildPopups(parent);
     for (JBPopup each : popups) {
-      if (each.isFocused()) return true;
+      if (each.isFocused()) return each;
     }
-    return false;
+    return null;
   }
 
   /**
index 3ab7842524117e72f778cfa14daf549d2a800644..b2ff1cba08a2332b2893e26189f5d99c84352848 100644 (file)
 package com.intellij.openapi.ui.popup;
 
 public class LightweightWindowEvent {
+
   private final LightweightWindow myWindow;
+  private boolean myOk;
 
   public LightweightWindowEvent(LightweightWindow window) {
+    this(window, false);
+  }
+
+  public LightweightWindowEvent(LightweightWindow window, boolean isOk) {
     myWindow = window;
+    myOk = isOk;
+  }
+
+  public boolean isOk() {
+    return myOk;
   }
 
   public Balloon asBalloon() {
index e39dc903ed20ac32bbdb5a8e74ba063a5ec5f96a..79f95d34bb9349bc791bfc79fedf7dd49e679cca 100644 (file)
@@ -191,7 +191,7 @@ public class PopupChooserBuilder {
       @Override
       public void mousePressed(MouseEvent e) {
         if (UIUtil.isActionClick(e) && !isSelectionButtonDown(e) && !e.isConsumed()) {
-          closePopup(true, e);
+          closePopup(true, e, true);
         }
       }
     });
@@ -273,17 +273,21 @@ public class PopupChooserBuilder {
         if (!shouldPerformAction && myChooserComponent instanceof ListWithFilter) {
           if (((ListWithFilter)myChooserComponent).resetFilter()) return;
         }
-        closePopup(shouldPerformAction, null);
+        closePopup(shouldPerformAction, null, shouldPerformAction);
       }
     }, keyStroke, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
   }
 
-  private void closePopup(boolean shouldPerformAction, MouseEvent e) {
+  private void closePopup(boolean shouldPerformAction, MouseEvent e, boolean isOk) {
     if (shouldPerformAction) {
       myPopup.setFinalRunnable(myItemChoosenRunnable);
     }
 
-    myPopup.cancel(e);
+    if (isOk) {
+      myPopup.closeOk(e);
+    } else {
+      myPopup.cancel(e);
+    }
   }
 
   @NotNull
diff --git a/platform/platform-api/src/com/intellij/util/ui/PositionTracker.java b/platform/platform-api/src/com/intellij/util/ui/PositionTracker.java
new file mode 100644 (file)
index 0000000..5d7c0b6
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2000-2010 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.util.ui;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.ui.awt.RelativePoint;
+
+import java.awt.*;
+import java.awt.event.*;
+
+public abstract class PositionTracker<T> implements Disposable, HierarchyBoundsListener, HierarchyListener, ComponentListener {
+
+  private Component myComponent;
+  private Client<T> myClient;
+
+  public PositionTracker(Component component) {
+    myComponent = component;
+  }
+
+  public final void init(Client<T> client) {
+    myClient = client;
+
+    Disposer.register(client, this);
+
+    myComponent.addHierarchyBoundsListener(this);
+    myComponent.addHierarchyListener(this);
+    myComponent.addComponentListener(this);
+  }
+
+  public final Component getComponent() {
+    return myComponent;
+  }
+
+  public final void ancestorMoved(HierarchyEvent e) {
+    revalidate();
+  }
+
+  public final void ancestorResized(HierarchyEvent e) {
+    revalidate();
+  }
+
+  public final void hierarchyChanged(HierarchyEvent e) {
+    revalidate();
+  }
+
+  public void componentResized(ComponentEvent e) {
+    revalidate();
+  }
+
+  public void componentMoved(ComponentEvent e) {
+    revalidate();
+  }
+
+  public void componentShown(ComponentEvent e) {
+    revalidate();
+  }
+
+  public void componentHidden(ComponentEvent e) {
+    revalidate();
+  }
+
+  protected final void revalidate() {
+    myClient.revalidate(this);
+  }
+
+  public abstract RelativePoint recalculateLocation(T object);
+
+  public final void dispose() {
+    myComponent.removeHierarchyBoundsListener(this);
+    myComponent.removeHierarchyListener(this);
+    myComponent.removeComponentListener(this);
+  }
+
+  public static final class Static<T> extends PositionTracker<T> {
+
+    private RelativePoint myPoint;
+
+    public Static(RelativePoint point) {
+      super(point.getComponent());
+      myPoint = point;
+    }
+
+    @Override
+    public RelativePoint recalculateLocation(Object object) {
+      return myPoint;
+    }
+  }
+
+  public interface Client<T> extends Disposable {
+    
+    void revalidate(PositionTracker<T> tracker);
+
+  }
+
+}
index 218fafec124e322430de72d2011e3d675a36fd25..2f1e4b03e7e6c039a70f718424833b7318023684 100644 (file)
@@ -49,6 +49,7 @@ import com.intellij.ui.awt.RelativePoint;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.containers.CollectionFactory;
 import com.intellij.util.containers.HashMap;
+import com.intellij.util.ui.PositionTracker;
 import com.intellij.util.ui.UIUtil;
 import com.intellij.util.ui.update.UiNotifyConnector;
 import org.jdom.Element;
@@ -1050,7 +1051,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
     Icon actualIcon = icon != null ? icon : type.getDefaultIcon();
 
     final Balloon balloon =
-      JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text.replace("\n", "<br>"), actualIcon, type.getPopupBackground(), listener)
+      JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text.replace("\n", "<br>"), actualIcon, type.getPopupBackground(), listener).setHideOnClickOutside(false).setHideOnFrameResize(false)
         .createBalloon();
     Disposer.register(balloon, new Disposable() {
       public void dispose() {
@@ -1060,6 +1061,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
         stripe.repaint();
       }
     });
+    Disposer.register(getProject(), balloon);
 
     final StripeButton button = stripe.getButtonFor(toolWindowId);
     if (button == null) return;
@@ -1067,8 +1069,24 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
     final Runnable show = new Runnable() {
       public void run() {
         if (button.isShowing()) {
-          final Point point = new Point(button.getBounds().width / 2, button.getHeight() / 2 - 2);
-          balloon.show(new RelativePoint(button, point), position.get());
+          PositionTracker tracker = new PositionTracker<Balloon>(button) {
+            @Override
+            public RelativePoint recalculateLocation(Balloon object) {
+              Stripe twStripe = myToolWindowsPane.getStripeFor(toolWindowId);
+              StripeButton twButton = twStripe != null ? twStripe.getButtonFor(toolWindowId) : null;
+
+              if (twButton == null) return null;
+
+              if (getToolWindow(toolWindowId).getAnchor() != anchor) {
+                object.hide();
+                return null;
+              }
+
+              final Point point = new Point(twButton.getBounds().width / 2, twButton.getHeight() / 2 - 2);
+              return new RelativePoint(twButton, point);
+            }
+          };
+          balloon.show(tracker, position.get());
         }
         else {
           final Rectangle bounds = myToolWindowsPane.getBounds();
index 5a1cd609db409f22aa04a9e78d193854adedf6e7..c3c4621b0c06f187feb9fa7aff5750ce3caa3fa2 100644 (file)
@@ -29,10 +29,7 @@ import com.intellij.ui.awt.RelativePoint;
 import com.intellij.ui.components.panels.NonOpaquePanel;
 import com.intellij.ui.components.panels.Wrapper;
 import com.intellij.util.Alarm;
-import com.intellij.util.ui.Animator;
-import com.intellij.util.ui.BaseButtonBehavior;
-import com.intellij.util.ui.TimedDeadzone;
-import com.intellij.util.ui.UIUtil;
+import com.intellij.util.ui.*;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
@@ -45,7 +42,7 @@ import java.awt.geom.RoundRectangle2D;
 import java.awt.image.BufferedImage;
 import java.util.concurrent.CopyOnWriteArraySet;
 
-public class BalloonImpl implements Disposable, Balloon, LightweightWindow {
+public class BalloonImpl implements Disposable, Balloon, LightweightWindow, PositionTracker.Client<Balloon> {
 
   private MyComponent myComp;
   private JLayeredPane myLayeredPane;
@@ -96,6 +93,11 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow {
         }
       }
 
+      if (event instanceof MouseEvent && UIUtil.isCloseClick((MouseEvent)event)) {
+        hide();
+        return;
+      }
+
       if (myHideOnKey && (event.getID() == KeyEvent.KEY_PRESSED)) {
         final KeyEvent ke = (KeyEvent)event;
         if (SwingUtilities.isDescendingFrom(ke.getComponent(), myComp) || ke.getComponent() == myComp) return;
@@ -110,6 +112,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow {
 
   private final CopyOnWriteArraySet<JBPopupListener> myListeners = new CopyOnWriteArraySet<JBPopupListener>();
   private boolean myVisible;
+  private PositionTracker<Balloon> myTracker;
 
   private boolean isInsideBalloon(MouseEvent me) {
     if (!me.getComponent().isShowing()) return true;
@@ -188,13 +191,41 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow {
     show(target, pos);
   }
 
+  public void show(PositionTracker<Balloon> tracker, Balloon.Position position) {
+    Position pos = BELOW;
+    switch (position) {
+      case atLeft:
+        pos = AT_LEFT;
+        break;
+      case atRight:
+        pos = AT_RIGHT;
+        break;
+      case below:
+        pos = BELOW;
+        break;
+      case above:
+        pos = ABOVE;
+        break;
+    }
+
+    show(tracker, pos);
+  }
+
+
   private void show(RelativePoint target, Position position) {
+    show(new PositionTracker.Static<Balloon>(target), position);
+  }
+
+  private void show(PositionTracker<Balloon> tracker, Position position) {
     if (isVisible()) return;
 
     assert !myDisposed : "Balloon is already disposed";
-    assert target.getComponent().isShowing() : "Target component is not showing: " + target;
+    assert tracker.getComponent().isShowing() : "Target component is not showing: " + tracker;
+
+    myTracker = tracker;
+    myTracker.init(this);
 
-    final Window window = SwingUtilities.getWindowAncestor(target.getComponent());
+    final Window window = SwingUtilities.getWindowAncestor(tracker.getComponent());
 
     JRootPane root = null;
     if (window instanceof JFrame) {
@@ -219,7 +250,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow {
                                : new EmptyBorder(getNormalInset(), getNormalInset(), getNormalInset(), getNormalInset());
     myComp = new MyComponent(myContent, this, border);
 
-    myTargetPoint = target.getPoint(myLayeredPane);
+    myTargetPoint = tracker.recalculateLocation(this).getPoint(myLayeredPane);
 
     myComp.clear();
     myComp.myAlpha = 0f;
@@ -247,6 +278,15 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow {
                                                                            KeyEvent.KEY_EVENT_MASK);
   }
 
+  public void revalidate(PositionTracker<Balloon> tracker) {
+    RelativePoint newPosition = tracker.recalculateLocation(this);
+
+    if (newPosition != null) {
+      myTargetPoint = newPosition.getPoint(myLayeredPane);
+      myPosition.updateLocation(this);
+    }
+  }
+
   public void show(JLayeredPane pane) {
     show(pane, null);
   }
index bf3a130fb19f5ab741d480a23a68fb1ef7a7e3f3..f478155c40caae6027e6c5df6728dddfb0edcfe9 100644 (file)
@@ -118,6 +118,8 @@ public class AbstractPopup implements JBPopup {
 
   private Runnable myFinalRunnable;
 
+  protected boolean myOk;
+
   protected final SpeedSearch mySpeedSearch = new SpeedSearch() {
     boolean searchFieldShown = false;
     protected void update() {
@@ -460,6 +462,11 @@ public class AbstractPopup implements JBPopup {
     return relativePoint;
   }
 
+  public final void closeOk(@Nullable InputEvent e) {
+    setOk(true);
+    cancel(e);
+  }
+
   public final void cancel() {
     cancel(null);
   }
@@ -505,7 +512,7 @@ public class AbstractPopup implements JBPopup {
 
       if (myListeners != null) {
         for (JBPopupListener each : myListeners) {
-          each.onClosed(new LightweightWindowEvent(this));
+          each.onClosed(new LightweightWindowEvent(this, myOk));
         }
       }
     }
@@ -1253,4 +1260,8 @@ public class AbstractPopup implements JBPopup {
   public void setFinalRunnable(Runnable finalRunnable) {
     myFinalRunnable = finalRunnable;
   }
+
+  public void setOk(boolean ok) {
+    myOk = ok;
+  }
 }
index 6d5d990c30f35d0132ee820d549617e9c34bcd32..fbb5bdecdc3de8169b9c1cfe4084baa919c1a951 100644 (file)
@@ -420,4 +420,13 @@ public abstract class WizardPopup extends AbstractPopup implements ActionListene
       getParent().setFinalRunnable(runnable);
     }
   }
+
+  @Override
+  public void setOk(boolean ok) {
+    if (getParent() == null) {
+      super.setOk(ok);
+    } else {
+      getParent().setOk(ok);
+    }
+  }
 }
index 3f7847aad0fc9fe168e5c6834e219f0ba1e071f4..ba05ea8c7d66c920f316e1ed9d559881d42cd089 100644 (file)
@@ -302,6 +302,7 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
 
     if (myListModel.getSize() == 0) {
       setFinalRunnable(myStep.getFinalRunnable());
+      setOk(true);
       disposeAllParents(e);
       setIndexForShowingChild(-1);
       return true;
@@ -337,6 +338,7 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
       return false;
     }
     else {
+      setOk(true);
       setFinalRunnable(myStep.getFinalRunnable());
       disposeAllParents(e);
       setIndexForShowingChild(-1);
index 06d214216f78c22a68d03c206bffdc9d3a204dfa..7127fac428805ec0ca4e053cbf67c94af9b48731 100644 (file)
@@ -324,6 +324,7 @@ public class TreePopupImpl extends WizardPopup implements TreePopup {
         final PopupStep queriedStep = myStep.onChosen(userObject, handleFinalChoices);
         if (queriedStep == PopupStep.FINAL_CHOICE || !hasNextStep) {
           setFinalRunnable(myStep.getFinalRunnable());
+          setOk(true);
           disposeAllParents(e);
         }
         else {