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

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

fix #10554 - Add notes dialog (patch by ToeBee)

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