Subject: [PATCH] Fix #22727: Use a lock instead of a boolean to avoid infinite recursions
---
Index: src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
--- a/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 18699)
+++ b/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(date 1679925628510)
@@ -29,6 +29,7 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.Future;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.UnaryOperator;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -298,7 +299,7 @@
     private void addButtonsForImageLayers() {
         List<MoveImgDisplayPanel<?>> alreadyAdded = this.getImageTabs().collect(Collectors.toList());
         // Avoid the setVisible call recursively calling this method and adding duplicates
-        alreadyAdded.forEach(m -> m.finishedAddingButtons = false);
+        alreadyAdded.forEach(m -> m.finishedAddingButtons.lock());
         List<Layer> availableLayers = MainApplication.getLayerManager().getLayers();
         List<IGeoImageLayer> geoImageLayers = availableLayers.stream()
                 .sorted(Comparator.comparingInt(entry -> /*reverse*/-availableLayers.indexOf(entry)))
@@ -322,9 +323,9 @@
                     do {
                         int index = layers.indexOfTabComponent(source);
                         if (index >= 0) {
-                            getImageTabs().forEach(m -> m.finishedAddingButtons = false);
+                            getImageTabs().forEach(m -> m.finishedAddingButtons.lock());
                             removeImageTab(((MoveImgDisplayPanel<?>) layers.getComponentAt(index)).layer);
-                            getImageTabs().forEach(m -> m.finishedAddingButtons = true);
+                            getImageTabs().forEach(m -> m.finishedAddingButtons.unlock());
                             getImageTabs().forEach(m -> m.setVisible(m.isVisible()));
                             return;
                         }
@@ -343,7 +344,7 @@
                 .collect(Collectors.toList()).forEach(this::removeImageTab);
 
         // This is need to avoid the first button becoming visible, and then recalling this method.
-        this.getImageTabs().forEach(m -> m.finishedAddingButtons = true);
+        alreadyAdded.forEach(m -> m.finishedAddingButtons.unlock());
         // After that, trigger the visibility set code
         this.getImageTabs().forEach(m -> m.setVisible(m.isVisible()));
     }
@@ -768,7 +769,7 @@
          * The purpose of this field is to avoid having the same tab added to the dialog multiple times. This is only a problem when the dialog
          * has multiple tabs on initialization (like from a session).
          */
-        boolean finishedAddingButtons;
+        private final ReentrantLock finishedAddingButtons = new ReentrantLock();
         MoveImgDisplayPanel(ImageDisplay imgDisplay, T layer) {
             super(new BorderLayout());
             this.layer = layer;
@@ -780,18 +781,23 @@
             super.setVisible(visible);
             JTabbedPane layers = ImageViewerDialog.getInstance().layers;
             int index = layers.indexOfComponent(this);
-            if (visible && this.finishedAddingButtons) {
-                if (!this.layer.getSelection().isEmpty() && !this.layer.getSelection().contains(ImageViewerDialog.getCurrentImage())) {
-                    ImageViewerDialog.getInstance().displayImages(this.layer.getSelection());
-                    this.layer.invalidate(); // This will force the geoimage layers to update properly.
-                }
-                if (this.imgDisplay.getParent() != this) {
-                    this.add(this.imgDisplay, BorderLayout.CENTER);
-                    this.imgDisplay.invalidate();
-                    this.revalidate();
-                }
-                if (index >= 0) {
-                    layers.setTitleAt(index, "* " + getLabel(MainApplication.getLayerManager().getLayers()));
+            if (visible && !this.finishedAddingButtons.isLocked()) {
+                this.finishedAddingButtons.lock();
+                try {
+                    if (!this.layer.getSelection().isEmpty() && !this.layer.getSelection().contains(ImageViewerDialog.getCurrentImage())) {
+                        ImageViewerDialog.getInstance().displayImages(this.layer.getSelection());
+                        this.layer.invalidate(); // This will force the geoimage layers to update properly.
+                    }
+                    if (this.imgDisplay.getParent() != this) {
+                        this.add(this.imgDisplay, BorderLayout.CENTER);
+                        this.imgDisplay.invalidate();
+                        this.revalidate();
+                    }
+                    if (index >= 0) {
+                        layers.setTitleAt(index, "* " + getLabel(MainApplication.getLayerManager().getLayers()));
+                    }
+                } finally {
+                    this.finishedAddingButtons.unlock();
                 }
             } else if (index >= 0) {
                 layers.setTitleAt(index, getLabel(MainApplication.getLayerManager().getLayers()));
