org.eclipse.birt.chart.device.swing.SwingEventHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.birt.chart.device.swing.SwingEventHandler.java

Source

/***********************************************************************
 * Copyright (c) 2004 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Actuate Corporation - initial API and implementation
 ***********************************************************************/

package org.eclipse.birt.chart.device.swing;

import java.awt.Cursor;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.DefaultButtonModel;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

import org.apache.commons.codec.binary.Base64;
import org.eclipse.birt.chart.device.ICallBackNotifier;
import org.eclipse.birt.chart.device.IUpdateNotifier;
import org.eclipse.birt.chart.device.extension.i18n.Messages;
import org.eclipse.birt.chart.device.util.DeviceUtil;
import org.eclipse.birt.chart.event.StructureSource;
import org.eclipse.birt.chart.log.ILogger;
import org.eclipse.birt.chart.log.Logger;
import org.eclipse.birt.chart.model.attribute.ActionType;
import org.eclipse.birt.chart.model.attribute.ActionValue;
import org.eclipse.birt.chart.model.attribute.CallBackValue;
import org.eclipse.birt.chart.model.attribute.CursorType;
import org.eclipse.birt.chart.model.attribute.EmbeddedImage;
import org.eclipse.birt.chart.model.attribute.MultiURLValues;
import org.eclipse.birt.chart.model.attribute.TooltipValue;
import org.eclipse.birt.chart.model.attribute.TriggerCondition;
import org.eclipse.birt.chart.model.attribute.URLValue;
import org.eclipse.birt.chart.model.data.Action;
import org.eclipse.birt.chart.render.InteractiveRenderer;
import org.eclipse.birt.chart.util.SecurityUtil;
import org.eclipse.emf.common.util.EList;

import com.ibm.icu.util.ULocale;

/**
 * Provides a reference implementation into handling events generated on a SWING
 * JComponent with a rendered chart.
 */
public final class SwingEventHandler implements MouseListener, MouseMotionListener, KeyListener, FocusListener {

    private static ILogger logger = Logger.getLogger("org.eclipse.birt.chart.device.extension/swing"); //$NON-NLS-1$

    private Action acTooltip = null;

    // private ShapedAction saHighlighted = null;

    private final Map<TriggerCondition, List<ShapedAction>> lhmAllTriggers;

    private final IUpdateNotifier iun;

    private final ULocale lcl;

    private InteractiveRenderer iv = null;

    private StructureSource srcHighlight;

    private StructureSource srcToggleDataPoint;

    private StructureSource srcToggleVisibility;

    private JPopupMenu popupMenu;

    /**
     * The constructor.
     * 
     * @param _lhmAllTriggers
     * @param _jc
     * @param _lcl
     */
    SwingEventHandler(InteractiveRenderer iv, Map<TriggerCondition, List<ShapedAction>> _lhmAllTriggers,
            IUpdateNotifier _jc, ULocale _lcl) {
        lhmAllTriggers = _lhmAllTriggers;
        iun = _jc;
        lcl = _lcl;
        this.iv = iv;
    }

    private final boolean isLeftButton(MouseEvent e) {
        return (e.getButton() == MouseEvent.BUTTON1);
    }

    private final ShapedAction getShapedActionForConditionPoint(TriggerCondition[] tca, Point p) {
        if (tca == null || tca.length == 0) {
            return null;
        }
        for (int i = 0; i < tca.length; i++) {
            List<ShapedAction> tal = lhmAllTriggers.get(tca[i]);

            if (tal != null) {
                // iterate backwards to get the latest painted shape
                for (int j = tal.size() - 1; j >= 0; j--) {
                    ShapedAction sa = tal.get(j);
                    if (p == null || sa.getShape().contains(p)) {
                        return sa;
                    }
                }

            }
        }

        return null;
    }

    private void handleAction(TriggerCondition[] tg, ComponentEvent event) {
        handleAction(tg, event, true);
    }

