Package org.twodividedbyzero.idea.findbugs.gui.tree.view

Source Code of org.twodividedbyzero.idea.findbugs.gui.tree.view.QuickSearch$RecentSearchesPopup

/*
* Copyright 2008-2013 Andre Pfeiler
*
* This file is part of FindBugs-IDEA.
*
* FindBugs-IDEA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FindBugs-IDEA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FindBugs-IDEA.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.twodividedbyzero.idea.findbugs.gui.tree.view;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.ui.JBColor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.twodividedbyzero.idea.findbugs.common.util.GuiUtil;
import org.twodividedbyzero.idea.findbugs.gui.common.AbstractBar;
import org.twodividedbyzero.idea.findbugs.gui.common.ListFacade;
import org.twodividedbyzero.idea.findbugs.gui.common.ScrollPaneFacade;
import org.twodividedbyzero.idea.findbugs.gui.common.ToolBarButton;
import org.twodividedbyzero.idea.findbugs.resources.GuiResources;

import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.IllegalComponentStateException;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.List;
import java.util.ListIterator;
import java.util.Stack;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;


/**
* $Date: 2014-06-19 18:40:49 -0500 (Thu, 19 Jun 2014) $
*
* @author Andre Pfeiler<andrep@twodividedbyzero.org>
* @version $Revision: 314 $
* @since 0.9.29-dev
*/
@SuppressWarnings({"HardCodedStringLiteral", "MagicCharacter", "AnonymousInnerClass", "AnonymousInnerClass"})
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings({"SE_BAD_FIELD_INNER_CLASS"})
public abstract class QuickSearch<E> {

  private static final Logger LOGGER = Logger.getInstance(QuickSearch.class.getName());

  private int _searchDelay = 500;
  private boolean _searchActivated;
  private JLayeredPane _layerPane;
  private SearchPopup _popup;
  private RecentSearchesPopup _recentSearchesPopup;
  private JComponent _component;
  private Color _noMatchForeground;
  private KeyListener _keyListener;
  private FocusListener _focusListener;
  private ComponentListener _componentListener;
  private int _cursor = -1;
  private Color _foregroundColor = JBColor.BLACK;
  private Color _backgroundColor = new JBColor(new Color(255, 255, 200), JBColor.LIGHT_GRAY);
  private final Stack<String> _recentSearches;


  protected QuickSearch() {
    _recentSearches = new Stack<String>();
  }


  protected void install(@NotNull final JComponent component) {
    _component = component;
    //_component.setFocusable(true);
    installListeners();
  }


  private void installListeners() {
    if (_componentListener == null) {
      _componentListener = createComponentListener();
    }
    getComponent().addComponentListener(_componentListener);

    if (_keyListener == null) {
      _keyListener = createKeyListener();
    }
    getComponent().addKeyListener(_keyListener);

    if (_focusListener == null) {
      _focusListener = createFocusListener();
    }
    getComponent().addFocusListener(_focusListener);
  }


  protected abstract void uninstallListeners();


  protected abstract int getElementCount();


  protected abstract String convertElementToString(final E element);


  protected abstract List<?> getElementsCache();


  @Nullable
  protected abstract E getElementAt(int index);


  protected abstract void setSelectedElement(final int index);


  protected void setComponent(final JComponent component) {
    _component = component;
  }


  protected SearchPopup createSearchPopup(final String searchText) {
    return new SearchPopup(searchText);
  }


  protected KeyListener createKeyListener() {
    return new KeyAdapter() {
      @Override
      public void keyTyped(final KeyEvent e) {
        keyTypedOrPressed(e);
      }


      @Override
      public void keyPressed(final KeyEvent e) {
        keyTypedOrPressed(e);
      }
    };
  }


  protected FocusListener createFocusListener() {
    return new FocusAdapter() {
      @Override
      public void focusGained(final FocusEvent e) {
        _searchActivated = true;
      }


      @Override
      public void focusLost(final FocusEvent focusevent) {
        //final Component component = focusevent.getOppositeComponent();

        /*if(component != null) {
          final Container parent = component.getParent();
          if(parent != null && QuickSearch.NavigationToolBar.class.isInstance(parent)) {
            _component.requestFocus();
            focusevent.setSource(_component);
            focusGained(focusevent);
            return;
          }
        }*/

        _searchActivated = false;
        hidePopup();

      }
    };
  }


