Использование интерфейса ComboBoxEditor с пользовательским JComponent, разрешение редактирования и отображение списка - PullRequest
0 голосов
/ 29 октября 2018

Я проверял документацию.

... и я пытаюсь использовать свой пользовательский ComboBox, реализующий интерфейс ComboBoxEditor.

Вот мой полный код ...

У меня есть один JPanel с JComponents ...

class ThePanel extends JPanel {

  private Font intFont = new Font("Monospaced", Font.PLAIN, 8);

  private final JToggleButton jtbEnabled = new JToggleButton();
  private final JToggleButton jtbDefaultOperation = new JToggleButton();
  private final JTextField jtfText = new JTextField("", 0);

  private final JPanel jpThePanel = new JPanel();

  public ThePanel(Font extFont) {
    this(new TheModel(), extFont);
  }

  public ThePanel(TheModel data, Font extFont) {
    super();
    intFont = extFont;
    initEnabled();
    initDefaultOperation();
    initText();
    setData(data);

    jpThePanel.setLayout(new BoxLayout(jpThePanel, BoxLayout.LINE_AXIS));
    jpThePanel.add(Box.createRigidArea(new Dimension(2, 0)));
    jpThePanel.add(jtbEnabled);
    jpThePanel.add(jtbDefaultOperation);
    jpThePanel.add(jtfText);
    jpThePanel.add(Box.createRigidArea(new Dimension(2, 0)));
    init();
  }

  private void init() {
    setFont(intFont);
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
    setBackground(new Color(0, 0, 0, 0/*64*/));
    add(jpThePanel);
    Dimension d = getMinimumSize();
    setSize(d);
    setPreferredSize(d);
    setMaximumSize(d);
    setSize(d);
  }

  private void initEnabled() {
    jtbEnabled.setFont(intFont);
    jtbEnabled.setIcon(new ImageIcon(getClass().getResource("Unselected.png")));
    jtbEnabled.setSelectedIcon(new ImageIcon(getClass().getResource("Selected.png")));
  }

  private void initDefaultOperation() {
    jtbDefaultOperation.setFont(intFont);
    jtbDefaultOperation.setIcon(new ImageIcon(getClass().getResource("Insert.png")));
    jtbDefaultOperation.setSelectedIcon(new ImageIcon(getClass().getResource("Extract.png")));
    jtbDefaultOperation.setDisabledIcon(new ImageIcon(getClass().getResource("DisabledInsert.png")));
    jtbDefaultOperation.setDisabledSelectedIcon(new ImageIcon(getClass().getResource("DisabledExtract.png")));
  }

  private void initText() {
    jtfText.setFont(intFont);
  }

  public TheModel getData() {
    return new TheModel(
        jtbEnabled.isSelected(),
        jtbDefaultOperation.isSelected(),
        jtfText.getText()
    );
  }

  public void setData(TheModel data) {
    jtbEnabled.setSelected(data.getEnabled());
    jtbDefaultOperation.setEnabled(data.getEnabled());
    jtbDefaultOperation.setSelected(data.getDefaultOperation());
    jtfText.setText(data.getText());
  }

  public void selectAll() {
    jtfText.selectAll();
  }

}

Теперь у меня есть данные модели для его внутренних компонентов ...

class TheModel {

  private Boolean enabled = true;
  private Boolean defaultOperation = true;
  private String text = "";

  public TheModel() {
    this("");
  }

  public TheModel(String text) {
    this(true, text);
  }

  public TheModel(Boolean defaultOperation, String text) {
    this(true, defaultOperation, text);
  }

  public TheModel(Boolean enabled, Boolean defaultOperation, String text) {
    this.enabled = enabled;
    this.defaultOperation = defaultOperation;
    this.text = text;
  }

  public Boolean getEnabled() {
    return enabled;
  }

  public void setEnabled(Boolean enabled) {
    this.enabled = enabled;
  }

  public Boolean getDefaultOperation() {
    return defaultOperation;
  }

  public void setDefaultOperation(Boolean defaultOperation) {
    this.defaultOperation = defaultOperation;
  }

  public String getText() {
    return text;
  }

  public void setText(String text) {
    this.text = text;
  }

  @Override
  public String toString() {
    return "TheModel{" + "enabled=" + enabled + ", defaultOperation=" + defaultOperation + ", text=" + text + '}';
  }

}

