private JComponent createLogo() {
      NonOpaquePanel panel = new NonOpaquePanel(new BorderLayout());
      ApplicationInfoEx app = ApplicationInfoEx.getInstanceEx();
      JLabel logo = new JLabel(IconLoader.getIcon(app.getWelcomeScreenLogoUrl()));
      logo.setBorder(JBUI.Borders.empty(30, 0, 10, 0));
      logo.setHorizontalAlignment(SwingConstants.CENTER);
      panel.add(logo, BorderLayout.NORTH);
      JLabel appName = new JLabel(ApplicationNamesInfo.getInstance().getFullProductName());
      Font font = getProductFont();
      appName.setForeground(JBColor.foreground());
      appName.setFont(font.deriveFont(JBUI.scale(36f)).deriveFont(Font.PLAIN));
      appName.setHorizontalAlignment(SwingConstants.CENTER);
      String appVersion = "Version " + app.getFullVersion();

      if (app.isEAP() && app.getBuild().getBuildNumber() < Integer.MAX_VALUE) {
        appVersion += " (" + app.getBuild().asString() + ")";
      }

      JLabel version = new JLabel(appVersion);
      version.setFont(getProductFont().deriveFont(JBUI.scale(16f)));
      version.setHorizontalAlignment(SwingConstants.CENTER);
      version.setForeground(Gray._128);

      panel.add(appName);
      panel.add(version, BorderLayout.SOUTH);
      panel.setBorder(JBUI.Borders.emptyBottom(20));
      return panel;
    }
  /** Sets current LAF. The method doesn't update component hierarchy. */
  @Override
  public void setCurrentLookAndFeel(UIManager.LookAndFeelInfo lookAndFeelInfo) {
    if (findLaf(lookAndFeelInfo.getClassName()) == null) {
      LOG.error("unknown LookAndFeel : " + lookAndFeelInfo);
      return;
    }
    // Set L&F
    if (IdeaLookAndFeelInfo.CLASS_NAME.equals(
        lookAndFeelInfo.getClassName())) { // that is IDEA default LAF
      IdeaLaf laf = new IdeaLaf();
      MetalLookAndFeel.setCurrentTheme(new IdeaBlueMetalTheme());
      try {
        UIManager.setLookAndFeel(laf);
      } catch (Exception e) {
        Messages.showMessageDialog(
            IdeBundle.message(
                "error.cannot.set.look.and.feel", lookAndFeelInfo.getName(), e.getMessage()),
            CommonBundle.getErrorTitle(),
            Messages.getErrorIcon());
        return;
      }
    } else if (DarculaLookAndFeelInfo.CLASS_NAME.equals(lookAndFeelInfo.getClassName())) {
      DarculaLaf laf = new DarculaLaf();
      try {
        UIManager.setLookAndFeel(laf);
        JBColor.setDark(true);
        IconLoader.setUseDarkIcons(true);
      } catch (Exception e) {
        Messages.showMessageDialog(
            IdeBundle.message(
                "error.cannot.set.look.and.feel", lookAndFeelInfo.getName(), e.getMessage()),
            CommonBundle.getErrorTitle(),
            Messages.getErrorIcon());
        return;
      }
    } else { // non default LAF
      try {
        LookAndFeel laf =
            ((LookAndFeel) Class.forName(lookAndFeelInfo.getClassName()).newInstance());
        if (laf instanceof MetalLookAndFeel) {
          MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme());
        }
        UIManager.setLookAndFeel(laf);
      } catch (Exception e) {
        Messages.showMessageDialog(
            IdeBundle.message(
                "error.cannot.set.look.and.feel", lookAndFeelInfo.getName(), e.getMessage()),
            CommonBundle.getErrorTitle(),
            Messages.getErrorIcon());
        return;
      }
    }
    myCurrentLaf =
        ObjectUtils.chooseNotNull(findLaf(lookAndFeelInfo.getClassName()), lookAndFeelInfo);

    checkLookAndFeel(lookAndFeelInfo, false);
  }
  private static Color getTypeTextColor(
      LookupElement item,
      Color foreground,
      LookupElementPresentation presentation,
      boolean selected,
      boolean nonFocusedSelection) {
    if (nonFocusedSelection) {
      return foreground;
    }

    return presentation.isTypeGrayed()
        ? getGrayedForeground(selected)
        : item instanceof EmptyLookupItem ? JBColor.foreground() : foreground;
  }
  public JavaFxHtmlPanel() {
    // System.setProperty("prism.lcdtext", "false");
    // System.setProperty("prism.text", "t2k");
    myPanelWrapper = new JPanel(new BorderLayout());
    myPanelWrapper.setBackground(JBColor.background());

    ApplicationManager.getApplication()
        .invokeLater(
            () ->
                PlatformImpl.startup(
                    () -> {
                      myWebView = new WebView();

                      updateFontSmoothingType(
                          myWebView,
                          MarkdownApplicationSettings.getInstance()
                              .getMarkdownPreviewSettings()
                              .isUseGrayscaleRendering());
                      myWebView.setContextMenuEnabled(false);

                      final WebEngine engine = myWebView.getEngine();
                      engine.getLoadWorker().stateProperty().addListener(myBridgeSettingListener);
                      engine
                          .getLoadWorker()
                          .stateProperty()
                          .addListener(myScrollPreservingListener);

                      final Scene scene = new Scene(myWebView);

                      ApplicationManager.getApplication()
                          .invokeLater(
                              () -> {
                                myPanel = new JFXPanelWrapper();
                                myPanel.setScene(scene);

                                setHtml("");
                                for (Runnable action : myInitActions) {
                                  Platform.runLater(action);
                                }
                                myInitActions.clear();

                                myPanelWrapper.add(myPanel, BorderLayout.CENTER);
                                myPanelWrapper.repaint();
                              });
                    }));

    subscribeForGrayscaleSetting();
  }
  private void determineColors() {
    switch (getWindowDecorationStyle()) {
      case JRootPane.FRAME:
        myActiveBackground = UIManager.getColor("activeCaption");
        myActiveForeground = UIManager.getColor("activeCaptionText");
        myActiveShadow = UIManager.getColor("activeCaptionBorder");
        break;
      case JRootPane.ERROR_DIALOG:
        myActiveBackground =
            new Color(
                43, 43, 43); // UIManager.getColor("OptionPane.errorDialog.titlePane.background");
        myActiveForeground = UIManager.getColor("OptionPane.errorDialog.titlePane.foreground");
        myActiveShadow = UIManager.getColor("OptionPane.errorDialog.titlePane.shadow");
        break;
      case JRootPane.QUESTION_DIALOG:
      case JRootPane.COLOR_CHOOSER_DIALOG:
      case JRootPane.FILE_CHOOSER_DIALOG:
        myActiveBackground =
            new Color(
                43, 43,
                43); // UIManager.getColor("OptionPane.questionDialog.titlePane.background");
        myActiveForeground = UIManager.getColor("OptionPane.questionDialog.titlePane.foreground");
        myActiveShadow = UIManager.getColor("OptionPane.questionDialog.titlePane.shadow");
        break;
      case JRootPane.WARNING_DIALOG:
        myActiveBackground =
            new Color(
                43, 43, 43); // UIManager.getColor("OptionPane.warningDialog.titlePane.background");
        myActiveForeground = UIManager.getColor("OptionPane.warningDialog.titlePane.foreground");
        myActiveShadow = UIManager.getColor("OptionPane.warningDialog.titlePane.shadow");
        break;
      case JRootPane.PLAIN_DIALOG:
      case JRootPane.INFORMATION_DIALOG:
      default:
        myActiveBackground = new Color(43, 43, 43); // UIManager.getColor("activeCaption");
        myActiveForeground = UIManager.getColor("activeCaptionText");
        myActiveShadow = UIManager.getColor("activeCaptionBorder");
        break;
    }

    myActiveBackground = new Color(43, 43, 43); // UIManager.getColor("activeCaption");
    myActiveForeground = JBColor.foreground(); // UIManager.getColor("activeCaptionText");
    myActiveShadow = UIManager.getColor("activeCaptionBorder");
  }
    private String setup(
        @NotNull String text,
        @NotNull Map<TextRange, ParameterInfoUIContextEx.Flag> flagsMap,
        @NotNull Color background) {
      myLabel.setBackground(background);
      setBackground(background);

      myLabel.setForeground(JBColor.foreground());

      if (flagsMap.isEmpty()) {
        myLabel.setText(XmlStringUtil.wrapInHtml(text));
      } else {
        String labelText = buildLabelText(text, flagsMap);
        myLabel.setText(labelText);
      }

      // IDEA-95904 Darcula parameter info pop-up colors hard to read
      if (UIUtil.isUnderDarcula()) {
        myLabel.setText(myLabel.getText().replace("<b>", "<b color=ffC800>"));
      }
      return myLabel.getText();
    }
  public void activate() {
    try {
      UIManager.setLookAndFeel(new MTLaf(this));
      JBColor.setDark(dark);
      IconLoader.setUseDarkIcons(dark);

      PropertiesComponent.getInstance().setValue(getSettingsPrefix() + ".theme", name());
    } catch (UnsupportedLookAndFeelException e) {
      e.printStackTrace();
    }

    String currentScheme = EditorColorsManager.getInstance().getGlobalScheme().getName();

    String makeActiveScheme =
        !EDITOR_COLORS_SCHEMES.contains(currentScheme) ? currentScheme : editorColorsScheme;

    EditorColorsScheme scheme = EditorColorsManager.getInstance().getScheme(makeActiveScheme);
    if (scheme != null) {
      EditorColorsManager.getInstance().setGlobalScheme(scheme);
    }

    UISettings.getInstance().fireUISettingsChanged();
    ActionToolbarImpl.updateAllToolbarsImmediately();
  }
  public CustomizePluginsStepPanel() {
    myCardLayout = new JBCardLayout();
    setLayout(myCardLayout);
    JPanel gridPanel = new JPanel(new GridLayout(0, COLS));
    myCustomizePanel = new IdSetPanel();
    JBScrollPane scrollPane =
        new JBScrollPane(
            gridPanel,
            ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    scrollPane.getVerticalScrollBar().setUnitIncrement(10);
    add(scrollPane, MAIN);
    add(myCustomizePanel, CUSTOMIZE);

    // PluginManager.loadDisabledPlugins(new File(PathManager.getConfigPath()).getPath(),
    // myDisabledPluginIds);
    // for (IdeaPluginDescriptor pluginDescriptor : myAllPlugins) {
    //  if (pluginDescriptor.getPluginId().getIdString().equals("com.intellij")) {
    ////    skip 'IDEA CORE' plugin
    // continue;
    // }
    //  //PluginManager.initClassLoader(PluginGroups.class.getClassLoader(),
    // (IdeaPluginDescriptorImpl)pluginDescriptor);
    // }
    Map<String, List<String>> groups = PluginGroups.getInstance().getTree();
    for (Map.Entry<String, List<String>> entry : groups.entrySet()) {
      final String group = entry.getKey();
      if (PluginGroups.CORE.equals(group)) continue;

      JPanel groupPanel = new JPanel(new GridBagLayout());
      GridBagConstraints gbc = new GridBagConstraints();
      gbc.insets = new Insets(0, 0, 10, 0);
      gbc.fill = GridBagConstraints.BOTH;
      gbc.gridwidth = GridBagConstraints.REMAINDER;
      gbc.weightx = 1;
      JLabel titleLabel =
          new JLabel(
              "<html><body><h2 style=\"text-align:left;\">" + group + "</h2></body></html>") {
            @Override
            public boolean isEnabled() {
              return isGroupEnabled(group);
            }
          };
      groupPanel.add(titleLabel, gbc);
      JLabel descriptionLabel =
          new JLabel(PluginGroups.getInstance().getDescription(group)) {
            @Override
            public Dimension getPreferredSize() {
              Dimension size = super.getPreferredSize();
              size.width = Math.min(size.width, 200);
              return size;
            }

            @Override
            public boolean isEnabled() {
              return isGroupEnabled(group);
            }

            @Override
            public Color getForeground() {
              return ColorUtil.withAlpha(UIManager.getColor("Label.foreground"), .75);
            }
          };
      groupPanel.add(descriptionLabel, gbc);
      gbc.weighty = 1;
      groupPanel.add(Box.createVerticalGlue(), gbc);
      gbc.weighty = 0;
      if (PluginGroups.getInstance().getSets(group).size() == 1) {
        groupPanel.add(
            createLink(SWITCH_COMMAND + ":" + group, getGroupSwitchTextProvider(group)), gbc);
      } else {
        JPanel buttonsPanel = new JPanel(new GridLayout(1, 2, 10, 5));
        LinkLabel customizeButton =
            createLink(CUSTOMIZE_COMMAND + ":" + group, CUSTOMIZE_TEXT_PROVIDER);
        buttonsPanel.add(customizeButton);
        LinkLabel disableAllButton =
            createLink(SWITCH_COMMAND + ":" + group, getGroupSwitchTextProvider(group));
        buttonsPanel.add(disableAllButton);
        groupPanel.add(buttonsPanel, gbc);
      }
      gridPanel.add(groupPanel);
    }

    int cursor = 0;
    Component[] components = gridPanel.getComponents();
    int rowCount = components.length / COLS;
    for (Component component : components) {
      ((JComponent) component)
          .setBorder(
              new CompoundBorder(
                  new CustomLineBorder(
                      ColorUtil.withAlpha(JBColor.foreground(), .2),
                      0,
                      0,
                      cursor / 3 < rowCount - 1 ? 1 : 0,
                      cursor % COLS != COLS - 1 ? 1 : 0) {
                    @Override
                    protected Color getColor() {
                      return ColorUtil.withAlpha(JBColor.foreground(), .2);
                    }
                  },
                  BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP)));
      cursor++;
    }
  }
