/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.object;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyDescriptor;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;

import java.util.Locale;

import net.sf.jaxodraw.util.JaxoPrefs;
import net.sf.jaxodraw.util.JaxoColor;
import net.sf.jaxodraw.util.graphics.JaxoGraphics2D;


/**
 * A general JaxoObject.
 *
 * @since 2.0
 */
public abstract class JaxoObject
        implements Serializable, Cloneable, Shape, PropertyChangeListener {

    /** An integer that indicates that the user did not grab any object. */
    public static final int SELECT_NONE = -1;

    /** First point (index 0). */
    public static final int SELECT_P1 = 0;

    /** The decimal format used for numbers in LaTeX output. */
    public static final DecimalFormat D_FORMAT =
        new DecimalFormat("######.###",
            new DecimalFormatSymbols(new Locale("en", "US")));

    /** The decimal format used gray scales in LaTeX output. */
    public static final DecimalFormat GRAY_SCALE_FORMAT =
        new DecimalFormat("#.###",
            new DecimalFormatSymbols(new Locale("en", "US")));

    static {
        setTransient(JaxoObject.class, new String[] {"x", "y"});
    }

    private static final long serialVersionUID = 2L;
    private static final int POINT_COUNT = 1;

      //
     // bean properties: to be serialized to XML
    //

    /** The color of this JaxoObject.*/
    private Color color;

    // Point coordinates are handled by "points" property. They are
    // not written themselves

    /** The x coordinate of the first point of this JaxoObject. */
    private int x;

    /** The y coordinate of the first point of this JaxoObject. */
    private int y;

      //
     // transients will not be serialized to XML!
    //

    /** A path to paint this object. */
    private transient GeneralPath gp = new GeneralPath();

    /** Determines whether this JaxoObject is part of a group or not. */
    private transient boolean marked;

    /** A list of PropertChangeListeners for this object. */
    private transient PropertyChangeSupport pceListeners
            = new PropertyChangeSupport(this);

    /**
     * Add a PropertyChangeListener to this object.
     * Whenever a bound property changes, the listeners will get notified
     * with an Event carrying the corresponding bean name as PropertyName.
     * The same listener object may be added more than once, and will be
     * called as many times as it is added.
     * If listener is null, no exception is thrown and no action is taken.
     *
     * @param listener a listener to add.
     */
    public void addPropertyChangeListener(final PropertyChangeListener listener) {
        synchronized (this) {
            pceListeners.addPropertyChangeListener(listener);
        }
    }

    /**
     * Remove the given PropertyChangeListener from this Object.
     * If listener is null, or was never added, no exception is thrown
     * and no action is taken.
     *
     * @param listener the listener to remove.
     */
    public void removePropertyChangeListener(final PropertyChangeListener listener) {
        synchronized (this) {
            pceListeners.removePropertyChangeListener(listener);
        }
    }

    /**
     * Notify all registered listeners of a changed property.
     *
     * @param propertyName the name of the changed property.
     *      This should be equal to the corresponding bean name.
     * @param oldValue the old value.
     * @param newValue the new value.
     */
    protected void firePropertyChange(final String propertyName,
            final Object oldValue, final Object newValue) {
        pceListeners.firePropertyChange(propertyName, oldValue, newValue);
    }

    /**
     * Make all 'properties' in the bean info for class 'c' transient.
     *
     * @param c the class.
     * @param propertyNames set of properties to make transient.
     */
    protected static void setTransient(final Class<?> c, final String[] propertyNames) {
        BeanInfo b = null;

        try {
            b = Introspector.getBeanInfo(c);
        } catch (IntrospectionException ex) {
            throw new ExceptionInInitializerError(ex);
        }

        final PropertyDescriptor[] pp = b.getPropertyDescriptors();

        for (int i = 0; i < pp.length; i++) {
            for (int j = 0; j < propertyNames.length; j++) {
                if (propertyNames[j].equals(pp[i].getName())) {
                    pp[i].setValue("transient", Boolean.TRUE);
                }
            }
        }
    }

    private void readObject(final ObjectInputStream in)
        throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        gp = new GeneralPath();
        pceListeners = new PropertyChangeSupport(this);
    }

      //
     // Bean getter and setter methods
    //

    /** Returns the number of points it takes to draw this object.
     * @return 1.
     */
    public int getPointCount() {
        return POINT_COUNT;
    }

    /** Set point count; currently only used for bean creation of objects,
     * where setPoints is used. Throws IllegalArgumentException if
     * 'value' is not the same as {@link #getPointCount()}.
     * @param value The value to set.
     */
    public void setPointCount(final int value) {
        if (value != getPointCount()) {
            throw new IllegalArgumentException();
        }
    }

    /** All points, in order.
     *  @see #getPointCount
     *  @see #getX(int)
     *  @see #getY(int)
     *  @return The points of this object.
     */
    public Point[] getPoints() {
        final int count = getPointCount();

        final Point[] result = new Point[count];

        for (int i = 0; i < count; i++) {
            result[i] = new Point(getX(i), getY(i));
        }

        return result;
    }

    /** Set all points; currently only used for bean creation of objects.
     *  This implementation is based on 'setPointCount' and
     *  'setX'/'setY'.
     *  @param value The points to set.
     */
    public void setPoints(final Point[] value) {
        setPointCount(value.length);

        for (int i = 0; i < value.length; i++) {
            setX(i, value[i].x);
            setY(i, value[i].y);
        }
    }

    /** X coordinate of Point 0 <= index < getPointCount().
     * @param index The index of the point to get.
     * @return The x coordinate of the point at the given index.
     */
    public int getX(final int index) {
        if (index != SELECT_P1) {
            throw new IllegalArgumentException();
        }
        return x;
    }

    /** Y coordinate of Point 0 <= index < getPointCount().
     * @param index The index of the point to get.
     * @return The y coordinate of the point at the given index.
     */
    public int getY(final int index) {
        if (index != SELECT_P1) {
            throw new IllegalArgumentException();
        }
        return y;
    }

    /** Set X coordinate of Point 0 <= index < getPointCount() to 'value'.
     * Throws IllegalArgumentException if the index is not valid for the given object.
     * @param index The index of the point to set.
     * @param value The value to set.
     */
    public void setX(final int index, final int value) {
        if (index != SELECT_P1) {
            throw new IllegalArgumentException();
        }
        x = value;
    }

    /** Set Y coordinate of Point 0 <= index < getPointCount() to 'value'.
    * Throws IllegalArgumentException if the index is not valid for the given object.
     * @param index The index of the point to set.
     * @param value The value to set.
     */
    public void setY(final int index, final int value) {
        if (index != SELECT_P1) {
            throw new IllegalArgumentException();
        }
        y = value;
    }

    /** Returns the x coordinate of this object.
     * @return The x coordinate of this object.
     */
    public int getX() {
        return getX(SELECT_P1);
    }

    /** Sets the x coordinate of this object.
     * @param newX The x coordinate of this object.
     */
    public void setX(final int newX) {
        setX(SELECT_P1, newX);
    }

    /** Returns the y coordinate of this object.
     * @return The y coordinate of this object.
     */
    public int getY() {
        return getY(SELECT_P1);
    }

    /** Sets the y coordinate of this object.
     * @param newY The y coordinate of this object.
     */
    public void setY(final int newY) {
        setY(SELECT_P1, newY);
    }

     /** Returns the width of this object.
     * @return Returns 0.
     */
    public int getWidth() {
        return 0;
    }

    /** Returns the height of this object.
     * @return Returns 0.
     */
    public int getHeight() {
        return 0;
    }

    /**
     * Returns the color of this object.
     *
     * @return The color of this object.
     */
    public Color getColor() {
        return color;
    }

    /**
     * Sets the color of this object.
     * A corresponding PropertyChangeEvent is fired to all registered Listeners.
     *
     * @param c The color of this object.
     */
    public void setColor(final Color c) {
        final Color old = getColor();
        this.color = c;
        firePropertyChange("color", old, c);
    }

      //
     // convenience methods
    //

    /** Sets the x and y coordinate of this object.
     * @param newX The x coordinate of this object.
     * @param newY The y coordinate of this object.
     */
    public void setLocation(final int newX, final int newY) {
        setX(newX);
        setY(newY);
    }

    /** Returns the GeneralPath that can be used while painting this JaxoObject.
     * @return The  GeneralPath of this JaxoObject.
     */
    protected final GeneralPath getGeneralPath() {
        return gp;
    }

    /** Sets this JaxoObject as element of a group.
     * @param mark A boolean variable indicating whether this
     * JaxoObject should be set as an element of a group or not.
     */
    public final void setAsMarked(final boolean mark) {
        this.marked = mark;
    }

    /** Determines whether this JaxoObject is marked as an element
     * of a group or not.
     * @return Boolean variable telling whether this JaxoObject is part
     * of a group or not
     */
    public final boolean isMarked() {
        return marked;
    }

    /** Reset the coordinates of the object when it is moved
     * by deltaX and deltaY.
     * @param deltaX The x displacement.
     * @param deltaY The y displacement.
     */
    public void moveBy(final int deltaX, final int deltaY) {
        if ((deltaX != 0) || (deltaY != 0)) {
            setX(getX() + deltaX);
            setY(getY() + deltaY);
        }
    }

    /**
     * Moves the object to the given x position.
     * The first point of the object will move to the given position and the
     * other points are translated accordingly.
     *
     * @param newX the new x position.
     *
     * @deprecated unused. Use {@link #moveBy(int,int)} instead.
     */
    public void setXPosition(final int newX) {
        moveBy(newX - getX(), 0);
    }

    /**
     * Moves the object to the given y position.
     * The first point of the object will move to the given position and the
     * other points are translated accordingly.
     *
     * @param newY the new y position.
     *
     * @deprecated unused. Use {@link #moveBy(int,int)} instead.
     */
    public void setYPosition(final int newY) {
        moveBy(0, newY - getY());
    }

    /** Paints a visual aid for the user during dragging
     * (moving or resizing) of this JaxoObject.
     * @param g2 The graphics context to paint the visual aid.
     */
    public void paintVisualAid(final JaxoGraphics2D g2) {
        // By default, do nothing.
        // The objects that do paint a visual aid override this method.
    }

    /** Determines if this JaxoObject is a copy of the specified one.
     * @param comp The JaxoObject to compare against.
     * @return True if the JaxoObjects are identical.
     */
    public boolean isCopy(final JaxoObject comp) {
        boolean isCopy = false;
        final boolean equalColors = (getColor() == null
                    ? comp.getColor() == null
                    : getColor().equals(comp.getColor()));
        if ((comp.getX() == getX())
                && (comp.getY() == getY()) && equalColors) {
            isCopy = true;
        }
        return isCopy;
    }

    /** Sets all parameters from the given object to the current one.
     * @param temp The object to copy from.
     */
    public void copyFrom(final JaxoObject temp) {
        setX(temp.getX());
        setY(temp.getY());
        setColor(temp.getColor());
    }

    /** Restore state to the values of 'o'. The object 'o' must be an
     *  object of the same "type" obtained by {@link #copy()}.
     * @param o the object to copy from.
     */
    public void setState(final JaxoObject o) {
        copyFrom(o);
    }

    /** Determines the smallest distance of any of the handles
     * of this JaxoObject from the given point.
     * @param px The x coordinate of the test point.
     * @param py The y coordinate of the test point.
     * @return The smallest distance.
     */
    public float smallestDistanceTo(final int px, final int py) {
        final int distX = px - getX();
        final int distY = py - getY();
        return ((float) Math.sqrt((distX * distX) + (distY * distY)));
    }

    /** Returns an exact copy of the given JaxoObject.
        This implementation uses Object.clone(), so the only thing to
        do in subclasses is to recursively clone mutable object properties.
     * @return The copy of the given JaxoObject.
     */
    public JaxoObject copy() {
        try {
            final JaxoObject copy = (JaxoObject) super.clone();

            copy.gp = new GeneralPath();
            copy.marked = false;
            copy.pceListeners = new PropertyChangeSupport(copy);

            return copy;
        } catch (CloneNotSupportedException e) {
            throw new Error(e);
        }
    }

    /**
     * Determines if the given coordinates are contained within
     * the given handle at the given point of this object.
     *
     * @param select One of the constants SELECT_* defined for this objects.
     * @param clickX The x coordinate of the point.
     * @param clickY The y coordinate of the point.
     * @param handle The test handle.
     *
     * @return True if (clickX, clickY) is within the handle at
     * the selected point, false otherwise.
     */
    public boolean isAround(final int select, final int clickX, final int clickY, final JaxoHandle handle) {
        return handle.contains(getX(select), getY(select), clickX, clickY);
    }

    /** Determines which handle the user has selected to move/resize/edit
     * an object.
     * @param clickX The x coordinate where the mouse click has ocurred.
     * @param clickY The y coordinate where the mouse click has ocurred.
     * @param h A handle object.
     * @return One of the static variables SELECT_* defined for this
     * JaxoObject that specifies the handle which the user has clicked.
     */
    public int getGrabbedHandle(final int clickX, final int clickY, final JaxoHandle h) {

        if (isAround(SELECT_P1, clickX, clickY, h)) {
            return SELECT_P1;
        }

        return SELECT_NONE;
    }

    /**
     * Return a Rectangle that contains this object and all its handles.
     *
     * @param h the handle.
     * @return a bounding box for this object with all its handles.
     */
    public Rectangle2D getBoundsWithHandles(final JaxoHandle h) {
        final Rectangle2D result = getBounds2D();

        for (int i = 0; i < getPointCount(); i++) {
            result.add(h.getBounds(getX(i), getY(i)));
        }

        return result;
    }

    /** Returns the latex (ie axodraw) coordinates of the first point
     * of this JaxoObject.
     * @param scaleFactor A scalefactor.
     * @param canvasHeight The current height of the canvas.
     * @return A Point2D.
     */
    protected Point2D getLaTexLocation(final float scaleFactor, final int canvasHeight) {
        final Point2D startVec = new Point2D.Float();
        startVec.setLocation(getX() / scaleFactor,
            (canvasHeight - getY()) / scaleFactor);
        return startVec;
    }

    /**
     * Applies default values to this JaxoObject.
     * All fields except location points are initialized with values taken
     * from the {@link net.sf.jaxodraw.util.JaxoPrefs preferences}.
     */
    public void setPreferences() {
        setColor(JaxoColor.getColor(
                JaxoPrefs.getStringPref(JaxoPrefs.PREF_COLOR),
                JaxoPrefs.getIntPref(JaxoPrefs.PREF_COLORSPACE)));
    }

    /**
     * Returns the name of this JaxoObject.
     * This is equal to the class name without the package.
     *
     * @return the name of this JaxoObject.
     */
    public final String getName() {
        String className = getClass().getName();
        final int firstChar = className.lastIndexOf('.') + 1;
        if (firstChar > 0) {
            className = className.substring(firstChar);
        }
        return className;
    }

    /**
     * Invokes a setter method for the given parameter name using reflection.
     * The setter method is constructed via standard java bean rules, ie the
     * first character of 'name' is capitalized and the String "set" is prepended.
     *
     * @param name the name of the parameter to set.
     *          Must not be null or empty.
     * @param type the class type of the parameter.
     * @param value the object value of the parameter to set.
     * @param failOnError false if any Exceptions should be swallowed.
     */
    public final void setParameter(final String name, final Class<?> type, final Object value,
            final boolean failOnError) {

        if (name.length() == 0) {
            throw new IllegalArgumentException("Empty parameter name");
        }

        final String method = setterName(name);

        try {
            final Method setter = getClass().getMethod(method, new Class<?>[] {type});
            setter.invoke(this, new Object[] {value});
        } catch (InvocationTargetException e) {
            if (failOnError) {
                throw new UnsupportedOperationException(e);
            }
        } catch (NoSuchMethodException e) {
            if (failOnError) {
                throw new UnsupportedOperationException(e);
            }
        } catch (IllegalAccessException e) {
            if (failOnError) {
                throw new UnsupportedOperationException(e);
            }
        }
    }

    /**
     * Invokes a setter method for the given parameter name using reflection.
     * The setter method is constructed via standard java bean rules, ie the
     * first character of 'name' is capitalized and the String "set" is prepended.
     *
     * This method throws an Exception if the current JaxoObject does not
     * support the operation, eg if no setter exists for the given parameter.
     *
     * @param name the name of the parameter to set.
     * @param type the class type of the parameter.
     * @param value the object value of the parameter to set.
     */

    public final void setParameter(final String name, final Class<?> type, final Object value) {
        setParameter(name, type, value, true);
    }

    /**
     * Invokes a setter method for the given parameter name and value.
     *
     * This method throws an Exception if the current JaxoObject does not
     * support the operation, eg if no setter exists for the given parameter.
     *
     * @param name the name of the parameter to set.
     * @param value the object value of the parameter to set.
     * @see #setParameter(String,Class,Object)
     */
    public final void setParameter(final String name, final int value) {
        setParameter(name, Integer.TYPE, Integer.valueOf(value));
    }

    /**
     * Invokes a setter method for the given parameter name and value.
     *
     * This method throws an Exception if the current JaxoObject does not
     * support the operation, eg if no setter exists for the given parameter.
     *
     * @param name the name of the parameter to set.
     * @param value the object value of the parameter to set.
     * @see #setParameter(String,Class,Object)
     */
    public final void setParameter(final String name, final float value) {
        setParameter(name, Float.TYPE, Float.valueOf(value));
    }

    /**
     * Invokes a setter method for the given parameter name and value.
     *
     * This method throws an Exception if the current JaxoObject does not
     * support the operation, eg if no setter exists for the given parameter.
     *
     * @param name the name of the parameter to set.
     * @param value the object value of the parameter to set.
     * @see #setParameter(String,Class,Object)
     */
    public final void setParameter(final String name, final boolean value) {
        setParameter(name, Boolean.TYPE, Boolean.valueOf(value));
    }

    /**
     * Invokes a setter method for the given parameter name and value.
     *
     * This method throws an Exception if the current JaxoObject does not
     * support the operation, eg if no setter exists for the given parameter.
     *
     * @param name the name of the parameter to set.
     * @param value the object value of the parameter to set.
     * @see #setParameter(String,Class,Object)
     */
    public final void setParameter(final String name, final Color value) {
        setParameter(name, Color.class, value);
    }

    /**
     * Invokes a setter method for the given parameter name and value.
     *
     * This method throws an Exception if the current JaxoObject does not
     * support the operation, eg if no setter exists for the given parameter.
     *
     * @param name the name of the parameter to set.
     * @param value the object value of the parameter to set.
     * @see #setParameter(String,Class,Object)
     */
    public final void setParameter(final String name, final String value) {
        setParameter(name, String.class, value);
    }

    private String setterName(final String paramName) {
        return "set" + capitalize(paramName);
    }

    private String capitalize(final String s) {
        if (Character.isUpperCase(s.charAt(0))) {
            return s;
        }

        char[] chars = s.toCharArray();
        chars[0] = Character.toUpperCase(chars[0]);

        return new String(chars);
    }

    /**
     * Set a new property. The propertyName of the PropertyChangeEvent
     * must match one of the bean properties of this object, otherwise
     * the event is ignored.
     *
     * @param evt the PropertyChangeEvent.
     */
    public void propertyChange(final PropertyChangeEvent evt) {
        final String name = evt.getPropertyName();
        final Object newValue = evt.getNewValue();
        Class<?> clazz = newValue.getClass();

        // isn't there a better way to convert these?
        if (clazz == Float.class) {
            clazz = Float.TYPE;
        } else if (clazz == Integer.class) {
            clazz = Integer.TYPE;
        } else if (clazz == Boolean.class) {
            clazz = Boolean.TYPE;
        }

        setParameter(name, clazz, newValue, false);
    }

      //
     // Methods for the Shape interface
    //

    /**
     * Tests if the specified coordinates are inside the
     * boundary of this JaxoObject.
     *
     * @param x0 the x coordinate to test.
     * @param y0 the y coordinate to test.
     * @return true if {@link #getBounds()} contains the given point.
     * @see java.awt.Shape#contains(double,double).
     */
    public boolean contains(final double x0, final double y0) {
        return getBounds().contains(x0, y0);
    }

    /**
     * Tests if the specified rectangular shape is entirely inside the
     * boundary of this JaxoObject.
     *
     * @param x0 the x coordinate to test.
     * @param y0 the y coordinate to test.
     * @param w the width of the rectangle.
     * @param h the height of the rectangle.
     * @return true if {@link #getBounds()} contains the given rectangle.
     * @see java.awt.Shape#contains(double,double,double,double).
     */
    public boolean contains(final double x0 , final double y0 , final double w, final double h) {
        return getBounds().contains(x0, y0, w, h);
    }

    /**
     * Tests if the specified point is inside the
     * boundary of this JaxoObject.
     *
     * @param p the point to test.
     * @return true if {@link #getBounds()} contains the given point.
     * @see java.awt.Shape#contains(java.awt.geom.Point2D).
     */
    public boolean contains(final Point2D p) {
        return getBounds().contains(p);
    }

    /**
     * Tests if the specified rectangle is entirely inside the
     * boundary of this JaxoObject.
     *
     * @param r the rectangle to test.
     * @return true if {@link #getBounds()} contains the given rectangle.
     * @see java.awt.Shape#contains(java.awt.geom.Rectangle2D).
     */
    public boolean contains(final Rectangle2D r) {
        return getBounds().contains(r);
    }

    /**
     * Return a high-precision bounding box of this JaxoObject.
     *
     * @return Rectangle2D
     * @see java.awt.Shape#getBounds2D().
     */
    public Rectangle2D getBounds2D() {
     return getBounds().getBounds2D();
    }

    /**
     * Returns an iterator that iterates along the the shape boundary
     * of this JaxoObject.
     *
     * @param at an optional AffineTransformation to be applied to be
     * applied to the coordinates of this object, or null for untransformed
     * coordinates.
     * @return PathIterator
     * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform).
     */
    public PathIterator getPathIterator(final AffineTransform at) {
        return getGeneralPath().getPathIterator(at);
    }

    /**
     * Returns an iterator that iterates along the the shape boundary
     * of this JaxoObject.
     *
     * @param at an optional AffineTransformation to be applied to be
     * applied to the coordinates of this object, or null for untransformed
     * coordinates.
     * @param flatness the flatness parameter.
     * @return PathIterator
     * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform,double).
     */
    public PathIterator getPathIterator(final AffineTransform at, final double flatness) {
        return getGeneralPath().getPathIterator(at, flatness);
    }

    /**
     * Test if the interior of this JaxoObject intersects the interior of the
     * given rectangular area.
     *
     * @param x0 the x-coordinate of the test rectangle.
     * @param y0 the y-coordinate of the test rectangle.
     * @param w the width of the test rectangle.
     * @param h the height of the test rectangle.
     * @return true if the shapes intersect.
     * @see java.awt.Shape#intersects(double,double,double,double).
     */
    public boolean intersects(final double x0 , final double y0 , final double w, final double h) {
        return getBounds().intersects(x0, y0, w, h);
    }

    /**
     * Test if the interior of this JaxoObject intersects the interior of the
     * given rectangular area.
     *
     * @param r the rectangle to test.
     * @return true if the shapes intersect.
     * @see java.awt.Shape#intersects(java.awt.geom.Rectangle2D).
     */
    public boolean intersects(final Rectangle2D r) {
        return getBounds().intersects(r);
    }

      //
     // Abstract methods
    //

    /** The method that paints the JaxoObject.
     * @param g2 The graphics context where the object has to be painted.
     */
    public abstract void paint(JaxoGraphics2D g2);

    /** Paints the handles of this JaxoObject that allow
     * to move/resize/edit it.
     * @param g2 The corresponding graphics context.
     * @param h A handle object to be used for painting.
     * @param editMode The edit mode that the handles are being painted in.
     */
    public abstract void paintHandles(JaxoGraphics2D g2, JaxoHandle h,
        int editMode);

    /** Determines if the object can be selected (moved, edited, etc)
     * from the given point in a given edit mode.
     * @param handle One of the static variables SELECT_* defined for
     * this JaxoObject.
     * @param mode The current edit mode.
     * @return True if the object may be selected from the given point,
     * false otherwise.
     */
    public abstract boolean canBeSelected(int handle, int mode);

    /** The LaTeX command that is necessary to draw the given JaxoObject
     * using the axodraw.sty package.
     * @param scale A scale factor to translate Java coordinates to
     * LaTeX coordinates.
     * @param canvasDim The current dimension of the canvas.
     * @return The corresponding axodraw LaTeX command.
     */
    public abstract String latexCommand(float scale, Dimension canvasDim);

    /** The LaTeX command that sets the width for this JaxoObject,
     * using the axodraw.sty package.
     * @return The corresponding LaTeX command.
     */
    public abstract String latexWidth();

    /** Rescale this JaxoObject by the scale factor scale,
     * keeping the point (orx, ory) fixed.
     * @param orx The x-coordinate of the fixed point.
     * @param ory The y-coordinate of the fixed point.
     * @param scale The scale parameter.
     */
    public abstract void rescaleObject(int orx, int ory, float scale);

    /**
     * Initializes the given editPanel to edit properties of this JaxoObject.
     *
     * @param editPanel the panel to prepare.
     */
    public abstract void prepareEditPanel(JaxoObjectEditPanel editPanel);
}
