source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java@ 16322

Last change on this file since 16322 was 16322, checked in by simon04, 4 years ago

fix #18883 - InspectPrimitiveDialog: improve mapstyle summary

  • Property svn:eol-style set to native
File size: 9.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.Dimension;
8import java.awt.GridBagLayout;
9import java.io.PrintWriter;
10import java.io.StringWriter;
11import java.text.Collator;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.List;
15import java.util.Locale;
16import java.util.Map;
17import java.util.Map.Entry;
18import java.util.TreeMap;
19
20import javax.swing.JPanel;
21import javax.swing.JScrollPane;
22import javax.swing.JTabbedPane;
23import javax.swing.SingleSelectionModel;
24
25import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
26import org.openstreetmap.josm.data.osm.IPrimitive;
27import org.openstreetmap.josm.data.osm.OsmData;
28import org.openstreetmap.josm.data.osm.PrimitiveComparator;
29import org.openstreetmap.josm.gui.ExtendedDialog;
30import org.openstreetmap.josm.gui.MainApplication;
31import org.openstreetmap.josm.gui.NavigatableComponent;
32import org.openstreetmap.josm.gui.mappaint.Cascade;
33import org.openstreetmap.josm.gui.mappaint.ElemStyles;
34import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
35import org.openstreetmap.josm.gui.mappaint.MultiCascade;
36import org.openstreetmap.josm.gui.mappaint.StyleCache;
37import org.openstreetmap.josm.gui.mappaint.StyleElementList;
38import org.openstreetmap.josm.gui.mappaint.StyleSource;
39import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
40import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
41import org.openstreetmap.josm.gui.util.GuiHelper;
42import org.openstreetmap.josm.gui.util.WindowGeometry;
43import org.openstreetmap.josm.gui.widgets.JosmTextArea;
44import org.openstreetmap.josm.tools.GBC;
45
46/**
47 * Panel to inspect one or more OsmPrimitives.
48 *
49 * Gives an unfiltered view of the object's internal state.
50 * Might be useful for power users to give more detailed bug reports and
51 * to better understand the JOSM data representation.
52 */
53public class InspectPrimitiveDialog extends ExtendedDialog {
54
55 protected transient List<IPrimitive> primitives;
56 private boolean mappaintTabLoaded;
57 private boolean editcountTabLoaded;
58
59 /**
60 * Constructs a new {@code InspectPrimitiveDialog}.
61 * @param primitives collection of primitives
62 * @param data data set
63 * @since 12672 (signature)
64 */
65 public InspectPrimitiveDialog(final Collection<? extends IPrimitive> primitives, OsmData<?, ?, ?, ?> data) {
66 super(MainApplication.getMainFrame(), tr("Advanced object info"), tr("Close"));
67 this.primitives = new ArrayList<>(primitives);
68 setRememberWindowGeometry(getClass().getName() + ".geometry",
69 WindowGeometry.centerInWindow(MainApplication.getMainFrame(), new Dimension(750, 550)));
70
71 setButtonIcons("ok");
72 final JTabbedPane tabs = new JTabbedPane();
73
74 tabs.addTab(tr("data"), genericMonospacePanel(new JPanel(), buildDataText(data, this.primitives)));
75
76 final JPanel pMapPaint = new JPanel();
77 tabs.addTab(tr("map style"), pMapPaint);
78 tabs.getModel().addChangeListener(e -> {
79 if (!mappaintTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 1) {
80 mappaintTabLoaded = true;
81 genericMonospacePanel(pMapPaint, buildMapPaintText());
82 }
83 });
84
85 final JPanel pEditCounts = new JPanel();
86 tabs.addTab(tr("edit counts"), pEditCounts);
87 tabs.getModel().addChangeListener(e -> {
88 if (!editcountTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 2) {
89 editcountTabLoaded = true;
90 genericMonospacePanel(pEditCounts, buildListOfEditorsText(primitives));
91 }
92 });
93
94 setContent(tabs, false);
95 }
96
97 protected static JPanel genericMonospacePanel(JPanel p, String s) {
98 p.setLayout(new GridBagLayout());
99 JosmTextArea jte = new JosmTextArea();
100 jte.setFont(GuiHelper.getMonospacedFont(jte));
101 jte.setEditable(false);
102 jte.append(s);
103 jte.setCaretPosition(0);
104 p.add(new JScrollPane(jte), GBC.std().fill());
105 return p;
106 }
107
108 protected static String buildDataText(OsmData<?, ?, ?, ?> data, List<IPrimitive> primitives) {
109 InspectPrimitiveDataText dt = new InspectPrimitiveDataText(data);
110 primitives.stream()
111 .sorted(PrimitiveComparator.orderingWaysRelationsNodes().thenComparing(PrimitiveComparator.comparingNames()))
112 .forEachOrdered(dt::addPrimitive);
113 return dt.toString();
114 }
115
116 protected static String buildMapPaintText() {
117 final Collection<? extends IPrimitive> sel = MainApplication.getLayerManager().getActiveData().getAllSelected();
118 ElemStyles elemstyles = MapPaintStyles.getStyles();
119 NavigatableComponent nc = MainApplication.getMap().mapView;
120 double scale = nc.getDist100Pixel();
121
122 final StringWriter stringWriter = new StringWriter();
123 final PrintWriter txtMappaint = new PrintWriter(stringWriter);
124 MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().lock();
125 try {
126 for (IPrimitive osm : sel) {
127 String heading = tr("Styles for \"{0}\":", osm.getDisplayName(DefaultNameFormatter.getInstance()));
128 txtMappaint.println(heading);
129 txtMappaint.println(repeatString("=", heading.length()));
130
131 MultiCascade mc = new MultiCascade();
132
133 for (StyleSource s : elemstyles.getStyleSources()) {
134 if (s.active) {
135 heading = tr("{0} style \"{1}\"", getSort(s), s.getDisplayString());
136 txtMappaint.println(heading);
137 txtMappaint.println(repeatString("-", heading.length()));
138 s.apply(mc, osm, scale, false);
139 txtMappaint.println(tr("Display range: {0}", mc.range));
140 for (Entry<String, Cascade> e : mc.getLayers()) {
141 txtMappaint.println(tr("Layer {0}", e.getKey()));
142 txtMappaint.print(" * ");
143 txtMappaint.println(e.getValue());
144 }
145 }
146 }
147 txtMappaint.println();
148 heading = tr("List of generated Styles:");
149 txtMappaint.println(heading);
150 txtMappaint.println(repeatString("-", heading.length()));
151 StyleElementList sl = elemstyles.get(osm, scale, nc);
152 for (StyleElement s : sl) {
153 txtMappaint.print(" * ");
154 txtMappaint.println(s);
155 }
156 txtMappaint.println();
157 txtMappaint.println();
158 }
159 } finally {
160 MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().unlock();
161 }
162 if (sel.size() == 2) {
163 List<IPrimitive> selList = new ArrayList<>(sel);
164 StyleCache sc1 = selList.get(0).getCachedStyle();
165 StyleCache sc2 = selList.get(1).getCachedStyle();
166 if (sc1 == sc2) {
167 txtMappaint.println(tr("The 2 selected objects have identical style caches."));
168 }
169 if (!sc1.equals(sc2)) {
170 txtMappaint.println(tr("The 2 selected objects have different style caches."));
171 }
172 if (sc1 != sc2 && sc1.equals(sc2)) {
173 txtMappaint.println(tr("Warning: The 2 selected objects have equal, but not identical style caches."));
174 }
175 }
176 return stringWriter.toString();
177 }
178
179 private static String repeatString(String string, int count) {
180 // Java 11: use String.repeat
181 return new String(new char[count]).replace("\0", string);
182 }
183
184 /* Future Ideas:
185 Calculate the most recent edit date from o.getTimestamp().
186 Sort by the count for presentation, so the most active editors are on top.
187 Count only tagged nodes (so empty way nodes don't inflate counts).
188 */
189 protected static String buildListOfEditorsText(Iterable<? extends IPrimitive> primitives) {
190 final Map<String, Integer> editCountByUser = new TreeMap<>(Collator.getInstance(Locale.getDefault()));
191
192 // Count who edited each selected object
193 for (IPrimitive o : primitives) {
194 if (o.getUser() != null) {
195 String username = o.getUser().getName();
196 Integer oldCount = editCountByUser.get(username);
197 if (oldCount == null) {
198 editCountByUser.put(username, 1);
199 } else {
200 editCountByUser.put(username, oldCount + 1);
201 }
202 }
203 }
204
205 // Print the count in sorted order
206 final StringBuilder s = new StringBuilder(48)
207 .append(trn("{0} user last edited the selection:", "{0} users last edited the selection:",
208 editCountByUser.size(), editCountByUser.size()))
209 .append("\n\n");
210 for (Map.Entry<String, Integer> entry : editCountByUser.entrySet()) {
211 final String username = entry.getKey();
212 final Integer editCount = entry.getValue();
213 s.append(String.format("%6d %s", editCount, username)).append('\n');
214 }
215 return s.toString();
216 }
217
218 private static String getSort(StyleSource s) {
219 if (s instanceof MapCSSStyleSource) {
220 return "MapCSS";
221 } else {
222 return tr("UNKNOWN");
223 }
224 }
225}
Note: See TracBrowser for help on using the repository browser.