/** @author peter */
public class LookupElementPresentation {
  private Icon myIcon;
  private Icon myTypeIcon;
  private String myItemText;
  private String myTypeText;
  private boolean myStrikeout;
  private Color myItemTextForeground = JBColor.foreground();
  private boolean myItemTextBold;
  private boolean myItemTextUnderlined;
  private boolean myTypeGrayed;
  @Nullable private List<TextFragment> myTail;

  public void setIcon(@Nullable Icon icon) {
    myIcon = icon;
  }

  public void setItemText(@Nullable String text) {
    myItemText = text;
  }

  public void setStrikeout(boolean strikeout) {
    myStrikeout = strikeout;
  }

  public void setItemTextBold(boolean bold) {
    myItemTextBold = bold;
  }

  public void setTailText(@Nullable String text) {
    setTailText(text, false);
  }

  public void clearTail() {
    myTail = null;
  }

  public void appendTailText(@NotNull String text, boolean grayed) {
    appendTailText(new TextFragment(text, grayed, null));
  }

  private void appendTailText(@NotNull TextFragment fragment) {
    if (myTail == null) {
      myTail = new SmartList<TextFragment>();
    }
    myTail.add(fragment);
  }

  public void setTailText(@Nullable String text, boolean grayed) {
    clearTail();
    if (text != null) {
      appendTailText(new TextFragment(text, grayed, null));
    }
  }

