blob: 067dd090f54bf5465b34bab28207ab2b44c9aeec [file] [log] [blame]
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed 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
*
* http://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 com.intellij.ui.components.editors;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.TableUtil;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBList;
import com.intellij.util.Function;
import com.intellij.util.PlatformIcons;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.EmptyIcon;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.List;
/**
* Solves rendering problems in JTable components when JComboBox objects are used as cell
* editors components. Known issues of using JComboBox component are the following:
* <p>1. Ugly view if row height is small enough
* <p>2. Truncated strings in the combobox popup if column width is less than text value width
* <p>
* <b>How to use:</b>
* <p>1. In get <code>getTableCellEditorComponent</code> method create or use existent
* <code>JBComboBoxTableCellEditorComponent</code> instance<br/>
* <p>2. Init component by calling <code>setCell</code>, <code>setOptions</code>,
* <code>setDefaultValue</code> methods
* <p>3. Return the instance
*
* @author Konstantin Bulenkov
* @see com.intellij.ui.components.JBComboBoxLabel
*/
public class JBComboBoxTableCellEditorComponent extends JBLabel {
private JTable myTable;
private int myRow = 0;
private int myColumn = 0;
private final JBList myList = new JBList();
private Object[] myOptions = {};
private Object myValue;
private Function<Object, String> myToString = StringUtil.createToStringFunction(Object.class);
private final List<ActionListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
@SuppressWarnings({"GtkPreferredJComboBoxRenderer"})
private ListCellRenderer myRenderer = new DefaultListCellRenderer() {
public Icon myEmptyIcon;
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
final JLabel label = (JLabel)super.getListCellRendererComponent(list, myToString.fun(value), index, isSelected, cellHasFocus);
if (value == myValue) {
label.setIcon(getIcon(isSelected));
} else {
label.setIcon(getEmptyIcon());
}
return label;
}
private Icon getEmptyIcon() {
if (myEmptyIcon == null) {
myEmptyIcon = EmptyIcon.create(getIcon(true).getIconWidth());
}
return myEmptyIcon;
}
private Icon getIcon(boolean selected) {
final boolean small = "small".equals(JBComboBoxTableCellEditorComponent.this.getClientProperty("JComponent.sizeVariant"));
return small
? selected ? PlatformIcons.CHECK_ICON_SMALL_SELECTED : PlatformIcons.CHECK_ICON_SMALL
: selected ? PlatformIcons.CHECK_ICON_SELECTED : PlatformIcons.CHECK_ICON;
}
};
public JBComboBoxTableCellEditorComponent() {
}
public JBComboBoxTableCellEditorComponent(JTable table) {
myTable = table;
}
public void setCell(JTable table, int row, int column) {
setTable(table);
setRow(row);
setColumn(column);
}
public void setTable(JTable table) {
myTable = table;
}
public void setRow(int row) {
myRow = row;
}
public void setColumn(int column) {
myColumn = column;
}
public Object[] getOptions() {
return myOptions;
}
public void setOptions(Object... options) {
myOptions = options;
}
@Override
public void addNotify() {
super.addNotify();
initAndShowPopup();
}
private void initAndShowPopup() {
myList.removeAll();
final Rectangle rect = myTable.getCellRect(myRow, myColumn, true);
final Point point = new Point(rect.x, rect.y);
myList.setModel(JBList.createDefaultListModel(myOptions));
if (myRenderer != null) {
myList.setCellRenderer(myRenderer);
}
JBPopupFactory.getInstance()
.createListPopupBuilder(myList)
.setItemChoosenCallback(new Runnable() {
@Override
public void run() {
final ActionEvent event = new ActionEvent(myList, ActionEvent.ACTION_PERFORMED, "elementChosen");
for (ActionListener listener : myListeners) {
listener.actionPerformed(event);
}
myValue = myList.getSelectedValue();
TableUtil.stopEditing(myTable);
myTable.setValueAt(myValue, myRow, myColumn); // on Mac getCellEditorValue() called before myValue is set.
myTable.tableChanged(new TableModelEvent(myTable.getModel(), myRow)); // force repaint
}
})
.setCancelCallback(new Computable<Boolean>() {
@Override
public Boolean compute() {
TableUtil.stopEditing(myTable);
return true;
}
})
.createPopup()
.show(new RelativePoint(myTable, point));
}
public Object getEditorValue() {
return myValue;
}
public void setRenderer(ListCellRenderer renderer) {
myRenderer = renderer;
}
public void setDefaultValue(Object value) {
myValue = value;
}
public void setToString(Function<Object, String> toString) {
myToString = toString;
}
public void addActionListener(ActionListener listener) {
myListeners.add(listener);
}
}