Я хочу использовать свой пользовательский компонент (ThePanel) в JComboBox и манипулировать внутренними элементами JToggleButton s и JTextField в соответствии с документацией ... Мне нужно реализовать ComboBoxEditor интерфейс . ПРИМЕЧАНИЕ: Интерфейс ListCellRenderer пытался показать список ... потому что не работал только ComboBoxEditor.

class TheComboBoxEditor implements ComboBoxEditor, ListCellRenderer {

  protected ThePanel editor;
  private final EventListenerList listenerList = new EventListenerList();

  public TheComboBoxEditor(Font extFont) {
    editor = new ThePanel(extFont);
  }

  private void fireActionEvent(ActionEvent evt) {
    for (ActionListener al : listenerList.getListeners(ActionListener.class)) {
      al.actionPerformed(evt);
    }
  }

  @Override
  public void addActionListener(ActionListener l) {
    listenerList.add(ActionListener.class, l);
  }

  @Override
  public void removeActionListener(ActionListener l) {
    listenerList.remove(ActionListener.class, l);
  }

  @Override
  public void selectAll() {
    editor.selectAll();
    editor.requestFocus();
  }

  @Override
  public Object getItem() {
    return editor.getData();
  }

  @Override
  public void setItem(Object anObject) {
    if (anObject != null) {
      if (anObject instanceof TheModel) {
        editor.setData((TheModel) anObject);
      }
      if (anObject instanceof String) {
        editor.setData(new TheModel(anObject.toString()));
      }
    } else {
      editor.setData(new TheModel());
    }
  }

  @Override
  public Component getEditorComponent() {
    return editor;
  }

  @Override
  public Component getListCellRendererComponent(JList list, Object value,
      int index, boolean isSelected, boolean cellHasFocus) {
    TheModel data = (TheModel) value;
    editor.setData(data);
    return editor;
  }

}

Теперь, пример использования моего TheComboBoxEditor класса .

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class ExampleComboBoxEditor extends JFrame {

  public ExampleComboBoxEditor() {
    TheModel items[] = {
      new TheModel(true, true, "True, True"),
      new TheModel(true, false, "True, False"),
      new TheModel(false, true, "False, True"),
      new TheModel(false, false, "False, False")};

    String strings[] = {
      "True, True", "True, False", "False, True", "False, False"};

    JComboBox internalComboBox = new JComboBox(items);
    internalComboBox.setEditable(true);
    internalComboBox.setEditor(new TheComboBoxEditor(this.getFont()));
    internalComboBox.setMaximumRowCount(4);
    internalComboBox.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
      }
    });

    JComboBox stringsComboBox = new JComboBox(strings);
    stringsComboBox.setEditable(true);
    stringsComboBox.setMaximumRowCount(4);
    stringsComboBox.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
      }
    });

    JPanel outer = new JPanel();
    outer.add(internalComboBox);
    outer.add(stringsComboBox);
    add(outer);
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
  }

  public static void main(String[] args) {
    ExampleComboBoxEditor app = new ExampleComboBoxEditor();
    app.setSize(840, 240);
    app.setVisible(true);
  }
}

Теперь у меня есть сомнения (для меня цели этих методов не ясны) ...

1. Когда я переопределяю метод selectAll(), что мне делать?

2. Тогда у меня есть не только один JTextField компонент, но и два JToggleButton. Каковы рекомендации для методов addActionListener(ActionListener l) и removeActionListener(ActionListener l)?

Я проверял этот пример ... http://www.java2s.com/Tutorials/Java/Swing_How_to/JComboBox/Add_JCheckBox_components_to_JComboBox.htm

enter image description here enter image description here

Как видите, список показан ...

Теперь, в моем примере ...

enter image description here enter image description here

3. Я не вижу своего списка компонентов, отображаемых правильно.

4. Я не могу редактировать свои компоненты jtfText, jtbEnabled и jtbDefaultOperation (сохранение состояния / свойства постоянно).

EDIT

Основываясь на ответе aterai, я изменяю индексную часть, сохраняя исходный класс TheModel. Теперь я хочу знать обратную сторону.

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

/*
//https://stackoverflow.com/posts/53046908/revisions

javac -Xlint:unchecked -Xlint:deprecation -encoding utf8 -d /Users/bz/Documentos/Java/classes /Users/jose/Documentos/Java/ExampleComboBoxEditorNOIndex.java
java -cp /Users/bz/Documentos/Java/classes ExampleComboBoxEditorNOIndex &

*/

