| /* |
| * Copyright 2000-2012 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.openapi.ui; |
| |
| import com.intellij.icons.AllIcons; |
| import com.intellij.openapi.ui.popup.*; |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import javax.swing.border.EmptyBorder; |
| import javax.swing.event.CellEditorListener; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.EventListenerList; |
| import javax.swing.table.TableCellEditor; |
| import javax.swing.table.TableCellRenderer; |
| import java.awt.*; |
| import java.awt.event.MouseEvent; |
| import java.lang.ref.WeakReference; |
| import java.util.EventObject; |
| import java.util.List; |
| |
| /** |
| * @author spleaner |
| */ |
| public class ComboBoxTableRenderer<T> extends JLabel implements TableCellRenderer, TableCellEditor, JBPopupListener { |
| |
| private final T[] myValues; |
| private WeakReference<ListPopup> myPopupRef; |
| private ChangeEvent myChangeEvent = null; |
| private T myValue; |
| private boolean myPaintArrow = true; |
| |
| protected EventListenerList myListenerList = new EventListenerList(); |
| |
| private Runnable myFinalRunnable; |
| |
| public ComboBoxTableRenderer(final T[] values) { |
| myValues = values; |
| setFont(UIUtil.getButtonFont()); |
| setBorder(new EmptyBorder(0, 5, 0, 5)); |
| } |
| |
| @Override |
| public Dimension getPreferredSize() { |
| return addIconSize(super.getPreferredSize()); |
| } |
| |
| @Override |
| public Dimension getMinimumSize() { |
| return getPreferredSize(); |
| } |
| |
| private static Dimension addIconSize(final Dimension d) { |
| return new Dimension(d.width + AllIcons.General.ArrowDown.getIconWidth() + 2, Math.max(d.height, AllIcons.General.ArrowDown |
| .getIconHeight())); |
| } |
| |
| protected String getTextFor(@NotNull T value) { |
| return value.toString(); |
| } |
| |
| protected Icon getIconFor(@NotNull T value) { |
| return null; |
| } |
| |
| public void setPaintArrow(final boolean paintArrow) { |
| myPaintArrow = paintArrow; |
| } |
| |
| protected Runnable onChosen(@NotNull final T value) { |
| stopCellEditing(value); |
| |
| return new Runnable() { |
| public void run() { |
| stopCellEditing(value); |
| } |
| }; |
| } |
| |
| @Override |
| protected void paintComponent(Graphics g) { |
| super.paintComponent(g); |
| |
| if (!StringUtil.isEmpty(getText()) && myPaintArrow) { |
| final Rectangle r = getBounds(); |
| final Insets i = getInsets(); |
| final int x = r.width - i.right - AllIcons.General.ArrowDown.getIconWidth(); |
| final int y = i.top + (r.height - i.top - i.bottom - AllIcons.General.ArrowDown.getIconHeight()) / 2; |
| AllIcons.General.ArrowDown.paintIcon(this, g, x, y); |
| } |
| } |
| |
| public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { |
| @SuppressWarnings("unchecked") final T t = (T)value; |
| customizeComponent(t, table, isSelected); |
| return this; |
| } |
| |
| public Component getTableCellEditorComponent(JTable table, final Object value, boolean isSelected, final int row, final int column) { |
| @SuppressWarnings("unchecked") final T t = (T)value; |
| myValue = t; |
| customizeComponent(t, table, true); |
| |
| //noinspection SSBasedInspection |
| SwingUtilities.invokeLater(new Runnable() { |
| public void run() { |
| showPopup(t, row); |
| } |
| }); |
| |
| return this; |
| } |
| |
| protected boolean isApplicable(final T value, final int row) { |
| return true; |
| } |
| |
| private void showPopup(final T value, final int row) { |
| List<T> filtered = ContainerUtil.findAll(myValues, new Condition<T>() { |
| @Override |
| public boolean value(T t) { |
| return isApplicable(t, row); |
| } |
| }); |
| final ListPopup popup = JBPopupFactory.getInstance().createListPopup(new ListStep<T>(filtered, value) { |
| @NotNull |
| public String getTextFor(T value) { |
| return ComboBoxTableRenderer.this.getTextFor(value); |
| } |
| |
| @Override |
| public Icon getIconFor(T value) { |
| return ComboBoxTableRenderer.this.getIconFor(value); |
| } |
| |
| public PopupStep onChosen(T selectedValue, boolean finalChoice) { |
| myFinalRunnable = ComboBoxTableRenderer.this.onChosen(selectedValue); |
| return FINAL_CHOICE; |
| } |
| |
| public void canceled() { |
| ComboBoxTableRenderer.this.cancelCellEditing(); |
| } |
| |
| public Runnable getFinalRunnable() { |
| return myFinalRunnable; |
| } |
| }); |
| |
| popup.addListener(this); |
| popup.setRequestFocus(false); |
| |
| myPopupRef = new WeakReference<ListPopup>(popup); |
| popup.showUnderneathOf(this); |
| } |
| |
| public void beforeShown(LightweightWindowEvent event) { |
| } |
| |
| public void onClosed(LightweightWindowEvent event) { |
| event.asPopup().removeListener(this); |
| fireEditingCanceled(); |
| } |
| |
| protected void customizeComponent(final T value, final JTable table, final boolean isSelected) { |
| setOpaque(true); |
| setText(value == null ? "" : getTextFor(value)); |
| if (value != null) { |
| setIcon(getIconFor(value)); |
| } |
| setBackground(isSelected ? table.getSelectionBackground() : table.getBackground()); |
| setForeground(isSelected ? table.getSelectionForeground() : table.getForeground()); |
| } |
| |
| public Object getCellEditorValue() { |
| return myValue; |
| } |
| |
| public boolean isCellEditable(EventObject event) { |
| if (event instanceof MouseEvent) { |
| return ((MouseEvent)event).getClickCount() >= 2; |
| } |
| |
| return true; |
| } |
| |
| public boolean shouldSelectCell(EventObject event) { |
| return true; |
| } |
| |
| private void stopCellEditing(final T value) { |
| myValue = value; |
| stopCellEditing(); |
| } |
| |
| public boolean stopCellEditing() { |
| fireEditingStopped(); |
| hidePopup(); |
| return true; |
| } |
| |
| public void cancelCellEditing() { |
| fireEditingCanceled(); |
| hidePopup(); |
| } |
| |
| protected void fireEditingStopped() { |
| // Guaranteed to return a non-null array |
| Object[] listeners = myListenerList.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 (myChangeEvent == null) { |
| myChangeEvent = new ChangeEvent(this); |
| } |
| ((CellEditorListener)listeners[i + 1]).editingStopped(myChangeEvent); |
| } |
| } |
| } |
| |
| protected void fireEditingCanceled() { |
| // Guaranteed to return a non-null array |
| Object[] listeners = myListenerList.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 (myChangeEvent == null) { |
| myChangeEvent = new ChangeEvent(this); |
| } |
| ((CellEditorListener)listeners[i + 1]).editingCanceled(myChangeEvent); |
| } |
| } |
| } |
| |
| private void hidePopup() { |
| if (myPopupRef != null) { |
| final ListPopup popup = myPopupRef.get(); |
| if (popup != null && popup.isVisible()) { |
| popup.cancel(); |
| } |
| |
| myPopupRef = null; |
| } |
| } |
| |
| public void addCellEditorListener(CellEditorListener l) { |
| myListenerList.add(CellEditorListener.class, l); |
| } |
| |
| public void removeCellEditorListener(CellEditorListener l) { |
| myListenerList.remove(CellEditorListener.class, l); |
| } |
| |
| private abstract static class ListStep<T> implements ListPopupStep<T> { |
| private final List<T> myValues; |
| private final T mySelected; |
| |
| protected ListStep(List<T> values, T selected) { |
| myValues = values; |
| mySelected = selected; |
| } |
| |
| public String getTitle() { |
| return null; |
| } |
| |
| public boolean hasSubstep(T selectedValue) { |
| return false; |
| } |
| |
| public boolean isMnemonicsNavigationEnabled() { |
| return false; |
| } |
| |
| public boolean isSpeedSearchEnabled() { |
| return false; |
| } |
| |
| public boolean isAutoSelectionEnabled() { |
| return false; |
| } |
| |
| @NotNull |
| public List<T> getValues() { |
| return myValues; |
| } |
| |
| public boolean isSelectable(T value) { |
| return true; |
| } |
| |
| public Icon getIconFor(T aValue) { |
| return null; |
| } |
| |
| public ListSeparator getSeparatorAbove(T value) { |
| return null; |
| } |
| |
| public int getDefaultOptionIndex() { |
| return mySelected == null ? 0 : myValues.indexOf(mySelected); |
| } |
| |
| public MnemonicNavigationFilter<T> getMnemonicNavigationFilter() { |
| return null; |
| } |
| |
| public SpeedSearchFilter<T> getSpeedSearchFilter() { |
| return null; |
| } |
| } |
| } |