001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.properties; 003 004import static org.openstreetmap.josm.tools.I18n.marktr; 005import static org.openstreetmap.josm.tools.I18n.tr; 006import static org.openstreetmap.josm.tools.I18n.trn; 007 008import java.awt.Color; 009import java.awt.Component; 010import java.awt.Font; 011import java.util.Collection; 012import java.util.Map; 013import java.util.Objects; 014import java.util.concurrent.CopyOnWriteArrayList; 015 016import javax.swing.JLabel; 017import javax.swing.JTable; 018import javax.swing.UIDefaults; 019import javax.swing.table.DefaultTableCellRenderer; 020import javax.swing.table.TableCellRenderer; 021 022import org.openstreetmap.josm.Main; 023import org.openstreetmap.josm.data.osm.OsmPrimitive; 024 025/** 026 * Cell renderer of tags table. 027 * @since 6314 028 */ 029public class PropertiesCellRenderer extends DefaultTableCellRenderer { 030 031 private final Collection<TableCellRenderer> customRenderer = new CopyOnWriteArrayList<>(); 032 033 private void setColors(Component c, String key, boolean isSelected) { 034 UIDefaults defaults = javax.swing.UIManager.getDefaults(); 035 if (OsmPrimitive.getDiscardableKeys().contains(key)) { 036 if (isSelected) { 037 c.setForeground(Main.pref.getColor(marktr("Discardable key: selection Foreground"), Color.GRAY)); 038 c.setBackground(Main.pref.getColor(marktr("Discardable key: selection Background"), 039 defaults.getColor("Table.selectionBackground"))); 040 } else { 041 c.setForeground(Main.pref.getColor(marktr("Discardable key: foreground"), Color.GRAY)); 042 c.setBackground(Main.pref.getColor(marktr("Discardable key: background"), defaults.getColor("Table.background"))); 043 } 044 } else { 045 c.setForeground(defaults.getColor("Table."+(isSelected ? "selectionF" : "f")+"oreground")); 046 c.setBackground(defaults.getColor("Table."+(isSelected ? "selectionB" : "b")+"ackground")); 047 } 048 } 049 050 @Override 051 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 052 for (TableCellRenderer renderer : customRenderer) { 053 final Component component = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 054 if (component != null) { 055 return component; 056 } 057 } 058 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column); 059 if (value == null) 060 return this; 061 if (c instanceof JLabel) { 062 String str = null; 063 if (value instanceof String) { 064 str = (String) value; 065 } else if (value instanceof Map<?, ?>) { 066 Map<?, ?> v = (Map<?, ?>) value; 067 if (v.size() != 1) { // Multiple values: give user a short summary of the values 068 Integer blankCount; 069 Integer otherCount; 070 if (v.get("") == null) { 071 blankCount = 0; 072 otherCount = v.size(); 073 } else { 074 blankCount = (Integer) v.get(""); 075 otherCount = v.size()-1; 076 } 077 StringBuilder sb = new StringBuilder("<"); 078 if (otherCount == 1) { 079 for (Map.Entry<?, ?> entry : v.entrySet()) { // Find the non-blank value in the map 080 if (!Objects.equals(entry.getKey(), "")) { 081 /* I18n: properties display partial string joined with comma, frst is count, second is value */ 082 sb.append(tr("{0} ''{1}''", entry.getValue().toString(), entry.getKey())); 083 } 084 } 085 } else { 086 /* I18n: properties display partial string joined with comma */ 087 sb.append(trn("{0} different", "{0} different", otherCount, otherCount)); 088 } 089 if (blankCount > 0) { 090 /* I18n: properties display partial string joined with comma */ 091 sb.append(trn(", {0} unset", ", {0} unset", blankCount, blankCount)); 092 } 093 sb.append('>'); 094 str = sb.toString(); 095 c.setFont(c.getFont().deriveFont(Font.ITALIC)); 096 097 } else { // One value: display the value 098 final Map.Entry<?, ?> entry = v.entrySet().iterator().next(); 099 str = (String) entry.getKey(); 100 } 101 } 102 ((JLabel) c).putClientProperty("html.disable", Boolean.TRUE); // Fix #8730 103 ((JLabel) c).setText(str); 104 if (Main.pref.getBoolean("display.discardable-keys", false)) { 105 String key = null; 106 if (column == 0) { 107 key = str; 108 } else if (column == 1) { 109 Object value0 = table.getModel().getValueAt(row, 0); 110 if (value0 instanceof String) { 111 key = (String) value0; 112 } 113 } 114 setColors(c, key, isSelected); 115 } 116 } 117 return c; 118 } 119 120 /** 121 * Adds a custom table cell renderer to render cells of the tags table. 122 * 123 * If the renderer is not capable performing a {@link TableCellRenderer#getTableCellRendererComponent}, 124 * it should return {@code null} to fall back to the 125 * {@link PropertiesCellRenderer#getTableCellRendererComponent default implementation}. 126 * @param renderer the renderer to add 127 * @since 9149 128 */ 129 public void addCustomRenderer(TableCellRenderer renderer) { 130 customRenderer.add(renderer); 131 } 132 133 /** 134 * Removes a custom table cell renderer. 135 * @param renderer the renderer to remove 136 * @since 9149 137 */ 138 public void removeCustomRenderer(TableCellRenderer renderer) { 139 customRenderer.remove(renderer); 140 } 141}