private boolean ensureSelectionExists() {
    if (myList.getSelectedIndex() < 0 || myList.getSelectedIndex() >= myList.getModel().getSize()) {
      if (myList.getModel().getSize() >= 0) {
        myList.setSelectedIndex(0);
        return false;
      }
    }

    return true;
  }
  private void suggestCompletion(final boolean selectReplacedText, final boolean isExplicitCall) {
    if (isExplicitCall) {
      myAutopopup = true;
    }

    if (!getField().isFocusOwner()) return;

    final CompletionResult result = new CompletionResult();
    if (myList != null && myCurrentCompletion != null) {
      int index = myList.getSelectedIndex();
      if (index >= 0 && index < myList.getModel().getSize()) {
        result.myPreselected = (LookupFile) myList.getSelectedValue();
      }
    }

    myUiUpdater.queue(
        new Update("textField.suggestCompletion") {
          public void run() {
            final String completionBase = getCompletionBase();
            if (completionBase != null) {
              final LookupFile file = myFinder.find(completionBase);
              if (file != null && !file.isDirectory()) {
                // we've entered a complete path already, no need to autopopup completion again
                // (IDEA-78996)
                return;
              }
            }
            result.myCompletionBase = completionBase;
            if (result.myCompletionBase == null) return;
            result.myFieldText = myPathTextField.getText();
            ApplicationManager.getApplication()
                .executeOnPooledThread(
                    new Runnable() {
                      public void run() {
                        processCompletion(result);
                        SwingUtilities.invokeLater(
                            new Runnable() {
                              public void run() {
                                if (!result.myCompletionBase.equals(getCompletionBase())) return;

                                int pos = selectCompletionRemoveText(result, selectReplacedText);

                                showCompletionPopup(result, pos, isExplicitCall);
                              }
                            });
                      }
                    });
          }
        });
  }
 private boolean isPopupShowing() {
   return myCurrentPopup != null && myList != null && myList.isShowing();
 }
 private @Nullable LookupFile getSelectedFileFromCompletionPopup() {
   if (myList == null) return null;
   return (LookupFile) myList.getSelectedValue();
 }
  private void showCompletionPopup(
      final CompletionResult result, int position, boolean isExplicit) {
    if (myList == null) {
      myList = new JBList();
      myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

      myList.setCellRenderer(
          new GroupedItemsListRenderer(
              new ListItemDescriptor() {
                public String getTextFor(final Object value) {
                  final LookupFile file = (LookupFile) value;

                  if (file.getMacro() != null) {
                    return file.getMacro();
                  } else {
                    return (myCurrentCompletion != null
                                && myCurrentCompletion.myKidsAfterSeparator.contains(file)
                            ? myFinder.getSeparator()
                            : "")
                        + file.getName();
                  }
                }

                public String getTooltipFor(final Object value) {
                  return null;
                }

                public Icon getIconFor(final Object value) {
                  final LookupFile file = (LookupFile) value;
                  return file.getIcon();
                }

                @Nullable
                private Separator getSeparatorAboveOf(Object value) {
                  if (myCurrentCompletion == null) return null;
                  final LookupFile file = (LookupFile) value;

                  final int fileIndex = myCurrentCompletion.myToComplete.indexOf(file);
                  if (fileIndex > 0 && !myCurrentCompletion.myMacros.contains(file)) {
                    final LookupFile prev = myCurrentCompletion.myToComplete.get(fileIndex - 1);
                    if (myCurrentCompletion.myMacros.contains(prev)) {
                      return new Separator("");
                    }
                  }

                  if (myCurrentCompletion.myKidsAfterSeparator.indexOf(file) == 0
                      && myCurrentCompletion.mySiblings.size() > 0) {
                    final LookupFile parent = file.getParent();
                    return parent == null ? new Separator("") : new Separator(parent.getName());
                  }

                  if (myCurrentCompletion.myMacros.size() > 0 && fileIndex == 0) {
                    return new Separator(
                        IdeBundle.message("file.chooser.completion.path.variables.text"));
                  }

                  return null;
                }

                public boolean hasSeparatorAboveOf(final Object value) {
                  return getSeparatorAboveOf(value) != null;
                }

                public String getCaptionAboveOf(final Object value) {
                  final FileTextFieldImpl.Separator separator = getSeparatorAboveOf(value);
                  return separator != null ? separator.getText() : null;
                }
              }));
    }

    if (myCurrentPopup != null) {
      closePopup();
    }

    myCurrentCompletion = result;
    myCurrentCompletionsPos = position;

    if (myCurrentCompletion.myToComplete.size() == 0) {
      showNoSuggestions(isExplicit);
      return;
    }

    myList.setModel(
        new AbstractListModel() {
          public int getSize() {
            return myCurrentCompletion.myToComplete.size();
          }

          public Object getElementAt(final int index) {
            return myCurrentCompletion.myToComplete.get(index);
          }
        });
    myList.getSelectionModel().clearSelection();
    final PopupChooserBuilder builder = JBPopupFactory.getInstance().createListPopupBuilder(myList);
    builder.addListener(
        new JBPopupListener() {
          public void beforeShown(LightweightWindowEvent event) {
            myPathTextField.registerKeyboardAction(
                myCancelAction,
                KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
                JComponent.WHEN_IN_FOCUSED_WINDOW);
            for (Action each : myDisabledTextActions) {
              each.setEnabled(false);
            }
          }

          public void onClosed(LightweightWindowEvent event) {
            myPathTextField.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
            for (Action each : myDisabledTextActions) {
              each.setEnabled(true);
            }
          }
        });

    myCurrentPopup =
        builder
            .setRequestFocus(false)
            .setAdText(getAdText(myCurrentCompletion))
            .setAutoSelectIfEmpty(false)
            .setResizable(false)
            .setCancelCallback(
                new Computable<Boolean>() {
                  public Boolean compute() {
                    final int caret = myPathTextField.getCaretPosition();
                    myPathTextField.setSelectionStart(caret);
                    myPathTextField.setSelectionEnd(caret);
                    myPathTextField.setFocusTraversalKeysEnabled(true);
                    SwingUtilities.invokeLater(
                        new Runnable() {
                          public void run() {
                            getField().requestFocus();
                          }
                        });
                    return Boolean.TRUE;
                  }
                })
            .setItemChoosenCallback(
                new Runnable() {
                  public void run() {
                    processChosenFromCompletion(false);
                  }
                })
            .setCancelKeyEnabled(false)
            .setAlpha(0.1f)
            .setFocusOwners(new Component[] {myPathTextField})
            .createPopup();

    if (result.myPreselected != null) {
      myList.setSelectedValue(result.myPreselected, false);
    }

    myPathTextField.setFocusTraversalKeysEnabled(false);

    myCurrentPopup.showInScreenCoordinates(getField(), getLocationForCaret(myPathTextField));
  }
  @NotNull
  @Override
  public RelativePoint guessBestPopupLocation(@NotNull 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 JTable) {
      JTable table = (JTable) component;
      int column = table.getColumnModel().getSelectionModel().getLeadSelectionIndex();
      int row =
          Math.max(
              table.getSelectionModel().getLeadSelectionIndex(),
              table.getSelectionModel().getAnchorSelectionIndex());
      Rectangle rect = table.getCellRect(row, column, false);
      if (!visibleRect.intersects(rect)) {
        table.scrollRectToVisible(rect);
      }
      popupMenuPoint = new Point(rect.x, rect.y + rect.height);
    } else if (component instanceof PopupOwner) {
      popupMenuPoint = ((PopupOwner) component).getBestPopupPosition();
    }
    if (popupMenuPoint == null) {
      popupMenuPoint =
          new Point(visibleRect.x + visibleRect.width / 2, visibleRect.y + visibleRect.height / 2);
    }

    return new RelativePoint(component, popupMenuPoint);
  }