  protected ComponentListener createComponentListener() {
    return new ComponentAdapter() {
      @Override
      public void componentHidden(final ComponentEvent e) {
        super.componentHidden(e);
        hidePopup();
      }


      @Override
      public void componentResized(final ComponentEvent e) {
        super.componentResized(e);
        relocateAndResize();
      }


      @Override
      public void componentMoved(final ComponentEvent e) {
        super.componentMoved(e);
        relocateAndResize();
      }
    };
  }


  protected void keyTypedOrPressed(final KeyEvent e) {
    if (e != null && (isActivationKey(e) || _searchActivated) && !isDeactivationKey(e)) {
      String searchingText = "";
      if (e.getID() == KeyEvent.KEY_TYPED) {
        if ((e.getModifiers() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0) { // alt mask
          return;
        }
        if (e.isAltDown()) {
          return;
        }

        searchingText = String.valueOf(e.getKeyChar());
      }

      if (_popup == null) {
        showPopup(searchingText);
      }

      if (e.getKeyCode() != KeyEvent.VK_ENTER) {
        _popup.processKeyEvent(e);
        //_popup.getSearchField().processKeyEvent(e);
        e.consume();
      }
    }
  }


  private void showPopup(final String text) {
    //final JRootPane rootPane = _component.getRootPane();
    final JComponent component = (JComponent) GuiUtil.getScrollPane(_component);
    if (component != null) {
      _component = component;
    }

    final JRootPane rootPane = SwingUtilities.getRootPane(component);
    if (rootPane != null) {
      _layerPane = rootPane.getLayeredPane();
      _popup = createSearchPopup(text);
      _layerPane.add(_popup, JLayeredPane.POPUP_LAYER);
      relocateAndResize();
      _popup.setVisible(true);
      _popup.validate();

      if (!_recentSearches.isEmpty()) {
        showRecentSearchesPopup();
      } else {
        initRecentSearchesPopup(false);
      }
    } else {
      //noinspection AssignmentToNull
      _layerPane = null;
    }

  }


  @SuppressWarnings({"AssignmentToNull"})
  protected void hidePopup() {
    if (_layerPane != null && _popup != null) {
      hideRecentSearchesPopup();
      //final Rectangle bounds = _popup.getBounds();
      _layerPane.remove(_popup);
      //_layerPane.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
      _layerPane.validate();
      _layerPane.repaint();
      _layerPane = null;
      _popup = null;
      setCursor(-1);
      //_component.requestFocusInWindow();
    }
  }


  private Point relocateAndResize() {
    if (_popup != null) {
      final Dimension popupSize;
      final Point componentLocation;
      try {
        popupSize = _popup.getPreferredSize();
        componentLocation = _component.getLocationOnScreen();
        SwingUtilities.convertPointFromScreen(componentLocation, _layerPane);
        componentLocation.y -= popupSize.height;
        if (componentLocation.y < 0) {
          componentLocation.y = 0;
        }
      } catch (final IllegalComponentStateException ignore) {
        return null;
      }

      _popup.setLocation(componentLocation);
      _popup.setSize(popupSize);

      return componentLocation;
    } else {
      return null;
    }
  }


  protected static boolean isNavigationKey(final KeyEvent e) {
    return isFindNextOccurenceKey(e) || isFindPreviousOccurenceKey(e);
  }


  protected static boolean isFindNextOccurenceKey(final KeyEvent e) {
    return e.getKeyCode() == KeyEvent.VK_F3;
  }


  protected static boolean isFindPreviousOccurenceKey(final KeyEvent e) {
    return e.getKeyCode() == KeyEvent.VK_F3 && e.getModifiersEx() == InputEvent.SHIFT_DOWN_MASK;
  }


  protected static boolean isActivationKey(final KeyEvent e) {
    final char keyChar = e.getKeyChar();
    return e.getID() == KeyEvent.KEY_TYPED && e.getKeyCode() != KeyEvent.VK_F4 && e.getModifiersEx() != InputEvent.ALT_MASK && e.getModifiersEx() != InputEvent.ALT_DOWN_MASK && (Character.isLetterOrDigit(keyChar) || keyChar == '*' || keyChar == '?');
  }


  protected static boolean isDeactivationKey(final KeyEvent e) {
    final int keyCode = e.getKeyCode();
    return keyCode == KeyEvent.VK_ESCAPE || keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_PAGE_UP || keyCode == KeyEvent.VK_PAGE_DOWN || keyCode == KeyEvent.VK_HOME || keyCode == KeyEvent.VK_END || keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT || keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN;
  }


  protected int getSearchDelay() {
    return _searchDelay;
  }


  protected void setSearchDelay(final int searchDelay) {
    _searchDelay = searchDelay;
  }


  protected Component getComponent() {
    return _component;
  }


  protected boolean isPopupVisible() {
    return _popup != null;
  }


  protected int getCursor() {
    return _cursor;
  }


  protected void setCursor(final int cursor) {
    _cursor = cursor;
  }


  protected Color getNoMatchForeground() {
    if (_noMatchForeground == null) {
      return JBColor.RED;
    } else {
      return _noMatchForeground;
    }
  }


  @SuppressWarnings("UnusedDeclaration")
  protected Color getForegroundColor() {
    return _foregroundColor;
  }


  @SuppressWarnings("UnusedDeclaration")
  protected void setForegroundColor(final Color foregroundColor) {
    _foregroundColor = foregroundColor;
  }


  @SuppressWarnings("UnusedDeclaration")
  protected Color getBackgroundColor() {
    return _backgroundColor;
  }


  @SuppressWarnings("UnusedDeclaration")
  protected void setBackgroundColor(final Color backgroundColor) {
    _backgroundColor = backgroundColor;
  }


  private int find(final String text) {
    final int count = getElementCount();
    if (count == 0) {
      return -1;
    }

    // find from cursor
    int cursor = getCursor();
    if (cursor == -1 && count > 0) {
      cursor = 0;
      setCursor(cursor);
    }

    for (int i = cursor; i < count; i++) {
      final E element = getElementAt(i);
      if (compare(element, text)) {
        addToRecentSearches(text);
        return i;
      }
    }

    // if not found, search from begin to cursor
    for (int i = 0; i < cursor; i++) {
      final E element = getElementAt(i);
      if (compare(element, text)) {
        //setCursor(i);
        addToRecentSearches(text);
        return i;
      }
    }

    return -1;

  }


  public int findNextOccurence(final String text) {
    final int count = getElementCount();
    if (count == 0) {
      return !text.isEmpty() ? -1 : 0;
    }

    for (int i = getCursor() + 1; i < count; i++) {
      final E element = getElementAt(i);
      if (compare(element, text)) {
        return i;
      }
    }

    return -1;
  }


  public int findPreviousOccurence(final String text) {
    final int count = getElementCount();
    if (count == 0) {
      return !text.isEmpty() ? -1 : 0;
    }

    for (int i = getCursor() - 1; i >= 0; i--) {
      final E element = getElementAt(i);
      if (compare(element, text)) {
        return i;
      }
    }

    return -1;
  }


  protected boolean compare(final E element, final String searchText) {
    if (searchText == null || searchText.trim().isEmpty()) {
      return true;
    }

    final String text = convertElementToString(element);
    if (text == null) {
      return false;
    }

    try {
      //_pattern = Pattern.compile(isFromStart() ? "^" + s : s, isCaseSensitive() ? 0 : Pattern.CASE_INSENSITIVE);
      Pattern pattern = Pattern.compile('^' + searchText + '^', 0);
      boolean found = pattern.matcher(text).find();
      if (found) {
        return found;
      }

      pattern = Pattern.compile('^' + searchText, 0);
      found = pattern.matcher(text).find();
      if (found) {
        return found;
      }

      pattern = Pattern.compile('^' + searchText, Pattern.CASE_INSENSITIVE);
      found = pattern.matcher(text).find();
      if (found) {
        return found;
      }

      pattern = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE);
      found = pattern.matcher(text).find();
      return found;
    } catch (final PatternSyntaxException ignore) {
      return false;
    }
  }


