001// License: GPL. For details, see LICENSE file. 002 003package org.openstreetmap.josm.gui.tagging.presets.items; 004 005import static org.openstreetmap.josm.tools.I18n.tr; 006import static org.openstreetmap.josm.tools.I18n.trc; 007 008import java.util.Objects; 009 010import javax.swing.ImageIcon; 011 012import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItem; 013import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetReader; 014import org.openstreetmap.josm.tools.AlphanumComparator; 015import org.openstreetmap.josm.tools.Utils; 016 017/** 018 * Preset list entry. 019 * <p> 020 * Used for controls that offer a list of items to choose from like {@link Combo} and 021 * {@link MultiSelect}. 022 */ 023public class PresetListEntry implements Comparable<PresetListEntry> { 024 /** Used to display an entry matching several different values. */ 025 protected static final PresetListEntry ENTRY_DIFFERENT = new PresetListEntry(KeyedItem.DIFFERENT, null); 026 /** Used to display an empty entry used to clear values. */ 027 protected static final PresetListEntry ENTRY_EMPTY = new PresetListEntry("", null); 028 029 /** 030 * This is the value that is going to be written to the tag on the selected primitive(s). Except 031 * when the value is {@code "<different>"}, which is never written, or the value is empty, which 032 * deletes the tag. {@code value} is never translated. 033 */ 034 public String value; // NOSONAR 035 /** The ComboMultiSelect that displays the list */ 036 public ComboMultiSelect cms; // NOSONAR 037 /** Text displayed to the user instead of {@link #value}. */ 038 public String display_value; // NOSONAR 039 /** Text to be displayed below {@link #display_value} in the combobox list. */ 040 public String short_description; // NOSONAR 041 /** The location of icon file to display */ 042 public String icon; // NOSONAR 043 /** The size of displayed icon. If not set, default is size from icon file */ 044 public short icon_size; // NOSONAR 045 /** The localized version of {@link #display_value}. */ 046 public String locale_display_value; // NOSONAR 047 /** The localized version of {@link #short_description}. */ 048 public String locale_short_description; // NOSONAR 049 050 private String cachedDisplayValue; 051 private String cachedShortDescription; 052 private ImageIcon cachedIcon; 053 054 /** 055 * Constructs a new {@code PresetListEntry}, uninitialized. 056 * 057 * Public default constructor is needed by {@link org.openstreetmap.josm.tools.XmlObjectParser.Parser#startElement} 058 */ 059 public PresetListEntry() { 060 } 061 062 /** 063 * Constructs a new {@code PresetListEntry}, initialized with a value and 064 * {@link ComboMultiSelect} context. 065 * 066 * @param value value 067 * @param cms the ComboMultiSelect 068 */ 069 public PresetListEntry(String value, ComboMultiSelect cms) { 070 this.value = value; 071 this.cms = cms; 072 } 073 074 /** 075 * Returns the contents displayed in the dropdown list. 076 * 077 * This is the contents that would be displayed in the current view plus a short description to 078 * aid the user. The whole contents is wrapped to {@code width}. 079 * 080 * @param width the width in px 081 * @return HTML formatted contents 082 */ 083 public String getListDisplay(int width) { 084 String displayValue = getDisplayValue(); 085 Integer count = getCount(); 086 087 if (count > 0 && cms.usage.getSelectedCount() > 1) { 088 displayValue = tr("{0} ({1})", displayValue, count); 089 } 090 091 if (this.equals(ENTRY_DIFFERENT)) { 092 return "<html><b>" + Utils.escapeReservedCharactersHTML(displayValue) + "</b></html>"; 093 } 094 095 String shortDescription = getShortDescription(); 096 097 if (shortDescription.isEmpty()) { 098 // avoids a collapsed list entry if value == "" 099 if (displayValue.isEmpty()) { 100 return " "; 101 } 102 return displayValue; 103 } 104 105 // RTL not supported in HTML. See: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4866977 106 return String.format("<html><div style=\"width: %d\"><b>%s</b><p style=\"padding-left: 10\">%s</p></div></html>", 107 width, 108 displayValue, 109 Utils.escapeReservedCharactersHTML(shortDescription)); 110 } 111 112 /** 113 * Returns the entry icon, if any. 114 * @return the entry icon, or {@code null} 115 */ 116 public ImageIcon getIcon() { 117 if (icon != null && cachedIcon == null) { 118 cachedIcon = TaggingPresetItem.loadImageIcon(icon, TaggingPresetReader.getZipIcons(), (int) icon_size); 119 } 120 return cachedIcon; 121 } 122 123 /** 124 * Returns the contents displayed in the current item view. 125 * @return the value to display 126 */ 127 public String getDisplayValue() { 128 if (cachedDisplayValue == null) { 129 if (cms != null && cms.values_no_i18n) { 130 cachedDisplayValue = Utils.firstNonNull(value, " "); 131 } else { 132 cachedDisplayValue = Utils.firstNonNull( 133 locale_display_value, tr(display_value), trc(cms == null ? null : cms.values_context, value), " "); 134 } 135 } 136 return cachedDisplayValue; 137 } 138 139 /** 140 * Returns the short description to display. 141 * @return the short description to display 142 */ 143 public String getShortDescription() { 144 if (cachedShortDescription == null) { 145 cachedShortDescription = Utils.firstNonNull(locale_short_description, tr(short_description), ""); 146 } 147 return cachedShortDescription; 148 } 149 150 /** 151 * Returns the tooltip for this entry. 152 * @param key the tag key 153 * @return the tooltip 154 */ 155 public String getToolTipText(String key) { 156 if (this.equals(ENTRY_DIFFERENT)) { 157 return tr("Keeps the original values of the selected objects unchanged."); 158 } 159 if (value != null && !value.isEmpty()) { 160 return tr("Sets the key ''{0}'' to the value ''{1}''.", key, value); 161 } 162 return tr("Clears the key ''{0}''.", key); 163 } 164 165 // toString is mainly used to initialize the Editor 166 @Override 167 public String toString() { 168 if (this.equals(ENTRY_DIFFERENT)) 169 return getDisplayValue(); 170 return getDisplayValue().replaceAll("\\s*<.*>\\s*", " "); // remove additional markup, e.g. <br> 171 } 172 173 @Override 174 public boolean equals(Object o) { 175 if (this == o) return true; 176 if (o == null || getClass() != o.getClass()) return false; 177 PresetListEntry that = (PresetListEntry) o; 178 return Objects.equals(value, that.value); 179 } 180 181 @Override 182 public int hashCode() { 183 return Objects.hash(value); 184 } 185 186 /** 187 * Returns how many selected primitives had this value set. 188 * @return see above 189 */ 190 public int getCount() { 191 Integer count = cms == null ? null : cms.usage.map.get(value); 192 return count == null ? 0 : count; 193 } 194 195 @Override 196 public int compareTo(PresetListEntry o) { 197 return AlphanumComparator.getInstance().compare(this.value, o.value); 198 } 199}