Subject: [PATCH] #22798: AbstractAction -> JosmAction
---
Index: src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java b/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java
--- a/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java	(revision 18683)
+++ b/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java	(date 1678199224748)
@@ -9,9 +9,9 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
-import java.awt.event.KeyEvent;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
@@ -21,7 +21,6 @@
 import java.util.concurrent.Future;
 import java.util.stream.Collectors;
 
-import javax.swing.AbstractAction;
 import javax.swing.Action;
 import javax.swing.DefaultListSelectionModel;
 import javax.swing.JCheckBox;
@@ -34,6 +33,7 @@
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
 
+import org.openstreetmap.josm.actions.JosmAction;
 import org.openstreetmap.josm.actions.OpenBrowserAction;
 import org.openstreetmap.josm.actions.downloadtasks.ChangesetHeaderDownloadTask;
 import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
@@ -59,11 +59,10 @@
 import org.openstreetmap.josm.io.NetworkManager;
 import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.OpenBrowser;
-import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
 import org.openstreetmap.josm.tools.Shortcut;
+import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
 
 /**
  * ChangesetDialog is a toggle dialog which displays the current list of changesets.
@@ -296,15 +295,22 @@
     /**
      * Selects objects for the currently selected changesets.
      */
-    class SelectObjectsAction extends AbstractAction implements ListSelectionListener, ItemListener {
+    class SelectObjectsAction extends JosmAction implements ListSelectionListener, ItemListener {
 
         SelectObjectsAction() {
-            putValue(NAME, tr("Select"));
-            putValue(SHORT_DESCRIPTION, tr("Select all objects assigned to the currently selected changesets"));
-            new ImageProvider("dialogs", "select").getResource().attachImageIcon(this, true);
+            super(tr("Select"), "dialogs/select", tr("Select all objects assigned to the currently selected changesets"),
+                    Shortcut.registerShortcut("changeset:select:objects",
+                            tr("Changesets: Select all objects assigned to the currently selected changesets"),
+                            KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
             updateEnabledState();
         }
 
+        /**
+         * Select objects based off of the changeset id
+         * @param ds The dataset to select objects from
+         * @param ids The ids to select
+         */
         public void selectObjectsByChangesetIds(DataSet ds, Set<Integer> ids) {
             if (ds == null || ids == null)
                 return;
@@ -327,6 +333,7 @@
             selectObjectsByChangesetIds(ds, sel);
         }
 
+        @Override
         protected void updateEnabledState() {
             setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
         }
@@ -347,11 +354,12 @@
      * Downloads selected changesets
      *
      */
-    class ReadChangesetsAction extends AbstractAction implements ListSelectionListener, ItemListener {
+    class ReadChangesetsAction extends JosmAction implements ListSelectionListener, ItemListener {
         ReadChangesetsAction() {
-            putValue(NAME, tr("Download"));
-            putValue(SHORT_DESCRIPTION, tr("Download information about the selected changesets from the OSM server"));
-            new ImageProvider("download").getResource().attachImageIcon(this, true);
+            super(tr("Download"), "download", tr("Download information about the selected changesets from the OSM server"),
+                    Shortcut.registerShortcut("changeset:download:information",
+                            tr("Changesets: Download information about the selected changeset"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
             updateEnabledState();
         }
 
@@ -365,6 +373,7 @@
             MainApplication.worker.submit(new PostDownloadHandler(task, task.download()));
         }
 
+        @Override
         protected void updateEnabledState() {
             setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0 && !NetworkManager.isOffline(OnlineResource.OSM_API));
         }
@@ -384,11 +393,12 @@
      * Closes the currently selected changesets
      *
      */
-    class CloseOpenChangesetsAction extends AbstractAction implements ListSelectionListener, ItemListener {
+    class CloseOpenChangesetsAction extends JosmAction implements ListSelectionListener, ItemListener {
         CloseOpenChangesetsAction() {
-            putValue(NAME, tr("Close open changesets"));
-            putValue(SHORT_DESCRIPTION, tr("Close the selected open changesets"));
-            new ImageProvider("closechangeset").getResource().attachImageIcon(this, true);
+            super(tr("Close open changesets"), "closechangeset", tr("Close the selected open changesets"),
+                    Shortcut.registerShortcut("changeset:close",
+                            tr("Changesets: Close the selected open changesets"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
             updateEnabledState();
         }
 
@@ -400,6 +410,7 @@
             MainApplication.worker.submit(new CloseChangesetTask(sel));
         }
 
+        @Override
         protected void updateEnabledState() {
             setEnabled(getCurrentChangesetListModel().hasSelectedOpenChangesets());
         }
@@ -419,11 +430,12 @@
      * Show information about the currently selected changesets
      *
      */
-    class ShowChangesetInfoAction extends AbstractAction implements ListSelectionListener, ItemListener {
+    class ShowChangesetInfoAction extends JosmAction implements ListSelectionListener, ItemListener {
         ShowChangesetInfoAction() {
-            putValue(NAME, tr("Show info"));
-            putValue(SHORT_DESCRIPTION, tr("Open a web page for each selected changeset"));
-            new ImageProvider("help/internet").getResource().attachImageIcon(this, true);
+            super(tr("Show info"), "closechangeset", tr("Open a web page for each selected changeset"),
+                    Shortcut.registerShortcut("changeset:info",
+                            tr("Changesets: Open a web page for each selected changeset"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
             updateEnabledState();
         }
 
@@ -440,6 +452,7 @@
             }
         }
 
+        @Override
         protected void updateEnabledState() {
             setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
         }
@@ -459,11 +472,13 @@
      * Show information about the currently selected changesets
      *
      */
-    class LaunchChangesetManagerAction extends AbstractAction {
+    class LaunchChangesetManagerAction extends JosmAction {
         LaunchChangesetManagerAction() {
-            putValue(NAME, tr("Details"));
-            putValue(SHORT_DESCRIPTION, tr("Opens the Changeset Manager window for the selected changesets"));
-            new ImageProvider("dialogs/changeset", "changesetmanager").getResource().attachImageIcon(this, true);
+            super(tr("Details"), "dialogs/changeset/changesetmanager", tr("Opens the Changeset Manager window for the selected changesets"),
+                    Shortcut.registerShortcut("changeset:launch:manager",
+                            tr("Changesets: Opens the Changeset Manager window for the selected changesets"),
+                            KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
         }
 
         @Override
Index: src/org/openstreetmap/josm/gui/dialogs/CommandStackDialog.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/gui/dialogs/CommandStackDialog.java b/src/org/openstreetmap/josm/gui/dialogs/CommandStackDialog.java
--- a/src/org/openstreetmap/josm/gui/dialogs/CommandStackDialog.java	(revision 18683)
+++ b/src/org/openstreetmap/josm/gui/dialogs/CommandStackDialog.java	(date 1678199273070)
@@ -16,7 +16,6 @@
 import java.util.List;
 import java.util.Set;
 
-import javax.swing.AbstractAction;
 import javax.swing.Box;
 import javax.swing.JComponent;
 import javax.swing.JLabel;
@@ -38,6 +37,7 @@
 
 import org.openstreetmap.josm.actions.AutoScaleAction;
 import org.openstreetmap.josm.actions.AutoScaleAction.AutoScaleMode;
+import org.openstreetmap.josm.actions.JosmAction;
 import org.openstreetmap.josm.command.Command;
 import org.openstreetmap.josm.command.PseudoCommand;
 import org.openstreetmap.josm.data.UndoRedoHandler;
@@ -53,7 +53,6 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
 import org.openstreetmap.josm.tools.GBC;
-import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.InputMapUtils;
 import org.openstreetmap.josm.tools.Shortcut;
 import org.openstreetmap.josm.tools.SubclassFilteredCollection;
@@ -410,15 +409,40 @@
     /**
      * Action that selects the objects that take part in a command.
      */
-    public class SelectAction extends AbstractAction implements IEnabledStateUpdating {
+    public class SelectAction extends JosmAction implements IEnabledStateUpdating {
 
         /**
          * Constructs a new {@code SelectAction}.
          */
         public SelectAction() {
-            putValue(NAME, tr("Select"));
-            putValue(SHORT_DESCRIPTION, tr("Selects the objects that take part in this command (unless currently deleted)"));
-            new ImageProvider("dialogs", "select").getResource().attachImageIcon(this, true);
+            this(tr("Select"), "dialogs/select", tr("Selects the objects that take part in this command (unless currently deleted)"),
+                    Shortcut.registerShortcut("command:stack:select", tr("Command Stack: Select"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, null, false);
+        }
+
+        /**
+         * Constructs a new {@code SelectAction} that calls
+         * {@link JosmAction#JosmAction(String, String, String, Shortcut, boolean, String, boolean)}
+         *
+         * The new super for all actions.
+         *
+         * Use this super constructor to setup your action.
+         *
+         * @param name the action's text as displayed on the menu (if it is added to a menu)
+         * @param iconName the filename of the icon to use
+         * @param tooltip  a longer description of the action that will be displayed in the tooltip. Please note
+         *           that html is not supported for menu actions on some platforms.
+         * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always
+         *            do want a shortcut, remember you can always register it with group=none, so you
+         *            won't be assigned a shortcut unless the user configures one. If you pass null here,
+         *            the user CANNOT configure a shortcut for your action.
+         * @param registerInToolbar register this action for the toolbar preferences?
+         * @param toolbarId identifier for the toolbar preferences. The iconName is used, if this parameter is null
+         * @param installAdapters false, if you don't want to install layer changed and selection changed adapters
+         */
+        protected SelectAction(String name, String iconName, String tooltip, Shortcut shortcut, boolean registerInToolbar,
+                               String toolbarId, boolean installAdapters) {
+            super(name, iconName, tooltip, shortcut, registerInToolbar, toolbarId, installAdapters);
         }
 
         @Override
@@ -464,10 +488,10 @@
          * Constructs a new {@code SelectAndZoomAction}.
          */
         public SelectAndZoomAction() {
-            putValue(NAME, tr("Select and zoom"));
-            putValue(SHORT_DESCRIPTION,
-                    tr("Selects the objects that take part in this command (unless currently deleted), then and zooms to it"));
-            new ImageProvider("dialogs/autoscale", "selection").getResource().attachImageIcon(this, true);
+            super(tr("Select and zoom"), "dialogs/autoscale/selection",
+                    tr("Selects the objects that take part in this command (unless currently deleted), then and zooms to it"),
+                    Shortcut.registerShortcut("command:stack:select_and_zoom", tr("Command Stack: Select and zoom"),
+                            KeyEvent.VK_UNDEFINED, Shortcut.NONE), false, null, false);
         }
 
         @Override
@@ -488,7 +512,7 @@
     /**
      * Action to undo or redo all commands up to (and including) the seleced item.
      */
-    protected class UndoRedoAction extends AbstractAction implements IEnabledStateUpdating {
+    protected class UndoRedoAction extends JosmAction implements IEnabledStateUpdating {
         private final UndoRedoType type;
         private final JTree tree;
 
@@ -497,17 +521,20 @@
          * @param type decide whether it is an undo action or a redo action
          */
         public UndoRedoAction(UndoRedoType type) {
+            // This is really annoying. JEP 8300786 might fix this.
+            super(UndoRedoType.UNDO == type ? tr("Undo") : tr("Redo"),
+                    UndoRedoType.UNDO == type ? "undo" : "redo",
+                    UndoRedoType.UNDO == type ? tr("Undo the selected and all later commands")
+                            : tr("Redo the selected and all earlier commands"),
+                    UndoRedoType.UNDO == type
+                            ? Shortcut.registerShortcut("command:stack:undo", tr("Command Stack: Undo"), KeyEvent.VK_UNDEFINED, Shortcut.NONE)
+                            : Shortcut.registerShortcut("command:stack:redo", tr("Command Stack: Redo"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
             this.type = type;
             if (UndoRedoType.UNDO == type) {
                 tree = undoTree;
-                putValue(NAME, tr("Undo"));
-                putValue(SHORT_DESCRIPTION, tr("Undo the selected and all later commands"));
-                new ImageProvider("undo").getResource().attachImageIcon(this, true);
             } else {
                 tree = redoTree;
-                putValue(NAME, tr("Redo"));
-                putValue(SHORT_DESCRIPTION, tr("Redo the selected and all earlier commands"));
-                new ImageProvider("redo").getResource().attachImageIcon(this, true);
             }
         }
 
Index: src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java b/src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java
--- a/src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java	(revision 18683)
+++ b/src/org/openstreetmap/josm/gui/dialogs/FilterDialog.java	(date 1678195778713)
@@ -26,6 +26,7 @@
 import javax.swing.table.TableColumnModel;
 import javax.swing.table.TableModel;
 
+import org.openstreetmap.josm.actions.JosmAction;
 import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.actions.search.SearchAction;
 import org.openstreetmap.josm.data.osm.Filter;
@@ -51,7 +52,6 @@
 import org.openstreetmap.josm.gui.util.MultikeyShortcutAction;
 import org.openstreetmap.josm.gui.util.TableHelper;
 import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;
-import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.InputMapUtils;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -118,12 +118,10 @@
             tr("Filter mode")
     };
 
-    private abstract class FilterAction extends AbstractAction implements IEnabledStateUpdating {
+    private abstract class FilterAction extends JosmAction implements IEnabledStateUpdating {
 
-        FilterAction(String name, String description, String icon) {
-            putValue(NAME, name);
-            putValue(SHORT_DESCRIPTION, description);
-            new ImageProvider("dialogs", icon).getResource().attachImageIcon(this, true);
+        FilterAction(String name, String description, String icon, Shortcut shortcut) {
+            super(name, "dialogs/" + icon, description, shortcut, false, false);
         }
 
         @Override
@@ -134,7 +132,8 @@
 
     private class AddAction extends FilterAction {
         AddAction() {
-            super(tr("Add"), tr("Add filter."), /* ICON(dialogs/) */ "add");
+            super(tr("Add"), tr("Add filter."), /* ICON(dialogs/) */ "add",
+                    Shortcut.registerShortcut("filter:add", tr("Filter: Add"), KeyEvent.VK_UNDEFINED, Shortcut.NONE));
         }
 
         @Override
@@ -153,7 +152,8 @@
 
     private class EditAction extends FilterAction {
         EditAction() {
-            super(tr("Edit"), tr("Edit filter."), /* ICON(dialogs/) */ "edit");
+            super(tr("Edit"), tr("Edit filter."), /* ICON(dialogs/) */ "edit",
+                    Shortcut.registerShortcut("filter:edit", tr("Filter: Edit"), KeyEvent.VK_UNDEFINED, Shortcut.NONE));
         }
 
         @Override
@@ -170,7 +170,8 @@
 
     private class DeleteAction extends FilterAction {
         DeleteAction() {
-            super(tr("Delete"), tr("Delete filter."), /* ICON(dialogs/) */ "delete");
+            super(tr("Delete"), tr("Delete filter."), /* ICON(dialogs/) */ "delete",
+                    Shortcut.registerShortcut("filter:delete", tr("Filter: Delete"), KeyEvent.VK_UNDEFINED, Shortcut.NONE));
         }
 
         @Override
@@ -181,7 +182,8 @@
 
     private class MoveUpAction extends FilterAction {
         MoveUpAction() {
-            super(tr("Up"), tr("Move filter up."), /* ICON(dialogs/) */ "up");
+            super(tr("Up"), tr("Move filter up."), /* ICON(dialogs/) */ "up",
+                    Shortcut.registerShortcut("filter:up", tr("Filter: Move up"), KeyEvent.VK_UNDEFINED, Shortcut.NONE));
         }
 
         @Override
@@ -197,7 +199,8 @@
 
     private class MoveDownAction extends FilterAction {
         MoveDownAction() {
-            super(tr("Down"), tr("Move filter down."), /* ICON(dialogs/) */ "down");
+            super(tr("Down"), tr("Move filter down."), /* ICON(dialogs/) */ "down",
+                    Shortcut.registerShortcut("filter:down", tr("Filter: Move down"), KeyEvent.VK_UNDEFINED, Shortcut.NONE));
         }
 
         @Override
@@ -213,7 +216,8 @@
 
     private class SortAction extends FilterAction {
         SortAction() {
-            super(tr("Sort"), tr("Sort filters."), /* ICON(dialogs/) */ "sort");
+            super(tr("Sort"), tr("Sort filters."), /* ICON(dialogs/) */ "sort",
+                    Shortcut.registerShortcut("filter:sort", tr("Filter: Sort"), KeyEvent.VK_UNDEFINED, Shortcut.NONE));
         }
 
         @Override
@@ -229,7 +233,8 @@
 
     private class ReverseAction extends FilterAction {
         ReverseAction() {
-            super(tr("Reverse"), tr("Reverse the filters order."), /* ICON(dialogs/) */ "reverse");
+            super(tr("Reverse"), tr("Reverse the filters order."), /* ICON(dialogs/) */ "reverse",
+                    Shortcut.registerShortcut("filter:reverse", tr("Filter: Reverse"), KeyEvent.VK_UNDEFINED, Shortcut.NONE));
         }
 
         @Override
Index: src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java b/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java
--- a/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java	(revision 18683)
+++ b/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java	(date 1678199297782)
@@ -310,17 +310,21 @@
         }
     }
 
-    protected class OnOffAction extends AbstractAction implements ListSelectionListener {
+    protected class OnOffAction extends JosmAction implements ListSelectionListener {
         /**
          * Constructs a new {@code OnOffAction}.
          */
         public OnOffAction() {
+            super(tr("On/Off"), "apply", tr("Turn selected styles on or off"),
+                    Shortcut.registerShortcut("map:paint:style:on_off", tr("Filter: Add"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
             putValue(NAME, tr("On/Off"));
             putValue(SHORT_DESCRIPTION, tr("Turn selected styles on or off"));
             new ImageProvider("apply").getResource().attachImageIcon(this, true);
             updateEnabledState();
         }
 
+        @Override
         protected void updateEnabledState() {
             setEnabled(!cbWireframe.isSelected() && tblStyles.getSelectedRowCount() > 0);
         }
@@ -341,7 +345,7 @@
     /**
      * The action to move down the currently selected entries in the list.
      */
-    protected class MoveUpDownAction extends AbstractAction implements ListSelectionListener {
+    protected class MoveUpDownAction extends JosmAction implements ListSelectionListener {
 
         private final int increment;
 
@@ -350,13 +354,18 @@
          * @param isDown {@code true} to move the entry down, {@code false} to move it up
          */
         public MoveUpDownAction(boolean isDown) {
+            super(isDown ? tr("Down") : tr("Up"), "dialogs/" + (isDown ? "down" : "up"),
+                    isDown ? tr("Move the selected entry one row down.") : tr("Move the selected entry one row up."),
+                    isDown ? Shortcut.registerShortcut("map:paint:style:down", tr("Map Paint Styles: Move selected entry down"),
+                            KeyEvent.VK_UNDEFINED, Shortcut.NONE)
+                    : Shortcut.registerShortcut("map:paint:style:up", tr("Map Paint Styles: Move selected entry up"),
+                            KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
             increment = isDown ? 1 : -1;
-            putValue(NAME, isDown ? tr("Down") : tr("Up"));
-            new ImageProvider("dialogs", isDown ? "down" : "up").getResource().attachImageIcon(this, true);
-            putValue(SHORT_DESCRIPTION, isDown ? tr("Move the selected entry one row down.") : tr("Move the selected entry one row up."));
             updateEnabledState();
         }
 
+        @Override
         public void updateEnabledState() {
             int[] sel = tblStyles.getSelectedRows();
             setEnabled(!cbWireframe.isSelected() && MapPaintStyles.canMoveStyles(sel, increment));
Index: src/org/openstreetmap/josm/gui/dialogs/NotesDialog.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/gui/dialogs/NotesDialog.java b/src/org/openstreetmap/josm/gui/dialogs/NotesDialog.java
--- a/src/org/openstreetmap/josm/gui/dialogs/NotesDialog.java	(revision 18683)
+++ b/src/org/openstreetmap/josm/gui/dialogs/NotesDialog.java	(date 1678199412368)
@@ -18,8 +18,8 @@
 import java.util.Objects;
 import java.util.function.Predicate;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
-import javax.swing.AbstractAction;
 import javax.swing.AbstractListModel;
 import javax.swing.DefaultListCellRenderer;
 import javax.swing.ImageIcon;
@@ -34,6 +34,7 @@
 import javax.swing.SwingUtilities;
 
 import org.openstreetmap.josm.actions.DownloadNotesInViewAction;
+import org.openstreetmap.josm.actions.JosmAction;
 import org.openstreetmap.josm.actions.UploadNotesAction;
 import org.openstreetmap.josm.actions.mapmode.AddNoteAction;
 import org.openstreetmap.josm.data.notes.Note;
@@ -342,7 +343,7 @@
             if (filter == null) {
                 filteredData.addAll(data);
             } else {
-                data.stream().filter(filter).forEach(filteredData::add);
+                filteredData.addAll(data.stream().filter(filter).collect(Collectors.toList()));
             }
             fireContentsChanged(this, 0, getSize());
             setTitle(data.isEmpty()
@@ -350,12 +351,19 @@
                     : tr("Notes: {0}/{1}", filteredData.size(), data.size()));
         }
 
+        /**
+         * Set the note data
+         * @param noteList The notes to show
+         */
         public void setData(Collection<Note> noteList) {
             data.clear();
             data.addAll(noteList);
             setFilter(filter);
         }
 
+        /**
+         * Clear the note data
+         */
         public void clearData() {
             displayList.clearSelection();
             data.clear();
@@ -363,15 +371,18 @@
         }
     }
 
-    class AddCommentAction extends AbstractAction {
+    /**
+     * The action to add a new comment to OSM
+     */
+    class AddCommentAction extends JosmAction {
 
         /**
          * Constructs a new {@code AddCommentAction}.
          */
         AddCommentAction() {
-            putValue(SHORT_DESCRIPTION, tr("Add comment"));
-            putValue(NAME, tr("Comment"));
-            new ImageProvider("dialogs/notes", "note_comment").getResource().attachImageIcon(this, true);
+            super(tr("Comment"), "dialogs/notes/note_comment", tr("Add comment"),
+                    Shortcut.registerShortcut("notes:comment:add", tr("Notes: Add comment"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
         }
 
         @Override
@@ -395,15 +406,18 @@
         }
     }
 
-    class CloseAction extends AbstractAction {
+    /**
+     * Close a note
+     */
+    class CloseAction extends JosmAction {
 
         /**
          * Constructs a new {@code CloseAction}.
          */
         CloseAction() {
-            putValue(SHORT_DESCRIPTION, tr("Close note"));
-            putValue(NAME, tr("Close"));
-            new ImageProvider("dialogs/notes", "note_closed").getResource().attachImageIcon(this, true);
+            super(tr("Close"), "dialogs/notes/note_closed", tr("Close note"),
+                    Shortcut.registerShortcut("notes:comment:close", tr("Notes: Close note"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
         }
 
         @Override
@@ -427,15 +441,18 @@
         }
     }
 
-    class NewAction extends AbstractAction {
+    /**
+     * Create a new note
+     */
+    class NewAction extends JosmAction {
 
         /**
          * Constructs a new {@code NewAction}.
          */
         NewAction() {
-            putValue(SHORT_DESCRIPTION, tr("Create a new note"));
-            putValue(NAME, tr("Create"));
-            new ImageProvider("dialogs/notes", "note_new").getResource().attachImageIcon(this, true);
+            super(tr("Create"), "dialogs/notes/note_new", tr("Create a new note"),
+                    Shortcut.registerShortcut("notes:comment:new", tr("Notes: New note"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
         }
 
         @Override
@@ -449,15 +466,18 @@
         }
     }
 
-    class ReopenAction extends AbstractAction {
+    /**
+     * Reopen a note
+     */
+    class ReopenAction extends JosmAction {
 
         /**
          * Constructs a new {@code ReopenAction}.
          */
         ReopenAction() {
-            putValue(SHORT_DESCRIPTION, tr("Reopen note"));
-            putValue(NAME, tr("Reopen"));
-            new ImageProvider("dialogs/notes", "note_open").getResource().attachImageIcon(this, true);
+            super(tr("Reopen"), "dialogs/notes/note_open", tr("Reopen note"),
+                    Shortcut.registerShortcut("notes:comment:reopen", tr("Notes: Reopen note"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
         }
 
         @Override
@@ -475,15 +495,18 @@
         }
     }
 
-    class SortAction extends AbstractAction {
+    /**
+     * Sort notes
+     */
+    class SortAction extends JosmAction {
 
         /**
          * Constructs a new {@code SortAction}.
          */
         SortAction() {
-            putValue(SHORT_DESCRIPTION, tr("Sort notes"));
-            putValue(NAME, tr("Sort"));
-            new ImageProvider("dialogs", "sort").getResource().attachImageIcon(this, true);
+            super(tr("Sort"), "dialogs/sort", tr("Sort notes"),
+                    Shortcut.registerShortcut("notes:comment:sort", tr("Notes: Sort notes"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
         }
 
         @Override
@@ -496,11 +519,14 @@
         }
     }
 
-    class OpenInBrowserAction extends AbstractAction {
+    /**
+     * Open the note in a browser
+     */
+    class OpenInBrowserAction extends JosmAction {
         OpenInBrowserAction() {
-            super(tr("Open in browser"));
-            putValue(SHORT_DESCRIPTION, tr("Open the note in an external browser"));
-            new ImageProvider("help", "internet").getResource().attachImageIcon(this, true);
+            super(tr("Open in browser"), "help/internet", tr("Open the note in an external browser"),
+                    Shortcut.registerShortcut("notes:comment:open_in_browser", tr("Notes: Open note in browser"),
+                            KeyEvent.VK_UNDEFINED, Shortcut.NONE), false, false);
         }
 
         @Override
Index: src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java b/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java
--- a/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java	(revision 18683)
+++ b/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java	(date 1678199514865)
@@ -18,7 +18,6 @@
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
-import javax.swing.AbstractAction;
 import javax.swing.AbstractListModel;
 import javax.swing.DefaultListSelectionModel;
 import javax.swing.FocusManager;
@@ -35,6 +34,7 @@
 
 import org.openstreetmap.josm.actions.ExpertToggleAction;
 import org.openstreetmap.josm.actions.HistoryInfoAction;
+import org.openstreetmap.josm.actions.JosmAction;
 import org.openstreetmap.josm.actions.relation.AddSelectionToRelations;
 import org.openstreetmap.josm.actions.relation.DeleteRelationsAction;
 import org.openstreetmap.josm.actions.relation.DuplicateRelationAction;
@@ -88,7 +88,6 @@
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.InputMapUtils;
 import org.openstreetmap.josm.tools.PlatformManager;
 import org.openstreetmap.josm.tools.Shortcut;
@@ -375,14 +374,17 @@
     /**
      * The action for creating a new relation.
      */
-    static class NewAction extends AbstractAction implements LayerChangeListener, ActiveLayerChangeListener {
+    static class NewAction extends JosmAction implements LayerChangeListener, ActiveLayerChangeListener {
         NewAction() {
-            putValue(SHORT_DESCRIPTION, tr("Create a new relation"));
-            putValue(NAME, tr("New"));
-            new ImageProvider("dialogs", "add").getResource().attachImageIcon(this, true);
+            super(tr("New"), "dialogs/add", tr("Create a new relation"),
+                    Shortcut.registerShortcut("relation:new", tr("Create a new relation"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, false);
             updateEnabledState();
         }
 
+        /**
+         * Make a new relation
+         */
         public void run() {
             RelationEditor.getEditor(MainApplication.getLayerManager().getEditLayer(), null, null).setVisible(true);
         }
@@ -392,6 +394,7 @@
             run();
         }
 
+        @Override
         protected void updateEnabledState() {
             setEnabled(MainApplication.getLayerManager().getEditLayer() != null);
         }
@@ -513,7 +516,7 @@
             if (removedPrimitives == null) return;
             // extract the removed relations
             Set<Relation> removedRelations = removedPrimitives.stream()
-                    .filter(p -> p instanceof Relation).map(p -> (Relation) p)
+                    .filter(Relation.class::isInstance).map(Relation.class::cast)
                     .collect(Collectors.toSet());
             if (removedRelations.isEmpty())
                 return;
@@ -597,6 +600,9 @@
             }
         }
 
+        /**
+         * Update the title for the relation list dialog
+         */
         public void updateTitle() {
             if (!relations.isEmpty() && relations.size() != getSize()) {
                 RelationListDialog.this.setTitle(tr("Relations: {0}/{1}", getSize(), relations.size()));
Index: src/org/openstreetmap/josm/gui/dialogs/UserListDialog.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/gui/dialogs/UserListDialog.java b/src/org/openstreetmap/josm/gui/dialogs/UserListDialog.java
--- a/src/org/openstreetmap/josm/gui/dialogs/UserListDialog.java	(revision 18683)
+++ b/src/org/openstreetmap/josm/gui/dialogs/UserListDialog.java	(date 1678197140023)
@@ -29,6 +29,7 @@
 import javax.swing.table.DefaultTableModel;
 
 import org.openstreetmap.josm.actions.AbstractInfoAction;
+import org.openstreetmap.josm.actions.JosmAction;
 import org.openstreetmap.josm.data.osm.DataSelectionListener;
 import org.openstreetmap.josm.data.osm.IPrimitive;
 import org.openstreetmap.josm.data.osm.OsmData;
@@ -45,7 +46,6 @@
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.OpenBrowser;
 import org.openstreetmap.josm.tools.Shortcut;
@@ -163,18 +163,24 @@
         return model.getSelectedUsers(rows);
     }
 
-    class SelectUsersPrimitivesAction extends AbstractAction implements ListSelectionListener {
+    /**
+     * Select the primitives that a user modified <i>last</i>.
+     */
+    class SelectUsersPrimitivesAction extends JosmAction implements ListSelectionListener {
 
         /**
          * Constructs a new {@code SelectUsersPrimitivesAction}.
          */
         SelectUsersPrimitivesAction() {
-            putValue(NAME, tr("Select"));
-            putValue(SHORT_DESCRIPTION, tr("Select objects submitted by this user"));
-            new ImageProvider("dialogs", "select").getResource().attachImageIcon(this, true);
+            super(tr("Select"), "dialogs/select", tr("Select objects submitted by this user"),
+                    Shortcut.registerShortcut("user:select_primitives", tr("User: objects submitted by selected user"),
+                            KeyEvent.VK_UNDEFINED, Shortcut.NONE), false, false);
             updateEnabledState();
         }
 
+        /**
+         * Select the primitives owned by the selected users
+         */
         public void select() {
             int[] indexes = userTable.getSelectedRows();
             if (indexes.length == 0)
@@ -187,6 +193,7 @@
             select();
         }
 
+        @Override
         protected void updateEnabledState() {
             setEnabled(userTable != null && userTable.getSelectedRowCount() > 0);
         }
@@ -203,10 +210,9 @@
     class ShowUserInfoAction extends AbstractInfoAction implements ListSelectionListener {
 
         ShowUserInfoAction() {
-            super(false);
-            putValue(NAME, tr("Show info"));
-            putValue(SHORT_DESCRIPTION, tr("Launches a browser with information about the user"));
-            new ImageProvider("help/internet").getResource().attachImageIcon(this, true);
+            super(tr("Show info"), "help/internet", tr("Launches a browser with information about the user"),
+                    Shortcut.registerShortcut("user:open_in_browser", tr("User: Show info in browser"), KeyEvent.VK_UNDEFINED, Shortcut.NONE),
+                    false, null, false);
             updateEnabledState();
         }
 
@@ -234,7 +240,7 @@
         protected String createInfoUrl(Object infoObject) {
             if (infoObject instanceof User) {
                 User user = (User) infoObject;
-                return Config.getUrls().getBaseUserUrl() + '/' + Utils.encodeUrl(user.getName()).replaceAll("\\+", "%20");
+                return Config.getUrls().getBaseUserUrl() + '/' + Utils.encodeUrl(user.getName()).replace("+", "%20");
             } else {
                 return null;
             }
