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 }