  private class SearchField extends JTextField {

    SearchField() {
    }


    @Override
    public Dimension getPreferredSize() {
      final Dimension size = super.getPreferredSize();
      size.width = getFontMetrics(getFont()).stringWidth(getText()) + 4;
      return size;
    }


    @Override
    public void processKeyEvent(final KeyEvent e) {

      final int keyCode = e.getKeyCode();
      if (keyCode == KeyEvent.VK_BACK_SPACE && getDocument().getLength() == 0) {
        e.consume();
        return;
      }

      final boolean isNavigationKey = isNavigationKey(e);

      if (isDeactivationKey(e) && !isNavigationKey) {
        hidePopup();
        //_component.setRequestFocusEnabled(true);
        //_component.requestFocusInWindow();
        e.consume();
        return;
      }

      if (keyCode == KeyEvent.VK_BACK_SPACE || isNavigationKey) {
        e.consume();
      }

      super.processKeyEvent(e);
    }


    /*private void handleBackspace() {
      final int caretPos = getCaretPosition();
      final String temp = getText();
      if (caretPos > 1) {
        setText(temp.substring(0, caretPos - 1) + (caretPos < temp.length() ? temp.substring(caretPos, temp.length()) : ""));
      } else {
        setText("" + (caretPos < temp.length() ? temp.substring(caretPos, temp.length()) : ""));
      }
      setCaretPosition(caretPos - 1);
    }*/
  }