public class ExampleComboBoxEditorNOIndex  {

  public Component makeUI() {
    TheModel[] items = {
      new TheModel(true, true, "00000"),
      new TheModel(true, false, "11111"),
      new TheModel(false, true, "22222"),
      new TheModel(false, false, "33333")
    };
    JComboBox<TheModel> internalComboBox = new JComboBox<>();
    internalComboBox.setModel(new InternalComboBoxModel<>(items));
    internalComboBox.setEditable(true);
    internalComboBox.setEditor(new TheComboBoxEditor(internalComboBox.getFont()));
    internalComboBox.setRenderer(new TheComboBoxRenderer<>(internalComboBox.getFont()));
    internalComboBox.setMaximumRowCount(4);

    InternalComboBoxModel internalComboBoxModel  = (InternalComboBoxModel)internalComboBox.getModel();
    internalComboBox.addItemListener(e -> {
      if (e.getStateChange() == ItemEvent.SELECTED) {
        JComboBox combo = ((JComboBox) e.getSource());
        int lastValidIndex = combo.getSelectedIndex();
        if (lastValidIndex > -1) {
          internalComboBoxModel.setLastValidIndex(lastValidIndex);
          System.out.println("You chose " + combo.getSelectedItem() 
          + ", in position:" + lastValidIndex + "!");
        }
        combo.getEditor().selectAll();
      }
    });

    JPanel outer = new JPanel();
    outer.add(internalComboBox);
    outer.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
    return outer;
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new ExampleComboBoxEditorNOIndex().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class TheModel {
  private Boolean enabled = true;
  private Boolean defaultOperation = true;
  private String text = "";
  //public int index = -1;

  public TheModel(/*int idx, */Boolean enabled, Boolean defaultOperation, String text) {
    this.enabled = enabled;
    this.defaultOperation = defaultOperation;
    this.text = text;
    //this.index = idx;
  }
  public Boolean getEnabled() {
    return enabled;
  }
  public void setEnabled(Boolean enabled) {
    this.enabled = enabled;
  }
  public Boolean getDefaultOperation() {
    return defaultOperation;
  }
  public void setDefaultOperation(Boolean defaultOperation) {
    this.defaultOperation = defaultOperation;
  }
  public String getText() {
    return text;
  }
  public void setText(String text) {
    this.text = text;
  }
  @Override
  public String toString() {
    return "TheModel{" + "enabled=" + enabled + ", defaultOperation=" + defaultOperation
      + ", text=" + text + '}';
  }
}


class TheComboBoxRenderer<E extends TheModel> implements ListCellRenderer<E> {
  protected ThePanel renderer;
  private int lastSelected = -1;
  public TheComboBoxRenderer(Font extFont) {
    renderer = new ThePanel(new TheModel(/*-1, */false, false, ""), extFont);
  }
  @Override
  public Component getListCellRendererComponent(JList<? extends E> list, E value,
      int index, boolean isSelected, boolean cellHasFocus) {
    if (isSelected) {
      this.lastSelected = index;
      //System.out.println("value: " + value + ", index: " + index + ", isSelected:" + isSelected);
    }
    renderer.setData(value);
    renderer.setBackground(isSelected ? new Color(200, 200, 255) : Color.WHITE);
    return renderer;
  }
}

class TheComboBoxEditor implements ComboBoxEditor {
  protected ThePanel editor;
  public TheComboBoxEditor(Font extFont) {
    super();
    editor = new ThePanel(new TheModel(/*-1, */false, false, ""), extFont);
    editor.setBorder(BorderFactory.createLineBorder(Color.GRAY));
  }
  @Override
  public void selectAll() {
    editor.selectAll();
    // editor.requestFocus(); // <-- The focus moves from jtfText to editor.
  }
  @Override
  public Object getItem() {
  //  System.out.println("getItem(): " + editor.getData());
    return editor.getData();
  }
  @Override
  public void setItem(Object anObject) {
  //  System.out.println("setItem: " + anObject);
    if (anObject instanceof TheModel) {
      TheModel d = (TheModel) anObject;
      editor.setData(d);
    } else {
      editor.setData(new TheModel(/*-1, */false, false, ""));
    }
  }
  @Override
  public Component getEditorComponent() {
    return editor;
  }
  @Override
  public void addActionListener(ActionListener l) {
  //  System.out.println("TheComboBoxEditor.addActionListener: " + l.getClass().getName());
    editor.addActionListener(l);
  }
  @Override
  public void removeActionListener(ActionListener l) {
  //  System.out.println("TheComboBoxEditor.removeActionListener: " + l.getClass().getName());
    editor.removeActionListener(l);
  }
}


class ThePanel extends JPanel {
  private Font intFont = new Font("Monospaced", Font.PLAIN, 8);
  // Since there is no Unselected.png etc,
  // use JCheckBox instead of JToggleButton instead.
  public final JCheckBox jtbEnabled = new JCheckBox();
  public final JCheckBox jtbDefaultOperation = new JCheckBox();
  public final JTextField jtfText = new JTextField("", 10);
  private TheModel data;

  public ThePanel(TheModel data, Font extFont) {
    super();
    this.data = data;
    setData(data);

    jtbEnabled.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        if (combo.getItemCount() > 0) {
          InternalComboBoxModel internalComboBoxModel = (InternalComboBoxModel) combo.getModel();
          int lastValidIndex = internalComboBoxModel.getLastValidIndex();
      //  TheModel o = (TheModel) combo.getSelectedItem();
      //  int idx = positionItemList(getComboBoxItems(combo), o);//int idx = model.getIndexOf(data);
          TheModel rd = (TheModel) combo.getItemAt(lastValidIndex);//(TheModel) combo.getItemAt(idx);
          boolean b = ((JCheckBox) e.getSource()).isSelected();
          rd.setEnabled(b);
          jtbDefaultOperation.setEnabled(b);
        }
      }
    });
    jtbEnabled.setOpaque(false);
    jtbEnabled.setFocusable(false);

