Package org.jvnet.substance.utils

Source Code of org.jvnet.substance.utils.PulseTracker

/*
* Copyright (c) 2005-2009 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.jvnet.substance.utils;

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

import javax.swing.*;
import javax.swing.JInternalFrame.JDesktopIcon;

import org.jvnet.substance.SubstanceLookAndFeel;

/**
* Tracker for pulsating (default and focused) <code>JButton</code>s. This class
* is <b>for internal use only</b>.
*
* @author Kirill Grouchnikov
*/
public class PulseTracker implements ActionListener {
  /**
   * 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, PulseTracker> trackers = new WeakHashMap<JButton, PulseTracker>();

  /**
   * Map (with weakly-referenced keys) of cycle counts. For each default
   * button which is shown <b>and </b> is in window that owns focus,
   * <code>this</code> map contains the cycle count (for animation purposes).
   * On each event of the associated <code>Timer</code> (see
   * {@link #actionPerformed(ActionEvent)}), the counter is incremented by 1.
   * For buttons that are in windows that lose focus, the counter is reverted
   * back to 0 (animation stops).
   */
  private static WeakHashMap<JButton, Long> cycles = new WeakHashMap<JButton, Long>();

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

  /**
   * The associated timer.
   */
  private Timer timer;

  /**
   * Simple constructor.
   *
   * @param jbutton
   */
  private PulseTracker(JButton jbutton) {
    // Create weak reference.
    this.buttonRef = new WeakReference<JButton>(jbutton);
    // Create coalesced timer.
    this.timer = new Timer(50, this);
    this.timer.setCoalesce(true);
    // Store event handler and initial cycle count.
    PulseTracker.trackers.put(jbutton, this);
    PulseTracker.cycles.put(jbutton, (long) 0);
  }

  /**
   * 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;
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
   */
  public void actionPerformed(ActionEvent event) {
    // get the button and check if it wasn't GC'd
    JButton jButton = this.buttonRef.get();
    if (jButton == null) {
      return;
    }
    if (PulseTracker.hasGlassPane(jButton.getTopLevelAncestor()))
      return;
    if (!isPulsating(jButton)) {
      // has since lost its default status
      PulseTracker tracker = trackers.get(jButton);
      tracker.stopTimer();
      tracker.buttonRef.clear();
      trackers.remove(jButton);
      cycles.remove(jButton);
    } else {
      if ((!PulseTracker.isInFocusedWindow(jButton.getTopLevelAncestor()))
          && (!isAttentionDrawingCloseButton(jButton))) {
        // no focus in button window - will restore original (not
        // animated) painting
        PulseTracker.update(jButton);
      } else {
        // check if it's enabled
        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() {
    if (!this.timer.isRunning()) {
      this.timer.start();
    }
  }

  /**
   * Stops the associated timer.
   */
  private void stopTimer() {
    if (this.timer.isRunning()) {
      this.timer.stop();
    }
  }

  /**
   * Returns the status of the associated timer.
   *
   * @return <code>true</code> is the associated timer is running,
   *         <code>false</code> otherwise.
   */
  private boolean isRunning() {
    return this.timer.isRunning();
  }

  /**
   * 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) {
    boolean hasFocus = PulseTracker.isInFocusedWindow(jButton
        .getTopLevelAncestor());
    PulseTracker 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 PulseTracker(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 long getCycles(JButton jButton) {
    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) {
    PulseTracker 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;
    }

    // attention-drawing close button pulsates.
    return isAttentionDrawingCloseButton(jButton);
  }

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

Related Classes of org.jvnet.substance.utils.PulseTracker

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.