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

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

see #15182 - deprecate Main.map and Main.isDisplayingMapView(). Replacements: gui.MainApplication.getMap() / gui.MainApplication.isDisplayingMapView()

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