  public void setTailText(@Nullable String text, @Nullable Color foreground) {
    clearTail();
    if (text != null) {
      appendTailText(new TextFragment(text, false, foreground));
    }
  }

  public void setTypeText(@Nullable String text) {
    setTypeText(text, null);
  }

  public void setTypeText(@Nullable String text, @Nullable Icon icon) {
    myTypeText = text;
    myTypeIcon = icon;
  }

  /**
   * Is equivalent to instanceof {@link
   * com.intellij.codeInsight.lookup.RealLookupElementPresentation} check.
   *
   * @return whether the presentation is requested to actually render lookup element on screen, or
   *     just to estimate its width. In the second, 'non-real' case, some heavy operations (e.g.
   *     getIcon()) can be omitted (only icon width is important)
   */
  public boolean isReal() {
    return false;
  }

  @Nullable
  public Icon getIcon() {
    return myIcon;
  }

  @Nullable
  public Icon getTypeIcon() {
    return myTypeIcon;
  }

  @Nullable
  public String getItemText() {
    return myItemText;
  }

  @NotNull
  public List<TextFragment> getTailFragments() {
    return myTail == null
        ? Collections.<TextFragment>emptyList()
        : Collections.unmodifiableList(myTail);
  }

  @Nullable
  public String getTailText() {
    if (myTail == null) return null;
    return StringUtil.join(myTail, fragment -> fragment.text, "");
  }

