1414 */
1515package net .rptools .maptool .client .swing ;
1616
17+ import java .awt .event .FocusAdapter ;
18+ import java .awt .event .FocusEvent ;
1719import java .beans .*;
1820import java .text .ParseException ;
1921import java .util .EnumSet ;
2628import net .rptools .lib .MathUtil ;
2729import org .apache .logging .log4j .LogManager ;
2830import org .apache .logging .log4j .Logger ;
29- import org .jetbrains .annotations .NotNull ;
3031
3132/**
3233 * Utility class for linking a numeric JSpinner, JSlider, and a variable. The slider and spinner are
@@ -54,8 +55,10 @@ public SpinnerSliderPaired(JSpinner spinner, JSlider slider) {
5455 this (spinner , slider , null );
5556 }
5657
58+ private static final Logger log = LogManager .getLogger (SpinnerSliderPaired .class );
59+
5760 public SpinnerSliderPaired (JSpinner spinner , JSlider slider , Consumer <Number > propertySetter ) {
58- this (spinner , slider , propertySetter , Number ::intValue , ( i ) -> (( Number ) i ). doubleValue () );
61+ this (spinner , slider , propertySetter , Number ::intValue , Integer :: doubleValue );
5962 log .debug ("spinner-slider pair using default relationship." );
6063 }
6164
@@ -66,8 +69,8 @@ public SpinnerSliderPaired(
6669 Function <Number , Integer > spinnerToSlider ,
6770 Function <Integer , Number > sliderToSpinner ) {
6871 // set functional relationship
69- setSpinnerToSlider (spinnerToSlider );
70- setSliderToSpinner (sliderToSpinner );
72+ setSpinnerToSliderFunction (spinnerToSlider );
73+ setSliderToSpinnerFunction (sliderToSpinner );
7174 // set the controls
7275 setLinkedSpinner (spinner );
7376 setLinkedSlider (slider );
@@ -79,25 +82,23 @@ public SpinnerSliderPaired(
7982 getLinkedSpinner ().setModel (commonModel .spinnerModelDelegate );
8083 }
8184
82- private static final Logger log = LogManager .getLogger (SpinnerSliderPaired .class );
83-
84- protected SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport (this );
85- protected VetoableChangeSupport vcs = new VetoableChangeSupport (this );
86-
85+ @ SuppressWarnings ("unused" )
8786 public void addVetoableChangeListener (VetoableChangeListener listener ) {
88- vcs .addVetoableChangeListener (listener );
87+ this . commonModel . modelVCS .addVetoableChangeListener (listener );
8988 }
9089
91- protected void removeVetoableChangeListener (VetoableChangeListener listener ) {
92- vcs .addVetoableChangeListener (listener );
90+ @ SuppressWarnings ("unused" )
91+ public void removeVetoableChangeListener (VetoableChangeListener listener ) {
92+ this .commonModel .modelVCS .addVetoableChangeListener (listener );
9393 }
9494
9595 public void addPropertyChangeListener (PropertyChangeListener listener ) {
96- pcs .addPropertyChangeListener (listener );
96+ this . commonModel . modelPCS .addPropertyChangeListener (listener );
9797 }
9898
99- protected void removePropertyChangeListener (PropertyChangeListener listener ) {
100- pcs .removePropertyChangeListener (listener );
99+ @ SuppressWarnings ("unused" )
100+ public void removePropertyChangeListener (PropertyChangeListener listener ) {
101+ this .commonModel .modelPCS .removePropertyChangeListener (listener );
101102 }
102103
103104 private JSpinner linkedSpinner ;
@@ -111,14 +112,44 @@ public JSlider getLinkedSlider() {
111112 return linkedSlider ;
112113 }
113114
114- private void setLinkedSlider (@ NotNull JSlider slider ) {
115+ private void setLinkedSlider (JSlider slider ) {
115116 this .linkedSlider = slider ;
117+ this .linkedSlider .addPropertyChangeListener (
118+ "value" ,
119+ pce -> {
120+ try {
121+ this .commonModel .modelVCS .fireVetoableChange (pce );
122+ this .commonModel .modelPCS .firePropertyChange (pce );
123+ } catch (PropertyVetoException ignored ) {
124+ }
125+ });
116126 }
117127
118128 private void setLinkedSpinner (JSpinner spinner ) {
119129 this .linkedSpinner = spinner ;
120- }
121-
130+ this .linkedSpinner .addPropertyChangeListener (
131+ "value" ,
132+ pce -> {
133+ try {
134+ this .commonModel .modelVCS .fireVetoableChange (pce );
135+ this .commonModel .modelPCS .firePropertyChange (pce );
136+ } catch (PropertyVetoException ignored ) {
137+ }
138+ });
139+ ((JSpinner .DefaultEditor ) this .linkedSpinner .getEditor ())
140+ .getTextField ()
141+ .addPropertyChangeListener (
142+ "value" ,
143+ pce -> {
144+ try {
145+ this .commonModel .modelVCS .fireVetoableChange (pce );
146+ this .commonModel .modelPCS .firePropertyChange (pce );
147+ } catch (PropertyVetoException ignored ) {
148+ }
149+ });
150+ }
151+
152+ @ SuppressWarnings ("unused" )
122153 // option to set the spinner to loop/wrap at end values
123154 public void setModelWraps (boolean b ) {
124155 commonModel .modelWraps = b ;
@@ -130,10 +161,12 @@ public void setModelWraps(boolean b) {
130161 private Supplier <Number > propertyGetter ;
131162 private String propertyName = "Property" ;
132163
164+ @ SuppressWarnings ("unused" )
133165 public Consumer <Number > getPropertySetter () {
134166 return propertySetter ;
135167 }
136168
169+ @ SuppressWarnings ("unused" )
137170 public Supplier <Number > getPropertyGetter () {
138171 return propertyGetter ;
139172 }
@@ -167,26 +200,29 @@ public void update() {
167200 // Instance of NumericModel that ties the two controls together
168201 private final NumericModel commonModel = new NumericModel ();
169202 // function that defines the value conversion from slider to spinner, defaults to 1:1
170- private Function <Integer , Number > sliderToSpinner = i -> (( Number ) i ). doubleValue () ;
203+ private Function <Integer , Number > sliderToSpinner = Integer :: doubleValue ;
171204 // function that defines the value conversion from spinner to slider
172205 private Function <Number , Integer > spinnerToSlider = Number ::intValue ;
173206
174- public void setSpinnerToSlider (Function <Number , Integer > function ) {
207+ public void setSpinnerToSliderFunction (Function <Number , Integer > function ) {
175208 spinnerToSlider = function ;
176209 }
177210
178- public void setSliderToSpinner (Function <Integer , Number > function ) {
211+ public void setSliderToSpinnerFunction (Function <Integer , Number > function ) {
179212 sliderToSpinner = function ;
180213 }
181214
215+ @ SuppressWarnings ("unused" )
182216 public Number getPairNextValue () {
183217 return commonModel .getModelNextValue ();
184218 }
185219
220+ @ SuppressWarnings ("unused" )
186221 public Number getPairPreviousValue () {
187222 return commonModel .getModelPreviousValue ();
188223 }
189224
225+ @ SuppressWarnings ("unused" )
190226 public int getPairSliderValue () {
191227 return commonModel .getModelNumber (true ).intValue ();
192228 }
@@ -199,14 +235,17 @@ public void incrementPairValue(Number n) {
199235 commonModel .incrementModelValue (n );
200236 }
201237
238+ @ SuppressWarnings ("unused" )
202239 public void setPairMaximum (Number n ) {
203240 commonModel .setModelMaximum (n );
204241 }
205242
243+ @ SuppressWarnings ("unused" )
206244 public void setPairMinimum (Number n ) {
207245 commonModel .setModelMinimum (n );
208246 }
209247
248+ @ SuppressWarnings ("unused" )
210249 public void setPairStepSize (Number n ) {
211250 commonModel .setModelStepSize (n );
212251 }
@@ -225,7 +264,13 @@ public void setCeiling(Number n) {
225264
226265 // The shared model - two delegates controlled from above
227266 private class NumericModel {
228- private NumericModel () {}
267+ protected SwingPropertyChangeSupport modelPCS ;
268+ protected VetoableChangeSupport modelVCS ;
269+
270+ private NumericModel () {
271+ modelPCS = new SwingPropertyChangeSupport (this );
272+ modelVCS = new VetoableChangeSupport (this );
273+ }
229274
230275 private boolean modelWraps = false ;
231276 private double floor ;
@@ -267,13 +312,21 @@ private void setDelegateValues() {
267312 getLinkedSlider ().addChangeListener (sliderChangeListener );
268313 getLinkedSlider ()
269314 .addMouseWheelListener (
270- e -> {
271- sliderModelDelegate .setValue (
272- Math .clamp (
273- sliderModelDelegate .getValue ()
274- + (int ) Math .round (e .getPreciseWheelRotation ()),
275- spinnerToSlider .apply (floor ),
276- spinnerToSlider .apply (ceiling )));
315+ e ->
316+ sliderModelDelegate .setValue (
317+ Math .clamp (
318+ sliderModelDelegate .getValue ()
319+ + (int ) Math .round (e .getPreciseWheelRotation ()),
320+ spinnerToSlider .apply (floor ),
321+ spinnerToSlider .apply (ceiling ))));
322+ getLinkedSpinner ()
323+ .getEditor ()
324+ .addFocusListener (
325+ new FocusAdapter () {
326+ @ Override
327+ public void focusLost (FocusEvent e ) {
328+ commitEdit (getLinkedSpinner ());
329+ }
277330 });
278331 }
279332
@@ -295,6 +348,7 @@ private void setModelValue(Number n) {
295348 }
296349
297350 private void setModelValue (Number n , Source s ) {
351+ // keep track of what changes have been made. Stop when spinner and slider have been done.
298352 if (!s .equals (Source .OTHER )
299353 && sources .contains (Source .SPINNER )
300354 && sources .contains (Source .SLIDER )) {
@@ -324,12 +378,11 @@ private void setModelValue(Number n, Source s) {
324378 newVal = Math .clamp (newVal , floor , ceiling );
325379 }
326380
327- setProperty (newVal );
328-
329- PropertyChangeEvent pce = new PropertyChangeEvent (this , "value" , currentVal , newVal );
381+ PropertyChangeEvent pce = new PropertyChangeEvent (s , "value" , currentVal , newVal );
330382
331383 try {
332- vcs .fireVetoableChange (pce );
384+ modelVCS .fireVetoableChange (pce );
385+ setProperty (newVal );
333386 if (s .equals (Source .OTHER )) {
334387 sources .add (Source .SPINNER );
335388 spinnerModelDelegate .setValue (newVal );
@@ -340,7 +393,7 @@ private void setModelValue(Number n, Source s) {
340393 } else {
341394 return ;
342395 }
343- pcs .firePropertyChange (pce );
396+ modelPCS .firePropertyChange (pce );
344397 } catch (PropertyVetoException ignored ) {
345398 }
346399 }
@@ -472,16 +525,21 @@ public String toString() {
472525 private final ChangeListener spinnerEditListener =
473526 e -> {
474527 JSpinner spinner = (JSpinner ) e .getSource ();
475- try {
476- spinner .commitEdit ();
477- } catch (ParseException pe ) {
478- // Edited value is invalid, revert the spinner to the last valid value,
479- JComponent editor = spinner .getEditor ();
480- if (editor instanceof JSpinner .NumberEditor ) {
481- ((JSpinner .NumberEditor ) editor ).getTextField ().setValue (spinner .getValue ());
482- }
483- }
528+ commitEdit (spinner );
484529 };
530+
531+ private void commitEdit (JSpinner spinner ) {
532+ try {
533+ spinner .commitEdit ();
534+ spinnerModelDelegate .setValue (spinner .getModel ().getValue ());
535+ } catch (ParseException pe ) {
536+ // Edited value is invalid, revert the spinner to the last valid value,
537+ JComponent editor = spinner .getEditor ();
538+ if (editor instanceof JSpinner .NumberEditor ) {
539+ ((JSpinner .NumberEditor ) editor ).getTextField ().setValue (spinner .getValue ());
540+ }
541+ }
542+ }
485543 }
486544
487545 @ Override
0 commit comments