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 extends OsmPrimitive> c = event.getPrimitives();
+ Iterator extends OsmPrimitive> 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