Package lupos.event.action.send

Source Code of lupos.event.action.send.SlidingWindow

/**
* Copyright (c) 2013, Institute of Information Systems (Sven Groppe and contributors of LUPOSDATE), University of Luebeck
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
*   - Redistributions of source code must retain the above copyright notice, this list of conditions and the following
*     disclaimer.
*   - 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.
*   - Neither the name of the University of Luebeck 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 HOLDER 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 lupos.event.action.send;

import java.awt.Color;
import java.awt.GraphicsEnvironment;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JComboBox;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JWindow;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

import lupos.event.action.send.Send;
import lupos.event.util.data.Handler;

import com.sun.awt.AWTUtilities;

/**
* A toplevel notification box, sliding in vertically from the upper or lower
* left corner, right corner or middle. Capable of displaying plain text, html
* or rtf. Stays for a given amount of time or as long as the mouse is hovering
* over it. Transparency is reduced while the mouse is hovering over it,
* allowing to read it more easily on demand while being less disturbing
* otherwise.
*/
@SuppressWarnings("restriction")
public class SlidingWindow extends JWindow implements ActionListener,
    MouseListener, Send {

  /**
   * Convenience type for easier content type selection of displayed messages.
   */
  public enum ContentType {
    PLAIN_TEXT, HTML, RTF
  }

  /**
   * Origin screen border (defining the side of the screen from which the
   * window slides in).
   */
  public enum ScreenBorder {
    Top, Bottom
  }

  /**
   * Popup position on the origin border (left corner, right corner or in the
   * middle).
   */
  public enum BorderPosition {
    Left, Middle, Right
  }

  /**
   * Animation states.
   */
  private enum AnimState {
    IDLE, SLIDING_OUT, DISPLAYING, SLIDING_IN
  }

  private static final long serialVersionUID = 1239937364810642875L;

  // transparency values if mouse is (not) hovering over the window
  private static final float hoveringTransparecy = 0.95f;
  private static final float normalTransparecy = 0.75f;

  // maximum window size
  private int maxHeight;
  private int maxWidth;

  // start/end height for sliding animation (reset internally based on content
  // size and max window size)
  private int startHeight = 1;
  private int endHeight;

  // animation parameters
  private Timer timer;
  private int slideStep = 1;
  private int slideTime = 1000; // time (in ms) it takes to fully slide out/in
  private int slideDelay; // (dynamically computed) timer delay to ensure the
              // slideTime at the current text size
  private int displayTime = 2000;

  // origin opffset, sliding direction
  private ScreenBorder originBorder = ScreenBorder.Top;
  private BorderPosition originPos = BorderPosition.Right;
  private int posOffset = 100;

  // textcolor is ignored for html/rtf contents
  private Color backgroundcolor = Color.YELLOW;
  private Color textcolor = Color.DARK_GRAY;

  // content type
  ContentType contentType = ContentType.HTML;

  // display components
  private JEditorPane textPane;
  private JScrollPane scrollPane;

  // current states
  private AnimState state = AnimState.IDLE;
  private boolean mouseHovering = false;

  /**
   * Constructor. Sets maximum window width/height to (screenWidth/4) /
   * (screenHeight/2).
   */
  public SlidingWindow() {
    this(-1, -1);
  }

  /**
   * Constructor.
   *
   * @param maxW
   *            Maximum window width.
   * @param maxH
   *            Maximum window height.
   */
  public SlidingWindow(final int maxW, final int maxH) {
    super();

    // we want a toplevel notification window
    setAlwaysOnTop(true);
    getContentPane().setBackground(this.backgroundcolor);

    // ... with a semi-transparent background
    AWTUtilities.setWindowOpacity(this, normalTransparecy);

    // need to display the text somehow ...
    this.textPane = new JEditorPane();
    this.textPane.setContentType("text/html");
    this.textPane.setEditable(false);
    this.textPane.setOpaque(true);
    this.textPane.setBackground(this.backgroundcolor);
    this.textPane.setForeground(this.textcolor);

    this.scrollPane = new JScrollPane(this.textPane,
        ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

    setLayout(new GridLayout(1, 1));
    add(this.scrollPane);

    this.textPane.addMouseListener(this);

    // determine the screen dimensions
    Rectangle screenRect = GraphicsEnvironment
        .getLocalGraphicsEnvironment().getMaximumWindowBounds();

    // set size constraints
    if (maxW > 0) {
      this.maxWidth = maxW;
    } else {
      this.maxWidth = (int) (screenRect.getWidth() / 4);
    }

    if (maxH > 0) {
      this.maxHeight = maxH;
    } else {
      this.maxHeight = (int) (screenRect.getHeight() / 2);
    }

    setSize(this.maxWidth, 1);

    // initialize animation timer
    this.timer = new Timer(100, this);
    this.timer.setInitialDelay(0);

    // hide until exec() is called
    setVisible(false);
  }

  /**
   * Convert a {@link ContentType} value to a content type string which can be
   * handed to {@link JEditorPane#setContentType(String)}. Default (fallback)
   * if contentType is not HTML or RTF: "text/plain".
   *
   * @param contentType_param
   *            {@link ContentType} to be converted.
   * @return The string representing the given {@link ContentType}.
   */
  @SuppressWarnings("incomplete-switch")
  private String contentTypeToStr(final ContentType contentType_param) {
    String contentTypeStr = "text/plain";
    switch (contentType_param) {
    case HTML:
      contentTypeStr = "text/html; charset=UTF-8";
      Handler.install();
      break;
    case RTF:
      contentTypeStr = "text/rtf";
      break;
    }
    return contentTypeStr;
  }

  /**
   * Change position settings.
   *
   * @param border
   *            The {@link ScreenBorder} to slide in from.
   * @param basePos
   *            The basic {@link BorderPosition} on the border.
   * @param offset
   *            A manual offset from the basePos (in px).
   */
  public void setPosition(final ScreenBorder border,
      final BorderPosition basePos, final int offset) {
    this.originBorder = border;
    this.originPos = basePos;
    this.posOffset = offset;
  }

  /**
   * Set the content type, i.e. the way a content string is interpreted and
   * displayed.
   *
   * @param type
   *            {@link ContentType} to be set.
   */
  public void setContentType(final ContentType type) {
    this.contentType = type;
  }

  /**
   * Set the display time, i.e. the amount of time between sliding in and
   * sliding out again.
   *
   * @param ms
   *            Number of milliseconds
   */
  public void setDisplayTime(final int ms) {
    this.displayTime = ms;
  }

  /**
   * Slide out, displaying the given content based on the currently set
   * content type, display time and position parameters.
   *
   * @param content
   * @see #setPosition(ScreenBorder, BorderPosition, int)
   * @see #setContentType(ContentType)
   * @see #setDisplayTime(int)
   */
  public void exec(final String content) {
    exec(content, this.displayTime, this.contentType);
  }

  /**
   * Slide out, display the given content according to the given content type
   * for the specified time (in ms), then sliding in again. Global position
   * parameters are used.
   *
   * @param content
   *            Content to be displayed according to contentType.
   * @param time
   *            Time (in ms) to display the text (time between sliding out and
   *            in again).
   * @param contentType_param
   *            {@link ContentType} telling how the content is to be
   *            interpreted and displayed.
   * @see #setPosition(ScreenBorder, BorderPosition, int)
   */
  public void exec(final String content, final int time,
      final ContentType contentType_param) {
    exec(content, time, contentTypeToStr(contentType_param));
  }

  /**
   * Slide out from a specified border, display the given content according to
   * the given content type for the specified time (int ms), then sliding in
   * again. Overriding all global settings (content type, display time,
   * position paremters).
   *
   * @param content
   *            Content to be displayed according to contentType.
   * @param time
   *            Time (in ms) to display the text (time between sliding out and
   *            in again).
   * @param contentType_param
   *            {@link ContentType}, specifying how the content is to be
   *            interpreted and displayed.
   * @param fromBorder
   *            {@link ScreenBorder} from which to slide in.
   * @param basePos
   *            {@link BorderPosition} on fromBorder.
   * @param positionOffset
   *            Manual offset from basePos (in px).
   */
  public void exec(final String content, final int time,
      final ContentType contentType_param, final ScreenBorder fromBorder,
      final BorderPosition basePos, final int positionOffset) {
    exec(content, time, contentTypeToStr(contentType_param), fromBorder, basePos,
        positionOffset);
  }

  /**
   * Slide out from a specified border, display the given content according to
   * the given content type for the specified time (int ms), then sliding in
   * again. Overriding all global settings (content type, display time,
   * position paremters).
   *
   * @param content
   *            Content to be displayed according to contentType.
   * @param time
   *            Time (in ms) to display the text (time between sliding out and
   *            in again).
   * @param contentType_param
   *            Content type (string) to be used. Currently supported:
   *            "text/plain", "text/html", "text/rtf".
   * @param fromBorder
   *            {@link ScreenBorder} from which to slide in.
   * @param basePos
   *            {@link BorderPosition} on fromBorder.
   * @param positionOffset
   *            Manual offset from basePos (in px).
   */
  public void exec(final String content, final int time,
      final String contentType_param, final ScreenBorder fromBorder,
      final BorderPosition basePos, final int positionOffset) {
    setPosition(fromBorder, basePos, positionOffset);
    exec(content, time, contentType_param);
  }

  /**
   * Slide out, display the given content according to the given content type
   * for the specified time (in ms), then sliding in again. Global position
   * parameters are used.
   *
   * @param content
   *            Content to be displayed.
   * @param time
   *            Time (in ms) to display the text (time between sliding out and
   *            in again).
   * @param contentType_param
   *            String containing the content type, telling how the content is
   *            to be interpreted and displayed.
   * @see #setPosition(ScreenBorder, BorderPosition, int)
   */
  public void exec(final String content, final int time,
      final String contentType_param) {
    // abort if an animation is already running
    if (this.timer.isRunning())
      return;

    // unhide if hidden
    setVisible(true);

    // textPane.setText(text);
    this.displayTime = time;

    // set desired content type
    this.textPane.setContentType(contentType_param);

    // calculate size constraints
    Rectangle screenRect = GraphicsEnvironment
        .getLocalGraphicsEnvironment().getMaximumWindowBounds();

    // initial size to leave as much vertical space as possible for the
    // content
    setSize(this.maxWidth, Short.MAX_VALUE);

    // set the content so we can now obtain the text pane's preferred
    // size
    this.textPane.setText(content);

    // set preferred width, clamping to maxWidth
    int newWidth = (int) Math.min(
        this.textPane.getPreferredSize().getWidth() + 10, this.maxWidth);

    setSize(newWidth, 1);

    // compute final height, clamping to maxHeight
    this.endHeight = Math.min(
        (int) this.textPane.getPreferredSize().getHeight() + 10, this.maxHeight);

    setSize(getWidth(), this.startHeight);

    // determine origin position by origin border
    int xPos = 0;
    int yPos = 0;

    int margin = 3;
    switch (this.originBorder) {
    case Top:
      yPos = (int) screenRect.getY();
      break;

    case Bottom:
      yPos = (int) (screenRect.getY() + screenRect.getHeight());
      break;
    }

    switch (this.originPos) {
    case Left:
      xPos = (int) screenRect.getX() + margin + this.posOffset;
      break;

    case Middle:
      xPos = (int) ((screenRect.getX() + screenRect.getWidth() - getWidth()) * 0.5f)
          + this.posOffset;
      break;

    case Right:
      xPos = (int) (screenRect.getX() + screenRect.getWidth()
          - getWidth() - margin)
          + this.posOffset;
    }
    setLocation(xPos, yPos);

    // start animating
    this.state = AnimState.SLIDING_OUT;
    this.slideDelay = this.slideTime * this.slideStep / this.endHeight;
    this.timer.setDelay(this.slideDelay);
    this.timer.setInitialDelay(this.slideDelay);
    this.timer.restart();
  }

  // simple state machine for animation controlling
  @SuppressWarnings("fallthrough")
  @Override
  public void actionPerformed(ActionEvent e) {
    switch (this.state) {
    case IDLE:
      break;

    case SLIDING_OUT: {
      // when the final height is reached, switch to DISPLAYING
      // mode and show scrollbars if needed
      if (getHeight() >= this.endHeight) {
        SwingUtilities.invokeLater(new Runnable() {
          @Override
          public void run() {
            SlidingWindow.this.timer.stop();
            SlidingWindow.this.state = AnimState.DISPLAYING;

            SlidingWindow.this.scrollPane
                .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
            SlidingWindow.this.scrollPane
                .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);

            SlidingWindow.this.timer.start();
          }
        });
      } else {
        // otherwise, grow by slideStep
        switch (this.originBorder) {
        case Bottom:
          // if coming from the bottom, we have to move the window,
          // too
          this.setLocation(getX(), getY() - this.slideStep);

        case Top:
          this.setSize(getWidth(), getHeight() + this.slideStep);
          break;
        }
      }

      break;
    }

    case DISPLAYING: {
      // restart the timer to fire after the desired displayTime has
      // elapsed, after which the window will slide back in

      SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
          SlidingWindow.this.timer.stop();
          SlidingWindow.this.state = AnimState.SLIDING_IN;
          SlidingWindow.this.timer.setInitialDelay(SlidingWindow.this.displayTime);
          SlidingWindow.this.timer.restart();
        }
      });
      break;
    }

    case SLIDING_IN: {
      // mouse hovering makes the window slide back out
      if (this.mouseHovering) {
        this.state = AnimState.SLIDING_OUT;
      } else {
        // hide scroll bars for visual elegance
        this.scrollPane
            .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        this.scrollPane
            .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);

        // if the minimal height (usually 1px) is reached, stop
        // the timer and hide the window completely
        if (getHeight() <= this.startHeight) {
          SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
              SlidingWindow.this.timer.stop();
              SlidingWindow.this.state = AnimState.IDLE;
              setVisible(false);
            }
          });
        } else {
          // otherwise, shrink by slideStep
          switch (this.originBorder) {
          case Bottom:
            // if coming from the bottom, we have to move the
            // window, too
            this.setLocation(getX(), getY() + this.slideStep);

          case Top:
            this.setSize(getWidth(), getHeight() - this.slideStep);
            break;
          }
        }
      }
      break;
    }
    }
  }

  // ---------------------
  // MouseListener methods
  // ---------------------
  @Override
  public void mouseClicked(MouseEvent e) {
    // ignore...
  }

  // watch for mouseEntered/mosueExited events, so we know when the mouse is
  // hovering over the window and adjust its transparency accordingly
  @Override
  public void mouseEntered(MouseEvent e) {
    this.mouseHovering = true;

    // ... with a still semi-transparent, but more opaque background
    AWTUtilities.setWindowOpacity(this, hoveringTransparecy);
  }

  @Override
  public void mouseExited(MouseEvent e) {
    this.mouseHovering = false;

    // ... with a semi-transparent background, a little less opaque
    AWTUtilities.setWindowOpacity(this, normalTransparecy);
  }

  @Override
  public void mousePressed(MouseEvent e) {
    // ignore
  }

  @Override
  public void mouseReleased(MouseEvent e) {
    // ignore
  }

  // ------------
  // Send methods
  // ------------
  @Override
  public void init() {
    // show an option pane to let the user choose the origin border,
    // position, offset, content type and display time
    final String[] strBorders = { "Top", "Bottom" };
    final String[] strPositions = { "Left", "Middle", "Right" };
    final String[] strContentTypes = { "Plain Text", "HTML", "RTF" };

    JComboBox cbBorder = new JComboBox(strBorders);
    JComboBox cbPosition = new JComboBox(strPositions);
    JSpinner spOffset = new JSpinner();

    JComboBox cbContentType = new JComboBox(strContentTypes);

    JSpinner spDisplayTime = new JSpinner(new SpinnerNumberModel(2500, 100,
        Integer.MAX_VALUE, 100));

    JPanel mainPanel = new JPanel();
    mainPanel.setLayout(new GridLayout(0, 2));

    mainPanel.add(new JLabel("Origin Border:"));
    mainPanel.add(cbBorder);

    mainPanel.add(new JLabel("Position:"));
    mainPanel.add(cbPosition);

    mainPanel.add(new JLabel("Position Offset:"));
    mainPanel.add(spOffset);

    mainPanel.add(new JLabel());
    mainPanel.add(new JLabel());

    mainPanel.add(new JLabel("Content Type:"));
    mainPanel.add(cbContentType);

    mainPanel.add(new JLabel());
    mainPanel.add(new JLabel());

    mainPanel.add(new JLabel("Display Time:"));
    mainPanel.add(spDisplayTime);

    JOptionPane.showMessageDialog(null, mainPanel,
        "Sliding window settings:", JOptionPane.OK_OPTION);

    // obtain actual values from selected indexes
    ScreenBorder border = ScreenBorder.Top;
    switch (cbBorder.getSelectedIndex()) {
    case 1:
      border = ScreenBorder.Bottom;
      break;
    }

    BorderPosition pos = BorderPosition.Left;
    switch (cbPosition.getSelectedIndex()) {
    case 1:
      pos = BorderPosition.Middle;
      break;
    case 2:
      pos = BorderPosition.Right;
      break;
    }
    int offset = (Integer) spOffset.getValue();

    ContentType type = ContentType.PLAIN_TEXT;
    switch (cbContentType.getSelectedIndex()) {
    case 1:
      type = ContentType.HTML;
      break;
    case 2:
      type = ContentType.RTF;
      break;
    }

    int time = (Integer) spDisplayTime.getValue();

    // apply settings
    setPosition(border, pos, offset);
    setContentType(type);
    setDisplayTime(time);
  }

  @Override
  public void sendContent(String content) {
    exec(content);
  }
}
TOP

Related Classes of lupos.event.action.send.SlidingWindow

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.