source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/NoteDialog.java@ 7720

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

fix #10739 - Improve note input dialogs (patch by ToeBee)

File size: 13.0 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.Image;
9import java.awt.event.ActionEvent;
10import java.text.SimpleDateFormat;
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.List;
14
15import javax.swing.AbstractAction;
16import javax.swing.AbstractListModel;
17import javax.swing.DefaultListCellRenderer;
18import javax.swing.ImageIcon;
19import javax.swing.JLabel;
20import javax.swing.JList;
21import javax.swing.JOptionPane;
22import javax.swing.JPanel;
23import javax.swing.JScrollPane;
24import javax.swing.ListCellRenderer;
25import javax.swing.ListSelectionModel;
26import javax.swing.event.ListSelectionEvent;
27import javax.swing.event.ListSelectionListener;
28
29import org.openstreetmap.josm.Main;
30import org.openstreetmap.josm.actions.UploadNotesAction;
31import org.openstreetmap.josm.actions.mapmode.AddNoteAction;
32import org.openstreetmap.josm.data.notes.Note;
33import org.openstreetmap.josm.data.notes.Note.State;
34import org.openstreetmap.josm.data.osm.NoteData;
35import org.openstreetmap.josm.gui.MapView;
36import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
37import org.openstreetmap.josm.gui.NoteInputDialog;
38import org.openstreetmap.josm.gui.SideButton;
39import org.openstreetmap.josm.gui.layer.Layer;
40import org.openstreetmap.josm.gui.layer.NoteLayer;
41import org.openstreetmap.josm.tools.ImageProvider;
42
43/**
44 * Dialog to display and manipulate notes
45 */
46public class NoteDialog extends ToggleDialog implements LayerChangeListener {
47
48
49 /** Small icon size for use in graphics calculations */
50 public static final int ICON_SMALL_SIZE = 16;
51 /** Large icon size for use in graphics calculations */
52 public static final int ICON_LARGE_SIZE = 24;
53 /** 24x24 icon for unresolved notes */
54 public static final ImageIcon ICON_OPEN = ImageProvider.get("dialogs/notes", "note_open.png");
55 /** 16x16 icon for unresolved notes */
56 public static final ImageIcon ICON_OPEN_SMALL =
57 new ImageIcon(ICON_OPEN.getImage().getScaledInstance(ICON_SMALL_SIZE, ICON_SMALL_SIZE, Image.SCALE_SMOOTH));
58 /** 24x24 icon for resolved notes */
59 public static final ImageIcon ICON_CLOSED = ImageProvider.get("dialogs/notes", "note_closed.png");
60 /** 16x16 icon for resolved notes */
61 public static final ImageIcon ICON_CLOSED_SMALL =
62 new ImageIcon(ICON_CLOSED.getImage().getScaledInstance(ICON_SMALL_SIZE, ICON_SMALL_SIZE, Image.SCALE_SMOOTH));
63 /** 24x24 icon for new notes */
64 public static final ImageIcon ICON_NEW = ImageProvider.get("dialogs/notes", "note_new.png");
65 /** 16x16 icon for new notes */
66 public static final ImageIcon ICON_NEW_SMALL =
67 new ImageIcon(ICON_NEW.getImage().getScaledInstance(ICON_SMALL_SIZE, ICON_SMALL_SIZE, Image.SCALE_SMOOTH));
68 /** Icon for note comments */
69 public static final ImageIcon ICON_COMMENT = ImageProvider.get("dialogs/notes", "note_comment.png");
70
71 private NoteTableModel model;
72 private JList<Note> displayList;
73 private final AddCommentAction addCommentAction;
74 private final CloseAction closeAction;
75 private final NewAction newAction;
76 private final ReopenAction reopenAction;
77 private final UploadNotesAction uploadAction;
78
79 private NoteData noteData;
80
81 /** Creates a new toggle dialog for notes */
82 public NoteDialog() {
83 super("Notes", "notes/note_open.png", "List of notes", null, 150);
84 if (Main.isDebugEnabled()) {
85 Main.debug("constructed note dialog");
86 }
87
88 addCommentAction = new AddCommentAction();
89 closeAction = new CloseAction();
90 newAction = new NewAction();
91 reopenAction = new ReopenAction();
92 uploadAction = new UploadNotesAction();
93 buildDialog();
94 }
95
96 @Override
97 public void showDialog() {
98 super.showDialog();
99 }
100
101 private void buildDialog() {
102 model = new NoteTableModel();
103 displayList = new JList<Note>(model);
104 displayList.setCellRenderer(new NoteRenderer());
105 displayList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
106 displayList.addListSelectionListener(new ListSelectionListener() {
107 @Override
108 public void valueChanged(ListSelectionEvent e) {
109 if (noteData != null) { //happens when layer is deleted while note selected
110 noteData.setSelectedNote(displayList.getSelectedValue());
111 }
112 updateButtonStates();
113 }});
114
115 JPanel pane = new JPanel(new BorderLayout());
116 pane.add(new JScrollPane(displayList), BorderLayout.CENTER);
117
118 createLayout(pane, false, Arrays.asList(new SideButton[]{
119 new SideButton(newAction, false),
120 new SideButton(addCommentAction, false),
121 new SideButton(closeAction, false),
122 new SideButton(reopenAction, false),
123 new SideButton(uploadAction, false)}));
124 updateButtonStates();
125 }
126
127 private void updateButtonStates() {
128 if (noteData == null || noteData.getSelectedNote() == null) {
129 closeAction.setEnabled(false);
130 addCommentAction.setEnabled(false);
131 reopenAction.setEnabled(false);
132 } else if (noteData.getSelectedNote().getState() == State.open){
133 closeAction.setEnabled(true);
134 addCommentAction.setEnabled(true);
135 reopenAction.setEnabled(false);
136 } else { //note is closed
137 closeAction.setEnabled(false);
138 addCommentAction.setEnabled(false);
139 reopenAction.setEnabled(true);
140 }
141 if(noteData == null || !noteData.isModified()) {
142 uploadAction.setEnabled(false);
143 } else {
144 uploadAction.setEnabled(true);
145 }
146 }
147
148 @Override
149 public void showNotify() {
150 MapView.addLayerChangeListener(this);
151 }
152
153 @Override
154 public void hideNotify() {
155 MapView.removeLayerChangeListener(this);
156 }
157
158 @Override
159 public void activeLayerChange(Layer oldLayer, Layer newLayer) { }
160
161 @Override
162 public void layerAdded(Layer newLayer) {
163 if (Main.isDebugEnabled()) {
164 Main.debug("layer added: " + newLayer);
165 }
166 if (newLayer instanceof NoteLayer) {
167 if (Main.isDebugEnabled()) {
168 Main.debug("note layer added");
169 }
170 noteData = ((NoteLayer)newLayer).getNoteData();
171 model.setData(noteData.getNotes());
172 }
173 }
174
175 @Override
176 public void layerRemoved(Layer oldLayer) {
177 if (oldLayer instanceof NoteLayer) {
178 if (Main.isDebugEnabled()) {
179 Main.debug("note layer removed. Clearing everything");
180 }
181 noteData = null;
182 model.clearData();
183 if (Main.map.mapMode instanceof AddNoteAction) {
184 Main.map.selectMapMode(Main.map.mapModeSelect);
185 }
186 }
187 }
188
189 /**
190 * Sets the list of notes to be displayed in the dialog.
191 * The dialog should match the notes displayed in the note layer.
192 * @param noteList List of notes to display
193 */
194 public void setNoteList(List<Note> noteList) {
195 model.setData(noteList);
196 updateButtonStates();
197 this.repaint();
198 }
199
200 /**
201 * Notify the dialog that the note selection has changed.
202 * Causes it to update or clear its selection in the UI.
203 */
204 public void selectionChanged() {
205 if (noteData == null || noteData.getSelectedNote() == null) {
206 displayList.clearSelection();
207 } else {
208 displayList.setSelectedValue(noteData.getSelectedNote(), true);
209 }
210 updateButtonStates();
211 }
212
213 private class NoteRenderer implements ListCellRenderer<Note> {
214
215 private DefaultListCellRenderer defaultListCellRenderer = new DefaultListCellRenderer();
216 private final SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy kk:mm");
217
218 @Override
219 public Component getListCellRendererComponent(JList<? extends Note> list, Note note, int index,
220 boolean isSelected, boolean cellHasFocus) {
221 Component comp = defaultListCellRenderer.getListCellRendererComponent(list, note, index, isSelected, cellHasFocus);
222 if (note != null && comp instanceof JLabel) {
223 String text = note.getFirstComment().getText();
224 String userName = note.getFirstComment().getUser().getName();
225 if (userName == null || userName.isEmpty()) {
226 userName = "<Anonymous>";
227 }
228 String toolTipText = userName + " @ " + sdf.format(note.getCreatedAt());
229 JLabel jlabel = (JLabel)comp;
230 jlabel.setText(text);
231 ImageIcon icon;
232 if (note.getId() < 0) {
233 icon = ICON_NEW_SMALL;
234 } else if (note.getState() == State.closed) {
235 icon = ICON_CLOSED_SMALL;
236 } else {
237 icon = ICON_OPEN_SMALL;
238 }
239 jlabel.setIcon(icon);
240 jlabel.setToolTipText(toolTipText);
241 }
242 return comp;
243 }
244 }
245
246 class NoteTableModel extends AbstractListModel<Note> {
247 private List<Note> data;
248
249 public NoteTableModel() {
250 data = new ArrayList<Note>();
251 }
252
253 @Override
254 public int getSize() {
255 if (data == null) {
256 return 0;
257 }
258 return data.size();
259 }
260
261 @Override
262 public Note getElementAt(int index) {
263 return data.get(index);
264 }
265
266 public void setData(List<Note> noteList) {
267 data.clear();
268 data.addAll(noteList);
269 fireContentsChanged(this, 0, noteList.size());
270 }
271
272 public void clearData() {
273 displayList.clearSelection();
274 data.clear();
275 fireIntervalRemoved(this, 0, getSize());
276 }
277 }
278
279 class AddCommentAction extends AbstractAction {
280
281 public AddCommentAction() {
282 putValue(SHORT_DESCRIPTION,tr("Add comment"));
283 putValue(NAME, tr("Comment"));
284 putValue(SMALL_ICON, ICON_COMMENT);
285 }
286
287 @Override
288 public void actionPerformed(ActionEvent e) {
289 Note note = displayList.getSelectedValue();
290 if (note == null) {
291 JOptionPane.showMessageDialog(Main.map,
292 "You must select a note first",
293 "No note selected",
294 JOptionPane.ERROR_MESSAGE);
295 return;
296 }
297 NoteInputDialog dialog = new NoteInputDialog(Main.parent, tr("Comment on note"), tr("Add comment"));
298 dialog.showNoteDialog(tr("Add comment to note:"), NoteDialog.ICON_COMMENT);
299 if (dialog.getValue() != 1) {
300 Main.debug("User aborted note reopening");
301 return;
302 }
303 noteData.addCommentToNote(note, dialog.getInputText());
304 }
305 }
306
307 class CloseAction extends AbstractAction {
308
309 public CloseAction() {
310 putValue(SHORT_DESCRIPTION,tr("Close note"));
311 putValue(NAME, tr("Close"));
312 putValue(SMALL_ICON, ICON_CLOSED);
313 }
314
315 @Override
316 public void actionPerformed(ActionEvent e) {
317 NoteInputDialog dialog = new NoteInputDialog(Main.parent, tr("Close note"), tr("Close note"));
318 dialog.showNoteDialog(tr("Close note with message:"), NoteDialog.ICON_CLOSED);
319 if (dialog.getValue() != 1) {
320 Main.debug("User aborted note closing");
321 return;
322 }
323 Note note = displayList.getSelectedValue();
324 noteData.closeNote(note, dialog.getInputText());
325 }
326 }
327
328 class NewAction extends AbstractAction {
329
330 public NewAction() {
331 putValue(SHORT_DESCRIPTION,tr("Create a new note"));
332 putValue(NAME, tr("Create"));
333 putValue(SMALL_ICON, ICON_NEW);
334 }
335
336 @Override
337 public void actionPerformed(ActionEvent e) {
338 if (noteData == null) { //there is no notes layer. Create one first
339 Main.map.mapView.addLayer(new NoteLayer());
340 }
341 Main.map.selectMapMode(new AddNoteAction(Main.map, noteData));
342 }
343 }
344
345 class ReopenAction extends AbstractAction {
346
347 public ReopenAction() {
348 putValue(SHORT_DESCRIPTION,tr("Reopen note"));
349 putValue(NAME, tr("Reopen"));
350 putValue(SMALL_ICON, ICON_OPEN);
351 }
352
353 @Override
354 public void actionPerformed(ActionEvent e) {
355 NoteInputDialog dialog = new NoteInputDialog(Main.parent, tr("Reopen note"), tr("Reopen note"));
356 dialog.showNoteDialog(tr("Reopen note with message:"), NoteDialog.ICON_OPEN);
357 if (dialog.getValue() != 1) {
358 Main.debug("User aborted note reopening");
359 return;
360 }
361
362 Note note = displayList.getSelectedValue();
363 noteData.reOpenNote(note, dialog.getInputText());
364 }
365 }
366}
Note: See TracBrowser for help on using the repository browser.