    private synchronized void handleAction(TriggerCondition[] tg, ComponentEvent event, boolean cleanState) {
        if (tg == null || event == null) {
            return;
        }
        Point p = null;

        if (event instanceof MouseEvent) {
            p = ((MouseEvent) event).getPoint();
        }

        if (event instanceof KeyEvent) {
            // TODO filter key ?
        }

        ShapedAction sa = getShapedActionForConditionPoint(tg, p);
        if (sa == null) {
            // If the event matches a trigger, disable its previous action.
            for (int i = 0; i < tg.length; i++) {
                if (lhmAllTriggers.get(tg[i]) != null)
                    disableActions(getActionTypesForConditions(tg));
            }
            return;
        }

        final StructureSource src = sa.getSource();
        Action ac = null;
        for (int i = 0; i < tg.length; i++) {
            ac = sa.getActionForCondition(tg[i]);
            if (ac != null)
                break;
        }
        if (ac == null) {

            return;
        }

        switch (ac.getType().getValue()) {
        case ActionType.URL_REDIRECT:
            ActionValue av = ac.getValue();
            if (av instanceof URLValue) {
                final URLValue uv = (URLValue) ac.getValue();
                openURL(uv);
            } else if (av instanceof MultiURLValues) {
                MultiURLValues muv = (MultiURLValues) av;
                int size = muv.getURLValues().size();
                if (size == 0) {
                    break;
                } else if (size == 1) {
                    openURL(muv.getURLValues().get(0));
                } else {
                    // Create a popup menu and show it for multiple hyperlinks.
                    openMultiULRs(muv, p);
                }
            }

            break;

        case ActionType.SHOW_TOOLTIP:

            if (ac != acTooltip) {
                hideTooltip();
            }

            acTooltip = ac;
            showTooltip(ac);
            break;

        case ActionType.TOGGLE_VISIBILITY:

            srcToggleVisibility = handleGraphicAction(src, srcToggleVisibility,
                    ActionType.TOGGLE_VISIBILITY_LITERAL, cleanState);

            break;

        case ActionType.TOGGLE_DATA_POINT_VISIBILITY:

            srcToggleDataPoint = handleGraphicAction(src, srcToggleDataPoint,
                    ActionType.TOGGLE_DATA_POINT_VISIBILITY_LITERAL, cleanState);

            break;

        case ActionType.HIGHLIGHT:

            srcHighlight = handleGraphicAction(src, srcHighlight, ActionType.HIGHLIGHT_LITERAL, cleanState);

            break;

        case ActionType.CALL_BACK:
            if (iun instanceof ICallBackNotifier) {
                final CallBackValue cv = (CallBackValue) ac.getValue();
                ((ICallBackNotifier) iun).callback(event, sa.getSource(), cv);
            } else {
                logger.log(ILogger.WARNING, Messages.getString("SwingEventHandler.info.improper.callback.notifier", //$NON-NLS-1$
                        new Object[] { iun }, lcl));
            }
            break;
        }

    }
    // if the event was fired without new action disable the previous action

    /**
     * URLItemModel
     */
    static class URLMenuItemModel extends DefaultButtonModel {

        private URLValue fUrlValue;

        void setURLValue(URLValue uv) {
            fUrlValue = uv;
        }

        URLValue getURLValue() {
            return fUrlValue;
        }
    }

    /**
     * @param muv
     * @param point
     */
    private void openMultiULRs(MultiURLValues muv, Point point) {
        if (popupMenu != null && popupMenu.isValid()) {
            // Remove previous menu object.
            popupMenu.setVisible(false);
            ((JComponent) iun.peerInstance()).remove(popupMenu);
        }

        popupMenu = new JPopupMenu();

        // Create popup menu items.
        for (URLValue uv : muv.getURLValues()) {
            JMenuItem menuItem = new JMenuItem();
            popupMenu.add(menuItem);
            menuItem.setText(uv.getLabel().getCaption().getValue());
            if (uv.getTooltip() != null && uv.getTooltip().length() > 0)
                menuItem.setToolTipText(uv.getTooltip());
            URLMenuItemModel uim = new URLMenuItemModel();
            uim.setURLValue(uv);
            menuItem.setModel(uim);
            menuItem.addActionListener(new ActionListener() {

                public void actionPerformed(ActionEvent e) {
                    URLValue urlValue = ((URLMenuItemModel) ((JMenuItem) e.getSource()).getModel()).getURLValue();
                    openURL(urlValue);
                }
            });

        }

        // Show menu.
        popupMenu.show((JComponent) iun.peerInstance(), point.x, point.y);
    }

    /**
     * @param uv
     */
    private void openURL(final URLValue uv) {
        logger.log(ILogger.INFORMATION, Messages.getString("SwingEventHandler.info.redirect.url", lcl) //$NON-NLS-1$
                + uv.getBaseUrl());
        DeviceUtil.openURL(uv.getBaseUrl());
    }

