// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.history; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.Adjustable; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.HashMap; import java.util.Observable; import java.util.Observer; import javax.swing.JCheckBox; import org.openstreetmap.josm.tools.CheckParameterUtil; /** * Synchronizes scrollbar adjustments between a set of * {@see Adjustable}s. Whenever the adjustment of one of * the registerd Adjustables is updated the adjustment of * the other registered Adjustables is adjusted too. * */ public class AdjustmentSynchronizer implements AdjustmentListener { private final ArrayList<Adjustable> synchronizedAdjustables; private final HashMap<Adjustable, Boolean> enabledMap; private final Observable observable; public AdjustmentSynchronizer() { synchronizedAdjustables = new ArrayList<Adjustable>(); enabledMap = new HashMap<Adjustable, Boolean>(); observable = new Observable(); } /** * registers an {@see Adjustable} for participation in synchronized * scrolling. * * @param adjustable the adjustable */ public void participateInSynchronizedScrolling(Adjustable adjustable) { if (adjustable == null) return; if (synchronizedAdjustables.contains(adjustable)) return; synchronizedAdjustables.add(adjustable); setParticipatingInSynchronizedScrolling(adjustable, true); adjustable.addAdjustmentListener(this); } /** * event handler for {@see AdjustmentEvent}s * */ public void adjustmentValueChanged(AdjustmentEvent e) { if (! enabledMap.get(e.getAdjustable())) return; for (Adjustable a : synchronizedAdjustables) { if (a != e.getAdjustable() && isParticipatingInSynchronizedScrolling(a)) { a.setValue(e.getValue()); } } } /** * sets whether adjustable participates in adjustment synchronization * or not * * @param adjustable the adjustable */ protected void setParticipatingInSynchronizedScrolling(Adjustable adjustable, boolean isParticipating) { CheckParameterUtil.ensureParameterNotNull(adjustable, "adjustable"); if (! synchronizedAdjustables.contains(adjustable)) throw new IllegalStateException(tr("Adjustable {0} not registered yet. Cannot set participation in synchronized adjustment.", adjustable)); enabledMap.put(adjustable, isParticipating); observable.notifyObservers(); } /** * returns true if an adjustable is participating in synchronized scrolling * * @param adjustable the adjustable * @return true, if the adjustable is participating in synchronized scrolling, false otherwise * @throws IllegalStateException thrown, if adjustable is not registered for synchronized scrolling */ protected boolean isParticipatingInSynchronizedScrolling(Adjustable adjustable) throws IllegalStateException { if (! synchronizedAdjustables.contains(adjustable)) throw new IllegalStateException(tr("Adjustable {0} not registered yet.", adjustable)); return enabledMap.get(adjustable); } /** * wires a {@see JCheckBox} to the adjustment synchronizer, in such a way that: * <li> * <ol>state changes in the checkbox control whether the adjustable participates * in synchronized adjustment</ol> * <ol>state changes in this {@see AdjustmentSynchronizer} are reflected in the * {@see JCheckBox}</ol> * </li> * * * @param view the checkbox to control whether an adjustable participates in synchronized * adjustment * @param adjustable the adjustable * @exception IllegalArgumentException thrown, if view is null * @exception IllegalArgumentException thrown, if adjustable is null */ protected void adapt(final JCheckBox view, final Adjustable adjustable) throws IllegalStateException { CheckParameterUtil.ensureParameterNotNull(adjustable, "adjustable"); CheckParameterUtil.ensureParameterNotNull(view, "view"); if (! synchronizedAdjustables.contains(adjustable)) { participateInSynchronizedScrolling(adjustable); } // register an item lister with the check box // view.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { switch(e.getStateChange()) { case ItemEvent.SELECTED: if (!isParticipatingInSynchronizedScrolling(adjustable)) { setParticipatingInSynchronizedScrolling(adjustable, true); } break; case ItemEvent.DESELECTED: if (isParticipatingInSynchronizedScrolling(adjustable)) { setParticipatingInSynchronizedScrolling(adjustable, false); } break; } } }); observable.addObserver( new Observer() { public void update(Observable o, Object arg) { boolean sync = isParticipatingInSynchronizedScrolling(adjustable); if (view.isSelected() != sync) { view.setSelected(sync); } } } ); setParticipatingInSynchronizedScrolling(adjustable, true); view.setSelected(true); } }