  @Nullable
  public String getTypeText() {
    return myTypeText;
  }

  public boolean isStrikeout() {
    return myStrikeout;
  }

  @Deprecated
  public boolean isTailGrayed() {
    return myTail != null && myTail.get(0).grayed;
  }

  @Nullable
  @Deprecated
  public Color getTailForeground() {
    return myTail != null ? myTail.get(0).fgColor : null;
  }

  public boolean isItemTextBold() {
    return myItemTextBold;
  }

  public boolean isItemTextUnderlined() {
    return myItemTextUnderlined;
  }

  public void setItemTextUnderlined(boolean itemTextUnderlined) {
    myItemTextUnderlined = itemTextUnderlined;
  }

  @NotNull
  public Color getItemTextForeground() {
    return myItemTextForeground;
  }

  public void setItemTextForeground(@NotNull Color itemTextForeground) {
    myItemTextForeground = itemTextForeground;
  }

  public void copyFrom(@NotNull LookupElementPresentation presentation) {
    myIcon = presentation.myIcon;
    myTypeIcon = presentation.myTypeIcon;
    myItemText = presentation.myItemText;

    List<TextFragment> thatTail = presentation.myTail;
    myTail = thatTail == null ? null : new SmartList<TextFragment>(thatTail);

    myTypeText = presentation.myTypeText;
    myStrikeout = presentation.myStrikeout;
    myItemTextBold = presentation.myItemTextBold;
    myTypeGrayed = presentation.myTypeGrayed;
    myItemTextUnderlined = presentation.myItemTextUnderlined;
    myItemTextForeground = presentation.myItemTextForeground;
  }

  public boolean isTypeGrayed() {
    return myTypeGrayed;
  }

  public void setTypeGrayed(boolean typeGrayed) {
    myTypeGrayed = typeGrayed;
  }

  public static LookupElementPresentation renderElement(LookupElement element) {
    LookupElementPresentation presentation = new LookupElementPresentation();
    element.renderElement(presentation);
    return presentation;
  }

  @Override
  public String toString() {
    return "LookupElementPresentation{"
        + ", itemText='"
        + myItemText
        + '\''
        + ", tail="
        + myTail
        + ", typeText='"
        + myTypeText
        + '\''
        + '}';
  }

