Package org.pushingpixels.substance.internal.animation

Source Code of org.pushingpixels.substance.internal.animation.RootPaneDefaultButtonTracker

/*
* Copyright (c) 2005-2010 Substance Kirill Grouchnikov. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*  o Redistributions of source code must retain the above copyright notice,
*    this list of conditions and the following disclaimer.
*    
*  o Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
*    
*  o Neither the name of Substance Kirill Grouchnikov nor the names of
*    its contributors may be used to endorse or promote products derived
*    from this software without specific prior written permission.
*    
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.pushingpixels.substance.internal.animation;

import java.awt.Component;
import java.awt.Container;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;

import javax.swing.*;

import org.pushingpixels.substance.api.SubstanceLookAndFeel;
import org.pushingpixels.trident.Timeline;
import org.pushingpixels.trident.Timeline.RepeatBehavior;
import org.pushingpixels.trident.Timeline.TimelineState;
import org.pushingpixels.trident.callback.UIThreadTimelineCallbackAdapter;

/**
* Tracker for pulsating (default and focused) <code>JButton</code>s. This class
* is <b>for internal use only</b>.
*
* @author Kirill Grouchnikov
*/
public class RootPaneDefaultButtonTracker extends
    UIThreadTimelineCallbackAdapter {
  /**
   * Map (with weakly-referenced keys) of all trackers. For each default
   * button which has not been claimed by GC, we have a tracker (with
   * associated <code>Timer</code>).
   */
  private static WeakHashMap<JButton, RootPaneDefaultButtonTracker> trackers = new WeakHashMap<JButton, RootPaneDefaultButtonTracker>();

  /**
   * Waek reference to the associated button.
   */
  private WeakReference<JButton> buttonRef;

  /**
   * The associated timeline.
   */
  private Timeline timeline;

  /**
   * Simple constructor.
   *
   * @param jbutton
   */
  private RootPaneDefaultButtonTracker(JButton jbutton) {
    // Create weak reference.
    this.buttonRef = new WeakReference<JButton>(jbutton);
    // Create coalesced timer.
    this.timeline = new Timeline(jbutton);
    this.timeline.addCallback(this);
    // Store event handler and initial cycle count.
    RootPaneDefaultButtonTracker.trackers.put(jbutton, this);
  }

  /**
   * Recursively checks whether the specified component or one of its inner
   * components has focus.
   *
   * @param component
   *            Component to check.
   * @return <code>true</code> if the specified component or one of its inner
   *         components has focus, <code>false</code> otherwise.
   */
  private static boolean isInFocusedWindow(Component component) {
    if (component == null) {
      return false;
    }

    // check if the component itself is focus owner
    if (component.isFocusOwner()) {
      return true;
    }

    // recursively find if has focus owner component
    if (component instanceof Container) {
      for (Component comp : ((Container) component).getComponents()) {
        if (isInFocusedWindow(comp)) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * Recursively checks whether the specified component has visible glass
   * pane.
   *
   * @param component
   *            Component to check.
   * @return <code>true</code> if the specified component has visible glass
   *         pane, <code>false</code> otherwise.
   */
  private static boolean hasGlassPane(Component component) {
    if (component == null) {
      return false;
    }
    // check if the component has visible glass pane
    Component glassPane = null;
    if (component instanceof JDialog) {
      glassPane = ((JDialog) component).getGlassPane();
    }
    if (component instanceof JFrame) {
      glassPane = ((JFrame) component).getGlassPane();
    }
    if ((glassPane != null) && (glassPane.isVisible())) {
      return true;
    }

    return false;
  }

  @Override
  public void onTimelineStateChanged(TimelineState oldState,
      TimelineState newState, float durationFraction,
      float timelinePosition) {
    this.onTimelineEvent();
  }

  @Override
  public void onTimelinePulse(float durationFraction, float timelinePosition) {
    this.onTimelineEvent();
  }

  void onTimelineEvent() {
    // get the button and check if it wasn't GC'd
    JButton jButton = this.buttonRef.get();
    if (jButton == null) {
      return;
    }

    if (!jButton.isDisplayable()) {
      timeline.abort();
      return;
    }

    if (RootPaneDefaultButtonTracker.hasGlassPane(jButton
        .getTopLevelAncestor()))
      return;
    if (!isPulsating(jButton)) {
      // has since lost its default status
      RootPaneDefaultButtonTracker tracker = trackers.get(jButton);
      tracker.stopTimer();
      tracker.buttonRef.clear();
      trackers.remove(jButton);
    } else {
      if (!RootPaneDefaultButtonTracker.isInFocusedWindow(jButton
          .getTopLevelAncestor())) {
        // && (!isAttentionDrawingCloseButton(jButton))) {
        // no focus in button window - will restore original (not
        // animated) painting
        RootPaneDefaultButtonTracker.update(jButton);
      } else {
        // check if it's enabled
        if (!jButton.isEnabled()) {
          if (timeline.getState() != TimelineState.SUSPENDED) {
            timeline.suspend();
          }
        } else {
          if (timeline.getState() == TimelineState.SUSPENDED) {
            timeline.resume();
          }
        }
        // if (jButton.isEnabled()) {
        // // increment cycle count for pulsating buttons.
        // long oldCycle = cycles.get(jButton);
        // if (oldCycle == 20) {
        // oldCycle = isAttentionDrawingCloseButton(jButton) ? -80
        // : 0;
        // }
        // cycles.put(jButton, oldCycle + 1);
        // } else {
        // // revert to 0 if it's not enabled
        // if (cycles.get(jButton) != 0) {
        // cycles.put(jButton, (long) 0);
        // }
        // }
      }
    }
    // maybe LAF has changed
    if (SubstanceLookAndFeel.isCurrentLookAndFeel())
      jButton.repaint();
  }

  /**
   * Starts the associated timer.
   */
  private void startTimer() {
    this.timeline.playLoop(RepeatBehavior.REVERSE);
  }

  /**
   * Stops the associated timer.
   */
  private void stopTimer() {
    this.timeline.cancel();
  }

  /**
   * Returns the status of the associated timer.
   *
   * @return <code>true</code> is the associated timer is running,
   *         <code>false</code> otherwise.
   */
  private boolean isRunning() {
    TimelineState state = this.timeline.getState();
    return ((state == TimelineState.PLAYING_FORWARD) || (state == TimelineState.PLAYING_REVERSE));
  }

  /**
   * Updates the state of the specified button which must be a default button
   * in some window. The button state is determined based on focus ownership.
   *
   * @param jButton
   *            Button.
   */
  public static void update(JButton jButton) {
    if (jButton == null)
      return;

    boolean hasFocus = RootPaneDefaultButtonTracker
        .isInFocusedWindow(jButton.getTopLevelAncestor());
    RootPaneDefaultButtonTracker tracker = trackers.get(jButton);
    if (!hasFocus) {
      // remove
      if (tracker == null) {
        return;
      }
      // if (cycles.get(jButton) == 0) {
      // return;
      // }
      // cycles.put(jButton, (long) 0);
      // System.out.println("r::" + trackers.size());
    } else {
      // add
      if (tracker != null) {
        tracker.startTimer();
        return;
      }
      tracker = new RootPaneDefaultButtonTracker(jButton);
      tracker.startTimer();
      trackers.put(jButton, tracker);
      // long initialCycle = isAttentionDrawingCloseButton(jButton) ? -80
      // : 0;
      // cycles.put(jButton, initialCycle);
      // System.out.println("a::" + trackers.size());
    }
  }

  /**
   * Retrieves the current cycle count for the specified button.
   *
   * @param jButton
   *            Button.
   * @return Current cycle count for the specified button.
   */
  public static float getTimelinePosition(JButton jButton) {
    RootPaneDefaultButtonTracker tracker = trackers.get(jButton);
    if (tracker == null)
      return 0;
    return tracker.timeline.getTimelinePosition();
    // Long cycleCount = cycles.get(jButton);
    // if (cycleCount == null) {
    // return 0;
    // }
    // long result = cycleCount.longValue();
    // if (result < 0)
    // result = 0;
    // return result;
  }

  /**
   * Retrieves the animation state for the specified button.
   *
   * @param jButton
   *            Button.
   * @return <code>true</code> if the specified button is being animated,
   *         <code>false</code> otherwise.
   */
  public static boolean isAnimating(JButton jButton) {
    RootPaneDefaultButtonTracker tracker = trackers.get(jButton);
    if (tracker == null) {
      return false;
    }
    return tracker.isRunning();
  }

  /**
   * Returns memory usage.
   *
   * @return Memory usage string.
   */
  static String getMemoryUsage() {
    StringBuffer sb = new StringBuffer();
    sb.append("PulseTracker: \n");
    sb.append("\t" + trackers.size() + " trackers");
    // + cycles.size();
    // + " cycles");
    return sb.toString();
  }

  // /**
  // * Checks whether the specified button is attention-drawing
  // * <code>close</code> button of some internal frame, root pane or desktop
  // * icon.
  // *
  // * @param jButton
  // * Button.
  // * @return <code>true</code> if the specified button is <code>close</code>
  // * button of some internal frame, root pane or desktop icon,
  // * <code>false</code> otherwise.
  // */
  // public static boolean isAttentionDrawingCloseButton(JButton jButton) {
  // if (SubstanceCoreUtilities.isTitleCloseButton(jButton)) {
  // // check if have windowModified property
  // Component comp = jButton;
  // boolean isWindowModified = false;
  // while (comp != null) {
  // if (comp instanceof JInternalFrame) {
  // JInternalFrame jif = (JInternalFrame) comp;
  // isWindowModified = Boolean.TRUE
  // .equals(jif
  // .getClientProperty(SubstanceLookAndFeel.WINDOW_MODIFIED));
  // break;
  // }
  // if (comp instanceof JRootPane) {
  // JRootPane jrp = (JRootPane) comp;
  // isWindowModified = Boolean.TRUE
  // .equals(jrp
  // .getClientProperty(SubstanceLookAndFeel.WINDOW_MODIFIED));
  // break;
  // }
  // if (comp instanceof JDesktopIcon) {
  // JDesktopIcon jdi = (JDesktopIcon) comp;
  // JInternalFrame jif = jdi.getInternalFrame();
  // isWindowModified = Boolean.TRUE
  // .equals(jif
  // .getClientProperty(SubstanceLookAndFeel.WINDOW_MODIFIED));
  // break;
  // }
  // comp = comp.getParent();
  // }
  // if (isWindowModified) {
  // return true;
  // }
  // }
  // return false;
  // }

  /**
   * Checks whether the specified button is pulsating.
   *
   * @param jButton
   *            Button.
   * @return <code>true</code> if the specified button is pulsating,
   *         <code>false</code> otherwise.
   */
  public static boolean isPulsating(JButton jButton) {
    // default button pulsates.
    boolean isDefault = jButton.isDefaultButton();
    if (isDefault) {
      return true;
    }
    return false;
    // attention-drawing close button pulsates.
    // return isAttentionDrawingCloseButton(jButton);
  }

  /**
   * Stops all timers.
   */
  public static void stopAllTimers() {
    for (RootPaneDefaultButtonTracker tracker : trackers.values()) {
      tracker.stopTimer();
    }
  }
}
TOP

Related Classes of org.pushingpixels.substance.internal.animation.RootPaneDefaultButtonTracker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.