    jtbDefaultOperation.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        if (combo.getItemCount() > 0) {
          InternalComboBoxModel internalComboBoxModel = (InternalComboBoxModel) combo.getModel();
          int lastValidIndex = internalComboBoxModel.getLastValidIndex();
      //  TheModel o = (TheModel) combo.getSelectedItem();
      //  int idx = positionItemList(getComboBoxItems(combo), o);//int idx = model.getIndexOf(data);
          TheModel rd = (TheModel) combo.getItemAt(lastValidIndex);//(TheModel) combo.getItemAt(idx);
          rd.setDefaultOperation(((JCheckBox) e.getSource()).isSelected());
        }
      }
    });
    jtbDefaultOperation.setOpaque(false);
    jtbDefaultOperation.setFocusable(false);

    jtfText.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        if (combo.getItemCount() > 0) {
          InternalComboBoxModel internalComboBoxModel = (InternalComboBoxModel) combo.getModel();
          int lastValidIndex = internalComboBoxModel.getLastValidIndex();
      //  TheModel o = (TheModel) combo.getSelectedItem();
      //  int idx = positionItemList(getComboBoxItems(combo), o);//int idx = model.getIndexOf(data);
          TheModel rd = (TheModel) combo.getItemAt(lastValidIndex);//(TheModel) combo.getItemAt(idx);
          rd.setText(((JTextField) e.getSource()).getText());
        }
      }
    });
    jtfText.setBorder(BorderFactory.createEmptyBorder());
    jtfText.setOpaque(false);

    setOpaque(true);
    setBackground(Color.WHITE);
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));

    add(Box.createRigidArea(new Dimension(2, 0)));
    add(jtbEnabled);
    add(jtbDefaultOperation);
    add(jtfText);
    add(Box.createRigidArea(new Dimension(2, 0)));
  }

  private <T> java.util.List<T> getComboBoxItems(JComboBox comboBox) {
    if (comboBox != null) {
      java.util.List<String> list = new ArrayList<>();
      for (int i = 0; i < comboBox.getItemCount(); i++) {
        list.add(comboBox.getItemAt(i).toString());
      }
      return (java.util.List<T>) list;//(ArrayList<T>)list; // list; // (List<T>)list;
    }
    return null;
  }

  private Integer positionItemList(java.util.List list, Object object) {
    Integer position = -1;
    Integer last = list.size();
    for (int pos = 0; pos < last; pos++) {
      if (list.get(pos).toString().equals(object.toString())) {
        position = pos;
      }
    }
    return position;
  }

  public TheModel getData() {
    return new TheModel(
        //data.index,
        jtbEnabled.isSelected(),
        jtbDefaultOperation.isSelected(),
        jtfText.getText());
  }

  public void setData(TheModel data) {
    //this.data.index = data.index;
    jtbEnabled.setSelected(data.getEnabled());
    jtbDefaultOperation.setEnabled(data.getEnabled());
    jtbDefaultOperation.setSelected(data.getDefaultOperation());
    jtfText.setText(data.getText());
  }
  public void selectAll() {
      jtfText.requestFocusInWindow();
      jtfText.selectAll();
  }
  public void addActionListener(ActionListener l) {

    jtfText.addActionListener(l);
    jtbEnabled.addActionListener(l);
    jtbDefaultOperation.addActionListener(l);
  }
  public void removeActionListener(ActionListener l) {
    jtfText.removeActionListener(l);
    jtbEnabled.removeActionListener(l);
    jtbDefaultOperation.removeActionListener(l);
  }

}