  public static class TextFragment {
    public final String text;
    private final boolean grayed;
    @Nullable private final Color fgColor;

    public TextFragment(String text, boolean grayed, @Nullable Color fgColor) {
      this.text = text;
      this.grayed = grayed;
      this.fgColor = fgColor;
    }

    @Override
    public String toString() {
      return "TextFragment{"
          + "text='"
          + text
          + '\''
          + ", grayed="
          + grayed
          + ", fgColor="
          + fgColor
          + '}';
    }

    public boolean isGrayed() {
      return grayed;
    }

    @Nullable
    public Color getForegroundColor() {
      return fgColor;
    }
  }
}
/**
 * @author peter
 * @author Konstantin Bulenkov
 */
public class LookupCellRenderer implements ListCellRenderer {
  private static final Logger LOG =
      Logger.getInstance("#com.intellij.codeInsight.lookup.impl.LookupCellRenderer");
  // TODO[kb]: move all these awesome constants to Editor's Fonts & Colors settings
  private static final int AFTER_TAIL = 10;
  private static final int AFTER_TYPE = 6;
  private Icon myEmptyIcon = EmptyIcon.create(5);
  private final Font myNormalFont;
  private final Font myBoldFont;
  private final FontMetrics myNormalMetrics;
  private final FontMetrics myBoldMetrics;

  public static final Color BACKGROUND_COLOR =
      new JBColor(new Color(235, 244, 254), JBColor.background());
  private static final Color FOREGROUND_COLOR = JBColor.foreground();
  private static final Color GRAYED_FOREGROUND_COLOR = new JBColor(Gray._160, Gray._110);
  private static final Color SELECTED_BACKGROUND_COLOR = new Color(0, 82, 164);
  private static final Color SELECTED_NON_FOCUSED_BACKGROUND_COLOR =
      new JBColor(new Color(110, 142, 162), new Color(85, 88, 90));
  private static final Color SELECTED_FOREGROUND_COLOR =
      new JBColor(JBColor.WHITE, JBColor.foreground());
  private static final Color SELECTED_GRAYED_FOREGROUND_COLOR =
      new JBColor(JBColor.WHITE, JBColor.foreground());

  static final Color PREFIX_FOREGROUND_COLOR =
      new JBColor(new Color(176, 0, 176), new Color(209, 122, 214));
  private static final Color SELECTED_PREFIX_FOREGROUND_COLOR =
      new JBColor(new Color(249, 236, 204), new Color(209, 122, 214));

  private final LookupImpl myLookup;

  private final SimpleColoredComponent myNameComponent;
  private final SimpleColoredComponent myTailComponent;
  private final SimpleColoredComponent myTypeLabel;
  private final LookupPanel myPanel;
  private final Map<Integer, Boolean> mySelected = new HashMap<Integer, Boolean>();

  private static final String ELLIPSIS = "\u2026";
  private int myMaxWidth = -1;

  public LookupCellRenderer(LookupImpl lookup) {
    EditorColorsScheme scheme = lookup.getEditor().getColorsScheme();
    myNormalFont = scheme.getFont(EditorFontType.PLAIN);
    myBoldFont = scheme.getFont(EditorFontType.BOLD);

    myLookup = lookup;
    myNameComponent = new MySimpleColoredComponent();
    myNameComponent.setIpad(new Insets(0, 0, 0, 0));

    myTailComponent = new MySimpleColoredComponent();
    myTailComponent.setIpad(new Insets(0, 0, 0, 0));

    myTypeLabel = new MySimpleColoredComponent();
    myTypeLabel.setIpad(new Insets(0, 0, 0, 0));

    myPanel = new LookupPanel();
    myPanel.add(myNameComponent, BorderLayout.WEST);
    myPanel.add(myTailComponent, BorderLayout.CENTER);
    myTailComponent.setBorder(new EmptyBorder(0, 0, 0, AFTER_TAIL));

    myPanel.add(myTypeLabel, BorderLayout.EAST);
    myTypeLabel.setBorder(new EmptyBorder(0, 0, 0, AFTER_TYPE));

    myNormalMetrics = myLookup.getEditor().getComponent().getFontMetrics(myNormalFont);
    myBoldMetrics = myLookup.getEditor().getComponent().getFontMetrics(myBoldFont);
  }

  private boolean myIsSelected = false;