  private class SearchPopup extends JPanel {

    private JLabel _label;
    private SearchField _searchField;
    private NavigationToolBar _toolBar;


    private SearchPopup(final String searchText) {
      initGui(searchText);
    }


    private void initGui(final String text) {
      //addKeyListener(_keyListener);

      _label = new JLabel("Search for: ");
      //noinspection MagicNumber
      _label.setFont(new Font(getFont().getName(), Font.BOLD, 12));
      _label.setForeground(_foregroundColor);
      _label.setVerticalAlignment(SwingConstants.BOTTOM);

      _toolBar = new NavigationToolBar("test", false, SwingConstants.HORIZONTAL);
      _toolBar.setForeground(_foregroundColor);
      _toolBar.setVisible(false);

      _searchField = new SearchField();
      _searchField.setForeground(_foregroundColor);
      _searchField.setFocusable(false);
      _searchField.setOpaque(false);
      _searchField.setBorder(BorderFactory.createEmptyBorder());
      _searchField.setCursor(getCursor());

      _searchField.getDocument().addDocumentListener(new DocumentListener() {
        private final Timer _timer = new Timer(200, new ActionListener() {
          public void actionPerformed(final ActionEvent e) {
            doFind();
          }
        });


        public void insertUpdate(final DocumentEvent e) {
          startTimer();
        }


        public void removeUpdate(final DocumentEvent e) {
          startTimer();
        }


        public void changedUpdate(final DocumentEvent e) {
          startTimer();
        }


        void doFind() {
          final String text = _searchField.getText().trim();
          if (!text.isEmpty()) {
            final int found = find(text);
            if (found == -1) {
              _noMatchForeground = getNoMatchForeground();
              _searchField.setForeground(_noMatchForeground);
              _toolBar.setVisible(false);
              remove(_toolBar);
              updatePopupBounds();
            } else {
              _searchField.setForeground(_foregroundColor);
              _toolBar.setVisible(true);
              add(_toolBar, BorderLayout.LINE_END);
              QuickSearch.this.setCursor(found);
              setSelectedElement(found);
              updatePopupBounds();
            }
          } else {
            hidePopup();
          }
        }


        void startTimer() {
          updatePopupBounds();
          if (getSearchDelay() > 0) {
            _timer.setInitialDelay(getSearchDelay());
            if (_timer.isRunning()) {
              _timer.restart();
            } else {
              _timer.setRepeats(false);
              _timer.start();
            }
          } else {
            doFind();
          }
        }

      });

      _searchField.setText(text);

      setBackground(_backgroundColor);
      setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(JBColor.GRAY, 1), BorderFactory.createEmptyBorder(1, 6, 1, 6)));
      setLayout(new BorderLayout(2, 0));
      /*final Dimension size = _label.getPreferredSize();
      size.height = _searchField.getPreferredSize().height;
      _label.setPreferredSize(size);

      final Dimension toolBarSize = _toolBar.getPreferredSize();
      toolBarSize.height = _searchField.getPreferredSize().height;*/

      final Dimension size = _label.getPreferredSize();
      size.height = _toolBar.getPreferredSize().height;
      _label.setPreferredSize(size);

      final Dimension sizeField = _searchField.getPreferredSize();
      sizeField.height = size.height;
      _searchField.setPreferredSize(sizeField);