class InternalComboBoxModel<E extends TheModel> extends DefaultComboBoxModel {
  private Integer lastValidIndex = 0;

  InternalComboBoxModel(final E items[]) {
    super(items);
  }

  InternalComboBoxModel(Vector<E> v) {
    super(v);
  }

  public Integer getLastValidIndex() {
    return lastValidIndex;
  }

  public void setLastValidIndex(Integer lastValidIndex) {
    this.lastValidIndex = lastValidIndex;
  }

}

И я хочу устранить все предупреждения, если это возможно.

MacBook-Air:Users bz$ javac -Xlint:unchecked -Xlint:deprecation -encoding utf8 -d /Users/bz/Documentos/Java/classes /Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java
/Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java:24: warning: [unchecked] unchecked method invocation: method setModel in class JComboBox is applied to given types
    internalComboBox.setModel(new InternalComboBoxModel<>(items));
                             ^
  required: ComboBoxModel<E>
  found: InternalComboBoxModel<TheModel>
  where E is a type-variable:
    E extends Object declared in class JComboBox
/Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java:24: warning: [unchecked] unchecked conversion
    internalComboBox.setModel(new InternalComboBoxModel<>(items));
                              ^
  required: ComboBoxModel<E>
  found:    InternalComboBoxModel<TheModel>
  where E is a type-variable:
    E extends Object declared in class JComboBox
/Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java:257: warning: [unchecked] unchecked cast
      return (java.util.List<T>) list;//(ArrayList<T>)list; // list; // (List<T>)list;
                                 ^
  required: List<T>
  found:    List<String>
  where T is a type-variable:
    T extends Object declared in method <T>getComboBoxItems(JComboBox)
/Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java:310: warning: [unchecked] unchecked call to DefaultComboBoxModel(E[]) as a member of the raw type DefaultComboBoxModel
    super(items);
         ^
  where E is a type-variable:
    E extends Object declared in class DefaultComboBoxModel
/Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java:314: warning: [unchecked] unchecked call to DefaultComboBoxModel(Vector<E>) as a member of the raw type DefaultComboBoxModel
    super(v);
         ^
  where E is a type-variable:
    E extends Object declared in class DefaultComboBoxModel
5 warnings
MacBook-Air:Users bz$ 

1 Ответ

0 голосов
/ 29 октября 2018

Отображение списка

Вам необходимо добавить средство визуализации ячеек.

internalComboBox.setRenderer(new TheComboBoxRenderer<>(font));

разрешить редактирование

Необходимо добавить прослушиватель действий для каждого компонента редактора комбо.

editor.jtbDefaultOperation.addActionListener(e -> {
  System.out.println("jtbDefaultOperation" + editor.getData().index);
  Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, editor);
  if (c instanceof JComboBox) {
    JComboBox<?> combo = (JComboBox<?>) c;
    TheModel o = (TheModel) combo.getItemAt(editor.getData().index);
    o.setDefaultOperation(((JCheckBox) e.getSource()).isSelected());
  }
});

Когда я переопределяю метод selectAll (), что мне делать?

Не перемещайте фокус на TheComboBoxEditor.

internalComboBox.addItemListener(e -> {
  if (e.getStateChange() == ItemEvent.SELECTED) {
    System.out.println("You chose ...");
    ((JComboBox) e.getSource()).getEditor().selectAll();
  }
});