  @Override
  public Component getListCellRendererComponent(
      final JList list, Object value, int index, boolean isSelected, boolean hasFocus) {

    boolean nonFocusedSelection =
        isSelected && myLookup.getFocusDegree() == LookupImpl.FocusDegree.SEMI_FOCUSED;
    if (!myLookup.isFocused()) {
      isSelected = false;
    }

    myIsSelected = isSelected;
    final LookupElement item = (LookupElement) value;
    final Color foreground = getForegroundColor(isSelected);
    final Color background =
        nonFocusedSelection
            ? SELECTED_NON_FOCUSED_BACKGROUND_COLOR
            : isSelected ? SELECTED_BACKGROUND_COLOR : BACKGROUND_COLOR;

    int allowedWidth = list.getWidth() - AFTER_TAIL - AFTER_TYPE - getIconIndent();

    FontMetrics normalMetrics = getRealFontMetrics(item, false);
    FontMetrics boldMetrics = getRealFontMetrics(item, true);
    final LookupElementPresentation presentation =
        new RealLookupElementPresentation(
            isSelected ? getMaxWidth() : allowedWidth, normalMetrics, boldMetrics, myLookup);
    AccessToken token = ReadAction.start();
    try {
      if (item.isValid()) {
        try {
          item.renderElement(presentation);
        } catch (Exception e) {
          LOG.error(e);
        } catch (Error e) {
          LOG.error(e);
        }
      } else {
        presentation.setItemTextForeground(JBColor.RED);
        presentation.setItemText("Invalid");
      }
    } finally {
      token.finish();
    }

    myNameComponent.clear();
    myNameComponent.setIcon(augmentIcon(presentation.getIcon(), myEmptyIcon));
    myNameComponent.setBackground(background);
    allowedWidth -=
        setItemTextLabel(
            item,
            new JBColor(
                isSelected ? SELECTED_FOREGROUND_COLOR : presentation.getItemTextForeground(),
                foreground),
            isSelected,
            presentation,
            allowedWidth);

    Font customFont = myLookup.getCustomFont(item, false);
    myTailComponent.setFont(customFont != null ? customFont : myNormalFont);
    myTypeLabel.setFont(customFont != null ? customFont : myNormalFont);

    myTypeLabel.clear();
    if (allowedWidth > 0) {
      allowedWidth -=
          setTypeTextLabel(
              item,
              background,
              foreground,
              presentation,
              isSelected ? getMaxWidth() : allowedWidth,
              isSelected,
              nonFocusedSelection,
              normalMetrics);
    }

    myTailComponent.clear();
    myTailComponent.setBackground(background);
    if (isSelected || allowedWidth >= 0) {
      setTailTextLabel(
          isSelected,
          presentation,
          foreground,
          isSelected ? getMaxWidth() : allowedWidth,
          nonFocusedSelection,
          normalMetrics);
    }

    if (mySelected.containsKey(index)) {
      if (!isSelected && mySelected.get(index)) {
        myPanel.setUpdateExtender(true);
      }
    }
    mySelected.put(index, isSelected);

    final double w =
        myNameComponent.getPreferredSize().getWidth()
            + myTailComponent.getPreferredSize().getWidth()
            + myTypeLabel.getPreferredSize().getWidth();

    boolean useBoxLayout =
        isSelected
            && w > list.getWidth()
            && ((JBList) list).getExpandableItemsHandler().isEnabled();
    if (useBoxLayout != myPanel.getLayout() instanceof BoxLayout) {
      myPanel.removeAll();
      if (useBoxLayout) {
        myPanel.setLayout(new BoxLayout(myPanel, BoxLayout.X_AXIS));
        myPanel.add(myNameComponent);
        myPanel.add(myTailComponent);
        myPanel.add(myTypeLabel);
      } else {
        myPanel.setLayout(new BorderLayout());
        myPanel.add(myNameComponent, BorderLayout.WEST);
        myPanel.add(myTailComponent, BorderLayout.CENTER);
        myPanel.add(myTypeLabel, BorderLayout.EAST);
      }
    }

    return myPanel;
  }

  private static Color getForegroundColor(boolean isSelected) {
    return isSelected ? SELECTED_FOREGROUND_COLOR : FOREGROUND_COLOR;
  }

  private int getMaxWidth() {
    if (myMaxWidth < 0) {
      final Point p = myLookup.getComponent().getLocationOnScreen();
      final Rectangle rectangle = ScreenUtil.getScreenRectangle(p);
      myMaxWidth = rectangle.x + rectangle.width - p.x - 111;
    }
    return myMaxWidth;
  }

  private void setTailTextLabel(
      boolean isSelected,
      LookupElementPresentation presentation,
      Color foreground,
      int allowedWidth,
      boolean nonFocusedSelection,
      FontMetrics fontMetrics) {
    int style = getStyle(false, presentation.isStrikeout(), false);

    for (LookupElementPresentation.TextFragment fragment : presentation.getTailFragments()) {
      if (allowedWidth < 0) {
        return;
      }

      String trimmed = trimLabelText(fragment.text, allowedWidth, fontMetrics);
      myTailComponent.append(
          trimmed,
          new SimpleTextAttributes(
              style, getTailTextColor(isSelected, fragment, foreground, nonFocusedSelection)));
      allowedWidth -= RealLookupElementPresentation.getStringWidth(trimmed, fontMetrics);
    }
  }

