Package jsynoptic.builtin

Source Code of jsynoptic.builtin.TextShape

/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info:  http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2003, by :
*     Corporate:
*         Astrium SAS
*         EADS CRC
*     Individual:
*         Nicolas Brodu
*         Jean-Baptiste Lièvremont
*
* $Id: TextShape.java,v 1.67 2009/01/19 17:24:08 ogor Exp $
*
* Changes
* -------
* 13-Oct-2003 : Initial version (NB);
*
*/
package jsynoptic.builtin;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ResourceBundle;
import java.util.TimeZone;
import java.util.Vector;

import javax.swing.undo.CompoundEdit;

import jsynoptic.base.DataSourceConsumer;
import jsynoptic.builtin.ui.TextPropertiesPanel;
import jsynoptic.ui.JSynoptic;
import jsynoptic.ui.JSynopticBatch;
import simtools.data.DataException;
import simtools.data.DataInfo;
import simtools.data.DataSource;
import simtools.data.DataSourcePool;
import simtools.data.EndNotificationListener;
import simtools.data.UnsupportedOperation;
import simtools.diagram.DiagramSelection;
import simtools.shapes.AbstractShape;
import simtools.shapes.undo.PropertyChangeEdit;
import simtools.ui.ColorMapper;
import simtools.ui.CustomizedLocale;
import simtools.ui.FontChooserPanel;
import simtools.ui.GenericMapper;
import simtools.ui.JPropertiesPanel;
import simtools.ui.MapperListener;
import simtools.ui.ResourceFinder;
import simtools.ui.TextMapper;
import simtools.util.PrintfFormat;