    private void disableActions(Set<ActionType> actions) {
        if (actions == null)
            return;

        for (Iterator<ActionType> iter = actions.iterator(); iter.hasNext();) {
            ActionType action = iter.next();
            if (action == null)
                continue;
            switch (action.getValue()) {
            case ActionType.SHOW_TOOLTIP:
                if (acTooltip != null) {
                    hideTooltip();
                    acTooltip = null;
                }
                break;
            case ActionType.HIGHLIGHT:
                if (srcHighlight != null) {
                    iv.unregisterAction(srcHighlight, ActionType.HIGHLIGHT_LITERAL);
                    srcHighlight = null;
                    iun.repaintChart();
                }
                break;
            case ActionType.TOGGLE_DATA_POINT_VISIBILITY:
                if (srcToggleDataPoint != null) {
                    iv.unregisterAction(srcToggleDataPoint, ActionType.TOGGLE_DATA_POINT_VISIBILITY_LITERAL);
                    srcToggleDataPoint = null;
                    iun.repaintChart();
                }
                break;
            case ActionType.TOGGLE_VISIBILITY:
                if (srcToggleVisibility != null) {
                    iv.unregisterAction(srcToggleVisibility, ActionType.TOGGLE_VISIBILITY_LITERAL);
                    srcToggleVisibility = null;
                    iun.repaintChart();
                }
                break;
            }
        }
    }

