Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.dua3.utility.swing;
import com.dua3.cabe.annotations.Nullable;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.UIManager;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import java.awt.Component;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
/**
* A custom JPanel-based ComboBox component with additional functionality for editing, adding, and removing items.
*
* @param the type of the items in the ComboBox
*/
public class ComboBoxEx extends JPanel {
private static final Logger LOG = LogManager.getLogger(ComboBoxEx.class);
private Comparator super T> comparator;
private final UnaryOperator edit;
private final Supplier extends T> add;
private final BiPredicate, T> remove;
private final Function format;
private final DefaultComboBoxModel model;
private final JComboBox comboBox;
private final JButton buttonEdit;
private final JButton buttonAdd;
private final JButton buttonRemove;
/**
* Constructs a ComboBoxEx with optional editing, adding, and removing functionality.
*
* @param edit a UnaryOperator that allows editing of items in the ComboBoxEx, or null if editing is not enabled
* @param add a Supplier that supplies new items to add to the ComboBoxEx, or null if adding is not enabled
* @param remove a BiPredicate that determines if an item can be removed from the ComboBoxEx, or null if removing is not enabled
* @param format a Function that formats each item in the ComboBoxEx as a string
* @param items the initial items to populate the ComboBoxEx with
*/
@SafeVarargs
public ComboBoxEx(@Nullable UnaryOperator edit, @Nullable Supplier extends T> add, @Nullable BiPredicate, T> remove, Function format, T... items) {
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
this.format = format;
this.model = new DefaultComboBoxModel<>(items);
this.comboBox = new JComboBox<>(model);
add(comboBox);
if (edit != null) {
this.edit = edit;
this.buttonEdit = new JButton(SwingUtil.createAction("✎", this::editItem));
add(buttonEdit);
comboBox.addItemListener(item -> buttonEdit.setEnabled(item != null));
} else {
this.edit = null;
this.buttonEdit = null;
}
if (add != null) {
this.add = add;
this.buttonAdd = new JButton(SwingUtil.createAction("+", this::addItem));
add(buttonAdd);
} else {
this.add = null;
this.buttonAdd = null;
}
if (remove != null) {
this.remove = remove;
this.buttonRemove = new JButton(SwingUtil.createAction("-", this::removeItem));
add(buttonRemove);
} else {
this.remove = null;
this.buttonRemove = null;
}
model.addListDataListener(new ListDataListener() {
@Override
public void intervalAdded(ListDataEvent e) {
updateButtonStates();
}
@Override
public void intervalRemoved(ListDataEvent e) {
updateButtonStates();
}
@Override
public void contentsChanged(ListDataEvent e) {
updateButtonStates();
}
});
updateButtonStates();
ListCellRenderer super T> renderer = new BasicComboBoxRenderer() {
@Override
public Component getListCellRendererComponent(JList> list, @Nullable Object value, int index, boolean isSelected, boolean cellHasFocus) {
String text = "";
if (value != null) {
try {
//noinspection unchecked
text = format.apply((T) value);
} catch (Exception e) {
LOG.warn("error during formatting", e);
text = String.valueOf(value);
}
}
setText(text);
if (isSelected) {
setBackground(UIManager.getColor("ComboBox.selectionBackground"));
setForeground(UIManager.getColor("ComboBox.selectionForeground"));
} else {
setBackground(UIManager.getColor("ComboBox.background"));
setForeground(UIManager.getColor("ComboBox.foreground"));
}
return this;
}
};
comboBox.setRenderer(renderer);
}
private void updateButtonStates() {
buttonEdit.setEnabled(model.getSelectedItem() != null);
buttonAdd.setEnabled(true);
buttonRemove.setEnabled(model.getSize() > 1 && model.getSelectedItem() != null);
}
private void editItem() {
int idx = comboBox.getSelectedIndex();
if (idx >= 0) {
T item = model.getElementAt(idx);
item = edit.apply(item);
if (item != null) {
model.removeElementAt(idx);
model.insertElementAt(item, idx);
model.setSelectedItem(item);
sortItems();
}
}
}
private void addItem() {
Optional.ofNullable(add.get()).ifPresent(item -> {
model.addElement(item);
model.setSelectedItem(item);
sortItems();
});
}
private void removeItem() {
//noinspection unchecked
T item = (T) model.getSelectedItem();
if (Optional.ofNullable(remove).orElse(ComboBoxEx::alwaysRemoveSelectedItem).test(this, item)) {
model.removeElement(item);
}
}
/**
* Method to be passed as a method reference in the {@code remove} parameter to the ComboBoxEx construvtor.
* Asks the user to confirm the removal of a selected item from the given ComboBoxEx.
*
* @param the type of the items in the ComboBox
* @param cb the ComboBoxEx from which the item should be removed
* @param item the item to be removed
* @return true if the user confirms the removal, false otherwise
*/
public static boolean askBeforeRemoveSelectedItem(ComboBoxEx cb, T item) {
int rc = JOptionPane.showConfirmDialog(
cb,
"Remove " + cb.format.apply(item) + "?",
"",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE
);
return rc == JOptionPane.YES_OPTION;
}
/**
* Method to be passed as a method reference in the {@code remove} parameter to the ComboBoxEx construvtor.
* Always remove selected item from the given ComboBoxEx without asking for user confirmation.
*
* @param the type of the items in the ComboBox
* @param cb the ComboBoxEx from which the item should be removed
* @param item the item to be removed
* @return always returns true to indicate removal is allowed
*/
@SuppressWarnings("SameReturnValue")
public static boolean alwaysRemoveSelectedItem(ComboBoxEx cb, T item) {
return true;
}
/**
* Retrieves the currently selected item from the ComboBoxEx.
*
* @return an Optional containing the selected item, or an empty Optional if no item is selected
*/
public Optional getSelectedItem() {
//noinspection unchecked
return Optional.ofNullable((T) comboBox.getSelectedItem());
}
/**
* Retrieves the list of items in the ComboBoxEx.
*
* @return a List containing all the items in the ComboBoxEx
*/
public List getItems() {
int n = model.getSize();
ArrayList items = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
items.add(model.getElementAt(i));
}
return items;
}
/**
* Sets the selected item in the ComboBoxEx to the specified item.
*
* @param item the item to be selected in the ComboBoxEx
*/
public void setSelectedItem(T item) {
comboBox.setSelectedItem(item);
}
/**
* Inserts the specified item at the specified position in the ComboBoxEx.
*
* @param item the item to be inserted in the ComboBoxEx
* @param index the index at which the item is to be inserted
*/
public void insertItemAt(T item, int index) {
comboBox.insertItemAt(item, index);
}
/**
* Adds an ActionListener to the ComboBoxEx.
*
* @param listener the ActionListener to be added to the ComboBoxEx
*/
public void addActionListener(ActionListener listener) {
comboBox.addActionListener(listener);
}
/**
* Adds an ItemListener to the ComboBoxEx.
*
* @param listener the ItemListener to be added to the ComboBoxEx
*/
public void addItemListener(ItemListener listener) {
comboBox.addItemListener(listener);
}
/**
* Adds a PopupMenuListener to the ComboBoxEx.
*
* @param listener the PopupMenuListener to be added to the ComboBoxEx
*/
public void addPopupMenuListener(PopupMenuListener listener) {
comboBox.addPopupMenuListener(listener);
}
/**
* Removes an ActionListener from the ComboBoxEx.
*
* @param listener the ActionListener to be removed from the ComboBoxEx
*/
public void removeActionListener(ActionListener listener) {
comboBox.removeActionListener(listener);
}
/**
* Removes an ItemListener from the ComboBoxEx.
*
* @param listener the ItemListener to be removed from the ComboBoxEx
*/
public void removeItemListener(ItemListener listener) {
comboBox.removeItemListener(listener);
}
/**
* Removes a PopupMenuListener from the ComboBoxEx.
*
* @param listener the PopupMenuListener to be removed from the ComboBoxEx
*/
public void removePopupMenuListener(PopupMenuListener listener) {
comboBox.removePopupMenuListener(listener);
}
/**
* Sets the Comparator used for sorting items in the ComboBoxEx.
*
* @param comparator the Comparator to be set for sorting items in the ComboBoxEx
*/
public void setComparator(Comparator super T> comparator) {
this.comparator = comparator;
sortItems();
}
/**
* Sorts the items in the ComboBoxEx using the current Comparator.
* If no Comparator is set, the method does nothing.
* The selected item is preserved after the sorting.
*/
public void sortItems() {
if (comparator == null) {
return;
}
Optional selectedItem = getSelectedItem();
int n = model.getSize();
List elements = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
elements.add(model.getElementAt(i));
}
elements.sort(comparator);
model.removeAllElements();
model.addAll(elements);
selectedItem.ifPresent(model::setSelectedItem);
}
}