class TheComboBoxEditor implements ComboBoxEditor {
  @Override
  public void selectAll() {
    editor.selectAll();
    // editor.requestFocus(); // <-- The focus moves from JTextField(jtfText) to JPanel(ThePanel).
  }

ExampleComboBoxEditor3.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class ExampleComboBoxEditor3 {
  public Component makeUI() {
    TheModel[] items = {
      new TheModel(0, true, true, "00000"),
      new TheModel(1, true, false, "11111"),
      new TheModel(2, false, true, "22222"),
      new TheModel(3, false, false, "33333")
    };
    JComboBox<TheModel> internalComboBox = new JComboBox<>(items);
    internalComboBox.setEditable(true);
    internalComboBox.setEditor(new TheComboBoxEditor(internalComboBox.getFont()));
    internalComboBox.setRenderer(new TheComboBoxRenderer<>(internalComboBox.getFont()));
    internalComboBox.setMaximumRowCount(4);
    // internalComboBox.addActionListener(e -> {
    //   System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
    // });
    internalComboBox.addItemListener(e -> {
      if (e.getStateChange() == ItemEvent.SELECTED) {
        System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
        ((JComboBox) e.getSource()).getEditor().selectAll();
      }
    });

    JPanel outer = new JPanel();
    outer.add(internalComboBox);
    outer.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
    return outer;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new ExampleComboBoxEditor3().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class TheModel {
  private Boolean enabled = true;
  private Boolean defaultOperation = true;
  private String text = "";
  public int index = -1;

  public TheModel(int idx, Boolean enabled, Boolean defaultOperation, String text) {
    this.enabled = enabled;
    this.defaultOperation = defaultOperation;
    this.text = text;
    this.index = idx;
  }
  public Boolean getEnabled() {
    return enabled;
  }
  public void setEnabled(Boolean enabled) {
    this.enabled = enabled;
  }
  public Boolean getDefaultOperation() {
    return defaultOperation;
  }
  public void setDefaultOperation(Boolean defaultOperation) {
    this.defaultOperation = defaultOperation;
  }
  public String getText() {
    return text;
  }
  public void setText(String text) {
    this.text = text;
  }
  @Override
  public String toString() {
    return "TheModel{" + "enabled=" + enabled + ", defaultOperation=" + defaultOperation
      + ", text=" + text + '}';
  }
}

class TheComboBoxRenderer<E extends TheModel> implements ListCellRenderer<E> {
  protected ThePanel renderer;
  public TheComboBoxRenderer(Font extFont) {
    renderer = new ThePanel(new TheModel(-1, false, false, ""), extFont);
  }
  @Override
  public Component getListCellRendererComponent(JList<? extends E> list, E value,
      int index, boolean isSelected, boolean cellHasFocus) {
    renderer.setData(value);
    renderer.setBackground(isSelected ? new Color(200, 200, 255) : Color.WHITE);
    return renderer;
  }
}

class TheComboBoxEditor implements ComboBoxEditor {
  protected ThePanel editor;
  public TheComboBoxEditor(Font extFont) {
    super();
    editor = new ThePanel(new TheModel(-1, false, false, ""), extFont);
    editor.setBorder(BorderFactory.createLineBorder(Color.GRAY));
  }
  @Override
  public void selectAll() {
    editor.selectAll();
    // editor.requestFocus(); // <-- The focus moves from jtfText to editor.
  }
  @Override
  public Object getItem() {
    return editor.getData();
  }
  @Override
  public void setItem(Object anObject) {
    if (anObject instanceof TheModel) {
      TheModel d = (TheModel) anObject;
      editor.setData(d);
    } else {
      editor.setData(new TheModel(-1, false, false, ""));
    }
  }
  @Override
  public Component getEditorComponent() {
    return editor;
  }
  @Override
  public void addActionListener(ActionListener l) {
    System.out.println("addActionListener: " + l.getClass().getName());
    editor.addActionListener(l);
  }
  @Override
  public void removeActionListener(ActionListener l) {
    System.out.println("removeActionListener: " + l.getClass().getName());
    editor.removeActionListener(l);
  }
}

class ThePanel extends JPanel {
  private Font intFont = new Font("Monospaced", Font.PLAIN, 8);
  // Since there is no Unselected.png etc,
  // use JCheckBox instead of JToggleButton instead.
  public final JCheckBox jtbEnabled = new JCheckBox();
  public final JCheckBox jtbDefaultOperation = new JCheckBox();
  public final JTextField jtfText = new JTextField("", 10);
  private TheModel data;

  public ThePanel(TheModel data, Font extFont) {
    super();
    this.data = data;
    setData(data);

    jtbEnabled.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(data.index);
        boolean b = ((JCheckBox) e.getSource()).isSelected();
        o.setEnabled(b);
        jtbDefaultOperation.setEnabled(b);
      }
    });
    jtbEnabled.setOpaque(false);
    jtbEnabled.setFocusable(false);

    jtbDefaultOperation.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(data.index);
        o.setDefaultOperation(((JCheckBox) e.getSource()).isSelected());
      }
    });
    jtbDefaultOperation.setOpaque(false);
    jtbDefaultOperation.setFocusable(false);

    jtfText.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(data.index);
        o.setText(((JTextField) e.getSource()).getText());
      }
    });
    jtfText.setBorder(BorderFactory.createEmptyBorder());
    jtfText.setOpaque(false);

    setOpaque(true);
    setBackground(Color.WHITE);
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));

    add(Box.createRigidArea(new Dimension(2, 0)));
    add(jtbEnabled);
    add(jtbDefaultOperation);
    add(jtfText);
    add(Box.createRigidArea(new Dimension(2, 0)));
  }

  public TheModel getData() {
    return new TheModel(
        data.index,
        jtbEnabled.isSelected(),
        jtbDefaultOperation.isSelected(),
        jtfText.getText());
  }

  public void setData(TheModel data) {
    this.data.index = data.index;
    jtbEnabled.setSelected(data.getEnabled());
    jtbDefaultOperation.setEnabled(data.getEnabled());
    jtbDefaultOperation.setSelected(data.getDefaultOperation());
    jtfText.setText(data.getText());
  }
  public void selectAll() {
      jtfText.requestFocusInWindow();
      jtfText.selectAll();
  }
  public void addActionListener(ActionListener l) {
    jtfText.addActionListener(l);
    jtbEnabled.addActionListener(l);
    jtbDefaultOperation.addActionListener(l);
  }
  public void removeActionListener(ActionListener l) {
    jtfText.removeActionListener(l);
    jtbEnabled.removeActionListener(l);
    jtbDefaultOperation.removeActionListener(l);
  }
}

