source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/NotesDialog.java@ 10611

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

see #11390 - sonar - squid:S1604 - Java 8: Anonymous inner classes containing only one method should become lambdas

  • Property svn:eol-style set to native
File size: 15.1 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;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
8import java.awt.event.ActionEvent;
9import java.awt.event.MouseAdapter;
10import java.awt.event.MouseEvent;
11import java.text.DateFormat;
12import java.util.ArrayList;
13import java.util.Arrays;
14import java.util.Collection;
15import java.util.List;
16
17import javax.swing.AbstractAction;
18import javax.swing.AbstractListModel;
19import javax.swing.DefaultListCellRenderer;
20import javax.swing.ImageIcon;
21import javax.swing.JLabel;
22import javax.swing.JList;
23import javax.swing.JOptionPane;
24import javax.swing.JPanel;
25import javax.swing.JScrollPane;
26import javax.swing.ListCellRenderer;
27import javax.swing.ListSelectionModel;
28import javax.swing.SwingUtilities;
29
30import org.openstreetmap.josm.Main;
31import org.openstreetmap.josm.actions.DownloadNotesInViewAction;
32import org.openstreetmap.josm.actions.UploadNotesAction;
33import org.openstreetmap.josm.actions.mapmode.AddNoteAction;
34import org.openstreetmap.josm.data.notes.Note;
35import org.openstreetmap.josm.data.notes.Note.State;
36import org.openstreetmap.josm.data.notes.NoteComment;
37import org.openstreetmap.josm.data.osm.NoteData;
38import org.openstreetmap.josm.gui.NoteInputDialog;
39import org.openstreetmap.josm.gui.NoteSortDialog;
40import org.openstreetmap.josm.gui.SideButton;
41import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
42import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
43import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
44import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
45import org.openstreetmap.josm.gui.layer.NoteLayer;
46import org.openstreetmap.josm.tools.ImageProvider;
47import org.openstreetmap.josm.tools.date.DateUtils;
48
49/**
50 * Dialog to display and manipulate notes.
51 * @since 7852 (renaming)
52 * @since 7608 (creation)
53 */
54public class NotesDialog extends ToggleDialog implements LayerChangeListener {
55
56 private NoteTableModel model;
57 private JList<Note> displayList;
58 private final AddCommentAction addCommentAction;
59 private final CloseAction closeAction;
60 private final DownloadNotesInViewAction downloadNotesInViewAction;
61 private final NewAction newAction;
62 private final ReopenAction reopenAction;
63 private final SortAction sortAction;
64 private final UploadNotesAction uploadAction;
65
66 private transient NoteData noteData;
67
68 /** Creates a new toggle dialog for notes */
69 public NotesDialog() {
70 super(tr("Notes"), "notes/note_open", tr("List of notes"), null, 150);
71 addCommentAction = new AddCommentAction();
72 closeAction = new CloseAction();
73 downloadNotesInViewAction = DownloadNotesInViewAction.newActionWithDownloadIcon();
74 newAction = new NewAction();
75 reopenAction = new ReopenAction();
76 sortAction = new SortAction();
77 uploadAction = new UploadNotesAction();
78 buildDialog();
79 Main.getLayerManager().addLayerChangeListener(this);
80 }
81
82 private void buildDialog() {
83 model = new NoteTableModel();
84 displayList = new JList<>(model);
85 displayList.setCellRenderer(new NoteRenderer());
86 displayList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
87 displayList.addListSelectionListener(e -> {
88 if (noteData != null) { //happens when layer is deleted while note selected
89 noteData.setSelectedNote(displayList.getSelectedValue());
90 }
91 updateButtonStates();
92 });
93 displayList.addMouseListener(new MouseAdapter() {
94 //center view on selected note on double click
95 @Override
96 public void mouseClicked(MouseEvent e) {
97 if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
98 if (noteData != null && noteData.getSelectedNote() != null) {
99 Main.map.mapView.zoomTo(noteData.getSelectedNote().getLatLon());
100 }
101 }
102 }
103 });
104
105 JPanel pane = new JPanel(new BorderLayout());
106 pane.add(new JScrollPane(displayList), BorderLayout.CENTER);
107
108 createLayout(pane, false, Arrays.asList(new SideButton[]{
109 new SideButton(downloadNotesInViewAction, false),
110 new SideButton(newAction, false),
111 new SideButton(addCommentAction, false),
112 new SideButton(closeAction, false),
113 new SideButton(reopenAction, false),
114 new SideButton(sortAction, false),
115 new SideButton(uploadAction, false)}));
116 updateButtonStates();
117 }
118
119 private void updateButtonStates() {
120 if (noteData == null || noteData.getSelectedNote() == null) {
121 closeAction.setEnabled(false);
122 addCommentAction.setEnabled(false);
123 reopenAction.setEnabled(false);
124 } else if (noteData.getSelectedNote().getState() == State.OPEN) {
125 closeAction.setEnabled(true);
126 addCommentAction.setEnabled(true);
127 reopenAction.setEnabled(false);
128 } else { //note is closed
129 closeAction.setEnabled(false);
130 addCommentAction.setEnabled(false);
131 reopenAction.setEnabled(true);
132 }
133 if (noteData == null || !noteData.isModified()) {
134 uploadAction.setEnabled(false);
135 } else {
136 uploadAction.setEnabled(true);
137 }
138 //enable sort button if any notes are loaded
139 if (noteData == null || noteData.getNotes().isEmpty()) {
140 sortAction.setEnabled(false);
141 } else {
142 sortAction.setEnabled(true);
143 }
144 }
145
146 @Override
147 public void layerAdded(LayerAddEvent e) {
148 if (e.getAddedLayer() instanceof NoteLayer) {
149 noteData = ((NoteLayer) e.getAddedLayer()).getNoteData();
150 model.setData(noteData.getNotes());
151 setNotes(noteData.getSortedNotes());
152 }
153 }
154
155 @Override
156 public void layerRemoving(LayerRemoveEvent e) {
157 if (e.getRemovedLayer() instanceof NoteLayer) {
158 noteData = null;
159 model.clearData();
160 if (Main.map.mapMode instanceof AddNoteAction) {
161 Main.map.selectMapMode(Main.map.mapModeSelect);
162 }
163 }
164 }
165
166 @Override
167 public void layerOrderChanged(LayerOrderChangeEvent e) {
168 // ignored
169 }
170
171 /**
172 * Sets the list of notes to be displayed in the dialog.
173 * The dialog should match the notes displayed in the note layer.
174 * @param noteList List of notes to display
175 */
176 public void setNotes(Collection<Note> noteList) {
177 model.setData(noteList);
178 updateButtonStates();
179 this.repaint();
180 }
181
182 /**
183 * Notify the dialog that the note selection has changed.
184 * Causes it to update or clear its selection in the UI.
185 */
186 public void selectionChanged() {
187 if (noteData == null || noteData.getSelectedNote() == null) {
188 displayList.clearSelection();
189 } else {
190 displayList.setSelectedValue(noteData.getSelectedNote(), true);
191 }
192 updateButtonStates();
193 // TODO make a proper listener mechanism to handle change of note selection
194 Main.main.menu.infoweb.noteSelectionChanged();
195 }
196
197 /**
198 * Returns the currently selected note, if any.
199 * @return currently selected note, or null
200 * @since 8475
201 */
202 public Note getSelectedNote() {
203 return noteData != null ? noteData.getSelectedNote() : null;
204 }
205
206 private static class NoteRenderer implements ListCellRenderer<Note> {
207
208 private final DefaultListCellRenderer defaultListCellRenderer = new DefaultListCellRenderer();
209 private final DateFormat dateFormat = DateUtils.getDateTimeFormat(DateFormat.MEDIUM, DateFormat.SHORT);
210
211 @Override
212 public Component getListCellRendererComponent(JList<? extends Note> list, Note note, int index,
213 boolean isSelected, boolean cellHasFocus) {
214 Component comp = defaultListCellRenderer.getListCellRendererComponent(list, note, index, isSelected, cellHasFocus);
215 if (note != null && comp instanceof JLabel) {
216 NoteComment fstComment = note.getFirstComment();
217 JLabel jlabel = (JLabel) comp;
218 if (fstComment != null) {
219 String text = note.getFirstComment().getText();
220 String userName = note.getFirstComment().getUser().getName();
221 if (userName == null || userName.isEmpty()) {
222 userName = "<Anonymous>";
223 }
224 String toolTipText = userName + " @ " + dateFormat.format(note.getCreatedAt());
225 jlabel.setToolTipText(toolTipText);
226 jlabel.setText(note.getId() + ": " +text);
227 } else {
228 jlabel.setToolTipText(null);
229 jlabel.setText(Long.toString(note.getId()));
230 }
231 ImageIcon icon;
232 if (note.getId() < 0) {
233 icon = ImageProvider.get("dialogs/notes", "note_new", ImageProvider.ImageSizes.SMALLICON);
234 } else if (note.getState() == State.CLOSED) {
235 icon = ImageProvider.get("dialogs/notes", "note_closed", ImageProvider.ImageSizes.SMALLICON);
236 } else {
237 icon = ImageProvider.get("dialogs/notes", "note_open", ImageProvider.ImageSizes.SMALLICON);
238 }
239 jlabel.setIcon(icon);
240 }
241 return comp;
242 }
243 }
244
245 class NoteTableModel extends AbstractListModel<Note> {
246 private final transient List<Note> data;
247
248 /**
249 * Constructs a new {@code NoteTableModel}.
250 */
251 NoteTableModel() {
252 data = new ArrayList<>();
253 }
254
255 @Override
256 public int getSize() {
257 if (data == null) {
258 return 0;
259 }
260 return data.size();
261 }
262
263 @Override
264 public Note getElementAt(int index) {
265 return data.get(index);
266 }
267
268 public void setData(Collection<Note> noteList) {
269 data.clear();
270 data.addAll(noteList);
271 fireContentsChanged(this, 0, noteList.size());
272 }
273
274 public void clearData() {
275 displayList.clearSelection();
276 data.clear();
277 fireIntervalRemoved(this, 0, getSize());
278 }
279 }
280
281 class AddCommentAction extends AbstractAction {
282
283 /**
284 * Constructs a new {@code AddCommentAction}.
285 */
286 AddCommentAction() {
287 putValue(SHORT_DESCRIPTION, tr("Add comment"));
288 putValue(NAME, tr("Comment"));
289 new ImageProvider("dialogs/notes", "note_comment").getResource().attachImageIcon(this, true);
290 }
291
292 @Override
293 public void actionPerformed(ActionEvent e) {
294 Note note = displayList.getSelectedValue();
295 if (note == null) {
296 JOptionPane.showMessageDialog(Main.map,
297 "You must select a note first",
298 "No note selected",
299 JOptionPane.ERROR_MESSAGE);
300 return;
301 }
302 NoteInputDialog dialog = new NoteInputDialog(Main.parent, tr("Comment on note"), tr("Add comment"));
303 dialog.showNoteDialog(tr("Add comment to note:"), ImageProvider.get("dialogs/notes", "note_comment"));
304 if (dialog.getValue() != 1) {
305 return;
306 }
307 int selectedIndex = displayList.getSelectedIndex();
308 noteData.addCommentToNote(note, dialog.getInputText());
309 noteData.setSelectedNote(model.getElementAt(selectedIndex));
310 }
311 }
312
313 class CloseAction extends AbstractAction {
314
315 /**
316 * Constructs a new {@code CloseAction}.
317 */
318 CloseAction() {
319 putValue(SHORT_DESCRIPTION, tr("Close note"));
320 putValue(NAME, tr("Close"));
321 new ImageProvider("dialogs/notes", "note_closed").getResource().attachImageIcon(this, true);
322 }
323
324 @Override
325 public void actionPerformed(ActionEvent e) {
326 NoteInputDialog dialog = new NoteInputDialog(Main.parent, tr("Close note"), tr("Close note"));
327 dialog.showNoteDialog(tr("Close note with message:"), ImageProvider.get("dialogs/notes", "note_closed"));
328 if (dialog.getValue() != 1) {
329 return;
330 }
331 Note note = displayList.getSelectedValue();
332 int selectedIndex = displayList.getSelectedIndex();
333 noteData.closeNote(note, dialog.getInputText());
334 noteData.setSelectedNote(model.getElementAt(selectedIndex));
335 }
336 }
337
338 class NewAction extends AbstractAction {
339
340 /**
341 * Constructs a new {@code NewAction}.
342 */
343 NewAction() {
344 putValue(SHORT_DESCRIPTION, tr("Create a new note"));
345 putValue(NAME, tr("Create"));
346 new ImageProvider("dialogs/notes", "note_new").getResource().attachImageIcon(this, true);
347 }
348
349 @Override
350 public void actionPerformed(ActionEvent e) {
351 if (noteData == null) { //there is no notes layer. Create one first
352 Main.getLayerManager().addLayer(new NoteLayer());
353 }
354 Main.map.selectMapMode(new AddNoteAction(Main.map, noteData));
355 }
356 }
357
358 class ReopenAction extends AbstractAction {
359
360 /**
361 * Constructs a new {@code ReopenAction}.
362 */
363 ReopenAction() {
364 putValue(SHORT_DESCRIPTION, tr("Reopen note"));
365 putValue(NAME, tr("Reopen"));
366 new ImageProvider("dialogs/notes", "note_open").getResource().attachImageIcon(this, true);
367 }
368
369 @Override
370 public void actionPerformed(ActionEvent e) {
371 NoteInputDialog dialog = new NoteInputDialog(Main.parent, tr("Reopen note"), tr("Reopen note"));
372 dialog.showNoteDialog(tr("Reopen note with message:"), ImageProvider.get("dialogs/notes", "note_open"));
373 if (dialog.getValue() != 1) {
374 return;
375 }
376
377 Note note = displayList.getSelectedValue();
378 int selectedIndex = displayList.getSelectedIndex();
379 noteData.reOpenNote(note, dialog.getInputText());
380 noteData.setSelectedNote(model.getElementAt(selectedIndex));
381 }
382 }
383
384 class SortAction extends AbstractAction {
385
386 /**
387 * Constructs a new {@code SortAction}.
388 */
389 SortAction() {
390 putValue(SHORT_DESCRIPTION, tr("Sort notes"));
391 putValue(NAME, tr("Sort"));
392 new ImageProvider("dialogs", "sort").getResource().attachImageIcon(this, true);
393 }
394
395 @Override
396 public void actionPerformed(ActionEvent e) {
397 NoteSortDialog sortDialog = new NoteSortDialog(Main.parent, tr("Sort notes"), tr("Apply"));
398 sortDialog.showSortDialog(noteData.getCurrentSortMethod());
399 if (sortDialog.getValue() == 1) {
400 noteData.setSortMethod(sortDialog.getSelectedComparator());
401 }
402 }
403 }
404}
Note: See TracBrowser for help on using the repository browser.