/**
* This shape displays some texts, and fits it to its dimensions.
*/
public class TextShape extends Abstract2DShape implements MapperListener,
        DataSourceConsumer {
    static final long serialVersionUID = -6782484932282488103L;

    public static ResourceBundle resources = ResourceFinder.get(TextShape.class);

    public static final int FORMAT_STRING = 0;

    public static final int FORMAT_DECIMAL = 1;

    public static final int FORMAT_SCIENTIFIC = 2;

    public static final int FORMAT_HEXADECIMAL = 3;

    public static final int FORMAT_OCTAL = 4;

    public static final int FORMAT_BINARY = 5;

    public static final int FORMAT_MAPPER = 6;

    public static final int FORMAT_DATE = 7;

    public static final int FORMAT_TIME = 8;

    public static final int FORMAT_DATE_TIME = 9;

    public static final int FORMAT_PRINTF = 10;

    public static final int FORMAT_USE_DATA_SOURCE = 11;

    public static final int MIN_WIDTH = 40;

    public static final int MIN_HEIGHT = 20;

    public static final int MARGINX = 5;

    public static final int MARGINY = 1;

    // Default values (can be overwriten using user file configuration)
    public static boolean DEFAULT_FIXED_FONT = true;

    public static boolean DEFAULT_FIXED_FRAME = false;

    public static int DEFAUT_CHAR_NUMBER = 5;

    static public int DEFAULT_TEXT_SIZE = 36;

    static public String DEFAULT_TEXT_FONT = "Monospaced";

    static public int DEFAULT_FORMAT = FORMAT_STRING;

    static public boolean DEFAULT_DISPLAY_NAME = false;

    static public boolean DEFAULT_DISPLAY_VALUE = true;

    static public boolean DEFAULT_DISPLAY_UNIT = false;

    static public boolean DEFAULT_DISPLAY_COMMENTS = false;

    static public  String dateFormatPattern = "MM/dd/yyyy";

    static public  String shortTimeFormatPattern = "HH:mm:ss.SSS";

    static public  String longTimeFormatPattern = "MM/dd/yyyy HH:mm:ss.SSS";

    protected int format;

    protected String printfFormat = null;

    protected transient PrintfFormat printfFormater = null;

    protected NumberFormat decimalFormatter;

    protected NumberFormat scientificFormatter;

    public static boolean groupTextDigits = false;

    /**
     * @deprecated
     */
    private DateFormat dateFormatter = null; // no more used : backward

    // compatibility
    /**
     * @deprecated
     */
    private DateFormat timeFormatter = null; // no more used : backward

    // compatibility
    protected DateFormat dateTimeFormatter;

    public static String timeZone;

    protected TextMapper mapper;

    /**
     * @deprecated use printf format instead
     */
    protected String displayPattern = null;

    protected Boolean displayDsName;

    protected Boolean displayDsValue;

    protected Boolean displayDsUnit;

    protected Boolean displayDsComments;

    protected String text;

    protected transient DataSource source;

    protected transient long sourceIndex;

    protected Color textColor = Color.black;

    protected transient Color textDynamicColor = null;

    protected ColorMapper textMapper;

    protected transient DataSource textMapperSource;

    protected transient long textMapperIndex;

    protected static Object referenceMutex = new Object(); // used to

    // synchronize when
    // defining
    // reference fields
    // in the first
    // constructor
    protected static Graphics referenceGraphics = null;

    // We keep _x and _y to 0 so the frame around the text field can be aligned
    // on the grid
    // But we need a delta for the text origin
    protected transient int baseline;

    // Do fitText() on reload => get baseline and font
    protected transient Font currentFont;

    protected transient FontMetrics referenceFontMetrics = null;

    protected transient Font referenceFont = null;

    // For data source notifications
    protected transient boolean dirtyText = false;

    protected transient boolean dirtyColor = false;

    protected transient boolean dirtyState = false;

    protected String fontName;

    protected int fontSize;

    protected boolean boldFont;

    protected boolean italicFont;

    /**
     * When the font is locked, the text never auto-adjusts to the size
     *
     * Default is true
     */
    protected boolean lockedFont;

    /**
     * When the frame is locked, - the text never auto-adjusts to the size - The
     * frame is determinated according to a number of characters to display, and
     * cannot be rezized
     *
     * Default is false
     */
    protected boolean lockedFrame;

    /**
     * When frame is locked, number of charcter to dispay in frame
     */
    protected Integer charNumber;

    /**
     * When margin is enabled, the text is not framed directly with the bounding
     * box, but has margins with MARGINX and MARGINY values Default is true
     */
    protected boolean marginEnabled = true;

    /**
     * when the margin is not enabled, it is necessary to multiply the size of
     * the font in Y to remove the line spacing, so that the text can be framed
     * directly with the bounding box
     */
    protected transient final double fontYScaleFactor = 1.34;
    /** used to initialize the reference fields at class creation */
    static {
        Component c;
        if (JSynoptic.gui != null) {
            c = JSynoptic.gui.getOwner();
        } else if (JSynopticBatch.batch != null) {
            c = (Component) JSynopticBatch.batch.getPrintable(0);
        } else {
            c = null;
        }
        Graphics g = (c != null) ? c.createImage(100, 100).getGraphics() : new BufferedImage(100, 100,
                BufferedImage.TYPE_INT_RGB).createGraphics();
        referenceGraphics = g;
    }

    /**
     * @param printfFormat
     *            the string value to apply to printf format
     */
    public void setPrintfFormat(String printfFormat) {
        this.printfFormat = printfFormat;
        if (printfFormat != null) {
            try {
                printfFormater = new PrintfFormat(printfFormat);
                return;
            } catch (IllegalArgumentException e) {
            }
        }
        printfFormater = null;
    }

    /**
     * @param format
     */
    protected void setFormat(int f) {
        format = f;
        if ((format < 0) || ((format == FORMAT_MAPPER) && (mapper == null))) {
            format = FORMAT_STRING;
        } else if (format == FORMAT_DATE) {
            dateTimeFormatter = new SimpleDateFormat(dateFormatPattern);
        } else if (format == FORMAT_TIME) {
            dateTimeFormatter = new SimpleDateFormat(shortTimeFormatPattern);
        } else if (format == FORMAT_DATE_TIME) {
            dateTimeFormatter = new SimpleDateFormat(longTimeFormatPattern);
        }
        if ((format == FORMAT_DATE) || (format == FORMAT_TIME) || (format == FORMAT_DATE_TIME)) {
            dateTimeFormatter.setTimeZone(TimeZone.getTimeZone(timeZone));
        }
    }

    /**
     * @return the current text displayed by this shape
     */
    public String getText() {
        return text;
    }

    /**
     * Set current text. In case frame is locked, keep only specifed first
     * characters
     *
     * @param text
     */
    public void setText(String text) {
        this.text = text;
    }

    protected Object getSourceValue() throws DataException {
        if (source != null) {
            return source.getValue(sourceIndex);
        }
        return null;
    }

    /**
     * Gets a new text value for this shape. The current formatter will be used
     * if the object is a Number
     *
     * @param o.
     *            Any object. If this is a Number, it will be
     *            formatted.Otherwise, toString() is used.
     * @return the computed new text
     */
    public String formatSourceValue(Object o) {
        String ret;
        if ((format == FORMAT_MAPPER) && (mapper != null)) {
            ret = mapper.getString(o);
            if ((ret == null) || (ret.equals(""))) {
                ret = " ";
            }
            return ret;
        }
        if (((format == FORMAT_PRINTF) || (format == FORMAT_USE_DATA_SOURCE)) && (printfFormater != null)) {
            try {
                ret = printfFormater.sprintf(o);
                return ret;
            } catch (IllegalArgumentException e) {
                ret = resources.getString("IncompatiblePrintfFormater");
            }
            return ret;
        }
        if (o == null) {
            ret = " ";
            return ret;
        }
        if (!(o instanceof Number)) {
            ret = o.toString();
            return ret;
        }
        Number n = (Number) o;
        switch (format) {
        case FORMAT_DECIMAL:
            ret = decimalFormatter.format(n);
            break;
        case FORMAT_SCIENTIFIC:
            ret = scientificFormatter.format(n);
            break;
        case FORMAT_HEXADECIMAL:
            ret = "0x" + Long.toHexString(n.longValue()).toUpperCase();
            break;
        case FORMAT_OCTAL:
            ret = "0" + Long.toOctalString(n.longValue());
            break;
        case FORMAT_BINARY:
            ret = Long.toBinaryString(n.longValue());
            break;
        case FORMAT_DATE:
        case FORMAT_TIME:
        case FORMAT_DATE_TIME:
            ret = dateTimeFormatter.format(new Date(n.longValue()));
            break;
        default:
        case FORMAT_STRING:
            ret = n.toString();
        }
        return ret;
    }

    /*
     * (non-Javadoc)
     *
     * @see jsynoptic.builtin.Abstract1DShape#getDelegateShape()
     */
    protected Shape getDelegateShape() {
        return new Rectangle(_ox, _oy - _h, _w, _h);
    }

    protected void init() {
        // Frame
        drawColor = null; // no frame
        // Dynamic options
        displayDsName = new Boolean(DEFAULT_DISPLAY_NAME);
        displayDsValue = new Boolean(DEFAULT_DISPLAY_VALUE);
        displayDsUnit = new Boolean(DEFAULT_DISPLAY_UNIT);
        displayDsComments = new Boolean(DEFAULT_DISPLAY_COMMENTS);
        // Decimal and scientific formaters
        decimalFormatter = NumberFormat.getNumberInstance(CustomizedLocale.get());
        decimalFormatter.setGroupingUsed(groupTextDigits); // Diable grouping
        // format whaterver
        // used Locale
        scientificFormatter = NumberFormat.getNumberInstance(CustomizedLocale.get());
        if (scientificFormatter instanceof DecimalFormat) {
            ((DecimalFormat) scientificFormatter).applyPattern("0.000E0");
        }
        scientificFormatter.setGroupingUsed(groupTextDigits); // Diable
        // grouping
        // format
        // whaterver
        // used Locale
        // Format
        setFormat(DEFAULT_FORMAT);
        if ((format == FORMAT_PRINTF) || (format == FORMAT_USE_DATA_SOURCE)) {
            setPrintfFormat(printfFormat);
        }
        // Size
        setLockedFont(DEFAULT_FIXED_FONT); // default behaviour since version
        // 1.6
        setLockedFrame(DEFAULT_FIXED_FRAME); // since version 2.6
        setCharNumber(new Integer(DEFAUT_CHAR_NUMBER)); // since version 2.6
    }

    public TextShape(String text, int width, int height) {
        this(text, 0, 0, width, height);
    }

    public TextShape(String text, int x, int y, int width, int height) {
        this(text, x, y, width, height, true);
    }
    public TextShape(String text, int x, int y, int width, int height, boolean fitBounds) {
        super(x, y, width, height);
        setText(text);
        init(); // Set all text properties

        updateFont();
        if (fitBounds){
            fitBounds();
        }
        fitText();
    }

    public boolean setFont(Font f) {
        if (f.equals(referenceFont)) {
            return false;
        }
        fontName = f.getName();
        fontSize = f.getSize();
        boldFont = f.isBold();
        italicFont = f.isItalic();
        referenceFont = new Font(fontName, f.getStyle(), fontSize);
        referenceFontMetrics = referenceGraphics.getFontMetrics(referenceFont);
        currentFont = referenceFont;
        return true;
    }

    public void setLockedFont(boolean lockedFont) {
        this.lockedFont = lockedFont;
        if (lockedFont) {
            lockedFrame = false;
        }
    }

    public void setLockedFrame(boolean lockedFrame) {
        this.lockedFrame = lockedFrame;
        if (lockedFrame) {
            lockedFont = false;
        }
    }

    protected void setCharNumber(Integer charNumber) {
        this.charNumber = charNumber;
    }

    protected boolean canResize() {
        return (!lockedFrame && super.canResize());
    }

    public void updateFont() {
        if (fontName == null) {
            fontName = DEFAULT_TEXT_FONT; // backward compatibility
            fontSize = DEFAULT_TEXT_SIZE;
            italicFont = false;
            boldFont = false;
        }
        currentFont = new Font(fontName, (boldFont ? Font.BOLD : 0) + (italicFont ? Font.ITALIC : 0), fontSize);
        referenceFont = currentFont;
        referenceFontMetrics = referenceGraphics.getFontMetrics(referenceFont);
    }

  
    /* (non-Javadoc)
     * @see jsynoptic.builtin.Abstract1DShape#setDelegateEndNotificationListener(simtools.data.EndNotificationListener)
     */
    public void setDelegateEndNotificationListener(EndNotificationListener denl) {
        if (source != null) {
            source.removeEndNotificationListener(delegateEndNotificationListener);
            source.addEndNotificationListener(denl);
        }

        if (textMapperSource != null) {
            textMapperSource.removeEndNotificationListener(delegateEndNotificationListener);
            textMapperSource.addEndNotificationListener(denl);
        }
       
        super.setDelegateEndNotificationListener(denl)

    }

    // Benefits from parent draw, add our own text
    protected void drawHook(Graphics2D g, boolean shapeDrawn) {
        if (!shapeDrawn) {
            return;
        }
        Object textRenderingHint = g.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                AbstractShape.ANTI_ALIASING ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
        // Prevent the text to get out of bounds even if it should not according
        // to java doc
        Rectangle oldclip = g.getClipBounds();
        g.setClip(_ox, _oy - _h, _w, _h);
        Color c = getTextColor();
        if (c != null) {
            g.setColor(c);
        }
        Font oldFont = g.getFont();
        g.setFont(currentFont);
       
        String displayedText = text;
        if (lockedFrame && (text != null)) {
            int lastIndex = (charNumber.intValue() < text.length()) ? charNumber.intValue() : text.length();
            displayedText = text.substring(0, lastIndex);
        }
       
        if (marginEnabled) {
            g.drawString(displayedText, _ox + MARGINX, _oy - baseline - MARGINY);
        } else {// margin not enabled
            g.drawString(displayedText, _ox, _oy - baseline);
        }
        g.setFont(oldFont);
        g.setClip(oldclip);
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, textRenderingHint);
    }

    /**
     * Set dimensions and fits the text to the new dimensions.
     */
    public void setDimensions(int width, int height) {
        _w = width;
        _h = height;
        if (_w < MIN_SIZE) {
            _w = MIN_SIZE;
        }
        if (_h < MIN_SIZE) {
            _h = MIN_SIZE;
        }
        ratio = (double) _w / (double) _h;
        fitText();
    }

    /**
     * Resize uses an increment for width and height. Public by side effect, do
     * not call. Use setDimensions instead.
     */
    public void resize(int dx, int dy) {
        super.resize(dx, dy); // from Abstract1DShape
        fitText();
    }

    /**
     * Used to make the bounds best fit the current size text size
     */
    public void fitBounds() {
        if (lockedFrame || lockedFont) {
            currentFont = referenceFont;
            int h=50, w=150;
            if (lockedFrame) { // Compute the bounding needed to display
                // specified number of characters
                h = referenceFontMetrics.getMaxAscent() + referenceFontMetrics.getMaxDescent();
                w = referenceFontMetrics.stringWidth("W") * charNumber.intValue(); // Don't
                // use
                // getMaxAdvance()
                // because wacky characters
                // in fonts.
               
            } else if (text != null){
                h = referenceFontMetrics.getMaxAscent() + referenceFontMetrics.getMaxDescent();
                w = referenceFontMetrics.stringWidth(text);
            }
            if (marginEnabled) {
                _w = w + 2 * MARGINX;
                _h = h + 2 * MARGINY;
            } else {
                _w = w;
                // divide by fontYScaleFactor to compensate the font line
                // spacing
                _h = (int) ((double) h / fontYScaleFactor);
            }
            if (_w < MIN_SIZE) {
                _w = MIN_SIZE;
            }
            if (_h < MIN_SIZE) {
                _h = MIN_SIZE;
            }
            ratio = (double) _w / (double) _h;
            computeBaseLine();
            if (transform != null) {
                updateTransform();
            } else {
                updateBounds();
            }
        }
    }

    /**
     * Compute base line used with a not scaled font
     */
    protected void computeBaseLine() {
        if (marginEnabled) {
            baseline = referenceFontMetrics.getMaxDescent();
        } else {// margin not enabled
            baseline = (int) ((double) referenceFontMetrics.getMaxDescent() / fontYScaleFactor);
        }
    }

    /**
     * Used to make the text best fit the current size
     */
    public void fitText() {
        if (!(lockedFrame || lockedFont)) {
            double innerw, innerh;
            double scaleX, scaleY;
            int referenceHeight = referenceFontMetrics.getMaxAscent() + referenceFontMetrics.getMaxDescent();
            int referenceWidth = referenceFontMetrics.stringWidth(text);
            if (marginEnabled) {
                innerw = _w - 2 * MARGINX;
                innerh = _h - 2 * MARGINY;
                if ((innerw <= 0) || (innerh <= 0)) {
                    return;
                }
                scaleX = innerw / referenceWidth;
                scaleY = innerh / referenceHeight;
            } else {// margin not enabled
                innerw = _w;
                innerh = _h;
                if ((innerw <= 0) || (innerh <= 0)) {
                    return;
                }
                scaleX = innerw / referenceWidth;
                // multiplication by fontYScaleFactor to compensate the font
                // line spacing
                scaleY = (innerh / referenceHeight) * fontYScaleFactor;
            }
            baseline = (int) (referenceFontMetrics.getMaxDescent() * scaleY);
            currentFont = referenceFont.deriveFont(AffineTransform.getScaleInstance(scaleX, scaleY));
        }
    }

    /**
     * Updates the current text, by reading the data source value if connected
     * to a data source Then, notify the system to update the display
     *
     * @param readAgainFromSource
     *            Can be set to false to prevent reading the text from the
     *            source
     */
    public boolean updateText() {
        if (source == null) {
            return false;
        }
        String oldText = text;
        String newText = "";
        if (displayDsName.booleanValue()) {
            String name = DataInfo.getLabel(source);
            if ((name != null) && !name.equals("")) {
                newText += name;
                if (displayDsValue.booleanValue()) {
                    newText += "=";
                }
            }
        }
        try {
            if (displayDsValue.booleanValue()) {
                newText += formatSourceValue(getSourceValue());
            }
        } catch (DataException e) {
            newText += resources.getString("NoValue");
        }
        if (displayDsUnit.booleanValue()) {
            String unit = DataInfo.getUnit(source);
            if ((unit != null) && !unit.equals("")) {
                newText += " [" + unit + "]";
            }
        }
        if (displayDsComments.booleanValue()) {
            String comments = DataInfo.getComment(source);
            if ((comments != null) && !comments.equals("")) {
                newText += "  " + comments;
            }
        }
        setText(newText);
        // no change in text => no need to repaint
        if (text.equals(oldText)) {
            return false;
        }
        fitText();
        return true;
    }

    public void DataSourceValueChanged(DataSource ds, long minIndex, long maxIndex) {
        // Change the text to display if the source match (we also listen to
        // mapper sources)
        if (ds.equals(source)) {
            // We care only about our index value change
            if ((sourceIndex >= minIndex) && (sourceIndex <= maxIndex)) {
                dirtyText = true;
            }
        }
        // now check if this is our mapper source
        if (ds.equals(textMapperSource)) {
            // We care only about our index value change
            if ((textMapperIndex >= minIndex) && (textMapperIndex <= maxIndex)) {
                dirtyColor = true;
            }
        }
        super.DataSourceValueChanged(ds, minIndex, maxIndex);
    }

    /*
     * (non-Javadoc)
     *
     * @see simtools.data.DataSourceListener#DataSourceIndexRangeChanged(simtools.data.DataSource,
     *      long, long)
     */
    public void DataSourceIndexRangeChanged(DataSource ds, long startIndex, long lastIndex) {
        // Change the text to display if the source match (we also listen to
        // mapper sources)
        if (ds.equals(source)) {
            sourceIndex = lastIndex;
            dirtyText = true;
        }
        // mind our own business
        if (ds.equals(this.textMapperSource)) {
            textMapperIndex = lastIndex;
            dirtyColor = true;
        }
        super.DataSourceIndexRangeChanged(ds, startIndex, lastIndex);
    }

    /*
     * (non-Javadoc)
     *
     * @see simtools.data.DataSourceListener#DataSourceReplaced(simtools.data.DataSource,
     *      simtools.data.DataSource)
     */
    public void DataSourceReplaced(DataSource oldData, DataSource newData) {
        if (oldData == source) {
            source = newData;
            if (source != null) {
                source.addListener(this);
                source.addEndNotificationListener(delegateEndNotificationListener);
                try {
                    sourceIndex = source.getLastIndex();
                } catch (UnsupportedOperation uo1) {
                    sourceIndex = 0;
                }
                dirtyText = true;
            }
            oldData.removeListener(this);
            oldData.removeEndNotificationListener(delegateEndNotificationListener);
        }
        if (oldData == textMapperSource) {
            textMapperSource = newData;
            if (textMapperSource != null) {
                textMapperSource.addListener(this);
                textMapperSource.addEndNotificationListener(delegateEndNotificationListener);
                try {
                    textMapperIndex = textMapperSource.getLastIndex();
                } catch (UnsupportedOperation uo1) {
                    textMapperIndex = 0;
                }
                dirtyText = true;
            }
            oldData.removeListener(this);
            oldData.removeEndNotificationListener(delegateEndNotificationListener);
        }
        super.DataSourceReplaced(oldData, newData);
    }

    public Color getTextColor(){
        return  (textDynamicColor!=null) ? textDynamicColor : textColor;
    }
   
    /*
     * (non-Javadoc)
     *
     * @see simtools.data.EndNotificationListener#notificationEnd(java.lang.Object)
     */
    public void notificationEnd(Object referer) {
        if (dirtyText) {
            dirty |= updateText();
            dirtyText = false;
        }
        if (dirtyColor) {
            Color c = null;
            if (textMapper != null) {
                c = (Color) textMapper.getPaint(textMapperSource, textMapperIndex);
            }
            // dirty field to true only if necessary
            if (c == null) {
                dirty |= (textDynamicColor != null);
            } else {
                dirty |= !c.equals(textDynamicColor);
            }
            textDynamicColor = c;
            dirtyColor = false;
        }
        super.notificationEnd(referer);
    }


    /* (non-Javadoc)
     * @see simtools.shapes.AbstractShape#subscribeToDataNotifications()
     */
    public void processShapeRestoring(){

        if (source != null) {
            source.addListener(this);
            source.addEndNotificationListener(delegateEndNotificationListener);
            try {
                sourceIndex = source.getLastIndex();
            } catch (UnsupportedOperation uo1) {
                sourceIndex = 0;
            }
        }

        if (textMapperSource != null) {
            textMapperSource.addListener(this);
            textMapperSource.addEndNotificationListener(delegateEndNotificationListener);
            try {
                textMapperIndex = textMapperSource.getLastIndex();
            } catch (UnsupportedOperation uo1) {
                textMapperIndex = 0;
            }
        }
       
        super.processShapeRestoring();
    }


    /* (non-Javadoc)
     * @see simtools.shapes.AbstractShape#unsubscribeToDataNotifications()
     */
    public void processShapeRemoving(){
        if (source != null) {
            source.removeListener(this);
            source.removeEndNotificationListener(delegateEndNotificationListener);
        }
       
        if (textMapperSource != null) {
            textMapperSource.removeListener(this);
            textMapperSource.removeEndNotificationListener(delegateEndNotificationListener);
        }
        super.processShapeRemoving();
    }
   
   
    public String[] getActions(double x, double y, Object o, int context) {
        if (context == MOUSE_OVER_CONTEXT) {
            return null;
        }
        if (context == MOUSE_OUT_CONTEXT) {
            return null;
        }
        if (context == MOUSE_PRESSED_CONTEXT) {
            return null;
        }
        Vector v = new Vector();
        String[] actions = super.getActions(x, y, o, context);
        if (actions != null) {
            for (int i = 0; i < actions.length; ++i) {
                v.add(actions[i]);
            }
        }
        if (context == EDITOR_CONTEXT) {
            if ((o instanceof DataSource) && (canAddDataSource(((DataSource) o)))) {
                v.add(resources.getString("SetSource"));
            }
        }
        return (String[]) v.toArray(new String[v.size()]);
    }

    public void setSource(DataSource ds) {
        if (source != null) {
            source.removeListener(this);
            source.removeEndNotificationListener(delegateEndNotificationListener);
        }
        source = ds;
        if (source == null) {
            return;
        }
        source.addListener(this);
        source.addEndNotificationListener(delegateEndNotificationListener);
        try {
            sourceIndex = source.computeLastIndex();
        } catch (UnsupportedOperation e) {
            try {
                sourceIndex = source.getLastIndex();
            } catch (UnsupportedOperation e1) {
                try {
                    sourceIndex = source.getStartIndex();
                } catch (UnsupportedOperation e2) {
                    sourceIndex = 0;
                }
            }
        }
        if ((format == FORMAT_PRINTF) || (format == FORMAT_USE_DATA_SOURCE)) {
            setPrintfFormat(DataInfo.getPrintfFormat(ds));
        }
        updateText();
    }

    /*
     * (non-Javadoc)
     *
     * @see jsynoptic.base.SelectionContextualActionProvider#getCollectiveActions()
     */
    public LinkedHashMap getCollectiveActions(DiagramSelection sel, double x, double y, Object o, int context) {
        LinkedHashMap res = super.getCollectiveActions(sel, x, y, o, context);
        res.put(resources.getString("AdjustSize"), null);
 
        // Body action
        for (int i = 0; i < FontChooserPanel.SIZES.length; i++) {
            res.put(resources.getString("Body") + ";" + FontChooserPanel.SIZES[i], null);
        }
        // Font action
        for (int i = 0; i < AbstractShape.FONT_NAMES.length; i++) {
            res.put(resources.getString("Font") + ";" + AbstractShape.FONT_NAMES[i], null);
        }
       
        if (context == EDITOR_CONTEXT) {
            if ((o instanceof DataSource) && (canAddDataSource(((DataSource) o)))) {
                res.put(resources.getString("SetSource"), null);
            }
        }
        return res;
    }
   
    /*
     * (non-Javadoc)
     *
     * @see jsynoptic.base.ContextualActionProvider#doAction(double, double,
     *      java.lang.Object, java.lang.String)
     */
    public boolean doAction(double x, double y, Object o, String action, CompoundEdit undoableEdit) {
      
        if (action.equals(resources.getString("SetSource"))) {
            // Save old params
            CompoundEdit ce = new CompoundEdit();
            HashMap oldValues = new HashMap();
            oldValues.put("TEXT_SOURCE", source);
            oldValues.put("TEXT", getText());
           
            setSource((DataSource) o);
           
            // Notify changes
            Iterator it = oldValues.keySet().iterator();
            while (it.hasNext()) {
                String n = (String) it.next();
                ce.addEdit(new PropertyChangeEdit(this, n, oldValues.get(n), getPropertyValue(n)));
            }
            ce.end();
            if (undoableEdit != null) {
                undoableEdit.addEdit(ce);
            }
            return true;
        }
        if (action.startsWith(resources.getString("Font"))) {
            // Save old params
            CompoundEdit ce = new CompoundEdit();
            HashMap oldValues = new HashMap();
            oldValues.put("FONT", currentFont);
            // Make changes
            fontName = action.substring(resources.getString("Font").length() + 1);
            updateFont();
            Rectangle bounds = getBounds();
            fitBounds();
            // Notify changes
            Iterator it = oldValues.keySet().iterator();
            while (it.hasNext()) {
                String n = (String) it.next();
                ce.addEdit(new PropertyChangeEdit(this, n, oldValues.get(n), getPropertyValue(n)));
            }
            ce.end();
            if (undoableEdit != null) {
                undoableEdit.addEdit(ce);
            }
            // Repaint
            bounds.add(getBounds());
            notifyChange(bounds);
            return true;
        }
        if (action.startsWith(resources.getString("Body"))) {
            // Save old params
            CompoundEdit ce = new CompoundEdit();
            HashMap oldValues = new HashMap();
            oldValues.put("FONT", currentFont);
            // Make changes
            fontSize = new Integer(action.substring(resources.getString("Body").length() + 1)).intValue();
            updateFont();
            Rectangle bounds = getBounds();
            fitBounds();
            // Notify changes
            Iterator it = oldValues.keySet().iterator();
            while (it.hasNext()) {
                String n = (String) it.next();
                ce.addEdit(new PropertyChangeEdit(this, n, oldValues.get(n), getPropertyValue(n)));
            }
            ce.end();
            if (undoableEdit != null) {
                undoableEdit.addEdit(ce);
            }
           
            // Repaint
            bounds.add(getBounds());
            notifyChange(bounds);
            return true;
        }
        if (action.equals(resources.getString("AdjustSize"))) {
            CompoundEdit ce = new CompoundEdit();
            HashMap oldValues = new HashMap();
            oldValues.put("WIDTH", getPropertyValue("WIDTH"));
            oldValues.put("HEIGHT", getPropertyValue("HEIGHT"));
            Rectangle bounds = getBounds();
            fitBounds();
            Iterator it = oldValues.keySet().iterator();
            while (it.hasNext()) {
                String n = (String) it.next();
                ce.addEdit(new PropertyChangeEdit(this, n, oldValues.get(n), getPropertyValue(n)));
            }
            ce.end();
            if (undoableEdit != null) {
                undoableEdit.addEdit(ce);
            }
           
            bounds.add(getBounds());
            notifyChange(bounds);
            return true;
        }
        return super.doAction(x, y, o, action, undoableEdit);
    }



    /* (non-Javadoc)
     * @see jsynoptic.builtin.Abstract2DShape#getPanel(java.util.List)
     */
    public JPropertiesPanel getPanel(List properties) {
        if (properties == null){
            return null;
        }

        if (properties.containsAll(Arrays.asList(new TextShapePropertiesNames().getPropertyNames()))){
            return new TextPropertiesPanel(true, Builtin.resources.getString("Text"));

        } else {
            return super.getPanel(properties);
        }
    }

    /* (non-Javadoc)
     * @see simtools.shapes.AbstractShape#getShapeName()
     */
    public String getShapeName(){
        return Builtin.resources.getString("Text");
    }
    // Take care of serialisation. Special handling for datasources
    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
        out.defaultWriteObject();
        DataSourcePool.global.writeDataSource(out, textMapperSource);
        DataSourcePool.global.writeDataSource(out, source);
    }

    protected void synchronizeFormat() {
        // backward compatibility
        if ((format == FORMAT_DATE) && (dateFormatter != null)) {
            dateTimeFormatter = dateFormatter;
        }
        if ((format == FORMAT_TIME) && (timeFormatter != null)) {
            dateTimeFormatter = timeFormatter;
        }
        dateFormatter = null; // no more used
        timeFormatter = null; // no more used
    }

    private void readObject(java.io.ObjectInputStream in) throws java.lang.ClassNotFoundException, java.io.IOException {
        in.defaultReadObject();
        if (displayDsName == null) {
            displayDsName = new Boolean(false);
        }
        if (displayDsValue == null) {
            displayDsValue = new Boolean(true);
        }
        if (displayDsUnit == null) {
            displayDsUnit = new Boolean(false);
        }
        if (displayDsComments == null) {
            displayDsComments = new Boolean(false);
        }
        if (charNumber == null) {
            charNumber = new Integer(DEFAUT_CHAR_NUMBER); // since version 2.6
        }
        // Create decimal and scientific formaters
        if (decimalFormatter == null) {
            decimalFormatter = NumberFormat.getNumberInstance(CustomizedLocale.get());
        }
        decimalFormatter.setGroupingUsed(groupTextDigits); // Diable grouping
        // format whaterver
        // used Locale
        if (scientificFormatter == null) {
            scientificFormatter = NumberFormat.getNumberInstance(CustomizedLocale.get());
            if (scientificFormatter instanceof DecimalFormat) {
                ((DecimalFormat) scientificFormatter).applyPattern("0.000E0");
            }
        }
        scientificFormatter.setGroupingUsed(groupTextDigits); // Diable
        // grouping
        // format
        // whaterver
        // used Locale
        synchronizeFormat();
      
        // Set format regarding current defined formats
        setFormat(format);
       
        if (mapper != null) {
            mapper = TextMapper.updateTextMapper(mapper);
        }
        if (mapper != null) {
            mapper.addListener(this);
        }
        // Objects using this feature should handle this in their own readObject
      
        textMapperSource = DataSourcePool.global.readDataSource(in);
        if (textMapperSource != null) {
            textMapperSource.addListener(this);
            textMapperSource.addEndNotificationListener(delegateEndNotificationListener);
            try {
                textMapperIndex = textMapperSource.getLastIndex();
            } catch (UnsupportedOperation uo1) {
                textMapperIndex = 0;
            }
        }
        if (textMapper != null) {
            textMapper = ColorMapper.updateColorMapper(textMapper);
        }
        if ((textMapper != null) && (textMapperSource != null)) {
            textDynamicColor = (Color) textMapper.getPaint(textMapperSource, textMapperIndex);
        } else {
            textDynamicColor = null;
        }
        updateFont();
        computeBaseLine();
        source = DataSourcePool.global.readDataSource(in);
        setPrintfFormat(printfFormat);
        if (source != null) {
            source.addListener(this);
            source.addEndNotificationListener(delegateEndNotificationListener);
            try {
                sourceIndex = source.computeLastIndex();
            } catch (UnsupportedOperation e) {
                try {
                    sourceIndex = source.getLastIndex();
                } catch (UnsupportedOperation e1) {
                    try {
                        sourceIndex = source.getStartIndex();
                    } catch (UnsupportedOperation e2) {
                        sourceIndex = 0;
                    }
                }
            }
            updateText();
        }
        fitText();
    }

    /*
     * (non-Javadoc)
     *
     * @see simtools.shapes.AbstractShape#cloneShape()
     */
    protected AbstractShape cloneShape() {
        TextShape ts = (TextShape) super.cloneShape();
        if (ts.source != null) {
            ts.source.addEndNotificationListener(ts.delegateEndNotificationListener);
            ts.source.addListener(ts);
        }
        if (ts.textMapperSource != null) {
            ts.textMapperSource.addEndNotificationListener(ts.delegateEndNotificationListener);
            ts.textMapperSource.addListener(ts);
        }
        ts.decimalFormatter = (decimalFormatter == null) ? null : (NumberFormat) decimalFormatter.clone();
        ts.scientificFormatter = (scientificFormatter == null) ? null : (NumberFormat) scientificFormatter.clone();
        ts.dateFormatter = null; // no more used : backward compatibility
        ts.timeFormatter = null; // no more used : backward compatibility
        ts.dateTimeFormatter = (dateTimeFormatter == null) ? null : (DateFormat) dateTimeFormatter.clone();
        ts.updateFont();
        ts.fitText();
        return ts;
    }

    /*
     * (non-Javadoc)
     *
     * @see simtools.ui.MapperListener#mappingChanged(simtools.ui.GenericMapper)
     */
    public void mappingChanged(GenericMapper mapper) {
        if (mapper.equals(this.mapper)) {
            updateText();
        }
        notifyChange();
    }

    /*
     * (non-Javadoc)
     *
     * @see simtools.shapes.AbstractShape#notifyChange(java.awt.Rectangle)
     */
    protected synchronized void notifyChange(Rectangle changedArea) {
        // Override this for delegate end notification listener to know if this
        // object was
        // dirty or not.
        // The delegate should set dirtyState to false before calling our end
        // notification handler
        // If it was dirty, this function is called by Abstract1DShape. If not,
        // no call
        // The the delegate can check if the dirty state has changed.
        dirtyState = true;
        super.notifyChange(changedArea);
    }

    /**
     * @return Returns the dirtyState.
     */
    public boolean isDirtyState() {
        return dirtyState;
    }

    /**
     * @param dirtyState
     *            The dirtyState to set.
     */
    public void setDirtyState(boolean dirtyState) {
        this.dirtyState = dirtyState;
    }

    /*
     * (non-Javadoc)
     *
     * @see jsynoptic.base.DataSourceConsumer#addDataSource(simtools.data.DataSource)
     */
    public boolean addDataSource(DataSource d) {
        setSource(d);
        notifyChange();
        return true;
    }

    /*
     * (non-Javadoc)
     *
     * @see jsynoptic.base.DataSourceConsumer#canAddDataSource(simtools.data.DataSource)
     */
    public boolean canAddDataSource(DataSource d) {
        return true;
    }

    public Object getPropertyValue(String name) {
        Object res = super.getPropertyValue(name);
        if (name.equalsIgnoreCase("TEXT")) {
            res = getText();
        } else if (name.equalsIgnoreCase("TEXT_COLOR")) {
            res = textColor;
        } else if (name.equalsIgnoreCase("DISPLAY_NAME")) {
            res = displayDsName;
        } else if (name.equalsIgnoreCase("DISPLAY_VALUE")) {
            res = displayDsValue;
        } else if (name.equalsIgnoreCase("DISPLAY_UNIT")) {
            res = displayDsUnit;
        } else if (name.equalsIgnoreCase("DISPLAY_COMMENTS")) {
            res = displayDsComments;
        } else if (name.equalsIgnoreCase("TEXT_SOURCE")) {
            res = source;
        } else if (name.equalsIgnoreCase("VALUE_FORMAT")) {
            res = new Integer(format);
        } else if (name.equalsIgnoreCase("FORMAT_MAPPER")) {
            res = mapper;
        } else if (name.equalsIgnoreCase("DECIMAL_DIGITS")) {
            long l = decimalFormatter.getMaximumFractionDigits();
            if (format == FORMAT_SCIENTIFIC) {
                l = scientificFormatter.getMaximumFractionDigits();
            }
            res = new Integer((int) l);
        } else if (name.equalsIgnoreCase("VALUE_PRINTF_FORMAT")) {
            res = printfFormat;
        } else if (name.equalsIgnoreCase("TEXT_MAPPER")) {
            res = textMapper;
        } else if (name.equalsIgnoreCase("TEXT_MAPPER_SOURCE")) {
            res = textMapperSource;
        } else if (name.equalsIgnoreCase("ENABLE_MARGIN")) {
            res = new Boolean(marginEnabled);
        } else if (name.equalsIgnoreCase("FONT_LOCK")) {
            res = new Boolean(lockedFont);
        } else if (name.equalsIgnoreCase("FONT")) {
            res = currentFont;
        } else if (name.equalsIgnoreCase("FRAME_LOCK")) {
            res = new Boolean(lockedFrame);
        } else if (name.equalsIgnoreCase("CHAR_NUMBER")) {
            res = charNumber;
        }
        return res;
    }

    public void setPropertyValue(String name, Object value) {
        // Let text shape manage width and heigth bounds when these properties
        // have to be updated
        if (!(name.equalsIgnoreCase("WIDTH")) && !(name.equalsIgnoreCase("HEIGHT"))
                && !(name.equalsIgnoreCase("ALLOW_RESIZE"))) {
           
            super.setPropertyValue(name, value);
           
            if (name.equalsIgnoreCase("TEXT")) {
                if (value instanceof String) {
                    if (value != null) {
                        setText((String) value);
                    }
                }
            } else if (name.equalsIgnoreCase("TEXT_COLOR")) {
                if (value instanceof Color) {
                    textColor = (Color) value;
                } else {
                    textColor = null;
                }
            } else if (name.equalsIgnoreCase("DISPLAY_NAME")) {
                if (value instanceof Boolean) {
                    displayDsName = ((Boolean) value);
                }
            } else if (name.equalsIgnoreCase("DISPLAY_VALUE")) {
                if (value instanceof Boolean) {
                    displayDsValue = ((Boolean) value);
                }
            } else if (name.equalsIgnoreCase("DISPLAY_UNIT")) {
                if (value instanceof Boolean) {
                    displayDsUnit = ((Boolean) value);
                }
            } else if (name.equalsIgnoreCase("DISPLAY_COMMENTS")) {
                if (value instanceof Boolean) {
                    displayDsComments = ((Boolean) value);
                }
            } else if (name.equalsIgnoreCase("VALUE_FORMAT")) {
                if (value instanceof Integer) {
                    setFormat(((Integer) value).intValue());
                    updateText();
                }
            } else if (name.equalsIgnoreCase("FORMAT_MAPPER")) {
                if (mapper != null) {
                    mapper.removeListener(this);
                }
                if (value instanceof TextMapper) {
                    mapper = (TextMapper) value;
                    mapper.addListener(this);
                } else {
                    mapper = null;
                }
            } else if (name.equalsIgnoreCase("TEXT_SOURCE")) {
                setSource((DataSource) value);
            } else if (name.equalsIgnoreCase("DECIMAL_DIGITS")) {
                if (value instanceof Integer) {
                    int l = ((Integer) value).intValue();
                    if (format == FORMAT_DECIMAL) {
                        decimalFormatter.setMaximumFractionDigits(l);
                    } else if (format == FORMAT_SCIENTIFIC) {
                        if (scientificFormatter instanceof DecimalFormat) {
                            String p = "0.";
                            for (int i = 0; i < l; ++i) {
                                p += "0";
                            }
                            p += "E0";
                            ((DecimalFormat) scientificFormatter).applyPattern(p);
                        }
                    }
                }
            } else if (name.equalsIgnoreCase("VALUE_PRINTF_FORMAT")) {
                if ((format == FORMAT_PRINTF) || (format == FORMAT_USE_DATA_SOURCE)) {
                    setPrintfFormat((String) value);
                    updateText();
                }
            } else if (name.equalsIgnoreCase("TEXT_MAPPER")) {
                if (value instanceof ColorMapper) {
                    textMapper = (ColorMapper) value;
                } else {
                    textMapper = null;
                }
            } else if (name.equalsIgnoreCase("TEXT_MAPPER_SOURCE")) {
                if (textMapperSource != null) {
                    textMapperSource.removeListener(TextShape.this);
                    textMapperSource.removeEndNotificationListener(delegateEndNotificationListener);
                }
                if (value instanceof DataSource) {
                    textMapperSource = (DataSource) value;
                    try {
                        textMapperIndex = textMapperSource.getLastIndex();
                        textMapperIndex = textMapperSource.getLastIndex();
                    } catch (UnsupportedOperation e) {
                        textMapperIndex = 0;
                    }
                    textMapperSource.addListener(this);
                    textMapperSource.addEndNotificationListener(delegateEndNotificationListener);
                } else {
                    textMapperSource = null;
                }
                if ((textMapper != null) && (textMapperSource != null)) {
                    textDynamicColor = (Color) textMapper.getPaint(textMapperSource, textMapperIndex);
                } else {
                    textDynamicColor = null;
                }
            } else if (name.equalsIgnoreCase("FONT_LOCK")) {
                if (value instanceof Boolean) {
                    boolean nv = ((Boolean) value).booleanValue();
                    if (nv != lockedFont) {
                        setLockedFont(nv);
                        if (lockedFont) {
                            fitBounds(); // adjust size to use a locked font
                        }
                    }
                }
            } else if (name.equalsIgnoreCase("FRAME_LOCK")) {
                if (value instanceof Boolean) {
                    boolean nv = ((Boolean) value).booleanValue();
                    if (nv != lockedFrame) {
                        setLockedFrame(nv);
                        if (lockedFrame) {
                            fitBounds(); // adjust size to use a locked frame
                        }
                    }
                }
            } else if (name.equalsIgnoreCase("CHAR_NUMBER")) {
                if (value instanceof Integer) {
                    Integer ncn = (Integer) value;
                    if (!ncn.equals(this.charNumber)) {
                        setCharNumber((Integer) value);
                        if (lockedFrame) {
                            updateText();
                            setText(getText()); // Set text to keep only fisrt caracters
                            fitBounds(); // adjust size to use a locked font
                        }
                    }
                }
            } else if (name.equalsIgnoreCase("ENABLE_MARGIN")) {
                if (value instanceof Boolean) {
                    boolean nv = ((Boolean) value).booleanValue();
                    if (nv != marginEnabled) {
                        marginEnabled = nv;
                        fitBounds();
                    }
                }
            } else if (name.equalsIgnoreCase("FONT")) {
                if (value instanceof Font) {
                    if (!value.equals(referenceFont)) {
                        setFont((Font) value);
                        fitBounds();
                    }
                }
            }
            fitText(); // If frame or font are not locked, fit text to bounds
        }
    }

    public String[] getPropertyNames() {
        if (_propertyNames == null) {
            _propertyNames = new TextShapePropertiesNames().getPropertyNames();
        }
        return _propertyNames;
    }

    public static class TextShapePropertiesNames extends Abstract2DShapePropertiesNames {
        private static transient String[] props = new String[] {
                "TEXT",
                "TEXT_COLOR",
                "DISPLAY_NAME",
                "DISPLAY_VALUE",
                "DISPLAY_UNIT",
                "DISPLAY_COMMENTS",
                "TEXT_SOURCE",
                "FORMAT_MAPPER",
                "VALUE_FORMAT",
                "DECIMAL_DIGITS",
                "VALUE_PRINTF_FORMAT",
                "TEXT_MAPPER",
                "TEXT_MAPPER_SOURCE",
                "FONT_LOCK",
                "FRAME_LOCK",
                "CHAR_NUMBER",
                "FONT",
                "ENABLE_MARGIN", };

        public TextShapePropertiesNames() {
            super();
            for (int i = 0; i < props.length; i++) {
                propertyNames.add(props[i]);
            }
        }
    }
}
TOP

Related Classes of jsynoptic.builtin.TextShape

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.