001package votorola.s.gwt.stage.vote; // Copyright 2012, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. 002 003import com.google.gwt.core.client.Scheduler; 004import com.google.gwt.dom.client.*; 005import votorola.a.count.*; 006import votorola.a.count.gwt.*; 007import votorola.a.diff.*; 008import votorola.a.web.gwt.*; 009import votorola.g.lang.*; 010import votorola.g.web.gwt.*; 011import votorola.g.web.gwt.event.*; 012import votorola.s.gwt.stage.*; 013import votorola.s.gwt.stage.light.*; 014import votorola.s.gwt.stage.link.*; 015 016import static votorola.s.gwt.stage.vote.LightableDifference.ACTOR_LINK_VARIANT_BASE; 017import static votorola.s.gwt.stage.link.NominalDifferenceTargeter.NOMINAL_DIFF; 018 019 020/** A stage light to indicate {@linkplain votorola.a.diff.DiffKey position differences}. 021 * The light either actuates according to the {@linkplain PositionSensor position sensor} 022 * pointed by the user, or it falls back to the sensor of the {@linkplain #baseActuator() 023 * base actuator}. Whatever the sensor, if it has an associated {@linkplain 024 * #differenceFor(String,String) lightable difference}, then that difference is lit. 025 */ 026public abstract class DifferenceLight<D extends LightableDifference> implements Light<PositionSensor> 027{ 028 029 030 /** Does nothing itself but the call forces static initialization of this class. 031 */ 032 public static void forceInitClass() {} 033 034 035 036 /** Partially creates a DifferenceLight for {@linkplain #init(Stage) init} to finish. 037 * Construct at most one for the entire life of the document, as currently it does 038 * not unregister its listeners or otherwise clean up after itself. 039 * 040 * @see #voteTrack() 041 */ 042 protected DifferenceLight( VoteTrack _voteTrack ) 043 { 044 voteTrack = _voteTrack; 045 if( voteTrack == null ) throw new NullPointerException(); // eagerly 046 047 actuator = baseActuator = sceneName == null? new BaseActuator1(): 048 new BaseActuatorSD(); 049 } 050 051 052 053 private static DifferenceLight<?> instance; 054 055 { 056 if( instance != null ) throw new IllegalStateException(); 057 058 instance = DifferenceLight.this; 059 } 060 061 062 063 /** Completes the creation of this DifferenceLight. Call once only. 064 */ 065 protected void init( final Stage stage ) 066 { 067 addStageRelClasses(); 068 stage.lightBank().addLight( DifferenceLight.this ); // no need to remove, co-extant 069 stage.addInitializer( new TheatreInitializerC() // auto-removed 070 { 071 // LinkTrackV accessible and stage settled when: 072 public void initComplete( final Stage s, boolean _rPending ) 073 { 074 new DifferenceStager( DifferenceLight.this, s ); 075 baseActuator.init( new LinkLighter(baseActuator,LinkTrackV.i(StageV.i()),s), s ); 076 } 077 }); 078 } 079 080 081 082 // ------------------------------------------------------------------------------------ 083 084 085 /** Adds an {@linkplain LightableDifference#stageRelClass() existential style class} 086 * for {@linkplain #run(LightableDifference.Runner) each} lightable difference to the 087 * <code>#StageV-top</code> element. This method is called during initialization. 088 */ 089 protected final void addStageRelClasses() 090 { 091 run( new LightableDifference.Runner<D>() 092 { 093 final Element eStageV = Document.get().getElementById( "StageV-top" ); 094 public void run( final D diff ) { eStageV.addClassName( diff.stageRelClass() ); } 095 }); 096 } 097 098 099 /** Removes the {@linkplain LightableDifference#stageRelClass() existential style 100 * class} for {@linkplain #run(LightableDifference.Runner) each} lightable 101 * difference from the <code>#StageV-top</code> element. 102 */ 103 public final void removeStageRelClasses() 104 { 105 run( new LightableDifference.Runner<D>() 106 { 107 final Element eStageV = Document.get().getElementById( "StageV-top" ); 108 public void run( final D diff ) { eStageV.removeClassName( diff.stageRelClass() ); } 109 }); 110 } 111 112 113 114 /** The actuator for the sensor defined by the current {@linkplain Stage#getActorName() 115 * actor} and {@linkplain Stage#getPollName() poll}, which engages whenever the user 116 * is not pointing to a particular {@linkplain PositionSensor position sensor}. 117 */ 118 protected final Actuator_DL baseActuator() { return baseActuator; } 119 120 121 private final BaseActuator baseActuator; 122 123 124 125 /** The style class {@value} assigned to the document body when a {@linkplain 126 * #setScene(String,String) scene difference} is set and is currently lighted from 127 * the vantage of the anchoring author. 128 */ 129 public static final String BODY_ANCHOR = "voLiDi-anchor"; 130 131 132 private String ancClassLit; 133 134 135 136 /** The style class {@value} assigned to the document body when a {@linkplain 137 * #setScene(String,String) scene difference} is set and is currently lighted. The 138 * scene may assign the initial value (adding it to the body or leaving it off) 139 * depending on how it wishes to be styled before this light initializes and assigns 140 * the correct value. 141 */ 142 public static final String BODY_SCENE = "voLiDi-scene"; 143 144 // Changing? Grep value and change where hardcoded, e.g. in s/wic/diff/WP_D.html 145 146 private String scnClassLit = ElementX.hasClassName( 147 /*allNames*/Document.get().getBody().getClassName(), BODY_SCENE )? BODY_SCENE: null; 148 149 150 151 /** Returns the lightable difference for the specified person and poll, or null if 152 * there is none. 153 */ 154 protected abstract D differenceFor( String personName, String pollName ); 155 156 157 158 /** Answers whether the lightable differences of this light are also {@linkplain 159 * DiffLook difference looks} and thus able to be directly staged. 160 */ 161 protected abstract boolean hasDiffLooks(); 162 163 164 165 /** The maximum number of differences ({@value}) that can exist in the vote track. 166 * The number includes up to twenty dart-sectored authors and one mosquito for each 167 * of two boards ({@linkplain VoteTrack#voters() voters} and {@linkplain 168 * VoteTrack#peers() peers}), plus the {@linkplain VoteTrack#candidate() candidate} 169 * and less the {@linkplain VoteTrack#anchor() anchoring author}, versus whose draft 170 * the others are compared. 171 */ 172 public static final int MAX_DIFFS = (20 + 1) * 2 + 1 - 1; 173 174 175 176 /** Redirects the light according to the current light actuator. 177 * 178 * @return the difference associated with the actuator's sensor, or null if there 179 * is none. 180 */ 181 protected LightableDifference redirect() 182 { 183 final LightableDifference diff = actuator.diff(); 184 final String ancClass; 185 final String scnClass; 186 final String ordClass; 187 final String relClass; 188 final String secClass; 189 if( diff == null ) 190 { 191 ancClass = null; 192 scnClass = null; 193 ordClass = null; 194 relClass = null; 195 secClass = null; 196 } 197 else 198 { 199 ancClass = actuator.bodyAncClass(); 200 scnClass = actuator.bodyScnClass(); 201 ordClass = diff.bodyOrdClass(); 202 relClass = diff.bodyRelClass(); 203 secClass = diff.bodySecClass(); 204 } 205 final Element body = Document.get().getBody(); 206 ElementX.replaceNullClassName( ancClassLit, ancClass, body ); 207 ElementX.replaceNullClassName( scnClassLit, scnClass, body ); 208 ElementX.replaceNullClassName( ordClassLit, ordClass, body ); 209 ElementX.replaceNullClassName( relClassLit, relClass, body ); 210 ElementX.replaceNullClassName( secClassLit, secClass, body ); 211 ancClassLit = ancClass; 212 scnClassLit = scnClass; 213 ordClassLit = ordClass; 214 relClassLit = relClass; 215 secClassLit = secClass; 216 return diff; 217 } 218 219 220 221 /** Passes all lightable differences through the specified runner. 222 */ 223 protected abstract void run( LightableDifference.Runner<D> runner ); 224 225 226 227 /** The name of the other author of the scene difference as opposed to the anchor; or 228 * null if no scene difference is set. 229 * 230 * @see #setScene(String,String) 231 */ 232 public static String sceneName() { return sceneName; } 233 234 235 private static String sceneName; 236 237 238 239 /** The {@linkplain LightableDifference style symbol} for the cast relation of the 240 * other author of the scene difference as regarded by the anchor, e.g. {@linkplain 241 * LightableDifference#REL_VOTER REL_VOTER}; or '\u0000' if no scene difference is 242 * set. 243 * 244 * @see #setScene(String,String) 245 */ 246 public static char sceneRel() { return sceneRel; } 247 248 249 private static char sceneRel; 250 251 252 253 /** Specifies a difference that is displayed in the scene. Call this method from the 254 * global configuration function {@linkplain StageMod voGWTConfig.s_gwt_stage} in 255 * this fashion:<pre 256 * 257 *> s_gwt_stage_vote_DifferenceLight_setScene( 'Frank-FlippityNet' ); 258 * // default is null, which lights no default difference</pre> 259 * 260 * A {@linkplain Stage#getDefaultActorName() default actor} and {@linkplain 261 * Stage#getDefaultDifference() difference} should also be set on stage. If those 262 * are set, then the scene difference will be lighted and style class {@value 263 * BODY_SCENE} assigned to the document body whenever either of the two authors is on 264 * stage, while at other times the class will be removed. 265 * 266 * @see #sceneName() 267 * @see #sceneRel() 268 */ 269 public static @GWTConfigCallback void setScene( String _sceneName, 270 String _sceneRel ) 271 { 272 sceneName = _sceneName; 273 if( _sceneRel.length() != 1 ) throw new IllegalArgumentException(); 274 275 sceneRel = _sceneRel.charAt( 0 ); 276 } 277 278 279 private static native void exposeScene() 280 /*-{ 281 $wnd.s_gwt_stage_vote_DifferenceLight_setScene = $entry( 282 @votorola.s.gwt.stage.vote.DifferenceLight::setScene(Ljava/lang/String;Ljava/lang/String;) ); 283 }-*/; 284 285 286 static 287 { 288 assert StageMod.isForcedInit(): "forced init " + DifferenceLight.class.getName(); 289 exposeScene(); 290 } 291 292 293 294 /** The staged instance of the vote track. 295 */ 296 protected final VoteTrack voteTrack() { return voteTrack; } 297 298 299 private final VoteTrack voteTrack; 300 301 302 303 // - L i g h t ------------------------------------------------------------------------ 304 305 306 public Actuator_DL assignActuator( final PositionSensor sensor ) 307 { 308 final Actuator1 actuator = sceneName == null? new Actuator1( sensor ): 309 new ActuatorSD( sensor ); 310 actuator.init(); 311 return actuator; 312 } 313 314 315 316 public final PositionSensor tryCast( final Sensor sensor ) 317 { 318 return sensor instanceof PositionSensor? (PositionSensor)sensor: null; 319 } 320 321 322 323 // ==================================================================================== 324 325 326 /** An actuator for the difference light. 327 */ 328 public static interface Actuator_DL extends Actuator<PositionSensor> 329 { 330 331 /** Completes the creation of this Actuator_DL. Call once only. 332 */ 333 void init(); 334 335 336 // -------------------------------------------------------------------------------- 337 338 339 /** The style class {@value votorola.s.gwt.stage.vote.DifferenceLight#BODY_ANCHOR} 340 * to assign to the document body, or null to assign none. 341 */ 342 String bodyAncClass(); 343 344 345 /** The style class {@value votorola.s.gwt.stage.vote.DifferenceLight#BODY_ANCHOR} 346 * to assign to the document body, or null to assign none. 347 */ 348 String bodyScnClass(); 349 350 351 /** Responds to a change in the assigned sensor's properties. 352 */ 353 public void changed(); 354 355 356 /** The difference currently associated with this actuator's sensor, or null if 357 * there is none. 358 * 359 * @see #differenceFor(String,String) 360 */ 361 LightableDifference diff(); 362 363 364 } 365 366 367 368//// P r i v a t e /////////////////////////////////////////////////////////////////////// 369 370 371 private Actuator_DL actuator; // never null after init, instead set to baseActuator 372 373 374 375 private String ordClassLit; 376 377 378 379 private String relClassLit; 380 381 382 383 private String secClassLit; 384 385 386 387 private LightableDifference syntheticSceneDiff; 388 389 390 391 // ==================================================================================== 392 393 394 private final class ActorSensor implements PositionSensor, PropertyChangeHandler 395 { 396 397 /** Completes the creation of this ActorSensor. Call once only. 398 */ 399 void init( final Stage stage ) 400 { 401 GWTX.i().bus().addHandlerToSource( PropertyChange.TYPE, /*source*/stage, 402 ActorSensor.this ); // no need to unregister, registry does not outlive this listener 403 } 404 405 406 // - P o s i t i o n - S e n s o r ------------------------------------------------ 407 408 409 public String personName() { return Stage.i().getActorName(); } 410 411 412 public String pollName() { return Stage.i().getPollName(); } 413 414 415 // - P r o p e r t y - C h a n g e - H a n d l e r -------------------------------- 416 417 418 public void onPropertyChange( final PropertyChange e ) 419 { 420 final String name = e.propertyName(); 421 if( name.equals("actorName") || name.equals("pollName") ) changed(); 422 } 423 424 425 // - S e n s o r ------------------------------------------------------------------ 426 427 428 public void changed() { baseActuator.changed(); } 429 430 431 public void out() {} // base sensor, not pointer activated 432 433 434 public void over() {} // " 435 436 } 437 438 439 440 // ==================================================================================== 441 442 443 private class Actuator1 implements Actuator_DL 444 { 445 446 /** Partially creates an Actuator1 for {@linkplain #init() init} to finish. 447 */ 448 Actuator1( PositionSensor _sensor ) { sensor = _sensor; } 449 450 451 public final void init() { changed(); } 452 453 454 // -------------------------------------------------------------------------------- 455 456 457 void changedHook() {} 458 459 460 final PositionSensor sensor; 461 462 463 // - A c t u a t o r -------------------------------------------------------------- 464 465 466 public final void changed( PositionSensor _sensor ) { changed(); } 467 468 469 public final Light<PositionSensor> light() { return DifferenceLight.this; } 470 471 472 public final void out( PositionSensor _sensor ) 473 { 474 if( actuator != Actuator1.this ) return; // already out 475 476 actuator = baseActuator; // to default 477 redirect(); 478 } 479 480 481 public void over( PositionSensor _sensor ) 482 { 483 if( diff == null || /*already over*/actuator == Actuator1.this ) return; 484 485 actuator = Actuator1.this; 486 redirect(); 487 } 488 489 490 // - A c t u a t o r - D L ------------------------------------------------------- 491 492 493 public String bodyAncClass() { return null; } 494 495 496 public String bodyScnClass() { return null; } 497 498 499 public void changed() 500 { 501 diff = differenceFor( sensor.personName(), sensor.pollName() ); 502 changedHook(); 503 if( actuator == Actuator1.this ) redirect(); 504 } 505 506 507 public final LightableDifference diff() { return diff; } 508 509 510 LightableDifference diff; 511 512 } 513 514 515 516 // ==================================================================================== 517 518 519 private class ActuatorSD extends Actuator1 // for use when scene difference set 520 { 521 522 /** Partially creates an ActuatorSD for {@linkplain #init() init} to finish. 523 */ 524 ActuatorSD( PositionSensor _sensor ) { super( _sensor ); } 525 526 527 boolean isAnchor; 528 529 530 private String personName; 531 532 533 // - A c t u a t o r -------------------------------------------------------------- 534 535 536 public final @Override void over( PositionSensor _sensor ) 537 { 538 // let redirect set correct bodyScnClass even for non-drafters (null diff) 539 if( personName == null || /*already over*/actuator == ActuatorSD.this ) return; 540 541 actuator = ActuatorSD.this; 542 redirect(); 543 } 544 545 546 // - A c t u a t o r _ D L ------------------------------------------------------- 547 548 549 public final @Override String bodyAncClass() { return bodyAncClass; } 550 551 552 private String bodyAncClass; 553 554 555 public final @Override String bodyScnClass() { return bodyScnClass; } 556 557 558 private String bodyScnClass; 559 560 561 public @Override void changed() 562 { 563 final Stage stage = Stage.i(); 564 565 // isAnchor, bodyScnClass 566 // ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` 567 isAnchor = false; 568 bodyScnClass = null; // unless proven otherwise 569 personName = sensor.personName(); 570 if( personName != null ) 571 { 572 final String pollName = sensor.pollName(); 573 if( pollName != null && pollName.equals( stage.getDefaultPollName() )) 574 { 575 if( personName.equals( stage.getDefaultActorName() )) 576 { 577 isAnchor = true; 578 bodyScnClass = BODY_SCENE; 579 } 580 else if( personName.equals( sceneName )) bodyScnClass = BODY_SCENE; 581 } 582 } 583 584 // bodyAncClass 585 // ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` 586 bodyAncClass = isAnchor? BODY_ANCHOR: null; 587 588 // ` ` ` 589 super.changed(); // calls changedHook before redirecting 590 } 591 592 593 // - A c t u a t o r - 1 ---------------------------------------------------------- 594 595 596 final @Override void changedHook() 597 { 598 // diff 599 // ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` 600 if( diff == null && isAnchor ) 601 { 602 // anchor to light and scene diff set (this being ActuatorSD), but no formal diff 603 if( syntheticSceneDiff == null ) 604 { 605 final DiffLook defaultDiff = Stage.i().getDefaultDifference(); 606 if( defaultDiff == null ) assert false: "default diff set with scene diff"; 607 else syntheticSceneDiff = new LightableDifference1( sceneRel, 608 /*sec, unknown*/-2, defaultDiff.selectand(), sensor.personName(), 609 sensor.pollName() ); 610 } 611 diff = syntheticSceneDiff; 612 } 613 } 614 615 } 616 617 618 619 // ==================================================================================== 620 621 622 private static interface BaseActuator extends Actuator_DL 623 { 624 625 /** Completes the creation of this BaseActuator. Call once only. 626 */ 627 void init( Object linkLighter, Stage stage ); /* Object because "non-static class 628 LinkLighter cannot be referenced from a static context" */ 629 630 631 String actorLinkVariant(); 632 633 } 634 635 636 637 // ==================================================================================== 638 639 640 private final class BaseActuator1 extends Actuator1 implements BaseActuator 641 { 642 643 /** Partially creates a BaseActuator1 for {@linkplain #init(LinkTrackV) init} to 644 * finish. 645 */ 646 BaseActuator1() { super( new ActorSensor() ); } 647 648 649 @SuppressWarnings("unchecked") 650 public final @Override void init( Object _linkLighter, final Stage stage ) 651 { 652 linkLighter = (LinkLighter)_linkLighter; 653 ((ActorSensor)sensor).init( stage ); 654 init(); 655 } 656 657 658 private LinkLighter linkLighter; // final after init 659 660 661 // - A c t u a t o r _ D L ------------------------------------------------------- 662 663 664 public @Override void changed() 665 { 666 super.changed(); 667 linkLighter.schedule(); 668 } 669 670 671 // - B a s e - A c t u a t o r ---------------------------------------------------- 672 673 674 public String actorLinkVariant() 675 { 676 return diff == null? ACTOR_LINK_VARIANT_BASE: diff.actorLinkVariant(); 677 } 678 679 } 680 681 682 683 // ==================================================================================== 684 685 686 private final class BaseActuatorSD extends ActuatorSD implements BaseActuator 687 { 688 689 /** Partially creates a BaseActuatorSD for {@linkplain #init(LinkTrackV) init} to 690 * finish. 691 */ 692 BaseActuatorSD() { super( new ActorSensor() ); } 693 694 695 @SuppressWarnings("unchecked") 696 public final @Override void init( Object _linkLighter, final Stage stage ) 697 { 698 linkLighter = (LinkLighter)_linkLighter; 699 ((ActorSensor)sensor).init( stage ); 700 init(); 701 } 702 703 704 private LinkLighter linkLighter; // final after init 705 706 707 // - A c t u a t o r _ D L ------------------------------------------------------- 708 709 710 public @Override void changed() 711 { 712 super.changed(); 713 linkLighter.schedule(); 714 } 715 716 717 // - B a s e - A c t u a t o r ---------------------------------------------------- 718 719 720 public String actorLinkVariant() 721 { 722 final String v; 723 if( diff == null ) v = ACTOR_LINK_VARIANT_BASE; 724 else if( isAnchor ) 725 { 726 // Actor is anchor. Scene diff is set (this being BaseActuatorSD), so 727 // variant will depend on cast relation (it's not the usual orange-brown 728 // anchor styling). But need to reverse relation because it's expressed 729 // in terms of other person. 730 final String other = diff.actorLinkVariant(); 731 if( other.endsWith( "-voter" )) v = ACTOR_LINK_VARIANT_BASE + "-candidate"; 732 else if( other.endsWith( "-candidate" )) v = ACTOR_LINK_VARIANT_BASE + "-voter"; 733 else if( other.endsWith( "-peer-a" )) v = ACTOR_LINK_VARIANT_BASE + "-peer-b"; 734 else if( other.endsWith( "-peer-b" )) v = ACTOR_LINK_VARIANT_BASE + "-peer-a"; 735 else if( other.endsWith( "-unknown-a" )) v = ACTOR_LINK_VARIANT_BASE + "-unknown-b"; 736 else if( other.endsWith( "-unknown-b" )) v = ACTOR_LINK_VARIANT_BASE + "-unknown-a"; 737 else throw new IllegalStateException(); 738 } 739 else v = diff.actorLinkVariant(); 740 return v; 741 } 742 743 } 744 745 746 747 //// ==================================================================================== 748 // 749 // 750 // private final class FastDeselector implements PropertyChangeHandler 751 // { 752 // 753 // FastDeselector( final Stage stage ) 754 // { 755 // // GWTX.i().bus().addHandlerToSource( PropertyChange.TYPE, /*source*/stage, 756 // // FastDeselector.this ); // no need to unregister, registry does not outlive this listener 757 // } 758 // 759 // 760 // // - P r o p e r t y - C h a n g e - H a n d l e r -------------------------------- 761 // 762 // 763 // public void onPropertyChange( final PropertyChange e ) 764 // { 765 // final CountNodeJS anchor = voteTrack.anchor(); 766 // if( anchor == null ) return; 767 // 768 // final String name = e.propertyName(); 769 // if( name.equals("actorName") || name.equals("pollName") ) // staged position changed ... 770 // { 771 // final Stage s = Stage.i(); 772 // if( anchor.name().equals(s.getActorName()) // ... to anchor 773 // && voteTrack.count().pollName().equals(s.getPollName()) ) 774 // { 775 // if( actuator != baseActuator ) // user is over another sensor 776 // { 777 // // Other sensor may be position control (like NodeV) and user 778 // // may have clicked it just now to deselect and revert to anchor. 779 // // In any case, don't wait for user to move from sensor, but instead 780 // // force an immediate redirect to anchor for sake of feedback: 781 // actuator = baseActuator; // whose ActorSensor is (or will be) at anchor 782 // redirect(); 783 // } 784 // } 785 // } 786 // } 787 // 788 // } 789 //// no longer needed, and redirect no longer without effect on node under mouse 790 791 792 793 // ==================================================================================== 794 795 796 private final class LinkLighter extends CoalescingSchedulerS 797 implements PropertyChangeHandler, Scheduler.ScheduledCommand 798 { 799 800 LinkLighter( BaseActuator _baseActuator, LinkTrackV _linkTrackV, final Stage stage ) 801 { 802 super( Scheduler.get(), CoalescingSchedulerS.DEFERRED ); 803 baseActuator = _baseActuator; 804 linkTrackV = _linkTrackV; 805 init( /*command*/LinkLighter.this ); 806 807 GWTX.i().bus().addHandlerToSource( PropertyChange.TYPE, /*source*/stage, 808 LinkLighter.this ); // no need to unregister, registry does not outlive this listener 809 schedule(); 810 } 811 812 813 private final BaseActuator baseActuator; 814 815 816 private final LinkTrackV linkTrackV; 817 818 819 void relight() 820 { 821 final String v = baseActuator.actorLinkVariant(); 822 linkTrackV.setActorLinkVariant( v ); 823 // if( v == null ) return; // no diff link variant, no need to correct it 824 //// v never null in practice, rather defaults to ACTOR_LINK_VARIANT_BASE 825 826 final Stage stage = Stage.i(); 827 final DiffLook dS = stage.getDifference(); // staged diff 828 if( dS == null || dS == NOMINAL_DIFF 829 || LightableDifference.TYPIFIER.isInstance(dS) ) return; 830 // dS cannot possibly mismatch dL, no need to correct link variant 831 832 final DiffLook dSDefault = stage.getDefaultDifference(); 833 if( dSDefault != null && dSDefault.key().equals(dS.key()) ) 834 { 835 if( sceneName != null ) // dS is scene diff 836 { 837 if( dS.selectand().equals( dSDefault.selectand() )) return; 838 // probably non-drafter; link self targets and will be disabled, no problem 839 840 final @SuppressWarnings("unchecked") BaseActuatorSD baseActuatorSD = 841 (BaseActuatorSD)baseActuator; 842 if( !baseActuatorSD.isAnchor && baseActuator.bodyScnClass() != null ) return; 843 // dL is scene diff too, okay 844 } 845 } 846 final LightableDifference dL = baseActuator.diff(); // lit diff 847 if( dL instanceof DiffLook && DiffLookJS.EQUATOR.equals(dS,(DiffLook)dL) ) return; 848 // dS and dL are same, all's well 849 850 linkTrackV.diffLink().resetVariant(); 851 // Forcefully clear to indicate the link now points to an unlit (older or 852 // newer) difference of the same pair. At least the pair is *expected* to 853 // be the same; DifferenceStager primes the stage according to the rules for 854 // stage.setDifference, which subsequent setters are also expected to obey. 855 } 856 857 858 // - S c h e d u l e r . S c h e d u l e d - C o m m a n d ------------------------ 859 860 861 public final void execute() { relight(); } 862 863 864 // - P r o p e r t y - C h a n g e - H a n d l e r -------------------------------- 865 866 867 public void onPropertyChange( final PropertyChange e ) 868 { 869 if( e.propertyName().equals( "difference" )) schedule(); 870 } 871 872 } 873 874 875}