      add(_label, BorderLayout.LINE_START);
      add(_searchField, BorderLayout.CENTER);
    }


    private void updatePopupBounds() {
      if (_popup != null) {
        _searchField.invalidate();
        try {
          final Dimension size = _searchField.getPreferredSize();
          size.width += _label.getPreferredSize().width;
          size.width += _toolBar.isVisible() ? _toolBar.getPreferredSize().width : 0;
          size.width += 20 /*new JLabel(_searchField.getText()).getPreferredSize().width*//* + 19*/;
          size.height = _popup.getSize().height;
          _popup.setSize(size);
          _popup.setPreferredSize(size);
          _popup.validate();
        } catch (final Exception e) {
          LOGGER.debug(e);
        }
      }
    }


    @Override
    public void processKeyEvent(final KeyEvent e) {
      _searchField.processKeyEvent(e);
      if (e.isConsumed()) {
        final String text = getSearchText();

        if (text.isEmpty()) {
          return;
        }

        int found = -1;
        boolean foundPrev = true;
        boolean foundNext = true;

        if (isFindNextOccurenceKey(e) && !isFindPreviousOccurenceKey(e)) {
          found = findNextOccurence(text);
          foundNext = found > 1;
        } else if (isFindPreviousOccurenceKey(e)) {
          found = findPreviousOccurence(text);
          foundPrev = found > -1;
        }

        _toolBar.setPrevEnabled(foundPrev);
        _toolBar.setNextEnabled(foundNext);

        if (found == -1) {
          //_searchField.setForeground(getNoMatchForeground());
        } else {
          _searchField.setForeground(UIManager.getColor("Toolip.foreground"));
          QuickSearch.this.setCursor(found);
          setSelectedElement(found);
        }
      }
      if (e.getKeyCode() != KeyEvent.VK_ENTER) {
        e.consume();
      }
      super.processKeyEvent(e);
    }


    public String getSearchText() {
      return _searchField.getText();
    }


    public SearchField getSearchField() {
      return _searchField;
    }


    public NavigationToolBar getToolbar() {
      return _toolBar;
    }


    @Override
    public String toString() {
      final StringBuilder sb = new StringBuilder();
      sb.append("SearchPopup");
      sb.append("{_label=").append(_label);
      sb.append(", _searchField=").append(_searchField);
      sb.append(", _toolBar=").append(_toolBar);
      sb.append('}');
      return sb.toString();
    }
  }


  private class NavigationToolBar extends AbstractBar {

    private final Icon _prevIcon = GuiResources.NAVIGATION_MOVEUP_ICON;
    private final Icon _nextIcon = GuiResources.NAVIGATION_MOVEDOWN_ICON;
    private ToolBarButton _prevButton;
    private ToolBarButton _nextButton;


    private NavigationToolBar(final String name, final boolean floatable, final int orientation) {
      super(name);
      setFloatable(floatable);
      setOrientation(orientation);
      setRollover(false);
      initGui();
    }


    private void initGui() {
      setOpaque(false);
      setFocusable(false);
      setRollover(false);
      setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));

      final AbstractBar.AbstractComponentAction prevAction = new AbstractComponentAction("Previous Occurence", "Previous Occurence (Ctrl+F3)", KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK, KeyEvent.VK_F3) {
        private static final long serialVersionUID = 0L;


        public void actionPerformed(final ActionEvent e) {
          _popup.processKeyEvent(new KeyEvent(_prevButton, 401, System.currentTimeMillis(), InputEvent.SHIFT_DOWN_MASK, KeyEvent.VK_F3, KeyEvent.CHAR_UNDEFINED));
        }
      };
      _prevButton = new ToolBarButton(prevAction, _prevIcon);
      add(_prevButton);


      final AbstractBar.AbstractComponentAction nextAction = new AbstractComponentAction("Next Occurence", "Next Occurence (Ctrl+F3)", KeyEvent.VK_F3, 0, KeyEvent.VK_F3) {
        private static final long serialVersionUID = 0L;


        public void actionPerformed(final ActionEvent e) {
          _popup.processKeyEvent(new KeyEvent(_nextButton, 401, System.currentTimeMillis(), 0, KeyEvent.VK_F3, KeyEvent.CHAR_UNDEFINED));
        }
      };
      _nextButton = new ToolBarButton(nextAction, _nextIcon);
      add(_nextButton);
    }


    public void setPrevEnabled(final boolean enable) {
      _prevButton.setEnabled(enable);
    }


    public void setNextEnabled(final boolean enable) {
      _nextButton.setEnabled(enable);
    }


    @Override
    public String toString() {
      final StringBuilder sb = new StringBuilder();
      sb.append("NavigationToolBar");
      sb.append("{_prevIcon=").append(_prevIcon);
      sb.append(", _nextIcon=").append(_nextIcon);
      sb.append(", _prevButton=").append(_prevButton);
      sb.append(", _nextButton=").append(_nextButton);
      sb.append('}');
      return sb.toString();
    }
  }


  private class RecentSearchesPopup extends JPanel {

    private JLabel _label;
    private DefaultListModel _listModel;
    private JList _list;
    private int _mouseOverIndex;


    private RecentSearchesPopup() {
      initGui();
    }


    private void initGui() {
      _label = new JLabel("Recent Searches");
      _label.setPreferredSize(new Dimension(getPreferredSize().width, _label.getPreferredSize().height + 5));
      _label.setForeground(_foregroundColor);
      _label.setBackground(_backgroundColor);
      _label.setOpaque(true);
      _label.setHorizontalAlignment(SwingConstants.CENTER);
      _label.setVerticalAlignment(SwingConstants.CENTER);

      _listModel = new DefaultListModel();
      _list = ListFacade.createList();
      _list.setModel(_listModel);
      _list.setBorder(BorderFactory.createEmptyBorder(8, 5, 8, 5));
      _list.setAutoscrolls(true);
      _list.setFocusable(false);
      _list.setRequestFocusEnabled(false);

      updateListData(_recentSearches);
      _list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

      _list.addMouseMotionListener(new MouseMotionAdapter() {
        @Override
        public void mouseMoved(final MouseEvent e) {
          locationToListIndex(e);
        }
      });

      _list.addListSelectionListener(new ListSelectionListener() {
        public void valueChanged(final ListSelectionEvent e) {
          if (e.getValueIsAdjusting()) {
            return;
          }

          if (_list.getSelectedIndex() > -1) {
            _popup.getSearchField().setText((String) _list.getSelectedValue());
          }
        }
      });

      //updatePopupBounds();
      final JScrollPane scrollPane = ScrollPaneFacade.createScrollPane();
      scrollPane.getViewport().setView(_list);
      scrollPane.setBorder(null);
      scrollPane.setForeground(_foregroundColor);

      setLayout(new BorderLayout());
      setBackground(_backgroundColor);
      setOpaque(true);
      setBorder(BorderFactory.createLineBorder(JBColor.GRAY, 1));

      add(_label, BorderLayout.NORTH);
      add(scrollPane, BorderLayout.CENTER);
    }


    private void locationToListIndex(final MouseEvent e) {
      final Point p = e.getPoint();
      int overIndex = _list.locationToIndex(p);

      final Rectangle itemRect = _list.getCellBounds(overIndex, overIndex);
      if (!itemRect.contains(p)) {
        overIndex = -1;
      }

      if (overIndex != _mouseOverIndex) {
        _list.setSelectedIndex(overIndex);
        _list.scrollRectToVisible(itemRect);
        _mouseOverIndex = overIndex;
      }
    }


    private void scrollIndexToVisible(final int index) {
      final Rectangle rect = _list.getCellBounds(index, index);
      _list.setSelectedIndex(index);
      _list.scrollRectToVisible(rect);
    }


    @Override
    public Dimension getPreferredSize() {
      return new Dimension(_label.getPreferredSize().width + 60, 200);
    }


    public String getLongestString() {
      int maxLen = 0;
      String longest = null;

      for (int i = 0; i < _listModel.size(); i++) {
        final String s = (String) _listModel.elementAt(i);
        if (s.length() > maxLen) {
          maxLen = s.length();
          longest = s;
        }
      }
      return longest;
    }


    void updateListData(final Stack<String> stack) {
      _listModel.removeAllElements();
      final ListIterator<String> iter = stack.listIterator(stack.size());
      while (iter.hasPrevious()) {
        _listModel.addElement(iter.previous());
      }
    }


    @Nullable
    private JList getList() {
      return _list;
    }


    @Override
    public String toString() {
      final StringBuilder sb = new StringBuilder();
      sb.append("RecentSearchesPopup");
      sb.append("{_label=").append(_label);
      sb.append(", _listModel=").append(_listModel);
      sb.append(", _list=").append(_list);
      sb.append(", _mouseOverIndex=").append(_mouseOverIndex);
      sb.append('}');
      return sb.toString();
    }
  }


  private void showRecentSearchesPopup() {
    //final JRootPane rootPane = _component.getRootPane();
    final JComponent component = (JComponent) GuiUtil.getScrollPane(_component);
    if (component != null) {
      _component = component;
    }

    final JRootPane rootPane = SwingUtilities.getRootPane(component);
    if (rootPane != null) {
      _layerPane = rootPane.getLayeredPane();
      initRecentSearchesPopup(true);
    }

  }


  private void initRecentSearchesPopup(final boolean visible) {
    _recentSearchesPopup = createRecentSearchesPopup();
    _layerPane.add(_recentSearchesPopup, JLayeredPane.POPUP_LAYER);
    relocateAndResizeRecentSearches();
    _recentSearchesPopup.setVisible(visible);
    _recentSearchesPopup.validate();
  }


  protected void hideRecentSearchesPopup() {
    if (_recentSearchesPopup != null) {
      //final Rectangle bounds = _popup.getBounds();
      _layerPane.remove(_recentSearchesPopup);
      //_layerPane.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
      _layerPane.validate();
      _layerPane.repaint();
      //_layerPane = null;
      //noinspection AssignmentToNull
      _recentSearchesPopup = null;
      setCursor(-1);
    }
  }


  private Point relocateAndResizeRecentSearches() {
    if (_recentSearchesPopup != null && _popup != null) {
      final Dimension popupSize;
      final Point componentLocation;
      try {
        popupSize = _recentSearchesPopup.getPreferredSize();
        componentLocation = _popup.getLocationOnScreen();
        SwingUtilities.convertPointFromScreen(componentLocation, _layerPane);
        componentLocation.y -= popupSize.height + 2;
        if (componentLocation.y < 0) {
          componentLocation.y = 2;
        }
      } catch (final IllegalComponentStateException ignore) {
        return null;
      }

      _recentSearchesPopup.setLocation(componentLocation);
      _recentSearchesPopup.setSize(popupSize);

      return componentLocation;
    } else {
      return null;
    }
  }


  protected RecentSearchesPopup createRecentSearchesPopup() {
    return new RecentSearchesPopup();
  }


  private void addToRecentSearches(final String text) {
    if (text.length() >= 3) {
      if (_recentSearches.size() >= 30) {
        _recentSearches.remove(_recentSearches.size() - 1);
      }

      if (_recentSearchesPopup != null) {
        if (!_recentSearches.contains(text)) {
          _recentSearches.push(text);
          //Collections.reverse(_recentSearches);
          _recentSearchesPopup.updateListData(_recentSearches);
        }

        final JList jList = _recentSearchesPopup.getList();
        if (jList != null) {
          final DefaultListModel listModel = (DefaultListModel) jList.getModel();
          final int index = listModel.indexOf(text);
          jList.setSelectedIndex(index);
          _recentSearchesPopup.scrollIndexToVisible(index);
        }
      }

    }
  }


  public String toString() {
    final StringBuilder sb = new StringBuilder();
    sb.append("QuickSearch");
    sb.append("{_searchDelay=").append(_searchDelay);
    sb.append(", _searchActivated=").append(_searchActivated);
    sb.append(", _layerPane=").append(_layerPane);
    sb.append(", _popup=").append(_popup);
    sb.append(", _recentSearchesPopup=").append(_recentSearchesPopup);
    sb.append(", _component=").append(_component);
    sb.append(", _noMatchForeground=").append(_noMatchForeground);
    sb.append(", _keyListener=").append(_keyListener);
    sb.append(", _focusListener=").append(_focusListener);
    sb.append(", _componentListener=").append(_componentListener);
    sb.append(", _cursor=").append(_cursor);
    sb.append(", _foregroundColor=").append(_foregroundColor);
    sb.append(", _backgroundColor=").append(_backgroundColor);
    sb.append(", _recentSearches=").append(_recentSearches);
    sb.append('}');
    return sb.toString();
  }
}
TOP

Related Classes of org.twodividedbyzero.idea.findbugs.gui.tree.view.QuickSearch$RecentSearchesPopup

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.