diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3eed211 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ + +notesolver/_deploy.bat +notesolver/build/GPL-v2.0.txt +notesolver/build/GPL-v3.0.txt +notesolver/build/README diff --git a/notesolver/MANIFEST b/notesolver/MANIFEST new file mode 100644 index 0000000..f653287 --- /dev/null +++ b/notesolver/MANIFEST @@ -0,0 +1,15 @@ +Manifest-Version: 1.0 +Ant-Version: Apache Ant 1.10.7 +Created-By: 1.8.0_251-b08 (Oracle Corporation) +Plugin-Mainversion: 10580 +Plugin-Version: UNKNOWN +Plugin-Class: org.openstreetmap.josm.plugins.notesolver.NoteSolverPlug + in +Plugin-Description: Used for closing notes when uploading a changeset + and referencing them in each other's comments to keep references. +Plugin-Date: 2020-05-10T23:07:54.659 +Author: Kai Michael Poppe +Plugin-Link: https://wiki.openstreetmap.org/wiki/User:Kmpoppe/Plugins# + noteSolver +Plugin-Canloadatruntime: true + diff --git a/notesolver/README b/notesolver/README new file mode 100644 index 0000000..d388c37 --- /dev/null +++ b/notesolver/README @@ -0,0 +1,11 @@ +README +====== + +Readme for your plugin + + * Plugin author and contact email address. + + * The license for your plugin source code. If you have no special preferences, + you can pick the license that is used for JOSM ("GPL v2 or later"). + + * Notes for future developers, if needed. diff --git a/notesolver/build.xml b/notesolver/build.xml new file mode 100644 index 0000000..5266a8a --- /dev/null +++ b/notesolver/build.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$1.class b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$1.class new file mode 100644 index 0000000..ec6f5be Binary files /dev/null and b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$1.class differ diff --git a/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$2.class b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$2.class new file mode 100644 index 0000000..83f2cb5 Binary files /dev/null and b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$2.class differ diff --git a/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$3.class b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$3.class new file mode 100644 index 0000000..c06214b Binary files /dev/null and b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$3.class differ diff --git a/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$4.class b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$4.class new file mode 100644 index 0000000..46021ed Binary files /dev/null and b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$4.class differ diff --git a/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$5.class b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$5.class new file mode 100644 index 0000000..967474e Binary files /dev/null and b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$5.class differ diff --git a/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$6.class b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$6.class new file mode 100644 index 0000000..4026986 Binary files /dev/null and b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$6.class differ diff --git a/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$7.class b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$7.class new file mode 100644 index 0000000..72d90a8 Binary files /dev/null and b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin$7.class differ diff --git a/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin.class b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin.class new file mode 100644 index 0000000..260d3ce Binary files /dev/null and b/notesolver/build/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin.class differ diff --git a/notesolver/dist/notesolver.jar b/notesolver/dist/notesolver.jar new file mode 100644 index 0000000..52c60d6 Binary files /dev/null and b/notesolver/dist/notesolver.jar differ diff --git a/notesolver/src/.vscode/launch.json b/notesolver/src/.vscode/launch.json new file mode 100644 index 0000000..ac60bd5 --- /dev/null +++ b/notesolver/src/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Debug (Launch) - Current File", + "request": "launch", + "mainClass": "${file}" + } + ] +} \ No newline at end of file diff --git a/notesolver/src/.vscode/settings.json b/notesolver/src/.vscode/settings.json new file mode 100644 index 0000000..2970199 --- /dev/null +++ b/notesolver/src/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "java.project.referencedLibraries": [ + "lib/**/*.jar", + "d:\\_dev\\osm\\josm\\josm\\core\\dist\\josm-custom.jar" + ] +} \ No newline at end of file diff --git a/notesolver/src/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin.java b/notesolver/src/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin.java new file mode 100644 index 0000000..5f30a19 --- /dev/null +++ b/notesolver/src/org/openstreetmap/josm/plugins/notesolver/NoteSolverPlugin.java @@ -0,0 +1,323 @@ +package org.openstreetmap.josm.plugins.notesolver; + +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.openstreetmap.josm.actions.*; +import org.openstreetmap.josm.actions.upload.*; + +import org.openstreetmap.josm.data.*; +import org.openstreetmap.josm.data.notes.*; +import org.openstreetmap.josm.data.osm.*; +import org.openstreetmap.josm.data.osm.NoteData.*; +import org.openstreetmap.josm.data.osm.event.*; + +import org.openstreetmap.josm.gui.*; +import org.openstreetmap.josm.gui.layer.*; +import org.openstreetmap.josm.gui.layer.LayerManager.*; +import org.openstreetmap.josm.gui.progress.ProgressMonitor; + +import org.openstreetmap.josm.plugins.*; + +import java.awt.event.*; +import java.awt.*; +import javax.swing.*; + +public class NoteSolverPlugin extends Plugin { + static JMenu noteSolverMenu; + public List rememberedNotes = new ArrayList<>(); + private List solvedNotes = new ArrayList<>(); + public Note selectedNote; + private int lastChangeSet; + private boolean autoUploadDecision = false; + NoteSolverPlugin me = this; + int maxMenuItemLen = 50; + + public NoteSolverPlugin(final PluginInformation info) { + super(info); + // Create Menu + updateMenu(); + setEnabledMenu(false); + // Register Layer Change Listener for Note Selection and Dataset Change + MainApplication.getLayerManager().addLayerChangeListener(layerChangeListener); + // Register Upload Hook + UploadAction.registerUploadHook(uploadHook, false); + } + + private final UploadHook uploadHook = new UploadHook() { + @Override + public boolean checkUpload(APIDataSet apiDataSet) { + boolean returnValue = true; + if (rememberedNotes != null && rememberedNotes.size() > 0) { + String noteList = ""; + for (Note note : solvedNotes) { + if (rememberedNotes.contains(note)) rememberedNotes.remove(note); + } + for (Note note : rememberedNotes) { + noteList = noteList + (char)13 + (char)10 + noteShortText(note); + } + int outVal = JOptionPane.showConfirmDialog( + null, + "Automatically Resolve Note" + (rememberedNotes.size() > 1 ? "s" : "") + (char)13 + (char)10 + noteList + (char)13 + (char)10 + "?", + null, + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE + ); + if (outVal == JOptionPane.CANCEL_OPTION) { + returnValue = false; + } else { + autoUploadDecision = (outVal == JOptionPane.YES_OPTION); + if (autoUploadDecision) { + String comment = MainApplication.getLayerManager().getEditDataSet().getChangeSetTags().get("comment"); + for (Note note : solvedNotes) { + String noteLink = "Closes https://www.openstreetmap.org/note/" + Long.toString(note.getId()); + comment = comment.replace("; " + noteLink, ""); + comment = comment.replace(noteLink, ""); + } + for (Note note : rememberedNotes) { + String noteLink = "Closes https://www.openstreetmap.org/note/" + Long.toString(note.getId()); + comment = (comment != null ? (comment.contains(noteLink) ? comment : comment + "; " + noteLink) : noteLink); + } + MainApplication.getLayerManager().getEditDataSet().addChangeSetTag("comment", comment); + } + returnValue = true; + } + } + return returnValue; + } + }; + + private final JosmAction forgetNoteAction = new JosmAction() { + private static final long serialVersionUID = 1927873880648933879L; + @Override + public void actionPerformed(ActionEvent event) { + if (selectedNote == null) { + JOptionPane.showMessageDialog(null, "No Note selected."); + } else { + boolean alreadyAdded = false; + if (rememberedNotes != null) { + for(Note note : rememberedNotes) + if (note.getId() == selectedNote.getId()) alreadyAdded = true; + if (alreadyAdded) rememberedNotes.remove(selectedNote); + } + } + updateMenu(); + } + }; + private final JosmAction rememberNoteAction = new JosmAction() { + private static final long serialVersionUID = 1927873880648933880L; + @Override + public void actionPerformed(ActionEvent event) { + if (selectedNote == null) { + JOptionPane.showMessageDialog(null, "No Note selected."); + } else { + boolean alreadyAdded = false; + if (rememberedNotes != null) { + for(Note note : rememberedNotes) + if (note.getId() == selectedNote.getId()) alreadyAdded = true; + if (!alreadyAdded) rememberedNotes.add(selectedNote); + } + } + updateMenu(); + } + }; + + private final NoteDataUpdateListener noteDataUpdateListener = new NoteDataUpdateListener() { + @Override + public void selectedNoteChanged(NoteData noteData) { + selectedNote = noteData.getSelectedNote(); + if (selectedNote != null) { + JPopupMenu contextMenu = new JPopupMenu(); + for (JMenuItem j : mainMenuEntries()) { + contextMenu.add(j); + } + Point p = MainApplication.getMainFrame().getMousePosition(); + contextMenu.setInvoker(MainApplication.getMainFrame().getComponentAt(p)); + contextMenu.setLocation(p); + contextMenu.setEnabled(true); + contextMenu.setVisible(true); + } + } + @Override + public void noteDataUpdated(NoteData noteData) { + // nothing to do here + } + }; + + private final LayerChangeListener layerChangeListener = new LayerChangeListener() { + @Override + public void layerAdded(LayerAddEvent e) { + changeListeners(e.getAddedLayer(), false); + } + + @Override + public void layerRemoving(LayerRemoveEvent e) { + changeListeners(e.getRemovedLayer(), true); + } + + @Override + public void layerOrderChanged(LayerOrderChangeEvent e) { + // nothing to do here + } + public void changeListeners(Layer layer, boolean isRemove) { + if (layer instanceof OsmDataLayer) { + DataSet ds = ((OsmDataLayer) layer).getDataSet(); + if (!isRemove) { + ds.addDataSetListener(dataSetListener); + } else { + ds.removeDataSetListener(dataSetListener); + } + } else if (layer instanceof NoteLayer) { + NoteData notes = ((NoteLayer) layer).getNoteData(); + if (!isRemove) { + notes.addNoteDataUpdateListener(noteDataUpdateListener); + } else { + notes.removeNoteDataUpdateListener(noteDataUpdateListener); + } + } + } + }; + + private final DataSetListener dataSetListener = new DataSetListener() { + @Override + public void wayNodesChanged(WayNodesChangedEvent event) { + } + + @Override + public void tagsChanged(TagsChangedEvent event) { + } + + @Override + public void relationMembersChanged(RelationMembersChangedEvent event) { + } + + @Override + public void primitivesRemoved(PrimitivesRemovedEvent event) { + } + + @Override + public void primitivesAdded(PrimitivesAddedEvent event) { + } + + @Override + public void otherDatasetChange(AbstractDatasetChangedEvent event) + { + if (event.getType() == AbstractDatasetChangedEvent.DatasetEventType.CHANGESET_ID_CHANGED && autoUploadDecision) { + Collection c = event.getPrimitives(); + Iterator oIter = c.iterator(); + int thisChangeSet = oIter.next().getChangesetId(); + if (lastChangeSet != thisChangeSet) { + lastChangeSet = thisChangeSet; + for (Note note : rememberedNotes) { + NoteData noteData = new NoteData(java.util.Collections.singleton(note)); + noteData.closeNote(note, "Resolved with changeset https://www.openstreetmap.org/changeset/" + Integer.toString(thisChangeSet)); + UploadNotesTask uploadNotesTask = new UploadNotesTask(); + ProgressMonitor pm = null; + uploadNotesTask.uploadNotes(noteData, pm); + solvedNotes.add(note); + } + rememberedNotes = new ArrayList<>(); + event.getDataset().addChangeSetTag("comment", ""); + updateMenu(); + } + } + } + + @Override + public void dataChanged(DataChangedEvent event) { + } + + @Override + public void nodeMoved(NodeMovedEvent event) { + } + }; + + public void updateMenu() { + final MainMenu menu = MainApplication.getMenu(); + + if (noteSolverMenu == null) { + noteSolverMenu = menu.addMenu( + "Note Solver", + "Note Solver", + 0, + menu.getDefaultMenuPos(), + "help" + ); + } else { + noteSolverMenu.removeAll(); + } + + if (rememberedNotes != null) { + for (JMenuItem j : mainMenuEntries()) { + noteSolverMenu.add(j); + } + } + noteSolverMenu.addSeparator(); + noteSolverMenu.add(new JMenuItem("List of selected Notes")); + noteSolverMenu.addSeparator(); + if (rememberedNotes != null) { + for(Note note : rememberedNotes) { + JMenuItem jMenuItem = new JMenuItem(noteShortText(note)); + jMenuItem.setToolTipText(note.getFirstComment().toString()); + jMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ev) { + rememberedNotes.remove(note); + updateMenu(); + } + }); + jMenuItem.setEnabled(true); + noteSolverMenu.add(jMenuItem); + } + } + } + + private List mainMenuEntries() { + List returnList = new ArrayList<>(); + JMenuItem addAction = new JMenuItem("Remember Note"); + addAction.setToolTipText("Add the selected Note to the list of Notes that should be automatically closed when uploading a changeset"); + addAction.addActionListener(rememberNoteAction); + addAction.setEnabled((selectedNote != null && rememberedNotes != null && !rememberedNotes.contains(selectedNote) && selectedNote.getState() != Note.State.CLOSED)); + returnList.add(addAction); + JMenuItem removeAction = new JMenuItem("Forget Note"); + removeAction.setToolTipText("Remove the selected Note from the list of Notes that should be automatically closed when uploading a changeset"); + removeAction.addActionListener(forgetNoteAction); + removeAction.setEnabled(selectedNote != null && rememberedNotes != null && rememberedNotes.contains(selectedNote)); + returnList.add(removeAction); + return returnList; + } + + private String noteShortText(Note n) { + String firstComment = n.getFirstComment().toString(); + if (firstComment.length() > maxMenuItemLen) firstComment = firstComment.substring(0, maxMenuItemLen) + "..."; + String menuItemText = + "* " + Long.toString(n.getId()) + " " + + "[" + n.getFirstComment().getUser().getName().toString() + + " @ " + DateFormat.getDateInstance().format(n.getCreatedAt()) + + ": " + firstComment + + "]"; + return menuItemText; + } + + @Override + public void mapFrameInitialized(final MapFrame oldFrame, final MapFrame newFrame) { + if (newFrame == null) + setEnabledMenu(false); + else { + setEnabledMenu(true); + } + } + + private void setEnabledMenu(final boolean isEnabled) { + + for (final Component me : noteSolverMenu.getMenuComponents()) { + if (me instanceof JMenuItem) { + ((JMenuItem) me).setEnabled(isEnabled); + } + } + + } +} \ No newline at end of file