  private String trimLabelText(@Nullable String text, int maxWidth, FontMetrics metrics) {
    if (text == null || StringUtil.isEmpty(text)) {
      return "";
    }

    final int strWidth = RealLookupElementPresentation.getStringWidth(text, metrics);
    if (strWidth <= maxWidth || myIsSelected) {
      return text;
    }

    if (RealLookupElementPresentation.getStringWidth(ELLIPSIS, metrics) > maxWidth) {
      return "";
    }

    int i = 0;
    int j = text.length();
    while (i + 1 < j) {
      int mid = (i + j) / 2;
      final String candidate = text.substring(0, mid) + ELLIPSIS;
      final int width = RealLookupElementPresentation.getStringWidth(candidate, metrics);
      if (width <= maxWidth) {
        i = mid;
      } else {
        j = mid;
      }
    }

    return text.substring(0, i) + ELLIPSIS;
  }

  private static Color getTypeTextColor(
      LookupElement item,
      Color foreground,
      LookupElementPresentation presentation,
      boolean selected,
      boolean nonFocusedSelection) {
    if (nonFocusedSelection) {
      return foreground;
    }

    return presentation.isTypeGrayed()
        ? getGrayedForeground(selected)
        : item instanceof EmptyLookupItem ? JBColor.foreground() : foreground;
  }

  private static Color getTailTextColor(
      boolean isSelected,
      LookupElementPresentation.TextFragment fragment,
      Color defaultForeground,
      boolean nonFocusedSelection) {
    if (nonFocusedSelection) {
      return defaultForeground;
    }

    if (fragment.isGrayed()) {
      return getGrayedForeground(isSelected);
    }

    if (!isSelected) {
      final Color tailForeground = fragment.getForegroundColor();
      if (tailForeground != null) {
        return tailForeground;
      }
    }

    return defaultForeground;
  }

  public static Color getGrayedForeground(boolean isSelected) {
    return isSelected ? SELECTED_GRAYED_FOREGROUND_COLOR : GRAYED_FOREGROUND_COLOR;
  }

  private int setItemTextLabel(
      LookupElement item,
      final Color foreground,
      final boolean selected,
      LookupElementPresentation presentation,
      int allowedWidth) {
    boolean bold = presentation.isItemTextBold();

    Font customItemFont = myLookup.getCustomFont(item, bold);
    myNameComponent.setFont(
        customItemFont != null ? customItemFont : bold ? myBoldFont : myNormalFont);
    int style = getStyle(bold, presentation.isStrikeout(), presentation.isItemTextUnderlined());

    final FontMetrics metrics = getRealFontMetrics(item, bold);
    final String name = trimLabelText(presentation.getItemText(), allowedWidth, metrics);
    int used = RealLookupElementPresentation.getStringWidth(name, metrics);

    renderItemName(item, foreground, selected, style, name, myNameComponent);
    return used;
  }

  private FontMetrics getRealFontMetrics(LookupElement item, boolean bold) {
    Font customFont = myLookup.getCustomFont(item, bold);
    if (customFont != null) {
      return myLookup.getEditor().getComponent().getFontMetrics(customFont);
    }

    return bold ? myBoldMetrics : myNormalMetrics;
  }

  @SimpleTextAttributes.StyleAttributeConstant
  private static int getStyle(boolean bold, boolean strikeout, boolean underlined) {
    int style = bold ? SimpleTextAttributes.STYLE_BOLD : SimpleTextAttributes.STYLE_PLAIN;
    if (strikeout) {
      style |= SimpleTextAttributes.STYLE_STRIKEOUT;
    }
    if (underlined) {
      style |= SimpleTextAttributes.STYLE_UNDERLINE;
    }
    return style;
  }

  private void renderItemName(
      LookupElement item,
      Color foreground,
      boolean selected,
      @SimpleTextAttributes.StyleAttributeConstant int style,
      String name,
      final SimpleColoredComponent nameComponent) {
    final SimpleTextAttributes base = new SimpleTextAttributes(style, foreground);

    final String prefix = item instanceof EmptyLookupItem ? "" : myLookup.itemPattern(item);
    if (prefix.length() > 0) {
      Iterable<TextRange> ranges = getMatchingFragments(prefix, name);
      if (ranges != null) {
        SimpleTextAttributes highlighted =
            new SimpleTextAttributes(
                style, selected ? SELECTED_PREFIX_FOREGROUND_COLOR : PREFIX_FOREGROUND_COLOR);
        SpeedSearchUtil.appendColoredFragments(nameComponent, name, ranges, base, highlighted);
        return;
      }
    }
    nameComponent.append(name, base);
  }

