// -------->
 // original code:
 // ftp://ftp.oreilly.de/pub/examples/english_examples/jswing2/code/goodies/Mapper.java
 // modified by terai
 //     private Hashtable<Object, ArrayList<KeyStroke>> buildReverseMap(InputMap im) {
 //         Hashtable<Object, ArrayList<KeyStroke>> h = new Hashtable<>();
 //         if (Objects.isNull(im.allKeys())) {
 //             return h;
 //         }
 //         for (KeyStroke ks: im.allKeys()) {
 //             Object name = im.get(ks);
 //             if (h.containsKey(name)) {
 //                 h.get(name).add(ks);
 //             } else {
 //                 ArrayList<KeyStroke> keylist = new ArrayList<>();
 //                 keylist.add(ks);
 //                 h.put(name, keylist);
 //             }
 //         }
 //         return h;
 //     }
 private void loadBindingMap(Integer focusType, InputMap im, ActionMap am) {
   if (Objects.isNull(im.allKeys())) {
     return;
   }
   ActionMap tmpAm = new ActionMap();
   for (Object actionMapKey : am.allKeys()) {
     tmpAm.put(actionMapKey, am.get(actionMapKey));
   }
   for (KeyStroke ks : im.allKeys()) {
     Object actionMapKey = im.get(ks);
     Action action = am.get(actionMapKey);
     if (Objects.isNull(action)) {
       model.addBinding(new Binding(focusType, "____" + actionMapKey.toString(), ks.toString()));
     } else {
       model.addBinding(new Binding(focusType, actionMapKey.toString(), ks.toString()));
     }
     tmpAm.remove(actionMapKey);
   }
   if (Objects.isNull(tmpAm.allKeys())) {
     return;
   }
   for (Object actionMapKey : tmpAm.allKeys()) {
     model.addBinding(new Binding(focusType, actionMapKey.toString(), ""));
   }
 }
 // Overridden for performance reasons. ---->
 @Override
 public boolean isOpaque() {
   Color back = getBackground();
   Component p = getParent();
   if (Objects.nonNull(p)) {
     p = p.getParent();
   } // p should now be the JTable.
   boolean colorMatch =
       Objects.nonNull(back)
           && Objects.nonNull(p)
           && back.equals(p.getBackground())
           && p.isOpaque();
   return !colorMatch && super.isOpaque();
 }
 @Override
 public Component getListCellRendererComponent(
     JList<? extends E> list, E value, int index, boolean isSelected, boolean cellHasFocus) {
   label.setText(Objects.toString(value, ""));
   this.list = list;
   this.index = index;
   if (isSelected) {
     setBackground(list.getSelectionBackground());
     label.setForeground(list.getSelectionForeground());
   } else {
     setBackground(index % 2 == 0 ? EVEN_COLOR : list.getBackground());
     label.setForeground(list.getForeground());
   }
   MutableComboBoxModel m = (MutableComboBoxModel) list.getModel();
   if (index < 0 || m.getSize() - 1 <= 0) {
     setOpaque(false);
     deleteButton.setVisible(false);
     label.setForeground(list.getForeground());
   } else {
     boolean f = index == rolloverIndex;
     setOpaque(true);
     deleteButton.setVisible(true);
     deleteButton.getModel().setRollover(f);
     deleteButton.setForeground(f ? Color.WHITE : list.getForeground());
   }
   return this;
 }
 @Override
 public void updateUI() {
   if (Objects.nonNull(cbml)) {
     JList<?> list = getList();
     if (Objects.nonNull(list)) {
       list.removeMouseListener(cbml);
       list.removeMouseMotionListener(cbml);
     }
   }
   super.updateUI();
   setRenderer(new ButtonsRenderer<E>(this));
   JList<?> list = getList();
   if (Objects.nonNull(list)) {
     cbml = new CellButtonsMouseListener();
     list.addMouseListener(cbml);
     list.addMouseMotionListener(cbml);
   }
 }
 @Override
 protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
   // String literal pool
   // if (propertyName == "document" || ((propertyName == "font" || propertyName == "foreground")
   // && oldValue != newValue)) {
   if ("document".equals(propertyName)
       || !Objects.equals(oldValue, newValue)
           && ("font".equals(propertyName) || "foreground".equals(propertyName))) {
     super.firePropertyChange(propertyName, oldValue, newValue);
   }
 }
 @Override
 public void mousePressed(MouseEvent e) {
   JList list = (JList) e.getComponent();
   Point pt = e.getPoint();
   int index = list.locationToIndex(pt);
   if (index >= 0) {
     JButton button = getButton(list, pt, index);
     if (Objects.nonNull(button)) {
       listRepaint(list, list.getCellBounds(index, index));
     }
   }
 }
 @Override
 public void mouseMoved(MouseEvent e) {
   JList list = (JList) e.getComponent();
   Point pt = e.getPoint();
   int index = list.locationToIndex(pt);
   if (!list.getCellBounds(index, index).contains(pt)) {
     if (prevIndex >= 0) {
       Rectangle r = list.getCellBounds(prevIndex, prevIndex);
       listRepaint(list, r);
     }
     index = -1;
     prevButton = null;
     return;
   }
   if (index >= 0) {
     JButton button = getButton(list, pt, index);
     ButtonsRenderer renderer = (ButtonsRenderer) list.getCellRenderer();
     if (Objects.nonNull(button)) {
       renderer.rolloverIndex = index;
       if (!button.equals(prevButton)) {
         Rectangle r = list.getCellBounds(prevIndex, index);
         listRepaint(list, r);
       }
     } else {
       renderer.rolloverIndex = -1;
       Rectangle r = null;
       if (prevIndex == index) {
         if (prevIndex >= 0 && Objects.nonNull(prevButton)) {
           r = list.getCellBounds(prevIndex, prevIndex);
         }
       } else {
         r = list.getCellBounds(index, index);
       }
       listRepaint(list, r);
       prevIndex = -1;
     }
     prevButton = button;
   }
   prevIndex = index;
 }
 @Override
 public Component getTableCellRendererComponent(
     JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
   if (isSelected) {
     setForeground(table.getSelectionForeground());
     setBackground(table.getSelectionBackground());
   } else {
     setForeground(table.getForeground());
     setBackground(table.getBackground());
   }
   setFont(table.getFont());
   setText(Objects.toString(value, ""));
   return this;
 }
 protected void fireEditingCanceled() {
   // Guaranteed to return a non-null array
   Object[] listeners = listenerList.getListenerList();
   // Process the listeners last to first, notifying
   // those that are interested in this event
   for (int i = listeners.length - 2; i >= 0; i -= 2) {
     if (listeners[i] == CellEditorListener.class) {
       // Lazily create the event:
       if (Objects.isNull(changeEvent)) {
         changeEvent = new ChangeEvent(this);
       }
       ((CellEditorListener) listeners[i + 1]).editingCanceled(changeEvent);
     }
   }
 }
 @Override
 public Component getListCellRendererComponent(
     JList<? extends E> list, E value, int index, boolean isSelected, boolean cellHasFocus) {
   label.setText(Objects.toString(value, ""));
   this.index = index;
   if (isSelected) {
     setBackground(list.getSelectionBackground());
     label.setForeground(list.getSelectionForeground());
   } else {
     setBackground(index % 2 == 0 ? EVEN_COLOR : list.getBackground());
     label.setForeground(list.getForeground());
   }
   resetButtonStatus();
   if (Objects.nonNull(button)) {
     if (index == pressedIndex) {
       button.getModel().setSelected(true);
       button.getModel().setArmed(true);
       button.getModel().setPressed(true);
     } else if (index == rolloverIndex) {
       button.getModel().setRollover(true);
     }
   }
   return this;
 }
 @Override
 public void mousePressed(MouseEvent e) {
   // JList list = (JList) e.getComponent();
   Point pt = e.getPoint();
   int index = list.locationToIndex(pt);
   if (index >= 0) {
     JButton button = getButton(list, pt, index);
     if (Objects.nonNull(button)) {
       ButtonsRenderer renderer = (ButtonsRenderer) list.getCellRenderer();
       renderer.pressedIndex = index;
       renderer.button = button;
       listRepaint(list, list.getCellBounds(index, index));
     }
   }
 }
 @Override
 public Component getTableCellEditorComponent(
     JTable table, Object value, boolean isSelected, int row, int column) {
   System.out.println("getTableCellEditorComponent");
   setFont(table.getFont());
   setText(Objects.toString(value, ""));
   EventQueue.invokeLater(
       new Runnable() {
         @Override
         public void run() {
           setCaretPosition(getText().length());
           requestFocusInWindow();
           System.out.println("invokeLater: getTableCellEditorComponent");
         }
       });
   return scroll;
 }
 @Override
 public void paintIcon(Component c, Graphics g, int x, int y) {
   Graphics2D g2 = (Graphics2D) g.create();
   g2.setPaint(Objects.nonNull(c) ? c.getBackground() : Color.WHITE);
   g2.fillRect(x, y, getIconWidth(), getIconHeight());
   g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
   g2.setColor(ELLIPSE_COLOR);
   g2.translate(x, y);
   int size = list.size();
   for (int i = 0; i < size; i++) {
     float alpha = isRunning ? (i + 1) / (float) size : .5f;
     g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
     g2.fill(list.get(i));
   }
   // g2.translate(-x, -y);
   g2.dispose();
 }
 private static void listRepaint(JList list, Rectangle rect) {
   if (Objects.nonNull(rect)) {
     list.repaint(rect);
   }
 }
 @Override
 public String getTipText() {
   return Objects.nonNull(iconlabel) ? iconlabel.getText() : "";
 }