001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.history; 003 004import java.util.HashSet; 005import java.util.Objects; 006import java.util.Set; 007import java.util.function.BiFunction; 008import java.util.stream.IntStream; 009 010import javax.swing.ListSelectionModel; 011import javax.swing.event.ListSelectionEvent; 012import javax.swing.event.ListSelectionListener; 013 014import org.openstreetmap.josm.gui.util.TableHelper; 015 016/** 017 * Helper class to ensure that two (or more) {@link javax.swing.JTable}s always 018 * have the same entries selected. 019 * 020 * The tables are usually displayed side-by-side. 021 */ 022public class SelectionSynchronizer implements ListSelectionListener { 023 024 private final Set<ListSelectionModel> participants; 025 private boolean preventRecursion; 026 private BiFunction<Integer, ListSelectionModel, IntStream> selectionIndexMapper = (i, model) -> IntStream.of(i); 027 028 /** 029 * Constructs a new {@code SelectionSynchronizer}. 030 */ 031 public SelectionSynchronizer() { 032 participants = new HashSet<>(); 033 } 034 035 /** 036 * Add {@link ListSelectionModel} of the table to participate in selection 037 * synchronization. 038 * 039 * Call this method for all tables that should have their selection synchronized. 040 * @param model the selection model of the table 041 */ 042 public void participateInSynchronizedSelection(ListSelectionModel model) { 043 if (model == null) 044 return; 045 if (participants.contains(model)) 046 return; 047 participants.add(model); 048 model.addListSelectionListener(this); 049 } 050 051 void setSelectionIndexMapper(BiFunction<Integer, ListSelectionModel, IntStream> selectionIndexMapper) { 052 this.selectionIndexMapper = Objects.requireNonNull(selectionIndexMapper); 053 } 054 055 @Override 056 public void valueChanged(ListSelectionEvent e) { 057 if (preventRecursion) { 058 return; 059 } 060 preventRecursion = true; 061 ListSelectionModel referenceModel = (ListSelectionModel) e.getSource(); 062 for (ListSelectionModel model : participants) { 063 if (model == referenceModel) { 064 continue; 065 } 066 TableHelper.setSelectedIndices(model, 067 TableHelper.selectedIndices(referenceModel).flatMap(i -> selectionIndexMapper.apply(i, referenceModel))); 068 } 069 preventRecursion = false; 070 } 071}