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

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

see #15182 - see #15229 - remove GUI dependence in NoteData

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