<commit_before>/*
 * Copyright 2000-2009 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.CommonBundle;
import com.intellij.ide.DataManager;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.IdeTooltipManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionUtil;
import com.intellij.openapi.actionSystem.impl.ActionMenu;
import com.intellij.openapi.actionSystem.impl.Utils;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.VisualPosition;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.*;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.ex.WindowManagerEx;
import com.intellij.openapi.wm.impl.IdeFrameImpl;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.FocusTrackback;
import com.intellij.ui.HintHint;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.ui.popup.list.ListPopupImpl;
import com.intellij.ui.popup.mock.MockConfirmation;
import com.intellij.ui.popup.tree.TreePopupImpl;
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 javax.swing.event.HyperlinkListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class PopupFactoryImpl extends JBPopupFactory {
  private static final Logger LOG = Logger.getInstance("#com.intellij.ui.popup.PopupFactoryImpl");

  private static final Icon QUICK_LIST_ICON = IconLoader.getIcon("/actions/quickList.png");

  public ListPopup createConfirmation(String title, final Runnable onYes, int defaultOptionIndex) {
    return createConfirmation(title, CommonBundle.getYesButtonText(), CommonBundle.getNoButtonText(), onYes, defaultOptionIndex);
  }

  public ListPopup createConfirmation(String title, final String yesText, String noText, final Runnable onYes, int defaultOptionIndex) {
    return createConfirmation(title, yesText, noText, onYes, EmptyRunnable.getInstance(), defaultOptionIndex);
  }

  public JBPopup createMessage(String text) {
    return createListPopup(new BaseListPopupStep<String>(null, new String[]{text})); 
  }

  @Override
  public Balloon getParentBalloonFor(@Nullable Component c) {
    if (c == null) return null;
    Component eachParent = c;
    while (eachParent != null) {
      if (eachParent instanceof JComponent) {
        Object balloon = ((JComponent)eachParent).getClientProperty(Balloon.KEY);
        if (balloon instanceof Balloon) {
          return (Balloon)balloon;
        }
      }
      eachParent = eachParent.getParent();
    }

    return null;
  }

  public ListPopup createConfirmation(String title, final String yesText, String noText, final Runnable onYes, final Runnable onNo, int defaultOptionIndex) {

      final BaseListPopupStep<String> step = new BaseListPopupStep<String>(title, new String[]{yesText, noText}) {
        public PopupStep onChosen(String selectedValue, final boolean finalChoice) {
          if (selectedValue.equals(yesText)) {
            onYes.run();
          }
          else {
            onNo.run();
          }
          return FINAL_CHOICE;
        }

        public void canceled() {
          onNo.run();
        }

        public boolean isMnemonicsNavigationEnabled() {
          return true;
        }
      };
      step.setDefaultOptionIndex(defaultOptionIndex);

    final ApplicationEx app = ApplicationManagerEx.getApplicationEx();
    return app == null || !app.isUnitTestMode() ? new ListPopupImpl(step) : new MockConfirmation(step, yesText);
  }


  private static ListPopup createActionGroupPopup(final String title,
                                          final ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          boolean showNumbers,
                                          boolean useAlphaAsNumbers,
                                          boolean showDisabledActions,
                                          boolean honorActionMnemonics,
                                          final Runnable disposeCallback,
                                          final int maxRowCount) {
    return createActionGroupPopup(title, actionGroup, dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions, honorActionMnemonics, disposeCallback,
                                  maxRowCount, null, null);
  }

  public ListPopup createActionGroupPopup(final String title,
                                          final ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          boolean showNumbers,
                                          boolean showDisabledActions,
                                          boolean honorActionMnemonics,
                                          final Runnable disposeCallback,
                                          final int maxRowCount) {
    return createActionGroupPopup(title, actionGroup, dataContext, showNumbers, showDisabledActions, honorActionMnemonics, disposeCallback,
                                  maxRowCount, null);
  }

  private static ListPopup createActionGroupPopup(final String title,
                                                  final ActionGroup actionGroup,
                                                  @NotNull DataContext dataContext,
                                                  boolean showNumbers,
                                                  boolean useAlphaAsNumbers,
                                                  boolean showDisabledActions,
                                                  boolean honorActionMnemonics,
                                                  final Runnable disposeCallback,
                                                  final int maxRowCount,
                                                  final Condition<AnAction> preselectActionCondition, @Nullable final String actionPlace) {
    return new ActionGroupPopup(title, actionGroup, dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions, honorActionMnemonics,
                                disposeCallback, maxRowCount, preselectActionCondition, actionPlace);
  }

  public static class ActionGroupPopup extends ListPopupImpl {

    private final Runnable myDisposeCallback;
    private final Component myComponent;

    public ActionGroupPopup(final String title, final ActionGroup actionGroup, @NotNull DataContext dataContext,
                            boolean showNumbers, boolean useAlphaAsNumbers, boolean showDisabledActions, boolean honorActionMnemonics,
                            final Runnable disposeCallback, final int maxRowCount, final Condition<AnAction> preselectActionCondition,
                            @Nullable final String actionPlace) {
      super(createStep(title, actionGroup, dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions, honorActionMnemonics,
                       preselectActionCondition, actionPlace),
            maxRowCount);
      myDisposeCallback = disposeCallback;
      myComponent = PlatformDataKeys.CONTEXT_COMPONENT.getData(dataContext);

      addListSelectionListener(new ListSelectionListener() {
        public void valueChanged(ListSelectionEvent e) {
          final JList list = (JList)e.getSource();
          final ActionItem actionItem = (ActionItem)list.getSelectedValue();
          if (actionItem == null) return;
          AnAction action = actionItem.getAction();
          Presentation presentation = new Presentation();
          presentation.setDescription(action.getTemplatePresentation().getDescription());
          final String actualActionPlace = actionPlace == null ? ActionPlaces.UNKNOWN : actionPlace;
          action.update(new AnActionEvent(null, DataManager.getInstance().getDataContext(myComponent), actualActionPlace, presentation,
                                          ActionManager.getInstance(), 0));
          ActionMenu.showDescriptionInStatusBar(true, myComponent, presentation.getDescription());
        }
      });
    }

    private static ListPopupStep createStep(String title, ActionGroup actionGroup, @NotNull DataContext dataContext,
                                            boolean showNumbers, boolean useAlphaAsNumbers, boolean showDisabledActions,
                                            boolean honorActionMnemonics,
                                            Condition<AnAction> preselectActionCondition, @Nullable String actionPlace) {
      final Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(dataContext);

      final ActionStepBuilder builder =
        new ActionStepBuilder(dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions, honorActionMnemonics);
      if (actionPlace != null) {
        builder.setActionPlace(actionPlace);
      }
      builder.buildGroup(actionGroup);
      final List<ActionItem> items = builder.getItems();

      return new ActionPopupStep(items, title, component, showNumbers || honorActionMnemonics && itemsHaveMnemonics(items),
                                 preselectActionCondition, false, showDisabledActions);
    }

    @Override
    public void dispose() {
      if (myDisposeCallback != null) {
        myDisposeCallback.run();
      }
      ActionMenu.showDescriptionInStatusBar(true, myComponent, null);
      super.dispose();
    }
  }

  public ListPopup createActionGroupPopup(final String title,
                                          final ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          boolean showNumbers,
                                          boolean showDisabledActions,
                                          boolean honorActionMnemonics,
                                          final Runnable disposeCallback,
                                          final int maxRowCount,
                                          final Condition<AnAction> preselectActionCondition) {
    return createActionGroupPopup(title, actionGroup, dataContext, showNumbers, true, showDisabledActions, honorActionMnemonics,
                                  disposeCallback, maxRowCount, preselectActionCondition, null);
  }

  public ListPopup createActionGroupPopup(String title,
                                          ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          ActionSelectionAid selectionAidMethod,
                                          boolean showDisabledActions) {
    return createActionGroupPopup(title, actionGroup, dataContext,
                                  selectionAidMethod == ActionSelectionAid.NUMBERING || selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  showDisabledActions,
                                  selectionAidMethod == ActionSelectionAid.MNEMONICS,
                                  null, -1);
  }

  public ListPopup createActionGroupPopup(String title,
                                          ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          ActionSelectionAid selectionAidMethod,
                                          boolean showDisabledActions,
                                          @Nullable String actionPlace) {
    return createActionGroupPopup(title, actionGroup, dataContext,
                                  selectionAidMethod == ActionSelectionAid.NUMBERING || selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  showDisabledActions,
                                  selectionAidMethod == ActionSelectionAid.MNEMONICS,
                                  null, -1, null, actionPlace);
  }

  public ListPopup createActionGroupPopup(String title,
                                          ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          ActionSelectionAid selectionAidMethod,
                                          boolean showDisabledActions,
                                          Runnable disposeCallback,
                                          int maxRowCount) {
    return createActionGroupPopup(title, actionGroup, dataContext,
                                  selectionAidMethod == ActionSelectionAid.NUMBERING || selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  showDisabledActions,
                                  selectionAidMethod == ActionSelectionAid.MNEMONICS,
                                  disposeCallback,
                                  maxRowCount);
  }

  public ListPopupStep createActionsStep(final ActionGroup actionGroup,
                                         @NotNull DataContext dataContext,
                                         final boolean showNumbers,
                                         final boolean showDisabledActions,
                                         final String title,
                                         final Component component,
                                         final boolean honorActionMnemonics) {
    return createActionsStep(actionGroup, dataContext, showNumbers, showDisabledActions, title, component, honorActionMnemonics, 0, false);
  }

  private static ListPopupStep createActionsStep(ActionGroup actionGroup, @NotNull DataContext dataContext,
                                                 boolean showNumbers, boolean useAlphaAsNumbers, boolean showDisabledActions,
                                                 String title, Component component, boolean honorActionMnemonics,
                                                 final int defaultOptionIndex, final boolean autoSelectionEnabled) {
    final List<ActionItem> items = makeActionItemsFromActionGroup(actionGroup, dataContext, showNumbers, useAlphaAsNumbers,
                                                                  showDisabledActions, honorActionMnemonics);
    return new ActionPopupStep(items, title, component, showNumbers || honorActionMnemonics && itemsHaveMnemonics(items),
                               new Condition<AnAction>() {
                                 @Override
                                 public boolean value(AnAction action) {
                                   return defaultOptionIndex >= 0 &&
                                          defaultOptionIndex < items.size() &&
                                          items.get(defaultOptionIndex).getAction().equals(action);
                                 }
                               }, autoSelectionEnabled, showDisabledActions);
  }

  private static List<ActionItem> makeActionItemsFromActionGroup(ActionGroup actionGroup,
                                                                 DataContext dataContext,
                                                                 boolean showNumbers,
                                                                 boolean useAlphaAsNumbers,
                                                                 boolean showDisabledActions,
                                                                 boolean honorActionMnemonics) {
    final ActionStepBuilder builder = new ActionStepBuilder(dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions,
                                                            honorActionMnemonics);
    builder.buildGroup(actionGroup);
    return builder.getItems();
  }

  private static ListPopupStep createActionsStep(ActionGroup actionGroup, @NotNull DataContext dataContext,
                                                 boolean showNumbers, boolean useAlphaAsNumbers, boolean showDisabledActions,
                                                 String title, Component component, boolean honorActionMnemonics,
                                                 Condition<AnAction> preselectActionCondition, boolean autoSelectionEnabled) {
    final List<ActionItem> items = makeActionItemsFromActionGroup(actionGroup, dataContext, showNumbers, useAlphaAsNumbers,
                                                                  showDisabledActions, honorActionMnemonics);
    return new ActionPopupStep(items, title, component, showNumbers || honorActionMnemonics && itemsHaveMnemonics(items), preselectActionCondition,
                               autoSelectionEnabled, showDisabledActions);
  }

  public ListPopupStep createActionsStep(ActionGroup actionGroup, @NotNull DataContext dataContext, boolean showNumbers, boolean showDisabledActions,
                                         String title, Component component, boolean honorActionMnemonics, int defaultOptionIndex,
                                         final boolean autoSelectionEnabled) {
    return createActionsStep(actionGroup, dataContext, showNumbers, true, showDisabledActions, title, component, honorActionMnemonics,
                             defaultOptionIndex, autoSelectionEnabled);
  }

  private static boolean itemsHaveMnemonics(final List<ActionItem> items) {
    for (ActionItem item : items) {
      if (item.getAction().getTemplatePresentation().getMnemonic() != 0) return true;
    }

    return false;
  }

  public ListPopup createWizardStep(PopupStep step) {
    return new ListPopupImpl((ListPopupStep) step);
  }

  public ListPopup createListPopup(ListPopupStep step) {
    return new ListPopupImpl(step);
  }

  public TreePopup createTree(JBPopup parent, TreePopupStep aStep, Object parentValue) {
    return new TreePopupImpl(parent, aStep, parentValue);
  }

  public TreePopup createTree(TreePopupStep aStep) {
    return new TreePopupImpl(aStep);
  }

  public ComponentPopupBuilder createComponentPopupBuilder(JComponent content, JComponent prefferableFocusComponent) {
    return new ComponentPopupBuilderImpl(content, prefferableFocusComponent);
  }

 
  public RelativePoint guessBestPopupLocation(@NotNull DataContext dataContext) {
    KeyboardFocusManager focusManager=KeyboardFocusManager.getCurrentKeyboardFocusManager();
    Component component = focusManager.getFocusOwner();
    JComponent focusOwner=component instanceof JComponent ? (JComponent)component : null;

    if (focusOwner == null) {
      Project project = PlatformDataKeys.PROJECT.getData(dataContext);
      IdeFrameImpl frame = project == null ? null : ((WindowManagerEx)WindowManager.getInstance()).getFrame(project);
      focusOwner = frame == null ? null : frame.getRootPane();
      if (focusOwner == null) {
        throw new IllegalArgumentException("focusOwner cannot be null");
      }
    }

    final Point point = PlatformDataKeys.CONTEXT_MENU_POINT.getData(dataContext);
    if (point != null) {
      return new RelativePoint(focusOwner, point);
    }

    Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
    if (editor != null && focusOwner == editor.getContentComponent()) {
      return guessBestPopupLocation(editor);
    }
    else {
      return guessBestPopupLocation(focusOwner);
    }
  }

  public RelativePoint guessBestPopupLocation(final JComponent component) {
    Point popupMenuPoint = null;
    final Rectangle visibleRect = component.getVisibleRect();
    if (component instanceof JList) { // JList
      JList list = (JList)component;
      int firstVisibleIndex = list.getFirstVisibleIndex();
      int lastVisibleIndex = list.getLastVisibleIndex();
      int[] selectedIndices = list.getSelectedIndices();
      for (int index : selectedIndices) {
        if (firstVisibleIndex <= index && index <= lastVisibleIndex) {
          Rectangle cellBounds = list.getCellBounds(index, index);
          popupMenuPoint = new Point(visibleRect.x + visibleRect.width / 4, cellBounds.y + cellBounds.height);
          break;
        }
      }
    }
    else if (component instanceof JTree) { // JTree
      JTree tree = (JTree)component;
      int[] selectionRows = tree.getSelectionRows();
      for (int i = 0; selectionRows != null && i < selectionRows.length; i++) {
        int row = selectionRows[i];
        Rectangle rowBounds = tree.getRowBounds(row);
        if (visibleRect.y <= rowBounds.y && rowBounds.y <= visibleRect.y + visibleRect.height) {
          popupMenuPoint = new Point(visibleRect.x + visibleRect.width / 4, rowBounds.y + rowBounds.height);
          break;
        }
      }
    } else if (component instanceof PopupOwner){
      popupMenuPoint = ((PopupOwner)component).getBestPopupPosition();<commit_msg>IDEA-71443 Maven sidebar: opening context menu with keyboard selects wrong module<commit_after>/*
 * Copyright 2000-2009 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.CommonBundle;
import com.intellij.ide.DataManager;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.IdeTooltipManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionUtil;
import com.intellij.openapi.actionSystem.impl.ActionMenu;
import com.intellij.openapi.actionSystem.impl.Utils;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.VisualPosition;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.*;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.ex.WindowManagerEx;
import com.intellij.openapi.wm.impl.IdeFrameImpl;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.FocusTrackback;
import com.intellij.ui.HintHint;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.ui.popup.list.ListPopupImpl;
import com.intellij.ui.popup.mock.MockConfirmation;
import com.intellij.ui.popup.tree.TreePopupImpl;
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 javax.swing.event.HyperlinkListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

public class PopupFactoryImpl extends JBPopupFactory {
  private static final Logger LOG = Logger.getInstance("#com.intellij.ui.popup.PopupFactoryImpl");

  private static final Icon QUICK_LIST_ICON = IconLoader.getIcon("/actions/quickList.png");

  public ListPopup createConfirmation(String title, final Runnable onYes, int defaultOptionIndex) {
    return createConfirmation(title, CommonBundle.getYesButtonText(), CommonBundle.getNoButtonText(), onYes, defaultOptionIndex);
  }

  public ListPopup createConfirmation(String title, final String yesText, String noText, final Runnable onYes, int defaultOptionIndex) {
    return createConfirmation(title, yesText, noText, onYes, EmptyRunnable.getInstance(), defaultOptionIndex);
  }

  public JBPopup createMessage(String text) {
    return createListPopup(new BaseListPopupStep<String>(null, new String[]{text})); 
  }

  @Override
  public Balloon getParentBalloonFor(@Nullable Component c) {
    if (c == null) return null;
    Component eachParent = c;
    while (eachParent != null) {
      if (eachParent instanceof JComponent) {
        Object balloon = ((JComponent)eachParent).getClientProperty(Balloon.KEY);
        if (balloon instanceof Balloon) {
          return (Balloon)balloon;
        }
      }
      eachParent = eachParent.getParent();
    }

    return null;
  }

  public ListPopup createConfirmation(String title, final String yesText, String noText, final Runnable onYes, final Runnable onNo, int defaultOptionIndex) {

      final BaseListPopupStep<String> step = new BaseListPopupStep<String>(title, new String[]{yesText, noText}) {
        public PopupStep onChosen(String selectedValue, final boolean finalChoice) {
          if (selectedValue.equals(yesText)) {
            onYes.run();
          }
          else {
            onNo.run();
          }
          return FINAL_CHOICE;
        }

        public void canceled() {
          onNo.run();
        }

        public boolean isMnemonicsNavigationEnabled() {
          return true;
        }
      };
      step.setDefaultOptionIndex(defaultOptionIndex);

    final ApplicationEx app = ApplicationManagerEx.getApplicationEx();
    return app == null || !app.isUnitTestMode() ? new ListPopupImpl(step) : new MockConfirmation(step, yesText);
  }


  private static ListPopup createActionGroupPopup(final String title,
                                          final ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          boolean showNumbers,
                                          boolean useAlphaAsNumbers,
                                          boolean showDisabledActions,
                                          boolean honorActionMnemonics,
                                          final Runnable disposeCallback,
                                          final int maxRowCount) {
    return createActionGroupPopup(title, actionGroup, dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions, honorActionMnemonics, disposeCallback,
                                  maxRowCount, null, null);
  }

  public ListPopup createActionGroupPopup(final String title,
                                          final ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          boolean showNumbers,
                                          boolean showDisabledActions,
                                          boolean honorActionMnemonics,
                                          final Runnable disposeCallback,
                                          final int maxRowCount) {
    return createActionGroupPopup(title, actionGroup, dataContext, showNumbers, showDisabledActions, honorActionMnemonics, disposeCallback,
                                  maxRowCount, null);
  }

  private static ListPopup createActionGroupPopup(final String title,
                                                  final ActionGroup actionGroup,
                                                  @NotNull DataContext dataContext,
                                                  boolean showNumbers,
                                                  boolean useAlphaAsNumbers,
                                                  boolean showDisabledActions,
                                                  boolean honorActionMnemonics,
                                                  final Runnable disposeCallback,
                                                  final int maxRowCount,
                                                  final Condition<AnAction> preselectActionCondition, @Nullable final String actionPlace) {
    return new ActionGroupPopup(title, actionGroup, dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions, honorActionMnemonics,
                                disposeCallback, maxRowCount, preselectActionCondition, actionPlace);
  }

  public static class ActionGroupPopup extends ListPopupImpl {

    private final Runnable myDisposeCallback;
    private final Component myComponent;

    public ActionGroupPopup(final String title, final ActionGroup actionGroup, @NotNull DataContext dataContext,
                            boolean showNumbers, boolean useAlphaAsNumbers, boolean showDisabledActions, boolean honorActionMnemonics,
                            final Runnable disposeCallback, final int maxRowCount, final Condition<AnAction> preselectActionCondition,
                            @Nullable final String actionPlace) {
      super(createStep(title, actionGroup, dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions, honorActionMnemonics,
                       preselectActionCondition, actionPlace),
            maxRowCount);
      myDisposeCallback = disposeCallback;
      myComponent = PlatformDataKeys.CONTEXT_COMPONENT.getData(dataContext);

      addListSelectionListener(new ListSelectionListener() {
        public void valueChanged(ListSelectionEvent e) {
          final JList list = (JList)e.getSource();
          final ActionItem actionItem = (ActionItem)list.getSelectedValue();
          if (actionItem == null) return;
          AnAction action = actionItem.getAction();
          Presentation presentation = new Presentation();
          presentation.setDescription(action.getTemplatePresentation().getDescription());
          final String actualActionPlace = actionPlace == null ? ActionPlaces.UNKNOWN : actionPlace;
          action.update(new AnActionEvent(null, DataManager.getInstance().getDataContext(myComponent), actualActionPlace, presentation,
                                          ActionManager.getInstance(), 0));
          ActionMenu.showDescriptionInStatusBar(true, myComponent, presentation.getDescription());
        }
      });
    }

    private static ListPopupStep createStep(String title, ActionGroup actionGroup, @NotNull DataContext dataContext,
                                            boolean showNumbers, boolean useAlphaAsNumbers, boolean showDisabledActions,
                                            boolean honorActionMnemonics,
                                            Condition<AnAction> preselectActionCondition, @Nullable String actionPlace) {
      final Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(dataContext);

      final ActionStepBuilder builder =
        new ActionStepBuilder(dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions, honorActionMnemonics);
      if (actionPlace != null) {
        builder.setActionPlace(actionPlace);
      }
      builder.buildGroup(actionGroup);
      final List<ActionItem> items = builder.getItems();

      return new ActionPopupStep(items, title, component, showNumbers || honorActionMnemonics && itemsHaveMnemonics(items),
                                 preselectActionCondition, false, showDisabledActions);
    }

    @Override
    public void dispose() {
      if (myDisposeCallback != null) {
        myDisposeCallback.run();
      }
      ActionMenu.showDescriptionInStatusBar(true, myComponent, null);
      super.dispose();
    }
  }

  public ListPopup createActionGroupPopup(final String title,
                                          final ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          boolean showNumbers,
                                          boolean showDisabledActions,
                                          boolean honorActionMnemonics,
                                          final Runnable disposeCallback,
                                          final int maxRowCount,
                                          final Condition<AnAction> preselectActionCondition) {
    return createActionGroupPopup(title, actionGroup, dataContext, showNumbers, true, showDisabledActions, honorActionMnemonics,
                                  disposeCallback, maxRowCount, preselectActionCondition, null);
  }

  public ListPopup createActionGroupPopup(String title,
                                          ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          ActionSelectionAid selectionAidMethod,
                                          boolean showDisabledActions) {
    return createActionGroupPopup(title, actionGroup, dataContext,
                                  selectionAidMethod == ActionSelectionAid.NUMBERING || selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  showDisabledActions,
                                  selectionAidMethod == ActionSelectionAid.MNEMONICS,
                                  null, -1);
  }

  public ListPopup createActionGroupPopup(String title,
                                          ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          ActionSelectionAid selectionAidMethod,
                                          boolean showDisabledActions,
                                          @Nullable String actionPlace) {
    return createActionGroupPopup(title, actionGroup, dataContext,
                                  selectionAidMethod == ActionSelectionAid.NUMBERING || selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  showDisabledActions,
                                  selectionAidMethod == ActionSelectionAid.MNEMONICS,
                                  null, -1, null, actionPlace);
  }

  public ListPopup createActionGroupPopup(String title,
                                          ActionGroup actionGroup,
                                          @NotNull DataContext dataContext,
                                          ActionSelectionAid selectionAidMethod,
                                          boolean showDisabledActions,
                                          Runnable disposeCallback,
                                          int maxRowCount) {
    return createActionGroupPopup(title, actionGroup, dataContext,
                                  selectionAidMethod == ActionSelectionAid.NUMBERING || selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  selectionAidMethod == ActionSelectionAid.ALPHA_NUMBERING,
                                  showDisabledActions,
                                  selectionAidMethod == ActionSelectionAid.MNEMONICS,
                                  disposeCallback,
                                  maxRowCount);
  }

  public ListPopupStep createActionsStep(final ActionGroup actionGroup,
                                         @NotNull DataContext dataContext,
                                         final boolean showNumbers,
                                         final boolean showDisabledActions,
                                         final String title,
                                         final Component component,
                                         final boolean honorActionMnemonics) {
    return createActionsStep(actionGroup, dataContext, showNumbers, showDisabledActions, title, component, honorActionMnemonics, 0, false);
  }

  private static ListPopupStep createActionsStep(ActionGroup actionGroup, @NotNull DataContext dataContext,
                                                 boolean showNumbers, boolean useAlphaAsNumbers, boolean showDisabledActions,
                                                 String title, Component component, boolean honorActionMnemonics,
                                                 final int defaultOptionIndex, final boolean autoSelectionEnabled) {
    final List<ActionItem> items = makeActionItemsFromActionGroup(actionGroup, dataContext, showNumbers, useAlphaAsNumbers,
                                                                  showDisabledActions, honorActionMnemonics);
    return new ActionPopupStep(items, title, component, showNumbers || honorActionMnemonics && itemsHaveMnemonics(items),
                               new Condition<AnAction>() {
                                 @Override
                                 public boolean value(AnAction action) {
                                   return defaultOptionIndex >= 0 &&
                                          defaultOptionIndex < items.size() &&
                                          items.get(defaultOptionIndex).getAction().equals(action);
                                 }
                               }, autoSelectionEnabled, showDisabledActions);
  }

  private static List<ActionItem> makeActionItemsFromActionGroup(ActionGroup actionGroup,
                                                                 DataContext dataContext,
                                                                 boolean showNumbers,
                                                                 boolean useAlphaAsNumbers,
                                                                 boolean showDisabledActions,
                                                                 boolean honorActionMnemonics) {
    final ActionStepBuilder builder = new ActionStepBuilder(dataContext, showNumbers, useAlphaAsNumbers, showDisabledActions,
                                                            honorActionMnemonics);
    builder.buildGroup(actionGroup);
    return builder.getItems();
  }

  private static ListPopupStep createActionsStep(ActionGroup actionGroup, @NotNull DataContext dataContext,
                                                 boolean showNumbers, boolean useAlphaAsNumbers, boolean showDisabledActions,
                                                 String title, Component component, boolean honorActionMnemonics,
                                                 Condition<AnAction> preselectActionCondition, boolean autoSelectionEnabled) {
    final List<ActionItem> items = makeActionItemsFromActionGroup(actionGroup, dataContext, showNumbers, useAlphaAsNumbers,
                                                                  showDisabledActions, honorActionMnemonics);
    return new ActionPopupStep(items, title, component, showNumbers || honorActionMnemonics && itemsHaveMnemonics(items), preselectActionCondition,
                               autoSelectionEnabled, showDisabledActions);
  }

  public ListPopupStep createActionsStep(ActionGroup actionGroup, @NotNull DataContext dataContext, boolean showNumbers, boolean showDisabledActions,
                                         String title, Component component, boolean honorActionMnemonics, int defaultOptionIndex,
                                         final boolean autoSelectionEnabled) {
    return createActionsStep(actionGroup, dataContext, showNumbers, true, showDisabledActions, title, component, honorActionMnemonics,
                             defaultOptionIndex, autoSelectionEnabled);
  }

  private static boolean itemsHaveMnemonics(final List<ActionItem> items) {
    for (ActionItem item : items) {
      if (item.getAction().getTemplatePresentation().getMnemonic() != 0) return true;
    }

    return false;
  }

  public ListPopup createWizardStep(PopupStep step) {
    return new ListPopupImpl((ListPopupStep) step);
  }

  public ListPopup createListPopup(ListPopupStep step) {
    return new ListPopupImpl(step);
  }

  public TreePopup createTree(JBPopup parent, TreePopupStep aStep, Object parentValue) {
    return new TreePopupImpl(parent, aStep, parentValue);
  }

  public TreePopup createTree(TreePopupStep aStep) {
    return new TreePopupImpl(aStep);
  }

  public ComponentPopupBuilder createComponentPopupBuilder(JComponent content, JComponent prefferableFocusComponent) {
    return new ComponentPopupBuilderImpl(content, prefferableFocusComponent);
  }

 
  public RelativePoint guessBestPopupLocation(@NotNull DataContext dataContext) {
    KeyboardFocusManager focusManager=KeyboardFocusManager.getCurrentKeyboardFocusManager();
    Component component = focusManager.getFocusOwner();
    JComponent focusOwner=component instanceof JComponent ? (JComponent)component : null;

    if (focusOwner == null) {
      Project project = PlatformDataKeys.PROJECT.getData(dataContext);
      IdeFrameImpl frame = project == null ? null : ((WindowManagerEx)WindowManager.getInstance()).getFrame(project);
      focusOwner = frame == null ? null : frame.getRootPane();
      if (focusOwner == null) {
        throw new IllegalArgumentException("focusOwner cannot be null");
      }
    }

    final Point point = PlatformDataKeys.CONTEXT_MENU_POINT.getData(dataContext);
    if (point != null) {
      return new RelativePoint(focusOwner, point);
    }

    Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
    if (editor != null && focusOwner == editor.getContentComponent()) {
      return guessBestPopupLocation(editor);
    }
    else {
      return guessBestPopupLocation(focusOwner);
    }
  }

  public RelativePoint guessBestPopupLocation(final JComponent component) {
    Point popupMenuPoint = null;
    final Rectangle visibleRect = component.getVisibleRect();
    if (component instanceof JList) { // JList
      JList list = (JList)component;
      int firstVisibleIndex = list.getFirstVisibleIndex();
      int lastVisibleIndex = list.getLastVisibleIndex();
      int[] selectedIndices = list.getSelectedIndices();
      for (int index : selectedIndices) {
        if (firstVisibleIndex <= index && index <= lastVisibleIndex) {
          Rectangle cellBounds = list.getCellBounds(index, index);
          popupMenuPoint = new Point(visibleRect.x + visibleRect.width / 4, cellBounds.y + cellBounds.height);
          break;
        }
      }
    }
    else if (component instanceof JTree) { // JTree
      JTree tree = (JTree)component;
      int[] selectionRows = tree.getSelectionRows();
      if (selectionRows != null) {
        Arrays.sort(selectionRows);
        for (int i = 0; i < selectionRows.length; i++) {
          int row = selectionRows[i];
          Rectangle rowBounds = tree.getRowBounds(row);
          if (visibleRect.contains(rowBounds)) {
            popupMenuPoint = new Point(rowBounds.x + 2, rowBounds.y + rowBounds.height - 1);
            break;
          }
        }
        if (popupMenuPoint == null) {//All selected rows are out of visible rect
          Point visibleCenter = new Point(visibleRect.x + visibleRect.width / 2, visibleRect.y + visibleRect.height / 2);
          double minDistance = Double.POSITIVE_INFINITY;
          int bestRow = -1;
          Point rowCenter;
          double distance;
          for (int i = 0; i < selectionRows.length; i++) {
            int row = selectionRows[i];
            Rectangle rowBounds = tree.getRowBounds(row);
            rowCenter = new Point(rowBounds.x + rowBounds.width / 2, rowBounds.y + rowBounds.height / 2);
            distance = visibleCenter.distance(rowCenter);
            if (minDistance > distance) {
              minDistance = distance;
              bestRow = row;
            }
          }

          if (bestRow != -1) {
            Rectangle rowBounds = tree.getRowBounds(bestRow);
            tree.scrollRectToVisible(new Rectangle(rowBounds.x, rowBounds.y, Math.min(visibleRect.width, rowBounds.width), rowBounds.height));
            popupMenuPoint = new Point(rowBounds.x + 2, rowBounds.y + rowBounds.height - 1);
          }
        }
      }
    } else if (component instanceof PopupOwner){
      popupMenuPoint = ((PopupOwner)component).getBestPopupPosition();
{'fraction_non_alphanumeric': 0.06540245150334197, 'fraction_numerical': 0.0012409919226666086, 'mean_word_length': 2.04487901889294, 'pattern_counts': {'":': 0, '<': 42, '<?xml version=': 0, '>': 34, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 350, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5630029', 'n_tokens_mistral': 10176, 'n_tokens_neox': 9913, 'n_words': 2438}