source: josm/trunk/src/org/openstreetmap/josm/gui/layer/NoteLayer.java@ 8079

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

fix #11123 - IllegalArgumentException when resizing window with active notes popup

  • Property svn:eol-style set to native
File size: 8.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Dimension;
7import java.awt.Graphics2D;
8import java.awt.Point;
9import java.awt.event.MouseEvent;
10import java.awt.event.MouseListener;
11import java.io.File;
12import java.text.SimpleDateFormat;
13import java.util.ArrayList;
14import java.util.List;
15
16import javax.swing.Action;
17import javax.swing.Icon;
18import javax.swing.ImageIcon;
19import javax.swing.JToolTip;
20
21import org.openstreetmap.josm.Main;
22import org.openstreetmap.josm.actions.SaveActionBase;
23import org.openstreetmap.josm.data.Bounds;
24import org.openstreetmap.josm.data.notes.Note;
25import org.openstreetmap.josm.data.notes.Note.State;
26import org.openstreetmap.josm.data.notes.NoteComment;
27import org.openstreetmap.josm.data.osm.NoteData;
28import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
29import org.openstreetmap.josm.gui.MapView;
30import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
31import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
32import org.openstreetmap.josm.gui.dialogs.NotesDialog;
33import org.openstreetmap.josm.io.NoteExporter;
34import org.openstreetmap.josm.io.XmlWriter;
35import org.openstreetmap.josm.tools.ColorHelper;
36
37/**
38 * A layer to hold Note objects
39 */
40public class NoteLayer extends AbstractModifiableLayer implements MouseListener {
41
42 private final NoteData noteData;
43
44 /**
45 * Create a new note layer with a set of notes
46 * @param notes A list of notes to show in this layer
47 * @param name The name of the layer. Typically "Notes"
48 */
49 public NoteLayer(List<Note> notes, String name) {
50 super(name);
51 noteData = new NoteData(notes);
52 }
53
54 /** Convenience constructor that creates a layer with an empty note list */
55 public NoteLayer() {
56 super(tr("Notes"));
57 noteData = new NoteData();
58 }
59
60 @Override
61 public void hookUpMapView() {
62 Main.map.mapView.addMouseListener(this);
63 }
64
65 /**
66 * Returns the note data store being used by this layer
67 * @return noteData containing layer notes
68 */
69 public NoteData getNoteData() {
70 return noteData;
71 }
72
73 @Override
74 public boolean isModified() {
75 return noteData.isModified();
76 }
77
78 @Override
79 public boolean requiresUploadToServer() {
80 return isModified();
81 }
82
83 @Override
84 public boolean isSavable() {
85 return true;
86 }
87
88 @Override
89 public boolean requiresSaveToFile() {
90 Main.debug("associated notes file: " + getAssociatedFile());
91 return getAssociatedFile() != null && isModified();
92 }
93
94 @Override
95 public void paint(Graphics2D g, MapView mv, Bounds box) {
96 for (Note note : noteData.getNotes()) {
97 Point p = mv.getPoint(note.getLatLon());
98
99 ImageIcon icon = null;
100 if (note.getId() < 0) {
101 icon = NotesDialog.ICON_NEW_SMALL;
102 } else if (note.getState() == State.closed) {
103 icon = NotesDialog.ICON_CLOSED_SMALL;
104 } else {
105 icon = NotesDialog.ICON_OPEN_SMALL;
106 }
107 int width = icon.getIconWidth();
108 int height = icon.getIconHeight();
109 g.drawImage(icon.getImage(), p.x - (width / 2), p.y - height, Main.map.mapView);
110 }
111 if (noteData.getSelectedNote() != null) {
112 StringBuilder sb = new StringBuilder("<html>");
113 sb.append(tr("Note"));
114 sb.append(" " + noteData.getSelectedNote().getId());
115 List<NoteComment> comments = noteData.getSelectedNote().getComments();
116 SimpleDateFormat dayFormat = new SimpleDateFormat("MMM d, yyyy");
117 for (NoteComment comment : comments) {
118 String commentText = comment.getText();
119 //closing a note creates an empty comment that we don't want to show
120 if (commentText != null && commentText.trim().length() > 0) {
121 sb.append("<hr/>");
122 String userName = XmlWriter.encode(comment.getUser().getName());
123 if (userName == null || userName.trim().length() == 0) {
124 userName = "&lt;Anonymous&gt;";
125 }
126 sb.append(userName);
127 sb.append(" on ");
128 sb.append(dayFormat.format(comment.getCommentTimestamp()));
129 sb.append(":<br/>");
130 String htmlText = XmlWriter.encode(comment.getText(), true);
131 htmlText = htmlText.replace("&#xA;", "<br/>"); //encode method leaves us with entity instead of \n
132 sb.append(htmlText);
133 }
134 }
135 sb.append("</html>");
136 JToolTip toolTip = new JToolTip();
137 toolTip.setTipText(sb.toString());
138 Point p = mv.getPoint(noteData.getSelectedNote().getLatLon());
139
140 g.setColor(ColorHelper.html2color(Main.pref.get("color.selected")));
141 g.drawRect(p.x - (NotesDialog.ICON_SMALL_SIZE / 2), p.y - NotesDialog.ICON_SMALL_SIZE, NotesDialog.ICON_SMALL_SIZE - 1, NotesDialog.ICON_SMALL_SIZE - 1);
142
143 int tx = p.x + (NotesDialog.ICON_SMALL_SIZE / 2) + 5;
144 int ty = p.y - NotesDialog.ICON_SMALL_SIZE - 1;
145 g.translate(tx, ty);
146
147 //Carried over from the OSB plugin. Not entirely sure why it is needed
148 //but without it, the tooltip doesn't get sized correctly
149 for (int x = 0; x < 2; x++) {
150 Dimension d = toolTip.getUI().getPreferredSize(toolTip);
151 d.width = Math.min(d.width, (mv.getWidth() * 1 / 2));
152 if (d.width > 0 && d.height > 0) {
153 toolTip.setSize(d);
154 try {
155 toolTip.paint(g);
156 } catch (IllegalArgumentException e) {
157 // See #11123 - https://bugs.openjdk.java.net/browse/JDK-6719550
158 // Ignore the exception, as Netbeans does: http://hg.netbeans.org/main-silver/rev/c96f4d5fbd20
159 Main.error(e, false);
160 }
161 }
162 }
163 g.translate(-tx, -ty);
164 }
165 }
166
167 @Override
168 public Icon getIcon() {
169 return NotesDialog.ICON_OPEN_SMALL;
170 }
171
172 @Override
173 public String getToolTipText() {
174 return noteData.getNotes().size() + " " + tr("Notes");
175 }
176
177 @Override
178 public void mergeFrom(Layer from) {
179 throw new UnsupportedOperationException("Notes layer does not support merging yet");
180 }
181
182 @Override
183 public boolean isMergable(Layer other) {
184 return false;
185 }
186
187 @Override
188 public void visitBoundingBox(BoundingXYVisitor v) {
189 }
190
191 @Override
192 public Object getInfoComponent() {
193 StringBuilder sb = new StringBuilder();
194 sb.append(tr("Notes layer"));
195 sb.append("\n");
196 sb.append(tr("Total notes:"));
197 sb.append(" ");
198 sb.append(noteData.getNotes().size());
199 sb.append("\n");
200 sb.append(tr("Changes need uploading?"));
201 sb.append(" ");
202 sb.append(isModified());
203 return sb.toString();
204 }
205
206 @Override
207 public Action[] getMenuEntries() {
208 List<Action> actions = new ArrayList<>();
209 actions.add(LayerListDialog.getInstance().createShowHideLayerAction());
210 actions.add(LayerListDialog.getInstance().createDeleteLayerAction());
211 actions.add(new LayerListPopup.InfoAction(this));
212 actions.add(new LayerSaveAction(this));
213 actions.add(new LayerSaveAsAction(this));
214 return actions.toArray(new Action[actions.size()]);
215 }
216
217 @Override
218 public void mouseClicked(MouseEvent e) {
219 if (e.getButton() != MouseEvent.BUTTON1) {
220 return;
221 }
222 Point clickPoint = e.getPoint();
223 double snapDistance = 10;
224 double minDistance = Double.MAX_VALUE;
225 Note closestNote = null;
226 for (Note note : noteData.getNotes()) {
227 Point notePoint = Main.map.mapView.getPoint(note.getLatLon());
228 //move the note point to the center of the icon where users are most likely to click when selecting
229 notePoint.setLocation(notePoint.getX(), notePoint.getY() - NotesDialog.ICON_SMALL_SIZE / 2);
230 double dist = clickPoint.distanceSq(notePoint);
231 if (minDistance > dist && clickPoint.distance(notePoint) < snapDistance ) {
232 minDistance = dist;
233 closestNote = note;
234 }
235 }
236 noteData.setSelectedNote(closestNote);
237 }
238
239 @Override
240 public File createAndOpenSaveFileChooser() {
241 return SaveActionBase.createAndOpenSaveFileChooser(tr("Save GPX file"), NoteExporter.FILE_FILTER);
242 }
243
244 @Override
245 public void mousePressed(MouseEvent e) { }
246
247 @Override
248 public void mouseReleased(MouseEvent e) { }
249
250 @Override
251 public void mouseEntered(MouseEvent e) { }
252
253 @Override
254 public void mouseExited(MouseEvent e) { }
255}
Note: See TracBrowser for help on using the repository browser.