com.t3.client.ui.tokenpanel.InitiativeListCellRenderer.java Source code

Java tutorial

Introduction

Here is the source code for com.t3.client.ui.tokenpanel.InitiativeListCellRenderer.java

Source

/*
 * Copyright (c) 2014 tabletoptool.com team.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     rptools.com team - initial implementation
 *     tabletoptool.com team - further development
 */
package com.t3.client.ui.tokenpanel;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.Transparency;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.SwingConstants;
import javax.swing.border.Border;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.CellConstraints.Alignment;
import com.jgoodies.forms.layout.FormLayout;
import com.t3.client.AppPreferences;
import com.t3.client.TabletopTool;
import com.t3.client.ui.token.BarTokenOverlay;
import com.t3.client.ui.token.BooleanTokenOverlay;
import com.t3.image.ImageUtil;
import com.t3.model.InitiativeList.TokenInitiative;
import com.t3.model.Token;
import com.t3.swing.ImageLabel;
import com.t3.swing.SwingUtil;
import com.t3.util.GraphicsUtil;
import com.t3.util.ImageManager;

/**
 * This is the renderer that shows a token in the initiative panel.
 * 
 * @author Jay
 */
public class InitiativeListCellRenderer extends JPanel implements ListCellRenderer {

    /*---------------------------------------------------------------------------------------------
     * Instance Variables
     *-------------------------------------------------------------------------------------------*/

    /**
     * The label used to display the current item indicator.
     */
    private final JLabel currentIndicator;

    /**
     * The label used to display the item's name and icon.
     */
    private final JLabel name;

    /**
     * This is the panel showing initiative. It contains the state for display.
     */
    private final InitiativePanel panel;

    /**
     * Used to draw the background of the item.
     */
    private ImageLabel backgroundImageLabel;

    /**
     * The text height for the background image label. Only the text is painted inside, the token remains on the
     * outside,
     */
    private final int textHeight;

    /*---------------------------------------------------------------------------------------------
     * Class Variables
     *-------------------------------------------------------------------------------------------*/

    /**
     * The size of an indicator.
     */
    public static final Dimension INDICATOR_SIZE = new Dimension(18, 16);

    /**
     * The icon for the current indicator.
     */
    public static final Icon CURRENT_INDICATOR_ICON = new ImageIcon(
            InitiativePanel.class.getClassLoader().getResource("com/t3/client/image/currentIndicator.png"));

    /**
     * Border used to show that an item is selected
     */
    public static final Border SELECTED_BORDER = BorderFactory.createLineBorder(Color.BLACK);

