001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.gui.conflict.tags; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.awt.Component; 007 import java.awt.Font; 008 import java.awt.event.FocusAdapter; 009 import java.awt.event.FocusEvent; 010 import java.awt.event.KeyEvent; 011 import java.util.concurrent.CopyOnWriteArrayList; 012 013 import javax.swing.AbstractCellEditor; 014 import javax.swing.DefaultComboBoxModel; 015 import javax.swing.JLabel; 016 import javax.swing.JList; 017 import javax.swing.JTable; 018 import javax.swing.ListCellRenderer; 019 import javax.swing.UIManager; 020 import javax.swing.table.TableCellEditor; 021 022 import org.openstreetmap.josm.gui.widgets.JosmComboBox; 023 024 /** 025 * This is a table cell editor for selecting a possible tag value from a list of 026 * proposed tag values. The editor also allows to select all proposed valued or 027 * to remove the tag. 028 * 029 * The editor responds intercepts some keys and interprets them as navigation keys. It 030 * forwards navigation events to {@link NavigationListener}s registred with this editor. 031 * You should register the parent table using this editor as {@link NavigationListener}. 032 * 033 * {@link KeyEvent#VK_ENTER} and {@link KeyEvent#VK_TAB} trigger a {@link NavigationListener#gotoNextDecision()}. 034 */ 035 public class MultiValueCellEditor extends AbstractCellEditor implements TableCellEditor{ 036 037 public static interface NavigationListener { 038 void gotoNextDecision(); 039 void gotoPreviousDecision(); 040 } 041 042 /** the combo box used as editor */ 043 private JosmComboBox editor; 044 private DefaultComboBoxModel editorModel; 045 private CopyOnWriteArrayList<NavigationListener> listeners; 046 047 public void addNavigationListeners(NavigationListener listener) { 048 if (listener != null) { 049 listeners.addIfAbsent(listener); 050 } 051 } 052 053 public void removeavigationListeners(NavigationListener listener) { 054 listeners.remove(listener); 055 } 056 057 protected void fireGotoNextDecision() { 058 for (NavigationListener l: listeners) { 059 l.gotoNextDecision(); 060 } 061 } 062 063 protected void fireGotoPreviousDecision() { 064 for (NavigationListener l: listeners) { 065 l.gotoPreviousDecision(); 066 } 067 } 068 069 public MultiValueCellEditor() { 070 editorModel = new DefaultComboBoxModel(); 071 editor = new JosmComboBox(editorModel) { 072 @Override 073 public void processKeyEvent(KeyEvent e) { 074 if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_ENTER) { 075 fireGotoNextDecision(); 076 } if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_TAB) { 077 if (e.isShiftDown()) { 078 fireGotoPreviousDecision(); 079 } else { 080 fireGotoNextDecision(); 081 } 082 } else if ( e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_DELETE || e.getKeyCode() == KeyEvent.VK_BACK_SPACE) { 083 if (editorModel.getIndexOf(MultiValueDecisionType.KEEP_NONE) > 0) { 084 editorModel.setSelectedItem(MultiValueDecisionType.KEEP_NONE); 085 fireGotoNextDecision(); 086 } 087 } else if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_ESCAPE) { 088 cancelCellEditing(); 089 } 090 super.processKeyEvent(e); 091 } 092 }; 093 editor.addFocusListener( 094 new FocusAdapter() { 095 @Override 096 public void focusGained(FocusEvent e) { 097 editor.showPopup(); 098 } 099 } 100 ); 101 editor.setRenderer(new EditorCellRenderer()); 102 listeners = new CopyOnWriteArrayList<NavigationListener>(); 103 } 104 105 protected void initEditor(MultiValueResolutionDecision decision) { 106 editorModel.removeAllElements(); 107 for (String value: decision.getValues()) { 108 editorModel.addElement(value); 109 } 110 if (decision.canKeepNone()) { 111 editorModel.addElement(MultiValueDecisionType.KEEP_NONE); 112 } 113 if (decision.canKeepAll()) { 114 editorModel.addElement(MultiValueDecisionType.KEEP_ALL); 115 } 116 switch(decision.getDecisionType()) { 117 case UNDECIDED: 118 editor.setSelectedIndex(0); 119 break; 120 case KEEP_ONE: 121 editor.setSelectedItem(decision.getChosenValue()); 122 break; 123 case KEEP_NONE: 124 editor.setSelectedItem(MultiValueDecisionType.KEEP_NONE); 125 break; 126 case KEEP_ALL: 127 editor.setSelectedItem(MultiValueDecisionType.KEEP_ALL); 128 } 129 } 130 131 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 132 MultiValueResolutionDecision decision = (MultiValueResolutionDecision)value; 133 initEditor(decision); 134 editor.requestFocus(); 135 return editor; 136 } 137 138 public Object getCellEditorValue() { 139 return editor.getSelectedItem(); 140 } 141 142 /** 143 * The cell renderer used in the combo box 144 * 145 */ 146 static private class EditorCellRenderer extends JLabel implements ListCellRenderer { 147 148 public EditorCellRenderer() { 149 setOpaque(true); 150 } 151 152 protected void renderColors(boolean selected) { 153 if (selected) { 154 setForeground(UIManager.getColor("ComboBox.selectionForeground")); 155 setBackground(UIManager.getColor("ComboBox.selectionBackground")); 156 } else { 157 setForeground(UIManager.getColor("ComboBox.foreground")); 158 setBackground(UIManager.getColor("ComboBox.background")); 159 } 160 } 161 162 protected void renderValue(Object value) { 163 setFont(UIManager.getFont("ComboBox.font")); 164 if (String.class.isInstance(value)) { 165 setText(String.class.cast(value)); 166 } else if (MultiValueDecisionType.class.isInstance(value)) { 167 switch(MultiValueDecisionType.class.cast(value)) { 168 case KEEP_NONE: 169 setText(tr("none")); 170 setFont(UIManager.getFont("ComboBox.font").deriveFont(Font.ITALIC + Font.BOLD)); 171 break; 172 case KEEP_ALL: 173 setText(tr("all")); 174 setFont(UIManager.getFont("ComboBox.font").deriveFont(Font.ITALIC + Font.BOLD)); 175 break; 176 default: 177 // don't display other values 178 } 179 } 180 } 181 182 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, 183 boolean cellHasFocus) { 184 renderColors(isSelected); 185 renderValue(value); 186 return this; 187 } 188 } 189 }