content: /*****************************************************************
   *   Licensed to the Apache Software Foundation (ASF) under one
   *  or more contributor license agreements.  See the NOTICE file
   *  distributed with this work for additional information
   *  regarding copyright ownership.  The ASF licenses this file
   *  to you 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
   *
   *    https://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 org.apache.cayenne.modeler.util.combo;
  
  import java.awt.Color;
  import java.awt.event.ItemListener;
  import java.awt.event.MouseEvent;
  import java.awt.event.MouseListener;
  
  import javax.swing.BorderFactory;
  import javax.swing.ComboBoxModel;
  import javax.swing.DefaultListModel;
  import javax.swing.JComboBox;
  import javax.swing.event.MouseInputAdapter;
  import javax.swing.plaf.basic.BasicComboPopup;
  
  import org.apache.cayenne.modeler.util.CellRenderers;
  
  /**
   * SuggestionList is a combo-popup displaying all items matching for
   * autocompletion.
   * 
   */
  public class SuggestionList extends BasicComboPopup {
      /**
       * 'Strict' matching, i.e. whether 'startWith' or 'contains' function
       * should be used for checking match 
       */
      protected boolean strict;
      
      /**
       * Creates a strict suggestion-popup for a combobox
       */
      public SuggestionList(JComboBox cb) {
         this(cb, false); 
      }
      
      /**
       * Creates a suggestion-popup for a combobox
       */
      public SuggestionList(JComboBox cb, boolean strict) {
          super(cb);
          
          this.strict = strict;
          list.addMouseListener(new MouseHandler());
          setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
      }
      
      /**
       * 'Filters' the list, leaving only matching items
       * @param prefix user-typed string, used to filter
       */
      public void filter(String prefix) {
          ComboBoxModel model = comboBox.getModel();
          DefaultListModel lm = new DefaultListModel();
          
          for (int i = 0; i < model.getSize(); i++) {
              String item = CellRenderers.asString(model.getElementAt(i));
              
              if (matches(item, prefix)) {
                  lm.addElement(model.getElementAt(i));
              }
          }
          
          list.setModel(lm);
      }
      
      /**
       * Checks if an item matches input pattern
       */
      protected boolean matches(String item, String pattern) {
          if (strict) {
              return item.toLowerCase().startsWith(pattern.toLowerCase());
          } else {
              return item.toLowerCase().contains(pattern.toLowerCase());
          }
      }
      
      /**
       * Retrieves the height of the popup based on the current
       * ListCellRenderer and the maximum row count.
       * 
       * Overrriden to count for local list size
       */
      @Override
      protected int getPopupHeightForRowCount(int maxRowCount) {
          return super.getPopupHeightForRowCount(Math.min(maxRowCount, list.getModel().getSize()));
      }
  
      /**
       * @return selected index in popup
       */
      public int getSelectedIndex() {
          return list.getSelectedIndex();
      }
      
      /**
       * @return selected item in popup
       */
      public Object getSelectedValue() {
          return list.getSelectedValue();
      }
      
      /**
       * Selects an item in list
       */
      public void setSelectedIndex(int i) {
          list.setSelectedIndex(i);
          comboBox.setSelectedItem(list.getModel().getElementAt(i));
      }
      
      /**
       * @return current suggestions count
       */
      public int getItemCount() {
          return list.getModel().getSize();
      }
      
      /**
       * @return an item from the list
       */
      public Object getItemAt(int i) {
          return list.getModel().getElementAt(i);
      }
      
      @Override
      public MouseListener createListMouseListener() {
          return new MouseHandler();
      }
      
      /**
       * We don't want items in the list be automatically selected at all
       */
      @Override
      protected ItemListener createItemListener() {
          return e -> {};
      }
      
      /**
       * @return Whether match-check is 'strict'
       */
      public boolean isStrict() {
          return strict;
      }
      
      protected class MouseHandler extends MouseInputAdapter {
          @Override
          public void mouseReleased(MouseEvent e) {
              comboBox.setSelectedItem(list.getSelectedValue());
              comboBox.setPopupVisible(false);
  
              // Workaround for cancelling an edited item (JVM bug 4530953).
              if (comboBox.isEditable() && comboBox.getEditor() != null) {
                  comboBox.configureEditor(comboBox.getEditor(), comboBox.getSelectedItem());
              }
              
              hide();
          }
      }
  }
  