ExampleComboBoxEditor4.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class ExampleComboBoxEditor4 {
  public Component makeUI() {
    TheModel[] items = {
      new TheModel(true, true, "00000"),
      new TheModel(true, false, "11111"),
      new TheModel(false, true, "22222"),
      new TheModel(false, false, "33333")
    };
    JComboBox<TheModel> internalComboBox = new JComboBox<>(items);
    internalComboBox.setEditable(true);
    internalComboBox.setEditor(new TheComboBoxEditor(internalComboBox.getFont()));
    internalComboBox.setRenderer(new TheComboBoxRenderer<>(internalComboBox.getFont()));
    internalComboBox.setMaximumRowCount(4);
    // internalComboBox.addActionListener(e -> {
    //   System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
    // });
    internalComboBox.addItemListener(e -> {
      if (e.getStateChange() == ItemEvent.SELECTED) {
        System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
        ((JComboBox) e.getSource()).getEditor().selectAll();
      }
    });

    JPanel outer = new JPanel();
    outer.add(internalComboBox);
    outer.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
    return outer;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new ExampleComboBoxEditor4().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class TheModel {
  private Boolean enabled = true;
  private Boolean defaultOperation = true;
  private String text = "";

  public TheModel(Boolean enabled, Boolean defaultOperation, String text) {
    this.enabled = enabled;
    this.defaultOperation = defaultOperation;
    this.text = text;
  }
  public Boolean getEnabled() {
    return enabled;
  }
  public void setEnabled(Boolean enabled) {
    this.enabled = enabled;
  }
  public Boolean getDefaultOperation() {
    return defaultOperation;
  }
  public void setDefaultOperation(Boolean defaultOperation) {
    this.defaultOperation = defaultOperation;
  }
  public String getText() {
    return text;
  }
  public void setText(String text) {
    this.text = text;
  }
  @Override
  public String toString() {
    return "TheModel{" + "enabled=" + enabled + ", defaultOperation=" + defaultOperation
      + ", text=" + text + '}';
  }
//   // TEST:
//   @Override
//   public boolean equals(Object obj) {
//     if (obj instanceof TheModel) {
//       return Objects.equals(text, ((TheModel) obj).getText());
//     } else {
//       return false;
//     }
//   }
}

class TheComboBoxRenderer<E extends TheModel> implements ListCellRenderer<E> {
  protected ThePanel renderer;
  public TheComboBoxRenderer(Font extFont) {
    renderer = new ThePanel(new TheModel(false, false, ""), extFont);
  }
  @Override
  public Component getListCellRendererComponent(JList<? extends E> list, E value,
      int index, boolean isSelected, boolean cellHasFocus) {
    renderer.setData(value);
    renderer.setBackground(isSelected ? new Color(200, 200, 255) : Color.WHITE);
    return renderer;
  }
}

class TheComboBoxEditor implements ComboBoxEditor {
  protected ThePanel editor;
  public TheComboBoxEditor(Font extFont) {
    super();
    editor = new ThePanel(new TheModel(false, false, ""), extFont);
    editor.setBorder(BorderFactory.createLineBorder(Color.GRAY));
  }
  @Override
  public void selectAll() {
    editor.selectAll();
    // editor.requestFocus(); // <-- The focus moves from jtfText to editor.
  }
  @Override
  public Object getItem() {
    return editor.getData();
  }
  @Override
  public void setItem(Object anObject) {
    EventQueue.invokeLater(() -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, getEditorComponent());
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        int idx = combo.getSelectedIndex();
        if (idx >= 0 && idx != editor.lastValidIndex) {
          System.out.println("setItem: " + idx);
          editor.lastValidIndex = idx;
        }
      }
    });
    if (anObject instanceof TheModel) {
      TheModel d = (TheModel) anObject;
      editor.setData(d);
    } else {
      editor.setData(new TheModel(false, false, ""));
    }
  }
  @Override
  public Component getEditorComponent() {
    return editor;
  }
  @Override
  public void addActionListener(ActionListener l) {
    editor.addActionListener(l);
  }
  @Override
  public void removeActionListener(ActionListener l) {
    editor.removeActionListener(l);
  }
}