    private StructureSource handleGraphicAction(StructureSource src, StructureSource previousSrc,
            ActionType actionType, boolean cleanState) {
        if (previousSrc == null) {
            previousSrc = src;
            iv.registerAction(src, actionType);
            iun.repaintChart();
        } else if (!iv.getSource(src).equals(iv.getSource(previousSrc))) {
            if (actionType == ActionType.HIGHLIGHT_LITERAL) {
                // unhighlight previous region
                iv.unregisterAction(previousSrc, actionType);
                previousSrc = src;
                iv.registerAction(src, actionType);
            } else {
                previousSrc = src;
                // Register action only once.
                if (iv.isRegisteredAction(src, actionType)) {
                    iv.unregisterAction(src, actionType);
                } else {
                    iv.registerAction(src, actionType);
                }
            }
            iun.repaintChart();
        } else {
            if (cleanState) {
                iv.unregisterAction(previousSrc, actionType);
                previousSrc = null;
                iun.repaintChart();
            }
        }
        return previousSrc;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
     */
    public void mouseClicked(MouseEvent e) {
        if (isLeftButton(e)) {
            handleAction(new TriggerCondition[] { TriggerCondition.MOUSE_CLICK_LITERAL,
                    TriggerCondition.ONCLICK_LITERAL }, e);
        } else {
            handleAction(new TriggerCondition[] { TriggerCondition.ONRIGHTCLICK_LITERAL }, e);
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
     */
    public void mouseEntered(MouseEvent e) {
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
     */
    public void mouseExited(MouseEvent e) {
        // FILTER OUT ALL TRIGGERS FOR MOUSE OUT ONLY
        handleAction(new TriggerCondition[] { TriggerCondition.ONMOUSEOUT_LITERAL }, e);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
     */
    public void mousePressed(MouseEvent e) {
        if (!isLeftButton(e)) {
            return;
        }

        handleAction(new TriggerCondition[] { TriggerCondition.ONMOUSEDOWN_LITERAL }, e);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
     */
    public void mouseReleased(MouseEvent e) {
        if (!isLeftButton(e)) {
            return;
        }

        handleAction(new TriggerCondition[] { TriggerCondition.ONMOUSEUP_LITERAL }, e);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
     */
    public void mouseDragged(MouseEvent e) {
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
     */
    public void mouseMoved(MouseEvent e) {
        final Point p = e.getPoint();

        // 1. CHECK FOR MOUSE-CLICK TRIGGERS
        ShapedAction sa = getShapedActionForConditionPoint(
                new TriggerCondition[] { TriggerCondition.MOUSE_CLICK_LITERAL, TriggerCondition.ONCLICK_LITERAL,
                        TriggerCondition.ONMOUSEDOWN_LITERAL, TriggerCondition.ONMOUSEOVER_LITERAL,
                        TriggerCondition.ONMOUSEMOVE_LITERAL },
                p);

        if (sa != null) {
            setCursor((JComponent) iun.peerInstance(), sa.getCursor(), Cursor.getDefaultCursor());
        } else {
            setCursor((JComponent) iun.peerInstance(), null, Cursor.getDefaultCursor());
        }

        // 2. CHECK FOR MOUSE-HOVER CONDITION

        handleAction(new TriggerCondition[] { TriggerCondition.MOUSE_HOVER_LITERAL,
                TriggerCondition.ONMOUSEMOVE_LITERAL, TriggerCondition.ONMOUSEOVER_LITERAL }, e, false);
    }

    public void focusGained(FocusEvent e) {
        handleAction(new TriggerCondition[] { TriggerCondition.ONFOCUS_LITERAL }, e);
    }

    public void focusLost(FocusEvent e) {
        handleAction(new TriggerCondition[] { TriggerCondition.ONBLUR_LITERAL }, e);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
     */
    public void keyPressed(KeyEvent e) {
        handleAction(new TriggerCondition[] { TriggerCondition.ONKEYDOWN_LITERAL }, e);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
     */
    public void keyReleased(KeyEvent e) {
        handleAction(
                new TriggerCondition[] { TriggerCondition.ONKEYUP_LITERAL, TriggerCondition.ONKEYPRESS_LITERAL },
                e);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
     */
    public void keyTyped(KeyEvent e) {
    }

    private final void hideTooltip() {
        ((JComponent) iun.peerInstance()).setToolTipText(null);
    }

    private final void showTooltip(Action ac) {
        TooltipValue tv = (TooltipValue) ac.getValue();

        if (tv.getText() == null) {
            return;
        }
        // Handle character conversion of \n in the tooltip
        String tooltip = tv.getText().replaceAll("\\\n", "<br>"); //$NON-NLS-1$//$NON-NLS-2$
        if (!tooltip.equals(tv.getText())) {
            tooltip = "<html>" + tooltip + "</html>"; //$NON-NLS-1$ //$NON-NLS-2$
        } else {
            tooltip = tv.getText();
        }
        ((JComponent) iun.peerInstance()).setToolTipText(tooltip);
    }

    protected Set<ActionType> getActionTypesForConditions(TriggerCondition[] tca) {
        if (tca == null || tca.length == 0) {
            return null;
        }
        Set<ActionType> set = new HashSet<ActionType>();

        for (int i = 0; i < tca.length; i++) {
            List<ShapedAction> tal = lhmAllTriggers.get(tca[i]);
            if (tal == null)
                continue;
            for (Iterator<ShapedAction> iter = tal.iterator(); iter.hasNext();) {
                ShapedAction sa = iter.next();
                ActionType actionType = sa.getActionForCondition(tca[i]).getType();
                set.add(actionType);
            }
        }
        return set;

    }

    private void setCursor(JComponent composite, org.eclipse.birt.chart.model.attribute.Cursor cursor,
            Cursor defaultCursor) {
        if (cursor == null || cursor.getType() == CursorType.AUTO) {
            composite.setCursor(defaultCursor);
            return;
        } else if (cursor.getType() == CursorType.CUSTOM) {
            // Find the first valid image as custom cursor.
            EList<org.eclipse.birt.chart.model.attribute.Image> uris = cursor.getImage();
            for (org.eclipse.birt.chart.model.attribute.Image uri : uris) {
                try {
                    Image image = null;
                    if (uri instanceof EmbeddedImage) {
                        try {
                            byte[] data = Base64.decodeBase64(((EmbeddedImage) uri).getData().getBytes());

                            image = new ImageIcon(data).getImage();
                        } catch (Exception ilex) {
                            logger.log(ilex);
                        }
                    } else {
                        URI u = new URI(uri.getURL());
                        image = composite.getToolkit().createImage(SecurityUtil.toURL(u));
                    }

                    if (image != null) {
                        composite.setCursor(composite.getToolkit().createCustomCursor(image, new Point(0, 0), ""));//$NON-NLS-1$
                        return;
                    }
                } catch (URISyntaxException e) {
                    // Do not process exception here.
                } catch (MalformedURLException e) {
                    // Do not process exception here.
                }
            }

            // No valid image is found, set default cursor.
            composite.setCursor(defaultCursor);
            return;
        }

        composite.setCursor(Cursor.getPredefinedCursor(SwingHelper.CURSOR_MAP.get(cursor.getType()).intValue()));
    }
}