source: josm/trunk/src/org/openstreetmap/josm/gui/widgets/OrientationAction.java@ 18221

Last change on this file since 18221 was 18221, checked in by Don-vip, 3 years ago

fix #21319 - Refactoring of class hierarchy around JosmComboBox / AutoCompComboBox (patch by marcello):

  • Code reuse: JosmComboBox now uses a JosmTextField as editor
  • Code reuse: AutoCompComboBox now uses AutoCompTextField as editor
  • JosmComboBox uses more of the original L&F
  • JosmComboBox lists now expand all the way to the bottom or the top of the screen
  • Variable height items in combobox lists now work, see #19321
  • Autocomplete uses different algorithm, fix #21290
  • editable="false" comboboxes in Presets now work, fix #6157 see #11024 see #18714
  • The user may toggle LTR-RTL script in JosmTextField (menu and ctrl+space)
  • LTR-RTL automatically toggles according to key in AddTag and EditTag dialogs, fix #16163
  • Property svn:eol-style set to native
File size: 7.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.widgets;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.ComponentOrientation;
8import java.awt.event.ActionEvent;
9import java.awt.event.KeyEvent;
10import java.beans.PropertyChangeEvent;
11import java.beans.PropertyChangeListener;
12import java.util.Arrays;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Locale;
16import java.util.Set;
17import java.util.regex.Matcher;
18import java.util.regex.Pattern;
19
20import javax.swing.AbstractAction;
21import javax.swing.Action;
22import javax.swing.ImageIcon;
23import javax.swing.KeyStroke;
24
25import org.openstreetmap.josm.data.preferences.ListProperty;
26import org.openstreetmap.josm.gui.MainApplication;
27import org.openstreetmap.josm.spi.preferences.Config;
28import org.openstreetmap.josm.tools.ImageProvider;
29import org.openstreetmap.josm.tools.PlatformManager;
30
31/**
32 * An action that toggles text orientation.
33 * @since 18221
34 */
35public class OrientationAction extends AbstractAction implements PropertyChangeListener {
36 /** Default for {@link #RTL_LANGUAGES} */
37 public static final List<String> DEFAULT_RTL_LANGUAGES = Arrays.asList("ar", "he", "fa", "iw", "ur", "lld");
38
39 /** Default for {@link #LOCALIZED_KEYS} */
40 public static final List<String> DEFAULT_LOCALIZED_KEYS = Arrays.asList(
41 "(\\p{Alnum}+_)?name", "addr", "description", "fixme", "note", "source", "strapline", "operator");
42
43 /**
44 * Language codes of languages that are right-to-left
45 *
46 * @see #getValueOrientation
47 */
48 public static final ListProperty RTL_LANGUAGES = new ListProperty("properties.rtl-languages", DEFAULT_RTL_LANGUAGES);
49 /**
50 * Keys whose values are localized
51 *
52 * Regex fractions are allowed. The items will be merged into a regular expression.
53 *
54 * @see #getValueOrientation
55 */
56 public static final ListProperty LOCALIZED_KEYS = new ListProperty("properties.localized-keys", DEFAULT_LOCALIZED_KEYS);
57
58 private static final Pattern LANG_PATTERN = Pattern.compile(":([a-z]{2,3})$");
59
60 private Component component;
61 private ImageIcon iconRTL;
62 private ImageIcon iconLTR;
63 protected static Set<String> RTLLanguages = new HashSet<>(RTL_LANGUAGES.get());
64 protected static Pattern localizedKeys = compile_localized_keys();
65
66 /**
67 * Constructs a new {@code OrientationAction}.
68 *
69 * @param component The component to toggle
70 */
71 public OrientationAction(Component component) {
72 super(null);
73 this.component = component;
74 setEnabled(true);
75 if (Config.getPref().getBoolean("text.popupmenu.useicons", true)) {
76 iconLTR = new ImageProvider("dialogs/next").setSize(ImageProvider.ImageSizes.SMALLICON).get();
77 iconRTL = new ImageProvider("dialogs/previous").setSize(ImageProvider.ImageSizes.SMALLICON).get();
78 }
79 component.addPropertyChangeListener(this);
80 putValue(Action.ACCELERATOR_KEY, getShortcutKey());
81 updateState();
82 }
83
84 @Override
85 public void actionPerformed(ActionEvent e) {
86 firePropertyChange("orientationAction", null, getValue("newState"));
87 }
88
89 /**
90 * Updates the text and the icon.
91 */
92 public void updateState() {
93 if (component.getComponentOrientation().isLeftToRight()) {
94 putValue(Action.NAME, tr("Right to Left"));
95 putValue(Action.SMALL_ICON, iconRTL);
96 putValue(Action.SHORT_DESCRIPTION, tr("Switch the text orientation to Right-to-Left."));
97 putValue("newState", ComponentOrientation.RIGHT_TO_LEFT);
98 } else {
99 putValue(Action.NAME, tr("Left to Right"));
100 putValue(Action.SMALL_ICON, iconLTR);
101 putValue(Action.SHORT_DESCRIPTION, tr("Switch the text orientation to Left-to-Right."));
102 putValue("newState", ComponentOrientation.LEFT_TO_RIGHT);
103 }
104 }
105
106 @Override
107 public void propertyChange(PropertyChangeEvent evt) {
108 if ("componentOrientation".equals(evt.getPropertyName())) {
109 updateState();
110 }
111 }
112
113 /**
114 * Returns the shortcut key to assign to this action.
115 *
116 * @return the shortcut key
117 */
118 public static KeyStroke getShortcutKey() {
119 return KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, PlatformManager.getPlatform().getMenuShortcutKeyMaskEx());
120 }
121
122 /**
123 * Returns the default component orientation by the user's locale
124 *
125 * @return the default component orientation
126 */
127 public static ComponentOrientation getDefaultComponentOrientation() {
128 Component main = MainApplication.getMainFrame();
129 // is null while testing
130 return main != null ? main.getComponentOrientation() : ComponentOrientation.LEFT_TO_RIGHT;
131 }
132
133 /**
134 * Returns the text orientation of the value for the given key.
135 *
136 * This is intended for Preset Dialog comboboxes. The choices in the dropdown list are
137 * typically translated. Ideally the user needs not see the English value.
138 *
139 * The algorithm is as follows:
140 * <ul>
141 * <li>If the key has an explicit language suffix, return the text orientation for that
142 * language.
143 * <li>Else return the text orientation of the user's locale.
144 * </ul>
145 *
146 * You can configure which languages are RTL with the list property: {@code rtl-languages}.
147 *
148 * @param key the key
149 * @return the text orientation of the value
150 */
151 public static ComponentOrientation getValueOrientation(String key) {
152 if (key == null || key.isEmpty())
153 return ComponentOrientation.LEFT_TO_RIGHT;
154
155 // if the key has an explicit language suffix, use it
156 Matcher m = LANG_PATTERN.matcher(key);
157 if (m.find()) {
158 if (RTLLanguages.contains(m.group(1))) {
159 return ComponentOrientation.RIGHT_TO_LEFT;
160 }
161 return ComponentOrientation.LEFT_TO_RIGHT;
162 }
163 // return the user's locale
164 return ComponentOrientation.getOrientation(Locale.getDefault());
165 }
166
167 /**
168 * Returns the text orientation of the value for the given key.
169 *
170 * This expansion of {@link #getValueOrientation} is intended for Preset Dialog textfields and
171 * for the Add Tag and Edit Tag dialog comboboxes.
172 *
173 * The algorithm is as follows:
174 * <ul>
175 * <li>If the key has an explicit language suffix, return the text orientation for that
176 * language.
177 * <li>If the key is usually localized, return the text orientation of the user's locale.
178 * <li>Else return left to right.
179 * </ul>
180 *
181 * You can configure which keys are localized with the list property: {@code localized-keys}.
182 * You can configure which languages are RTL with the list property: {@code rtl-languages}.
183 *
184 * @param key the key
185 * @return the text orientation of the value
186 */
187 public static ComponentOrientation getNamelikeOrientation(String key) {
188 if (key == null || key.isEmpty())
189 return ComponentOrientation.LEFT_TO_RIGHT;
190
191 // if the key has an explicit language suffix, use it
192 Matcher m = LANG_PATTERN.matcher(key);
193 if (m.find()) {
194 if (RTLLanguages.contains(m.group(1))) {
195 return ComponentOrientation.RIGHT_TO_LEFT;
196 }
197 return ComponentOrientation.LEFT_TO_RIGHT;
198 }
199 // if the key is usually localized, use the user's locale
200 m = localizedKeys.matcher(key);
201 if (m.find()) {
202 return ComponentOrientation.getOrientation(Locale.getDefault());
203 }
204 // all other keys are LTR
205 return ComponentOrientation.LEFT_TO_RIGHT;
206 }
207
208 private static Pattern compile_localized_keys() {
209 return Pattern.compile("^(" + String.join("|", LOCALIZED_KEYS.get()) + ")$");
210 }
211}
Note: See TracBrowser for help on using the repository browser.