class ThePanel extends JPanel {
  private Font intFont = new Font("Monospaced", Font.PLAIN, 8);
  // Since there is no Unselected.png etc,
  // use JCheckBox instead of JToggleButton instead.
  public final JCheckBox jtbEnabled = new JCheckBox();
  public final JCheckBox jtbDefaultOperation = new JCheckBox();
  public final JTextField jtfText = new JTextField("", 10);
  private TheModel data;
  public int lastValidIndex = -1;

  public ThePanel(TheModel data, Font extFont) {
    super();
    this.data = data;
    setData(data);

    jtbEnabled.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(lastValidIndex);
        boolean b = ((JCheckBox) e.getSource()).isSelected();
        o.setEnabled(b);
        jtbDefaultOperation.setEnabled(b);
        combo.setSelectedIndex(lastValidIndex);
      }
    });
    jtbEnabled.setOpaque(false);
    jtbEnabled.setFocusable(false);

    jtbDefaultOperation.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(lastValidIndex);
        o.setDefaultOperation(((JCheckBox) e.getSource()).isSelected());
        combo.setSelectedIndex(lastValidIndex);
      }
    });
    jtbDefaultOperation.setOpaque(false);
    jtbDefaultOperation.setFocusable(false);

    jtfText.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(lastValidIndex);
        o.setText(((JTextField) e.getSource()).getText());
        combo.setSelectedIndex(lastValidIndex);
      }
    });
    jtfText.setBorder(BorderFactory.createEmptyBorder());
    jtfText.setOpaque(false);

    setOpaque(true);
    setBackground(Color.WHITE);
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));

    add(Box.createRigidArea(new Dimension(2, 0)));
    add(jtbEnabled);
    add(jtbDefaultOperation);
    add(jtfText);
    add(Box.createRigidArea(new Dimension(2, 0)));
  }

  public TheModel getData() {
    return new TheModel(
        jtbEnabled.isSelected(),
        jtbDefaultOperation.isSelected(),
        jtfText.getText());
  }

  public void setData(TheModel data) {
    jtbEnabled.setSelected(data.getEnabled());
    jtbDefaultOperation.setEnabled(data.getEnabled());
    jtbDefaultOperation.setSelected(data.getDefaultOperation());
    jtfText.setText(data.getText());
  }
  public void selectAll() {
    jtfText.requestFocusInWindow();
    jtfText.selectAll();
  }
  public void addActionListener(ActionListener l) {
    jtfText.addActionListener(l);
    jtbEnabled.addActionListener(l);
    jtbDefaultOperation.addActionListener(l);
  }
  public void removeActionListener(ActionListener l) {
    jtfText.removeActionListener(l);
    jtbEnabled.removeActionListener(l);
    jtbDefaultOperation.removeActionListener(l);
  }
}
...