  public static FList<TextRange> getMatchingFragments(String prefix, String name) {
    return new MinusculeMatcher("*" + prefix, NameUtil.MatchingCaseSensitivity.NONE)
        .matchingFragments(name);
  }

  private int setTypeTextLabel(
      LookupElement item,
      final Color background,
      Color foreground,
      final LookupElementPresentation presentation,
      int allowedWidth,
      boolean selected,
      boolean nonFocusedSelection,
      FontMetrics normalMetrics) {
    final String givenText = presentation.getTypeText();
    final String labelText =
        trimLabelText(
            StringUtil.isEmpty(givenText) ? "" : " " + givenText, allowedWidth, normalMetrics);

    int used = RealLookupElementPresentation.getStringWidth(labelText, normalMetrics);

    final Icon icon = presentation.getTypeIcon();
    if (icon != null) {
      myTypeLabel.setIcon(icon);
      used += icon.getIconWidth();
    }

    Color sampleBackground = background;

    Object o = item.isValid() ? item.getObject() : null;
    //noinspection deprecation
    if (o instanceof LookupValueWithUIHint && StringUtil.isEmpty(labelText)) {
      //noinspection deprecation
      Color proposedBackground = ((LookupValueWithUIHint) o).getColorHint();
      if (proposedBackground != null) {
        sampleBackground = proposedBackground;
      }
      myTypeLabel.append("  ");
      used += normalMetrics.stringWidth("WW");
    } else {
      myTypeLabel.append(labelText);
    }

    myTypeLabel.setBackground(sampleBackground);
    myTypeLabel.setForeground(
        getTypeTextColor(item, foreground, presentation, selected, nonFocusedSelection));
    return used;
  }

  public static Icon augmentIcon(@Nullable Icon icon, @NotNull Icon standard) {
    if (icon == null) {
      return standard;
    }

    if (icon.getIconHeight() < standard.getIconHeight()
        || icon.getIconWidth() < standard.getIconWidth()) {
      final LayeredIcon layeredIcon = new LayeredIcon(2);
      layeredIcon.setIcon(icon, 0, 0, (standard.getIconHeight() - icon.getIconHeight()) / 2);
      layeredIcon.setIcon(standard, 1);
      return layeredIcon;
    }

    return icon;
  }

  @Nullable
  Font getFontAbleToDisplay(LookupElementPresentation p) {
    String sampleString = p.getItemText() + p.getTailText() + p.getTypeText();

    // assume a single font can display all lookup item chars
    Set<Font> fonts = ContainerUtil.newHashSet();
    for (int i = 0; i < sampleString.length(); i++) {
      fonts.add(
          EditorUtil.fontForChar(sampleString.charAt(i), Font.PLAIN, myLookup.getEditor())
              .getFont());
    }

    eachFont:
    for (Font font : fonts) {
      if (font.equals(myNormalFont)) continue;

      for (int i = 0; i < sampleString.length(); i++) {
        if (!font.canDisplay(sampleString.charAt(i))) {
          continue eachFont;
        }
      }
      return font;
    }
    return null;
  }

  int updateMaximumWidth(final LookupElementPresentation p, LookupElement item) {
    final Icon icon = p.getIcon();
    if (icon != null
        && (icon.getIconWidth() > myEmptyIcon.getIconWidth()
            || icon.getIconHeight() > myEmptyIcon.getIconHeight())) {
      myEmptyIcon =
          new EmptyIcon(
              Math.max(icon.getIconWidth(), myEmptyIcon.getIconWidth()),
              Math.max(icon.getIconHeight(), myEmptyIcon.getIconHeight()));
    }

    return RealLookupElementPresentation.calculateWidth(
            p, getRealFontMetrics(item, false), getRealFontMetrics(item, true))
        + AFTER_TAIL
        + AFTER_TYPE;
  }

  public int getIconIndent() {
    return myNameComponent.getIconTextGap() + myEmptyIcon.getIconWidth();
  }

  private static class MySimpleColoredComponent extends SimpleColoredComponent {
    private MySimpleColoredComponent() {
      setFocusBorderAroundIcon(true);
    }

    @Override
    protected void applyAdditionalHints(Graphics g) {
      GraphicsUtil.setupAntialiasing(g);
    }
  }

  private class LookupPanel extends JPanel {
    boolean myUpdateExtender;

    public LookupPanel() {
      super(new BorderLayout());
    }

    public void setUpdateExtender(boolean updateExtender) {
      myUpdateExtender = updateExtender;
    }

    @Override
    public void paint(Graphics g) {
      if (!myLookup.isFocused() && myLookup.isCompletion()) {
        ((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f));
      }
      super.paint(g);
    }
  }
}