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

Last change on this file since 6070 was 5886, checked in by Don-vip, 11 years ago

see #4429 - Right click menu "undo, cut, copy, paste, delete, select all" for each text component (originally based on patch by NooN)

  • Property svn:eol-style set to native
File size: 14.2 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.Font;
9import java.awt.GridBagLayout;
10import java.util.ArrayList;
11import java.util.Collection;
12import java.util.Collections;
13import java.util.List;
14import java.util.Map.Entry;
15
16import javax.swing.JPanel;
17import javax.swing.JScrollPane;
18import javax.swing.JTabbedPane;
19import javax.swing.SingleSelectionModel;
20import javax.swing.event.ChangeEvent;
21import javax.swing.event.ChangeListener;
22
23import org.openstreetmap.josm.Main;
24import org.openstreetmap.josm.data.conflict.Conflict;
25import org.openstreetmap.josm.data.coor.EastNorth;
26import org.openstreetmap.josm.data.osm.BBox;
27import org.openstreetmap.josm.data.osm.Node;
28import org.openstreetmap.josm.data.osm.OsmPrimitive;
29import org.openstreetmap.josm.data.osm.OsmPrimitiveComparator;
30import org.openstreetmap.josm.data.osm.Relation;
31import org.openstreetmap.josm.data.osm.RelationMember;
32import org.openstreetmap.josm.data.osm.Way;
33import org.openstreetmap.josm.gui.DefaultNameFormatter;
34import org.openstreetmap.josm.gui.ExtendedDialog;
35import org.openstreetmap.josm.gui.NavigatableComponent;
36import org.openstreetmap.josm.gui.layer.OsmDataLayer;
37import org.openstreetmap.josm.gui.mappaint.Cascade;
38import org.openstreetmap.josm.gui.mappaint.ElemStyle;
39import org.openstreetmap.josm.gui.mappaint.ElemStyles;
40import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
41import org.openstreetmap.josm.gui.mappaint.MultiCascade;
42import org.openstreetmap.josm.gui.mappaint.StyleCache;
43import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
44import org.openstreetmap.josm.gui.mappaint.StyleSource;
45import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
46import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
47import org.openstreetmap.josm.gui.widgets.JosmTextArea;
48import org.openstreetmap.josm.tools.DateUtils;
49import org.openstreetmap.josm.tools.GBC;
50import org.openstreetmap.josm.tools.WindowGeometry;
51
52/**
53 * Panel to inspect one or more OsmPrimitives.
54 *
55 * Gives an unfiltered view of the object's internal state.
56 * Might be useful for power users to give more detailed bug reports and
57 * to better understand the JOSM data representation.
58 */
59public class InspectPrimitiveDialog extends ExtendedDialog {
60
61 protected List<OsmPrimitive> primitives;
62 protected OsmDataLayer layer;
63 private JosmTextArea txtData;
64 private JosmTextArea txtMappaint;
65 boolean mappaintTabLoaded;
66
67 public InspectPrimitiveDialog(Collection<OsmPrimitive> primitives, OsmDataLayer layer) {
68 super(Main.parent, tr("Advanced object info"), new String[] {tr("Close")});
69 this.primitives = new ArrayList<OsmPrimitive>(primitives);
70 this.layer = layer;
71 setRememberWindowGeometry(getClass().getName() + ".geometry",
72 WindowGeometry.centerInWindow(Main.parent, new Dimension(750, 550)));
73
74 setButtonIcons(new String[]{"ok.png"});
75 final JTabbedPane tabs = new JTabbedPane();
76 JPanel pData = buildDataPanel();
77 tabs.addTab(tr("data"), pData);
78 final JPanel pMapPaint = new JPanel();
79 tabs.addTab(tr("map style"), pMapPaint);
80 tabs.getModel().addChangeListener(new ChangeListener() {
81
82 @Override
83 public void stateChanged(ChangeEvent e) {
84 if (!mappaintTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 1) {
85 mappaintTabLoaded = true;
86 buildMapPaintPanel(pMapPaint);
87 createMapPaintText();
88 }
89 }
90 });
91 setContent(tabs, false);
92 }
93
94 protected JPanel buildDataPanel() {
95 JPanel p = new JPanel(new GridBagLayout());
96 txtData = new JosmTextArea();
97 txtData.setFont(new Font("Monospaced", txtData.getFont().getStyle(), txtData.getFont().getSize()));
98 txtData.setEditable(false);
99 txtData.setText(buildDataText());
100 txtData.setSelectionStart(0);
101 txtData.setSelectionEnd(0);
102
103 JScrollPane scroll = new JScrollPane(txtData);
104
105 p.add(scroll, GBC.std().fill());
106 return p;
107 }
108
109 protected String buildDataText() {
110 DataText dt = new DataText();
111 Collections.sort(primitives, new OsmPrimitiveComparator());
112 for (OsmPrimitive o : primitives) {
113 dt.addPrimitive(o);
114 }
115 return dt.toString();
116 }
117
118 class DataText {
119 static final String INDENT = " ";
120 static final String NL = "\n";
121
122 private StringBuilder s = new StringBuilder();
123
124 private DataText add(String title, String... values) {
125 s.append(INDENT).append(title);
126 for (String v : values) {
127 s.append(v);
128 }
129 s.append(NL);
130 return this;
131 }
132
133 private String getNameAndId(String name, long id) {
134 if (name != null) {
135 return name + tr(" ({0})", /* sic to avoid thousand seperators */ Long.toString(id));
136 } else {
137 return Long.toString(id);
138 }
139 }
140
141 public void addPrimitive(OsmPrimitive o) {
142
143 addHeadline(o);
144
145 if (!(o.getDataSet() != null && o.getDataSet().getPrimitiveById(o) != null)) {
146 s.append(NL).append(INDENT).append(tr("not in data set"));
147 return;
148 }
149 if (o.isIncomplete()) {
150 s.append(NL).append(INDENT).append(tr("incomplete"));
151 return;
152 }
153 s.append(NL);
154
155 addState(o);
156 addCommon(o);
157 addAttributes(o);
158 addSpecial(o);
159 addReferrers(s, o);
160 addConflicts(o);
161 s.append(NL);
162 }
163
164 void addHeadline(OsmPrimitive o) {
165 addType(o);
166 addNameAndId(o);
167 }
168
169 void addType(OsmPrimitive o) {
170 if (o instanceof Node) {
171 s.append(tr("Node: "));
172 } else if (o instanceof Way) {
173 s.append(tr("Way: "));
174 } else if (o instanceof Relation) {
175 s.append(tr("Relation: "));
176 }
177 }
178
179 void addNameAndId(OsmPrimitive o) {
180 String name = o.get("name");
181 if (name == null) {
182 s.append(o.getUniqueId());
183 } else {
184 s.append(getNameAndId(name, o.getUniqueId()));
185 }
186 }
187
188 void addState(OsmPrimitive o) {
189 StringBuilder sb = new StringBuilder(INDENT);
190 /* selected state is left out: not interesting as it is always selected */
191 if (o.isDeleted()) {
192 sb.append(tr("deleted")).append(INDENT);
193 }
194 if (!o.isVisible()) {
195 sb.append(tr("deleted-on-server")).append(INDENT);
196 }
197 if (o.isModified()) {
198 sb.append(tr("modified")).append(INDENT);
199 }
200 if (o.isDisabledAndHidden()) {
201 sb.append(tr("filtered/hidden")).append(INDENT);
202 }
203 if (o.isDisabled()) {
204 sb.append(tr("filtered/disabled")).append(INDENT);
205 }
206 if (o.hasDirectionKeys()) {
207 if (o.reversedDirection()) {
208 sb.append(tr("has direction keys (reversed)")).append(INDENT);
209 } else {
210 sb.append(tr("has direction keys")).append(INDENT);
211 }
212 }
213 String state = sb.toString().trim();
214 if (!state.isEmpty()) {
215 add(tr("State: "), sb.toString().trim());
216 }
217 }
218
219 void addCommon(OsmPrimitive o) {
220 add(tr("Data Set: "), Integer.toHexString(o.getDataSet().hashCode()));
221 add(tr("Edited at: "), o.isTimestampEmpty() ? tr("<new object>")
222 : DateUtils.fromDate(o.getTimestamp()));
223 add(tr("Edited by: "), o.getUser() == null ? tr("<new object>")
224 : getNameAndId(o.getUser().getName(), o.getUser().getId()));
225 add(tr("Version: "), Integer.toString(o.getVersion()));
226 add(tr("In changeset: "), Integer.toString(o.getChangesetId()));
227 }
228
229 void addAttributes(OsmPrimitive o) {
230 if (o.hasKeys()) {
231 add(tr("Tags: "));
232 for (String key : o.keySet()) {
233 s.append(INDENT).append(INDENT);
234 s.append(String.format("\"%s\"=\"%s\"\n", key, o.get(key)));
235 }
236 }
237 }
238
239 void addSpecial(OsmPrimitive o) {
240 if (o instanceof Node) {
241 addCoordinates((Node) o);
242 } else if (o instanceof Way) {
243 addBbox(o);
244 addWayNodes((Way) o);
245 } else if (o instanceof Relation) {
246 addBbox(o);
247 addRelationMembers((Relation) o);
248 }
249 }
250
251 void addRelationMembers(Relation r) {
252 add(trn("{0} Member: ", "{0} Members: ", r.getMembersCount(), r.getMembersCount()));
253 for (RelationMember m : r.getMembers()) {
254 s.append(INDENT).append(INDENT);
255 addHeadline(m.getMember());
256 s.append(tr(" as \"{0}\"", m.getRole()));
257 s.append(NL);
258 }
259 }
260
261 void addWayNodes(Way w) {
262 add(tr("{0} Nodes: ", w.getNodesCount()));
263 for (Node n : w.getNodes()) {
264 s.append(INDENT).append(INDENT);
265 addNameAndId(n);
266 s.append(NL);
267 }
268 }
269
270 void addBbox(OsmPrimitive o) {
271 BBox bbox = o.getBBox();
272 if (bbox != null) {
273 add(tr("Bounding box: "), bbox.toStringCSV(", "));
274 EastNorth bottomRigth = Main.getProjection().latlon2eastNorth(bbox.getBottomRight());
275 EastNorth topLeft = Main.getProjection().latlon2eastNorth(bbox.getTopLeft());
276 add(tr("Bounding box (projected): "),
277 Double.toString(topLeft.east()), ", ",
278 Double.toString(bottomRigth.north()), ", ",
279 Double.toString(bottomRigth.east()), ", ",
280 Double.toString(topLeft.north()));
281 }
282 }
283
284 void addCoordinates(Node n) {
285 if (n.getCoor() != null) {
286 add(tr("Coordinates: "),
287 Double.toString(n.getCoor().lat()), ", ",
288 Double.toString(n.getCoor().lon()));
289 add(tr("Coordinates (projected): "),
290 Double.toString(n.getEastNorth().east()), ", ",
291 Double.toString(n.getEastNorth().north()));
292 }
293 }
294
295 void addReferrers(StringBuilder s, OsmPrimitive o) {
296 List<OsmPrimitive> refs = o.getReferrers();
297 if (!refs.isEmpty()) {
298 add(tr("Part of: "));
299 for (OsmPrimitive p : refs) {
300 s.append(INDENT).append(INDENT);
301 addHeadline(p);
302 s.append(NL);
303 }
304 }
305 }
306
307 void addConflicts(OsmPrimitive o) {
308 Conflict<?> c = layer.getConflicts().getConflictForMy(o);
309 if (c != null) {
310 add(tr("In conflict with: "));
311 addNameAndId(c.getTheir());
312 }
313 }
314
315 @Override
316 public String toString() {
317 return s.toString();
318 }
319 }
320
321 protected void buildMapPaintPanel(JPanel p) {
322 p.setLayout(new GridBagLayout());
323 txtMappaint = new JosmTextArea();
324 txtMappaint.setFont(new Font("Monospaced", txtMappaint.getFont().getStyle(), txtMappaint.getFont().getSize()));
325 txtMappaint.setEditable(false);
326
327 p.add(new JScrollPane(txtMappaint), GBC.std().fill());
328 }
329
330 protected void createMapPaintText() {
331 final Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getAllSelected();
332 ElemStyles elemstyles = MapPaintStyles.getStyles();
333 NavigatableComponent nc = Main.map.mapView;
334 double scale = nc.getDist100Pixel();
335
336 for (OsmPrimitive osm : sel) {
337 txtMappaint.append(tr("Styles Cache for \"{0}\":", osm.getDisplayName(DefaultNameFormatter.getInstance())));
338
339 MultiCascade mc = new MultiCascade();
340
341 for (StyleSource s : elemstyles.getStyleSources()) {
342 if (s.active) {
343 txtMappaint.append(tr("\n\n> applying {0} style \"{1}\"\n", getSort(s), s.getDisplayString()));
344 s.apply(mc, osm, scale, null, false);
345 txtMappaint.append(tr("\nRange:{0}", mc.range));
346 for (Entry<String, Cascade> e : mc.getLayers()) {
347 txtMappaint.append("\n " + e.getKey() + ": \n" + e.getValue());
348 }
349 } else {
350 txtMappaint.append(tr("\n\n> skipping \"{0}\" (not active)", s.getDisplayString()));
351 }
352 }
353 txtMappaint.append(tr("\n\nList of generated Styles:\n"));
354 StyleList sl = elemstyles.get(osm, scale, nc);
355 for (ElemStyle s : sl) {
356 txtMappaint.append(" * " + s + "\n");
357 }
358 txtMappaint.append("\n\n");
359 }
360
361 if (sel.size() == 2) {
362 List<OsmPrimitive> selList = new ArrayList<OsmPrimitive>(sel);
363 StyleCache sc1 = selList.get(0).mappaintStyle;
364 StyleCache sc2 = selList.get(1).mappaintStyle;
365 if (sc1 == sc2) {
366 txtMappaint.append(tr("The 2 selected objects have identical style caches."));
367 }
368 if (!sc1.equals(sc2)) {
369 txtMappaint.append(tr("The 2 selected objects have different style caches."));
370 }
371 if (sc1.equals(sc2) && sc1 != sc2) {
372 txtMappaint.append(tr("Warning: The 2 selected objects have equal, but not identical style caches."));
373 }
374 }
375 }
376
377 private String getSort(StyleSource s) {
378 if (s instanceof XmlStyleSource) {
379 return tr("xml");
380 } else if (s instanceof MapCSSStyleSource) {
381 return tr("mapcss");
382 } else {
383 return tr("unknown");
384 }
385 }
386}
Note: See TracBrowser for help on using the repository browser.