    /**
     * Border used to show that an item is not selected
     */
    public static final Border UNSELECTED_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1);

    /**
     * Border used to show that an item is selected
     */
    public static final Border NAME_BORDER = BorderFactory.createEmptyBorder(2, 4, 3, 4);

    /**
     * The size of the ICON shown in the list renderer
     */
    public static final int ICON_SIZE = 50;

    /*---------------------------------------------------------------------------------------------
     * Constructor
     *-------------------------------------------------------------------------------------------*/

    /**
     * Create a renderer for the initiative panel.
     * 
     * @param aPanel
     *            The initiative panel containing view state.
     */
    public InitiativeListCellRenderer(InitiativePanel aPanel) {

        // Set up the panel
        panel = aPanel;
        setLayout(new FormLayout("1px pref 1px pref:grow", "fill:pref"));
        setBorder(SELECTED_BORDER);
        setBackground(Color.WHITE);

        // The current indicator 
        currentIndicator = new JLabel();
        currentIndicator.setPreferredSize(INDICATOR_SIZE);
        currentIndicator.setHorizontalAlignment(SwingConstants.CENTER);
        currentIndicator.setVerticalAlignment(SwingConstants.CENTER);
        add(currentIndicator, new CellConstraints(2, 1));

        // And the name
        name = new NameLabel();
        name.setText("Ty");
        name.setBorder(NAME_BORDER);
        name.setFont(getFont().deriveFont(Font.BOLD));
        textHeight = getFontMetrics(getFont()).getHeight();
        add(name, new CellConstraints(4, 1, CellConstraints.LEFT, CellConstraints.CENTER));
        validate();
    }

    /*---------------------------------------------------------------------------------------------
     * ListCellRenderer Interface Methods
     *-------------------------------------------------------------------------------------------*/

    /**
     * @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean,
     *      boolean)
     */
    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
            boolean cellHasFocus) {

        // Set the background by type
        Token token = null;
        TokenInitiative ti = (TokenInitiative) value;
        if (ti != null)
            token = ti.getToken();
        if (token == null) { // Can happen when deleting a token before all events have propagated
            currentIndicator.setIcon(null);
            name.setText(null);
            name.setIcon(null);
            setBorder(UNSELECTED_BORDER);
            return this;
        } // endif
        backgroundImageLabel = token.isVisible()
                ? token.getType() == Token.Type.NPC ? GraphicsUtil.BLUE_LABEL : GraphicsUtil.GREY_LABEL
                : GraphicsUtil.DARK_GREY_LABEL;
        name.setForeground(Color.BLACK);

        // Show the indicator?
        int currentIndex = panel.getList().getCurrent();
        if (currentIndex >= 0 && ti == panel.getList().getTokenInitiative(currentIndex)) {
            currentIndicator.setIcon(CURRENT_INDICATOR_ICON);
        } else {
            currentIndicator.setIcon(null);
        } // endif

        // Get the name string, add the state if displayed, then get the icon if needed
        boolean initStateSecondLine = panel.isInitStateSecondLine() && panel.isShowInitState();
        String sName = (initStateSecondLine ? "<html>" : "") + ti.getToken().getName();
        if (TabletopTool.getFrame().getInitiativePanel().hasGMPermission() && token.getGMName() != null
                && token.getGMName().trim().length() != 0)
            sName += " (" + token.getGMName().trim() + ")";
        if (panel.isShowInitState() && ti.getState() != null)
            sName += (initStateSecondLine ? "<br>" : " = ") + ti.getState();
        if (initStateSecondLine)
            sName += "</html>";
        Icon icon = null;
        if (panel.isShowTokens()) {
            icon = ti.getDisplayIcon();
            if (icon == null) {
                icon = new InitiativeListIcon(ti);
                ti.setDisplayIcon(icon);
            } // endif
        } // endif
        name.setText(sName);
        name.setIcon(icon);

        // Align it properly
        Alignment alignment = ti.isHolding() ? CellConstraints.RIGHT : CellConstraints.LEFT;
        FormLayout layout = (FormLayout) getLayout();
        layout.setConstraints(name, new CellConstraints(4, 1, alignment, CellConstraints.CENTER));
        if (alignment == CellConstraints.RIGHT) {
            name.setHorizontalTextPosition(SwingConstants.LEFT);
        } else {
            name.setHorizontalTextPosition(SwingConstants.RIGHT);
        } // endif 

        // Selected?
        if (isSelected) {
            setBorder(SELECTED_BORDER);
        } else {
            setBorder(UNSELECTED_BORDER);
        } // endif
        return this;
    }

    /*---------------------------------------------------------------------------------------------
     * NameLabel Inner Class
     *-------------------------------------------------------------------------------------------*/

    /**
     * This label contains the sized background image for the name component.
     * 
     * @author Jay
     */
    public class NameLabel extends JLabel {

        /**
         * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
         */
        @Override
        protected void paintComponent(Graphics g) {
            boolean initStateSecondLine = panel.isInitStateSecondLine() && panel.isShowInitState();
            Dimension s = name.getSize();
            int th = (textHeight + 2) * (initStateSecondLine ? 2 : 1);
            backgroundImageLabel.renderLabel((Graphics2D) g, 0, (s.height - th) / 2, s.width, th);
            super.paintComponent(g);
        }

        /**
         * @see javax.swing.JComponent#getPreferredSize()
         */
        @Override
        public Dimension getPreferredSize() {
            boolean initStateSecondLine = panel.isInitStateSecondLine() && panel.isShowInitState();
            Dimension s = super.getPreferredSize();
            int th = textHeight * (initStateSecondLine ? 2 : 1);
            Insets insets = getInsets();
            if (getIcon() != null)
                th = Math.max(th, getIcon().getIconHeight());
            s.height = th + insets.top + insets.bottom - 4;
            return s;
        }
    }

    /*---------------------------------------------------------------------------------------------
     * InitiativeListIcon Inner Class
     *-------------------------------------------------------------------------------------------*/

    /**
     * An icon that will show a token image and all of the states as needed.
     * 
     * @author Jay
     */
    public class InitiativeListIcon extends ImageIcon {

        /** Bounds sent to the token state */
        private final Rectangle bounds = new Rectangle(0, 0, ICON_SIZE, ICON_SIZE);

        /** The token painted by this icon */
        private final TokenInitiative tokenInitiative;

        /** The image that is displayed when states are not being shown. */
        private Image textTokenImage;

        /** The image that is displayed when states are being shown. */
        private Image stateTokenImage;

        /** Size of the text only token */
        private final int textTokenSize = Math.max(textHeight + 4, 16);

        /**
         * Create the image from the token and then build an icon suitable for displaying state.
         * 
         * @param aTokenInitiative
         *            The initiative item being rendered
         */
        public InitiativeListIcon(TokenInitiative aTokenInitiative) {
            tokenInitiative = aTokenInitiative;
            if (panel.isShowTokenStates()) {
                stateTokenImage = scaleImage();
            } else {
                textTokenImage = scaleImage();
            } // endif
        }

        /**
         * Scale the token's image to fit in the allotted space for text or state painting.
         * 
         * @return The properly scaled image.
         */
        public Image scaleImage() {
            Image image = ImageManager.getImageAndWait(tokenInitiative.getToken().getImageAssetId());
            BufferedImage bi = ImageUtil.createCompatibleImage(getIconWidth(), getIconHeight(),
                    Transparency.TRANSLUCENT);
            Dimension d = new Dimension(image.getWidth(null), image.getHeight(null));
            SwingUtil.constrainTo(d, getIconWidth(), getIconHeight());
            Graphics2D g = bi.createGraphics();
            g.drawImage(image, (getIconWidth() - d.width) / 2, (getIconHeight() - d.height) / 2, d.width, d.height,
                    null);
            setImage(bi);
            return bi;
        }

        /**
         * @see javax.swing.ImageIcon#getIconHeight()
         */
        @Override
        public int getIconHeight() {
            return panel.isShowTokenStates() ? ICON_SIZE : textTokenSize;
        }

        /**
         * @see javax.swing.ImageIcon#getIconWidth()
         */
        @Override
        public int getIconWidth() {
            return panel.isShowTokenStates() ? ICON_SIZE : textTokenSize;
        }

        /**
         * Paint the icon and then the image.
         * 
         * @see javax.swing.ImageIcon#paintIcon(java.awt.Component, java.awt.Graphics, int, int)
         */
        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {

            // Paint the halo if needed
            Token token = tokenInitiative.getToken();
            if (panel.isShowTokenStates() && token.hasHalo()) {
                Graphics2D g2d = (Graphics2D) g;
                Stroke oldStroke = g2d.getStroke();
                Color oldColor = g.getColor();
                g2d.setStroke(new BasicStroke(AppPreferences.getHaloLineWidth()));
                g.setColor(token.getHaloColor());
                g2d.draw(new Rectangle2D.Double(x, y, ICON_SIZE, ICON_SIZE));
                g2d.setStroke(oldStroke);
                g.setColor(oldColor);
            } // endif

            // Paint the icon, is that all that's needed?
            if (panel.isShowTokenStates() && getImage() != stateTokenImage) {
                if (stateTokenImage == null)
                    stateTokenImage = scaleImage();
                setImage(stateTokenImage);
            } else if (!panel.isShowTokenStates() && getImage() != textTokenImage) {
                if (textTokenImage == null)
                    textTokenImage = scaleImage();
                setImage(textTokenImage);
            } // endif
            super.paintIcon(c, g, x, y);
            if (!panel.isShowTokenStates())
                return;

            // Paint all the states
            g.translate(x, y);
            Shape old = g.getClip();
            g.setClip(bounds.intersection(old.getBounds()));
            for (String state : TabletopTool.getCampaign().getTokenStatesMap().keySet()) {
                BooleanTokenOverlay overlay = TabletopTool.getCampaign().getTokenStatesMap().get(state);
                if (overlay == null || !overlay.showPlayer(token, TabletopTool.getPlayer()))
                    continue;
                overlay.paintOverlay((Graphics2D) g, token, bounds, token.hasState(state));
            } // endfor
            for (String bar : TabletopTool.getCampaign().getTokenBarsMap().keySet()) {
                Float barSet = token.getBar(bar);
                BarTokenOverlay overlay = TabletopTool.getCampaign().getTokenBarsMap().get(bar);
                if (overlay == null || !overlay.showPlayer(token, TabletopTool.getPlayer()))
                    continue;
                overlay.paintOverlay((Graphics2D) g, token, bounds, barSet);
            } // endfor            
            g.setClip(old);
            g.translate(-x, -y);
        }
    }
}