/*

Copyright 2006 Rene Grothmann, modified by Eric Hakenholz

This file is part of C.a.R. software.

C.a.R. is a free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.

C.a.R. 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
t
 */
package rene.zirkel;

// file: ZirkelCanvas.java
import de.erichseifert.vectorgraphics2d.VectorGraphics2D;
import eric.GUI.ZDialog.ZDialog;
import eric.GUI.palette.JIconMouseAdapter;
import eric.GUI.palette.PaletteManager;
import eric.GUI.pipe_tools;
import eric.JGeneralMenuBar;
import eric.JSprogram.ScriptItem;
import eric.JSprogram.ScriptItemsArray;
import eric.JSprogram.ScriptPanel;
import eric.JSprogram.ScriptThread;
import eric.JZirkelCanvas;
import eric.animations.AnimationPanel;
import eric.bar.JProperties;
import eric.bar.JPropertiesBar;
import eric.controls.JCanvasPanel;
import eric.controls.JControlsManager;
import eric.jobs.JobManager;
import eric.macros.MacroTools;
import eric.restrict.RestrictContainer;
import eric.restrict.RestrictItems;
import java.awt.BasicStroke;
import java.awt.CheckboxMenuItem;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import static java.lang.Double.NaN;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.SwingUtilities;
import rene.dialogs.Warning;
import rene.gui.Global;
import rene.gui.MyCheckboxMenuItem;
import rene.gui.MyMenuItem;
import rene.util.FileName;
import rene.util.MyVector;
import rene.util.parser.StringParser;
import rene.util.sort.Sorter;
import rene.util.xml.XmlReader;
import rene.util.xml.XmlTag;
import rene.util.xml.XmlTagPI;
import rene.util.xml.XmlTagText;
import rene.util.xml.XmlTree;
import rene.util.xml.XmlWriter;
import rene.zirkel.construction.ChangedListener;
import rene.zirkel.construction.Construction;
import rene.zirkel.construction.ConstructionDisplayPanel;
import rene.zirkel.construction.ConstructionException;
import rene.zirkel.construction.Count;
import rene.zirkel.construction.Interpreter;
import rene.zirkel.construction.Selector;
import rene.zirkel.constructors.IntersectionConstructor;
import rene.zirkel.constructors.ObjectConstructor;
import rene.zirkel.expression.Expression;
import rene.zirkel.expression.InvalidException;
import rene.zirkel.graphics.Drawing;
import rene.zirkel.graphics.LatexOutput;
import rene.zirkel.graphics.MainGraphics;
import rene.zirkel.graphics.MyGraphics;
import rene.zirkel.graphics.TrackPainter;
import rene.zirkel.listener.AddEventListener;
import rene.zirkel.listener.DoneListener;
import rene.zirkel.listener.StatusListener;
import rene.zirkel.macro.Macro;
import rene.zirkel.macro.MacroBar;
import rene.zirkel.macro.MacroContextualPopupMenu;
import rene.zirkel.macro.MacroItem;
import rene.zirkel.macro.MacroMenu;
import rene.zirkel.macro.MacroRunner;
import rene.zirkel.objects.AngleObject;
import rene.zirkel.objects.AreaObject;
import rene.zirkel.objects.AxisObject;
import rene.zirkel.objects.ConstructionObject;
import rene.zirkel.objects.EquationXYObject;
import rene.zirkel.objects.ExpressionObject;
import rene.zirkel.objects.FixedAngleObject;
import rene.zirkel.objects.FixedCircleObject;
import rene.zirkel.objects.FunctionObject;
import rene.zirkel.objects.IntersectionObject;
import rene.zirkel.objects.JLocusTrackObject;
import rene.zirkel.objects.MoveableObject;
import rene.zirkel.objects.PointObject;
import rene.zirkel.objects.PointonObject;
import rene.zirkel.objects.PrimitiveCircleObject;
import rene.zirkel.objects.PrimitiveLineObject;
import rene.zirkel.objects.QuadricObject;
import rene.zirkel.objects.RayObject;
import rene.zirkel.objects.SegmentObject;
import rene.zirkel.objects.TwoPointLineObject;
import rene.zirkel.objects.UserFunctionObject;
import rene.zirkel.objects.VectorObject;
import rene.zirkel.structures.CoordinatesXY;
import rene.zirkel.structures.MagnetObj;
import rene.zirkel.tools.BinderTool;
import rene.zirkel.tools.BreakpointAnimator;
import rene.zirkel.tools.EditTool;
import rene.zirkel.tools.JSmacroTool;
import rene.zirkel.tools.LabelMover;
import rene.zirkel.tools.MagnetTool;
import rene.zirkel.tools.MetaMover;
import rene.zirkel.tools.MoverTool;
import rene.zirkel.tools.ObjectTracker;
import rene.zirkel.tools.SelectTool;
import rene.zirkel.tools.SetAwayTool;
import rene.zirkel.tools.SetCurveCenterTool;
import rene.zirkel.tools.SetFixedAngle;
import rene.zirkel.tools.SetFixedCircle;
import rene.zirkel.tools.SetRangeTool;
import rene.zirkel.tools.SetTargetsTool;
import rene.zirkel.tools.Tracker;
import rene.zirkel.tools.UniversalTracker;
import rene.zirkel.tools.ZoomerTool;
import rene.zirkel.tools.nullTool;

/**
 * @author Rene Main class, doing most of the work in the C.a.R. application.
 *         This canvas handles mouse and keyboard input, displays the
 *         construction and has tools to save and load constructions.
 */
// with addons by Dibs (patdeb) for 3D

public class ZirkelCanvas extends javax.swing.JPanel implements MouseListener,
        MouseMotionListener, ItemListener, AddEventListener, ActionListener,
        ChangedListener, MouseWheelListener // the canvas to do the construction
{

    private double minfontsize, minpointsize, minlinesize, arrowsize, selectionsize, monkeyspeed, gridopacity,
            digits_lengths, digits_edit, digits_angles, colorbackgroundx, colorbackgroundy,
            colorbackgroundPal, axis_color, axis_thickness;
    private boolean axis_show, axis_labels, axis_with_grid;
    private String colorbackground;
    private static JIconMouseAdapter JIMA=new JIconMouseAdapter();
    ObjectConstructor OC=new MoverTool();
    // the current object constructor
    Construction C=new Construction(), COriginal;
    // the construction (collection of Constructor-Objects)
    StatusListener SL=null;
    // for displaying a status message
    public Image I=null;
    MyGraphics IG;
    public UniversalTracker UniversalTrack=null;
    Image Background=null;
    FontMetrics FM;
//    public double PointSize=4.0; // Size of a point
//    double MinPointSize=4.0; // Default minimal point size
//    public double FontSize=12.0; // Size of font
//    double MinFontSize=12.0; // Default minimal font size
    public int IW=0, IH=0; // Image and its dimensions
    public double Xmin, DX, Ymin, DY;
    boolean ShowHidden=false; // show hidden objects
//    Frame F=new Frame();
    boolean ReadOnly;
    boolean AllowRightMouse=true;
    public MacroContextualPopupMenu PM;
    CheckboxMenuItem CheckboxHidden;
    MenuItem Replay, Empty;
    boolean Job=false; // this is a job (save as such)
    String Last=""; // Last displayed job object
    Vector Targets=new Vector(); // Target names
    ConstructionObject TargetO[], TargetS[]; // Target objects and solutions
    int ShowColor=0; // 0=all, 1=green etc. (black displays always)
    static public double // factors for rounding
            EditFactor=1000000.0, LengthsFactor=100.0, AnglesFactor=1.0;
    public boolean Visual=Global.getParameter("options.visual", true);
    boolean All;
    boolean Interactive=true;
    public JControlsManager JCM;
    private ScriptPanel Scripts;
    private AnimationPanel Animations;
    private JobManager Exercise;
    private RestrictContainer RestrictDialog=null; //GUI for Restricted icons editing
    private RestrictItems Restrict_items; // Restricted icons and palette zones
    private int OwnerWindowWidth, OwnerWindowHeight;
    private boolean islibrarymacrovisible=true;
//    public JUndo JU=new JUndo();
//    private dock DOCK;
    private boolean paintCalled=false;
    private Rectangle SelectionRectangle=null;
    private Color SelectionRectangleBackground=new Color(159, 166, 255, 30);
    private Color SelectionRectangleBorder=new Color(159, 166, 255);
    private int SelectionX, SelectionY;
    private ArrayList<ConstructionObject> MultipleSelection=new ArrayList();
    private Rectangle CopyRectangle=null;
    private Color CopyRectangleBackground=new Color(128, 177, 225, 100);
//    private Color CopyRectangleBackground=new Color(150, 150, 150, 150);
    private Color CopyRectangleBorder=new Color(0, 65, 129);
    private int CopyX, CopyY;
//    private CopyHandler copyE=null, copyW=null, copyN=null, copyS=null;
    private Color DPBackground=new Color(250, 250, 250);
    private Color DPBorder=new Color(163, 132, 0);
    private float DPStroke=3f;
//    private boolean
    private boolean DontPaint=false;
    private boolean firstLoad=true;
    private pm.Client.ClientNetworkTools cnt = null;

    public boolean isEuclidian() {
        return C.isEuclidian();
    }

    public void setEuclidian(boolean b) {
        C.setEuclidian(b);
    }

    public boolean getDontPaint() {
        return DontPaint;
    }

    public void setDontPaint(boolean b) {
        DontPaint=b;
    }

    /**
     * Initiate an empty construction. The display may have a popup menu (only
     * for readonly displays).
     *
     * @param readonly
     *            User cannot change the construction.
     * @param replay
     *            Replay option in popup menu.
     * @param hidden
     *            Show hidden option in popup menu.
     */
    public ZirkelCanvas(final boolean readonly, final boolean replay,
            final boolean hidden) {
        ReadOnly=readonly;
        AllowRightMouse=!readonly;

        // setLayout(new BoxLayout(this,BoxLayout.X_AXIS));
        setLayout(null);

        UniversalTrack=new UniversalTracker(this);
        JCM=new JControlsManager(this);

        if (Global.getParameter("options.nopopupmenu", false)) {
            PM=null;
        } else if (ReadOnly) {
            PM=new MacroContextualPopupMenu();
            CheckboxHidden=new MyCheckboxMenuItem(Global.name("popup.hidden"));
            CheckboxHidden.addItemListener(this);
            if (hidden) {
                PM.add(CheckboxHidden);
            }
            Replay=new MyMenuItem(Global.name("popup.replay"));
            Replay.addActionListener(this);
            if (replay) {
                PM.add(Replay);
            }
            if (hidden||replay) {
                add(PM);
            } else {
                PM=null;
            }
        } else {
            PM=new MacroContextualPopupMenu();
            Empty=new MyMenuItem(Global.name("popup.empty"));
            add(PM);
        }

        C.addChangedListener(this);
        clear();
        updateDigits();
        C.addAddEventListener(this);
        addMouseWheelListener(this);
        addMouseMotionListener(JIMA);

        /* Preferences which must be apply to
         * Every new Canvas :
         */
        Global.setParameter("axis_show", false);
        Global.setParameter("colorbackground", getColor("245,245,245"));
        Global.setParameter("colorbackgroundx", 139);
        Global.setParameter("colorbackgroundy", 9);
        Global.setParameter("colorbackgroundPal", 4);

        axis_show=false;
        //colorbackground="245,245,245";
        colorbackgroundx=139;
        colorbackgroundy=9;
        colorbackgroundPal=4;

        Scripts=new ScriptPanel(this);
        Animations=new AnimationPanel(this);
        Exercise=new JobManager(this);
        Restrict_items=new RestrictItems(this);
//        add(Animations);
//        add(Scripts);
//        add(RestrictDialog);
    }

    public ZirkelCanvas(final boolean readonly) {
        this(readonly, true, true);
    }

    public ZirkelCanvas() {
        this(false, true, true);
    }

    public double getOne() {
        return C.getOne();
    }

    public void scaleLocalPreferences(double scale) {
        minfontsize=minfontsize*scale;
        minpointsize=minpointsize*scale;
        minlinesize=minlinesize*scale;
        arrowsize=arrowsize*scale;
        gridopacity=gridopacity*scale;
        C.setOne(scale);
    }

    public void getLocalPreferences() {
        minfontsize=Global.getParameter("minfontsize", 12);
        minpointsize=Global.getParameter("minpointsize", 3);
        minlinesize=Global.getParameter("minlinesize", 1);
        arrowsize=Global.getParameter("arrowsize", 10);
        selectionsize=Global.getParameter("selectionsize", 8);
        monkeyspeed=Global.getParameter("monkeyspeed", 20);
        gridopacity=Global.getParameter("gridopacity", 20);
        digits_lengths=Global.getParameter("digits.lengths", 2);
        digits_edit=Global.getParameter("digits.edit", 4);
        digits_angles=Global.getParameter("digits.angles", 2);
        colorbackground=Global.getParameter("colorbackground", "245,245,245");
        colorbackgroundx=Global.getParameter("colorbackgroundx", 139);
        colorbackgroundy=Global.getParameter("colorbackgroundy", 9);
        colorbackgroundPal=Global.getParameter("colorbackgroundPal", 4);
        axis_color=Global.getParameter("axis_color", 0);
        axis_thickness=Global.getParameter("axis_thickness", 0);
        axis_show=Global.getParameter("axis_show", false);
        axis_labels=Global.getParameter("axis_labels", true);
        axis_with_grid=Global.getParameter("axis_with_grid", true);
        C.setOne(1.0);
    }

    public void setLocalPreferences() {
        Global.setParameter("minfontsize", minfontsize);
        Global.setParameter("minpointsize", minpointsize);
        Global.setParameter("minlinesize", minlinesize);
        Global.setParameter("arrowsize", arrowsize);
        Global.setParameter("selectionsize", selectionsize);
        Global.setParameter("gridopacity", gridopacity);
        Global.setParameter("monkeyspeed", monkeyspeed);
        Global.setParameter("digits.lengths", digits_lengths);
        Global.setParameter("digits.edit", digits_edit);
        Global.setParameter("digits.angles", digits_angles);
        Global.setParameter("colorbackground", colorbackground);
        Global.setParameter("colorbackgroundx", colorbackgroundx);
        Global.setParameter("colorbackgroundy", colorbackgroundy);
        Global.setParameter("colorbackgroundPal", colorbackgroundPal);
        Global.setParameter("axis_color", axis_color);
        Global.setParameter("axis_thickness", axis_thickness);
        Global.setParameter("axis_show", axis_show);
        Global.setParameter("axis_labels", axis_labels);
        Global.setParameter("axis_with_grid", axis_with_grid);
    }

    public void XmlTagWriter(final XmlWriter xml) {
        xml.startTagStart("Windowdim");
        xml.printArg("w", ""+pipe_tools.getWindowSize().width);
        xml.printArg("h", ""+pipe_tools.getWindowSize().height);
        xml.finishTagNewLine();
        xml.startTagStart("Preferences");
        xml.printArg("minfontsize", ""+(int) minfontsize);
        xml.printArg("minpointsize", ""+(int) minpointsize);
        xml.printArg("minlinesize", ""+(int) minlinesize);
        xml.printArg("arrowsize", ""+(int) arrowsize);
        xml.printArg("selectionsize", ""+(int) selectionsize);
        xml.printArg("monkeyspeed", ""+(int) monkeyspeed);
        xml.printArg("gridopacity", ""+(int) gridopacity);
        xml.printArg("digits.lengths", ""+(int) digits_lengths);
        xml.printArg("digits.edit", ""+(int) digits_edit);
        xml.printArg("digits.angles", ""+(int) digits_angles);
        xml.printArg("colorbackground", ""+ colorbackground);
        xml.printArg("colorbackgroundx", ""+(int) colorbackgroundx);
        xml.printArg("colorbackgroundy", ""+(int) colorbackgroundy);
        xml.printArg("colorbackgroundPal", ""+(int) colorbackgroundPal);
        xml.printArg("fig3D", String.valueOf(is3D()));
        xml.printArg("figDP", String.valueOf(isDP()));
        xml.finishTagNewLine();
    }

    public void XmlTagReader(final XmlTag tag) {
        if ((!(pipe_tools.isApplet()))&&(tag.name().equals("Windowdim"))&&(tag.hasParam("w"))&&(tag.hasParam("h"))) {
            OwnerWindowWidth=Integer.parseInt(tag.getValue("w"));
            OwnerWindowHeight=Integer.parseInt(tag.getValue("h"));
        } else if (tag.name().equals("Preferences")) {
            if ((tag.hasParam("minfontsize"))) {
                Global.setParameter("minfontsize", tag.getValue("minfontsize"));
            }
            if ((tag.hasParam("minpointsize"))) {
                Global.setParameter("minpointsize", tag.getValue("minpointsize"));
            }
            if ((tag.hasParam("minlinesize"))) {
                Global.setParameter("minlinesize", tag.getValue("minlinesize"));
            }
            if ((tag.hasParam("arrowsize"))) {
                Global.setParameter("arrowsize", tag.getValue("arrowsize"));
            }
            if ((tag.hasParam("selectionsize"))) {
                Global.setParameter("selectionsize", tag.getValue("selectionsize"));
            }
            if ((tag.hasParam("monkeyspeed"))) {
                Global.setParameter("monkeyspeed", tag.getValue("monkeyspeed"));
            }
            if ((tag.hasParam("gridopacity"))) {
                Global.setParameter("gridopacity", tag.getValue("gridopacity"));
            }
            if ((tag.hasParam("digits.lengths"))) {
                Global.setParameter("digits.lengths", tag.getValue("digits.lengths"));
            }
            if ((tag.hasParam("digits.edit"))) {
                Global.setParameter("digits.edit", tag.getValue("digits.edit"));
            }
            if ((tag.hasParam("digits.angles"))) {
                Global.setParameter("digits.angles", tag.getValue("digits.angles"));
            }
            if ((tag.hasParam("colorbackground"))) {
                Global.setParameter("colorbackground", getColor(tag.getValue("colorbackground")));
            }
            if ((tag.hasParam("colorbackgroundx"))) {
                Global.setParameter("colorbackgroundx", tag.getValue("colorbackgroundx"));
            }
            if ((tag.hasParam("colorbackgroundy"))) {
                Global.setParameter("colorbackgroundy", tag.getValue("colorbackgroundy"));
            }
            if ((tag.hasParam("colorbackgroundPal"))) {
                Global.setParameter("colorbackgroundPal", tag.getValue("colorbackgroundPal"));
            }
            if ((tag.hasParam("fig3D"))) {
                set3D(Boolean.valueOf(tag.getValue("fig3D")).booleanValue());
            }
            if ((tag.hasParam("figDP"))) {
                setDP(Boolean.valueOf(tag.getValue("figDP")).booleanValue());
            }
            getLocalPreferences();
        }
    }

    // get a Color object from a string like "231,145,122"
    private Color getColor(final String s) {
        final StringParser p=new StringParser(s);
        p.replace(',', ' ');
        int red, green, blue;
        red=p.parseint();
        green=p.parseint();
        blue=p.parseint();
        return new Color(red, green, blue);
    }

    public void setMode(int i) {
        C.setMode(i);
    }

    public int getMode() {
        return C.getMode();
    }

    public boolean is3D() {
        return C.is3D();
    }

    public boolean isDP() {
        return C.isDP();
    }

    public void set3D(boolean b) {
        C.set3D(b);
    }

    public void setDP(boolean b) {
        C.setDP(b);
    }

    public void deleteAxisObjects() {
        C.deleteAxisObjects();
        reloadCD();
        repaintCD();
    }

    public void createAxisObjects() {
        C.createAxisObjects();
    }

    private void setShowHideParameters(final String s, final boolean b) {
        Global.setParameter("options.point."+s, b);
        Global.setParameter("options.segment."+s, b);
        Global.setParameter("options.line."+s, b);
        Global.setParameter("options.circle."+s, b);
        Global.setParameter("options.angle."+s, b);
        Global.setParameter("options.text."+s, b);
        Global.setParameter("options.locus."+s, b);
        Global.setParameter("options."+s, b);
        // setShowNames(true);
    }

    public void JSsend(final String s) {
        if (s.equals("shownames")) {
            setShowHideParameters("shownames", true);
        } else if (s.equals("hidenames")) {
            setShowHideParameters("shownames", false);
        } else if (s.equals("showvalues")) {
            setShowHideParameters("showvalues", true);
        } else if (s.equals("hidevalues")) {
            setShowHideParameters("showvalues", false);
        } else {
            try {
                C.interpret(this, s, "");
            } catch (final Exception ex) {
            }
        }
    }

    public String JSreceive(final String s) {
        String r="ERROR...";
        try {
            double rep;
            final Expression exp=new Expression(s, C, null);
            rep=exp.getValue();
            r=String.valueOf(rep);
        } catch (final ConstructionException ex) {
            ex.printStackTrace();
        }
        return r;
    }
    public Dimension UseSize=null;

    @Override
    public Dimension getMinimumSize() {
        if (Background==null||!Global.getParameter("background.usesize", false)) {
            if (UseSize!=null) {
                return UseSize;
            } else {
                return new Dimension(600, 600);
            }
        } else {
            final int iw=Background.getWidth(this);
            if (iw<10) {
                return new Dimension(600, 600);
            }
            final int ih=Background.getHeight(this);
            if (Global.getParameter("background.usewidth", false)) {
                final int w=getSize().width, h=(int) ((double) ih/iw*w+0.5);
                return new Dimension(w, h);
            } else {
                return new Dimension(iw, ih);
            }
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return getMinimumSize();
    }

    public final void updateDigits() {
        EditFactor=Math.pow(10, Global.getParameter("digits.edit", 5));
        LengthsFactor=Math.pow(10, Global.getParameter("digits.lengths", 5));
        AnglesFactor=Math.pow(10, Global.getParameter("digits.angles", 0));
    }

    @Override
    public void itemStateChanged(final ItemEvent e) {
        if (e.getSource()==CheckboxHidden) {
            ShowHidden=CheckboxHidden.getState();
            repaint();
        }
    }
    ZirkelCanvasInterface ZCI;

    public void setZirkelCanvasListener(final ZirkelCanvasInterface zci) {
        ZCI=zci;
    }

    public String loadImage() {
        return ZCI.loadImage();
    }

    public Image doLoadImage(final String filename) {
        return ZCI.doLoadImage(filename);
    }

    @Override
    public void actionPerformed(final ActionEvent e) {
        if (!Interactive) {
            return;
        }
        if (e.getSource()==Replay) {
            if (ZCI!=null) {
                ZCI.replayChosen();
            }
        } else {
            final Enumeration en=Macros.elements();
            while (en.hasMoreElements()) {
                final MacroItem m=(MacroItem) en.nextElement();
                if (m.I==e.getSource()) {
                    if (ZCI!=null) {
                        ZCI.runMacro(m.M);
                    }
                    break;
                }
            }
        }
    }

    // Some transfer functions to compute screen coordinates etc.
    public double col(final double x) {
        return (x-Xmin)/DX*IW;
    }

    public double row(final double y) {
        return IH-(y-Ymin)/DY*IH;
    }

    public int width() {
        return IW;
    }

    public int height() {
        return IH;
    }

    public double x(final int c) {
        return Xmin+DX*c/IW;
    }

    public double y(final int r) {
        return Ymin+DY*(IH-r)/IH;
    }

    public double dx(final int c) {
        return DX*c/IW;
    }

    public double dy(final int r) {
        return DY*r/IH;
    }

    public double dx(final double c) {
        return DX*c/IW;
    }

    public double dy(final double r) {
        return DY*r/IH;
    }

    public double maxX() {
        return Xmin+DX;
    }

    public double minX() {
        return Xmin;
    }

    public double maxY() {
        return Ymin+DY;
    }

    public double minY() {
        return Ymin;
    }

    public boolean isInside(final double x, final double y) {
        return x>=Xmin&&x<Xmin+DX&&y>=Ymin&&y<Ymin+DY;
    }

    public double dCenter(final double x, final double y) {
        final double dx=x-(Xmin+DX/2), dy=y-(Ymin+DY/2);
        return Math.sqrt(dx*dx+dy*dy)/Math.max(DX/2, DY/2);
    }

    public void recompute() {
        if (IH<IW) {
            Xmin=C.getX()-C.getW();
            DX=C.getW()*2;
            DY=DX/IW*IH;
            Ymin=C.getY()-DY/2;
        } else {
            Ymin=C.getY()-C.getW();
            DY=C.getW()*2;
            DX=DY/IH*IW;
            Xmin=C.getX()-DY/2;
        }
        C.setH(DY);
        if (DX>0) {
            C.setPixel(getSize().width/DX);
        }
    }
    DoneListener DL;

    public void setDoneListener(final DoneListener dl) {
        DL=dl;
    }

    /**
     * Add an item to the construction and re-paint the construction.
     */
    public void addObject(final ConstructionObject o) { // called by the ObjectConstructor
	if(!Preview && !(o instanceof AreaObject)){
	    update_distant(o, 1);
	}
        C.add(o);
        if (Preview) {
            o.setIndicated(true);
            o.setSelectable(false);
        }
        C.updateCircleDep();
    }

    /**
     * A construction added an item. Check, if it solves a problem and notify
     * the DoneListener, if so.
     */
    @Override
    public void added(final Construction c, final ConstructionObject o) {

        if (isPreview()) {
            return;
        }
        if (C.loading()) {
            return;
        }

        if (o instanceof UserFunctionObject) {
            JPropertiesBar.EditObject(o);
        }

    }
    int Moved=0;
    boolean Dragging=false, RightClicked=false;
    boolean Control=false; // Control-Taste bei letztem Mausdruck aktiv?
    public boolean SmartBoardPreview=false;
    private boolean MouseAllowed=true;

    public void setMouseAllowed(boolean ma) {
        MouseAllowed=ma;
    }

    // mouse events:
    @Override
    public synchronized void mousePressed(final MouseEvent e) {
        if (!Interactive) {
            return;
        }
        if (!MouseAllowed) {
            return;
        }
        if (JControlsManager.createControl(this, e)) {
            return;
        }

        SmartBoardPreview=false;
        clearIndicated();
        clearPreview();
        repaint();
        requestFocus();
        Dragging=false;
        RightClicked=false;
        Moved=0;
        if (e.isMetaDown()&&AllowRightMouse) // right mouse button!
        {
            if (!ReadOnly) {
                ConstructionObject o=selectLabel(e.getX(), e.getY());
                if (o!=null) {
                    Dragging=true;
                    setTool(new LabelMover(OC, this, e.getX(), e.getY(), o, e.isShiftDown()));
                    return;
                }
                if (e.isShiftDown()&&e.isControlDown()) // hiding shortcut
                {
                    o=selectObject(e.getX(), e.getY());
                    if (o==null) {
                        return;
                    }
                    o.toggleHidden();
                    C.updateCircleDep();
                    repaint();
                    return;
                }
                if (e.isControlDown()) // edit conditionals
                {
                    o=selectObject(e.getX(), e.getY());
                    if (o==null) {
                        return;
                    }
                    JPropertiesBar.EditObject(o, true, false);
                    JPropertiesBar.SelectPropertiesTab(2);
                    // new EditConditionals(getFrame(), o);
                    validate();
                    repaint();
                    return;
                }
            }
            final ConstructionObject p=selectImmediateMoveableObject(
                    e.getX(), e.getY());
            RightClicked=true;
            if (p!=null) {
                Dragging=true;
                setTool(new MetaMover(OC, this, p, e));
                return;
            } else if (!Global.getParameter("options.nomousezoom", false)) // Drag
            // mit
            // rechter
            // Maustaste
            {
                if (selectObjects(e.getX(), e.getY()).size()==0) {
                    setTool(new ZoomerTool(OC, e, this));
                }
                return;
            }
        } else // left mouse button!
        {
            if (!SmartBoardPreview&&Global.getParameter("smartboard", false)&&OC.useSmartBoard()) {
                OC.mouseMoved(e, this, Global.getParameter(
                        "options.indicate.simple", false));
                SmartBoardPreview=true;
                return;
            } else {
                Control=e.isControlDown();
                OC.mousePressed(e, this); // pass to ObjectConstructor
                Control=false;
            }
        }
    }

    @Override
    public synchronized void mouseReleased(final MouseEvent e) {
        if (!Interactive) {
            return;
        }
        if (DT!=null) {
            DT.waitReady();
        }
        if (RightClicked) {
            //RightClicked=false; // déplacé plus bas ligne 910, ainsi, lorsqu'on appelle select(), il vaut toujours true
            OC.mouseReleased(e, this);
            if (Moved<=2&&AllowRightMouse&&!ReadOnly) {
                final MyVector v=selectObjects(e.getX(), e.getY());
                if (v.size()>0) {
                    final ConstructionObject o=select(v, e.getX(), e.getY());
                    if (o!=null) {
                        new EditTool().mousePressed(e, o, this);
                        return;
                    } else {
                        repaintCD();
                    }
                    return;
                }
            }
            if (Moved<=2&&PM!=null&&!Global.getParameter("restricted", false)) {
                int n=2;
                if (ReadOnly||!Global.getParameter("options.doubleclick", false)) {
                    n=1;
                }
                if (e.getClickCount()>=n&&(ReadOnly||!Macros.isEmpty())) {
                    PM.show(e.getComponent(), e.getX(), e.getY());
                }
            }
            repaintCD();
	    RightClicked=false;
            return;
        }
        if (SmartBoardPreview&&Global.getParameter("smartboard", false)) {
            Control=e.isControlDown();
            clearIndicated();
            clearPreview();
            repaint();
            Dragging=false;
            OC.mousePressed(e, this);
            SmartBoardPreview=false;
            mouseReleased(e);
            return;
        }
        if (!Dragging) {
            OC.mouseReleased(e, this);
            Dragging=false;
            repaintCD();
            return;
        }
        if (Moved<=1) {
            if (OC instanceof LabelMover) {
                ((LabelMover) OC).resetPoint();
                OC.mouseReleased(e, this);
            } else if (OC instanceof MetaMover) {
                OC.mouseReleased(e, this);
                if (!ReadOnly) {
                    new EditTool().mousePressed(e, this);
                }
            }
        } else {
            OC.mouseReleased(e, this);
        }
        repaintCD();
        Dragging=false;
    }

    @Override
    public synchronized void mouseClicked(final MouseEvent e) {
    }

    @Override
    public synchronized void mouseExited(final MouseEvent e) {
        if (!Interactive) {
            return;
        }
        clearIndicated();
        clearPreview();
        repaint();
        SmartBoardPreview=false;
        RightClicked=false;
        repaintCD();
    }

    @Override
    public void mouseEntered(final MouseEvent e) {
    }

    @Override
    public synchronized void mouseMoved(final MouseEvent e) {
        if (!Interactive||!Global.getParameter("options.indicate", true)) {
            return;
        }
        if (Global.getParameter("smartboard", false)) {
            return;
        }
        Count.setAllAlternate(true);
        OC.mouseMoved(e, this, Global.getParameter("options.indicate.simple",
                false));
        Count.setAllAlternate(false);
        repaintCD();
    }
    DragThread DT=null;

    @Override
    public synchronized void mouseDragged(final MouseEvent e) {
        if (!Interactive) {
            return;
        }
        if (DT==null) {
            DT=new DragThread(this);
        }
        if (SmartBoardPreview&&Global.getParameter("smartboard", false)) {
            OC.mouseMoved(e, this, Global.getParameter(
                    "options.indicate.simple", false));
        } else {
            DT.mouseDragged(e);
            Moved++;
        }
        repaintCD();
    }

    public synchronized void doMouseDragged(final MouseEvent e) {
        OC.mouseDragged(e, this);
    }
    ConstructionObject LastPaint=null;

    public void newImage() {
        I=null;
        repaint();
    }
    boolean Frozen=false;

    public void setFrozen(final boolean f) {
        Frozen=f;
    }

    public boolean getFrozen() {
        return Frozen;
    }
    final double PointSizeFactor=240.0;
    int CC=0;

    // public void repaint(){
    // // System.out.print("a ");
    // // super.repaint();
    // StackTraceElement[] trace = new Throwable().getStackTrace();
    // if (!trace[1].equals("javax.swing.JComponent")) super.repaint();
    //
    // // for (int i=0;i<trace.length;i++){
    // // System.out.println(trace[i].getClassName());
    // //// if (trace[i].getClassName().startsWith("rene.zirkel.tools")){
    // //// String s=trace[i].getClassName();
    // //// CallerObject=s.split("\\.")[3];
    // //// break;
    // //// }
    // // };
    // // System.out.println("*******************");
    // }
    // public void paintComponent(Graphics g){
    // System.out.print("a ");
    // }
    // repaint events
    public boolean isInMultipleSelection(ConstructionObject o) {
        for (int i=0; i<MultipleSelection.size(); i++) {
            if (o==MultipleSelection.get(i)) {
                return true;
            }
        }
        return false;
    }

    public void validCurrentMultipleSelection() {
        for (int i=0; i<MultipleSelection.size(); i++) {
            MultipleSelection.get(i).setInMultipleSelection(true);
        }
        clearSelectionRectangle();
        repaint();
        editMultipleSelection();
    }

    public void clearMultipleSelection() {
        MultipleSelection.clear();
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            o.setSelected(false);
            o.setInMultipleSelection(false);
        }
    }

    public void addMultipleSelection(ConstructionObject o) {
        if (!isInMultipleSelection(o)) {
            MultipleSelection.add(o);
            o.setSelected(true);
        } else {
            MultipleSelection.remove(o);
            o.setSelected(false);
        }
    }

    public void selectInRect(int x, int y, int w, int h) {
        Rectangle r=new Rectangle(x, y, w, h);
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if ((!o.isHidden())||(ShowHidden)) {
                if ((o.isInRect(r, this))&&(!isInMultipleSelection(o))) {
                    MultipleSelection.add(o);
                    o.setSelected(true);
                } else if ((!o.isInMultipleSelection())&&(!o.isInRect(r, this))&&(isInMultipleSelection(o))) {
                    MultipleSelection.remove(o);
                    o.setSelected(false);
                }
            }
        }
    }

    public void clearSelectionRectangle() {
        SelectionRectangle=null;
    }

    public void startSelectionRectangle(int x, int y) {
        SelectionRectangle=null;
        SelectionRectangle=new Rectangle(x, y, -1, -1);
        SelectionX=x;
        SelectionY=y;
    }

    public void actualiseSelectionRectangle(int x, int y) {
        if (SelectionRectangle!=null) {
            int x0=Math.min(SelectionX, x);
            int y0=Math.min(SelectionY, y);
            int w=Math.abs(x-SelectionX);
            int h=Math.abs(y-SelectionY);
            SelectionRectangle=null;
            SelectionRectangle=new Rectangle(x0, y0, w, h);
            repaint();
            selectInRect(x0, y0, w, h);
        }
    }

    public synchronized void paintSelectionRectangle(final MyGraphics IG) {
        if ((IG!=null)&&(SelectionRectangle!=null)) {
            Graphics2D g2d=(Graphics2D) IG.getGraphics();
            Rectangle r=SelectionRectangle;
            g2d.setColor(SelectionRectangleBackground);
            g2d.fillRect(r.x, r.y, r.width, r.height);
            g2d.setStroke(new BasicStroke(2f));
            g2d.setColor(SelectionRectangleBorder);
            g2d.drawRect(r.x, r.y, r.width, r.height);
        }
    }
    private boolean ShowCopyRectangle=false;

    public void keepCopyRectangleInside() {
        if (CopyRectangle!=null) {
            Rectangle r=new Rectangle(0, 0, IW, IH);
            CopyRectangle=CopyRectangle.intersection(r);
            if (CopyRectangle.width<40) {
                CopyRectangle.width=40;
            }
            if (CopyRectangle.height<40) {
                CopyRectangle.height=40;
            }
        }
    }

    public void showCopyRectangle() {
        if (CopyRectangle==null) {
            int w=IW*3/4;
            int h=IH*3/4;
            int x=(IW-w)/2;
            int y=(IH-h)/2;
            CopyRectangle=new Rectangle(x, y, w, h);
        } else {
            keepCopyRectangleInside();
        }

        ShowCopyRectangle=true;
        repaint();
    }

    public void hideCopyRectangle() {
        ShowCopyRectangle=false;
    }

    public Rectangle getCopyRectangle() {
        return CopyRectangle;
    }

    public void startCopyRectangle(int x, int y) {
        CopyRectangle=null;
        CopyRectangle=new Rectangle(x, y, 0, 0);
        CopyX=x;
        CopyY=y;
    }

    public void translateCopyRectangle(int x, int y) {
        if (CopyRectangle!=null) {
            CopyRectangle.translate(x, y);
            repaint();
        }
    }

    public void growCopyRectangle(int dw, int dh) {
        if (CopyRectangle!=null) {
            CopyRectangle.width+=dw;
            CopyRectangle.height+=dh;
            repaint();
        }
    }

    public void actualiseCopyRectangle(MouseEvent e) {
        int x=e.getX(), y=e.getY();

        if (CopyRectangle!=null) {
            int x0=Math.min(CopyX, x);
            int y0=Math.min(CopyY, y);
            int w=Math.abs(x-CopyX);
            int h=Math.abs(y-CopyY);
            if (e.isAltDown()) {
                w-=w%10;
                h-=h%10;
            }
            if (e.isShiftDown()) {
                h=w;
            }
            CopyRectangle=null;
            CopyRectangle=new Rectangle(x0, y0, w, h);
            repaint();
        }
    }

    public synchronized void paintCopyRectangle(final MyGraphics IG) {
        if ((ShowCopyRectangle)&&(IG!=null)) {
            Graphics2D g2d=(Graphics2D) IG.getGraphics();
            Rectangle r=CopyRectangle;
            g2d.setColor(CopyRectangleBackground);
            Area outside=new Area(new Rectangle2D.Double(0, 0, IW, IH));
            outside.subtract(new Area(r));
            g2d.setClip(outside);
            g2d.fillRect(0, 0, IW, IH);
            g2d.setClip(new Area(new Rectangle2D.Double(0, 0, IW, IH)));

            g2d.setStroke(new BasicStroke(1f));
            g2d.setColor(CopyRectangleBorder);

            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_OFF);
            g2d.drawRect(r.x, r.y, r.width, r.height);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

            int w=3;
            int h=20;
            int rnd=6;

            g2d.fillRoundRect(r.width+r.x-w, r.y+r.height/2-h, 2*w, 2*h, rnd, rnd);
            g2d.fillRoundRect(r.width/2+r.x-h, r.y+r.height-w, 2*h, 2*w, rnd, rnd);
            g2d.fillRoundRect(r.x-w, r.y+r.height/2-h, 2*w, 2*h, rnd, rnd);
            g2d.fillRoundRect(r.width/2+r.x-h, r.y-w, 2*h, 2*w, rnd, rnd);
            w--;
            Path2D corner=new Path2D.Double();
            corner.moveTo(r.width+r.x-w, r.y+r.height-h);
            corner.lineTo(r.width+r.x-w, r.y+r.height-w);
            corner.lineTo(r.width+r.x-h, r.y+r.height-w);
            corner.lineTo(r.width+r.x-h, r.y+r.height+w);
            corner.lineTo(r.width+r.x+w, r.y+r.height+w);
            corner.lineTo(r.width+r.x+w, r.y+r.height-h);
            corner.closePath();
            g2d.fill(corner);
            w++;
            g2d.setColor(Color.white);
            g2d.drawRoundRect(r.width+r.x-w, r.y+r.height/2-h, 2*w, 2*h, rnd, rnd);
            g2d.drawRoundRect(r.width/2+r.x-h, r.y+r.height-w, 2*h, 2*w, rnd, rnd);
            g2d.drawRoundRect(r.x-w, r.y+r.height/2-h, 2*w, 2*h, rnd, rnd);
            g2d.drawRoundRect(r.width/2+r.x-h, r.y-w, 2*h, 2*w, rnd, rnd);
            g2d.draw(corner);
        }
    }

    public MyGraphics getMyGraphics() {
        return IG;
    }

    public synchronized void paintDisquePoincare(final MyGraphics IG) {
        if (isDP()) {

            Graphics2D g2d=(Graphics2D) IG.getGraphics();
            g2d.setColor(DPBackground);
            if (isEuclidian()) {
                g2d.fillRect(0, 0, getWidth(), getHeight());
            } else {
                int x1=(int) col(-4);
                int y1=(int) row(4);
                int w=(int) (2*(col(0)-x1));
                g2d.fillOval(x1, y1, w, w);
                if (!(g2d instanceof VectorGraphics2D)) {
                    g2d.setColor(Color.lightGray);
                    String s=Global.Loc("canvas.DP.message1");
                    Font ft=new Font(Global.GlobalFont, 0, 12);
                    g2d.setFont(ft);
                    FontMetrics fm=getFontMetrics(ft);
                    g2d.drawString(s, (IW-fm.stringWidth(s))/2, fm.getHeight());
                    s=Global.Loc("canvas.DP.message2");
                    g2d.drawString(s, (IW-fm.stringWidth(s))/2, 2*fm.getHeight());
                }
                g2d.setColor(DPBorder);
                g2d.setStroke(new BasicStroke(DPStroke*(float) C.getOne()));
                g2d.drawOval(x1, y1, w, w);
            }
        }
    }

    @Override
    public void setBounds(Rectangle r) {
//        double dwPercent=(1.0*r.width)/(1.0*getBounds().width);
//        double dwUnit=DX*(r.width-getBounds().width)/IW;

        super.setBounds(r);
////        double dwUnit=DX*dwPixel/IW;
//        if (!Double.isNaN(DX)) {
////            System.out.println(dwPixel);
//            C.setXYW(C.getX()+dwUnit/2, C.getY(), C.getW()*dwPercent);
//        }
////System.out.println("X="+C.getX()+ " W="+C.getW());
//System.out.println("X-W="+(C.getX()-C.getW()));
////        System.out.println("setBounds : "+dw+" - "+(DX*dw/IW));
    }

    @Override
    public synchronized void paint(final Graphics g) {

        if (g==null) {
            return;
        }
        if (DontPaint) {
            return;
        }
        if (Frozen) {
            g.drawImage(I, 0, 0, this);
            return;
        }

        final int w=getSize().width, h=getSize().height;
        if (I==null||IW!=w||IH!=h) {

            if (w==0||h==0) {
                return;
            }
            IW=w;
            IH=h;
            I=createImage(IW, IH);
            IG=new MainGraphics((Graphics2D) I.getGraphics(), this);
            IG.setSize(IW, IH);



//            MinPointSize=Global.getParameter("minpointsize", 3);
            // if (PointSize<MinPointSize) PointSize=MinPointSize;
//            MinFontSize=Global.getParameter("minfontsize", 12);
            // if (FontSize<MinFontSize) FontSize=MinFontSize;

//            PointSize=MinPointSize+0.4;
//            FontSize=MinFontSize;



            UniversalTrack.createTrackImage();
            recompute();
            C.dovalidate();
        }

        IG.clearRect(0, 0, IW, IH, getBackground());

        if (Background!=null) {
            final int bw=Background.getWidth(this), bh=Background.getHeight(this);
            if (bw==IW&&bh==IH) {
                IG.drawImage(Background, 0, 0, this);
            } else if (Global.getParameter("background.tile", true)&&bw<IW&&bh<IH) {
                for (int i=(IW%bw)/2-bw; i<IW; i+=bw) {
                    for (int j=(IH%bh)/2-bh; j<IH; j+=bh) {
                        IG.drawImage(Background, i, j, this);
                    }
                }
            } else if (Global.getParameter("background.center", true)) {
                IG.drawImage(Background, (IW-bw)/2, (IH-bh)/2, this);
            } else {
                IG.drawImage(Background, 0, 0, IW, IH, this);
            }
        }


        dopaint(IG);


//        JCM.paintControls();

        paintCalled=true;
        paintChildren(I.getGraphics());
        paintCalled=false;

//        DOCK.paintDock(I.getGraphics());
        g.drawImage(I, 0, 0, this);


        JIconMouseAdapter.paintTool();


    }

    public synchronized void resetGraphics() {
        I=null;
        repaint();

    }

    public void forceComputeHeavyObjects() {
        C.computeHeavyObjects(this, true);
    }
    MyVector Breaks=new MyVector();

    public void updateBreakHide() {
        Breaks.removeAllElements();
        Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o==LastPaint||o==C.lastButN(0)) {
                break;
            }
            if (o.isBreak()) {
                Breaks.addElement(o);
            }
        }
        e=C.elements();
        ConstructionObject NextBreak=null;
        final Enumeration eb=Breaks.elements();
        if (eb.hasMoreElements()) {
            NextBreak=(ConstructionObject) eb.nextElement();
        }
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (NextBreak!=null&&NextBreak.isHideBreak()) {
                o.setBreakHide(true);
            } else {
                o.setBreakHide(false);
            }
            if (o==NextBreak) {
                if (eb.hasMoreElements()) {
                    NextBreak=(ConstructionObject) eb.nextElement();
                } else {
                    NextBreak=null;
                }
            }
            if (o==LastPaint) {
                break;
            }
        }
    }

    public void dopaint(final MyGraphics IG) {

        paintDisquePoincare(IG);
        C.computeHeavyObjects(this, false);
        UniversalTrack.draw();

        if (getAxis_show()) {
            if (Global.getParameter("axis_with_grid", true)) {
                paintGrid(IG);
            } else {
                paintAxes(IG);
            }
        }
//        long time=System.currentTimeMillis();
        updateBreakHide();
        // count z-buffer elements and mark all as non-painted
        Enumeration e=C.elements();
        int n=0;
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            o.HasZ=false;
            o.IsDrawn=false;
            try {
                if (!o.selected()) {
                    o.Value=-o.getZ();
                    o.HasZ=true;
                    n++;
                }
            } catch (final Exception ex) {
            }
            if (o==LastPaint) {
                break;
            }
        }
        // paint background objects
        e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isBack()&&!o.HasZ) {
                o.paint(IG, this);
                o.IsDrawn=true;
            }
            if (o==LastPaint) {
                break;
            }
        }
        // paint objects with z-buffer value in their order
        if (n>0) {
            final ConstructionObject os[]=new ConstructionObject[n];
            e=C.elements();
            n=0;
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                if (o.HasZ) {
                    os[n++]=o;
                }
            }
            Sorter.sort(os);
            for (int i=0; i<n; i++) {
                os[i].paint(IG, this);
                os[i].IsDrawn=true;
            }
        }
        // paint non-selected objects
        e=C.elements();
        final Vector Pts=new Vector();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (!o.selected()&&!o.IsDrawn) {
                if (o instanceof rene.zirkel.objects.PointObject) {
                    Pts.add(o);
                } else {
                    o.paint(IG, this);
                    o.IsDrawn=true;
                }
            }
            if (o==LastPaint) {
                break;
            }
        }
        // paint selected objects
        e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.selected()&&!o.IsDrawn) {
                if (o instanceof rene.zirkel.objects.PointObject) {
                    Pts.add(o);
                } else {
                    o.paint(IG, this);
                    o.IsDrawn=true;
                }
            }
            if (o==LastPaint) {
                break;
            }
        }
        e=Pts.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            o.paint(IG, this);
            o.IsDrawn=true;
        }

        if (LastPaint==null) {
            paintTrack(IG);
        }
        if (Interactive&&IndicatePoint!=null) {
            IndicatePoint.paint(IG, this);
        }
//        time=System.currentTimeMillis()-time;
//        if (time>250&&PolygonDrawer.step<50) {
//            PolygonDrawer.step=PolygonDrawer.step*2;
//        }
//        if (time<100&&PolygonDrawer.step>4) {
//            PolygonDrawer.step=PolygonDrawer.step/2;
//        }
        paintDrawings(IG);
        paintSelectionRectangle(IG);
        paintCopyRectangle(IG);
//         JMacrosTools.CurrentJZF.JCM.paintControls(IG.getGraphics());
    }
    double xg[]=new double[64], yg[]=new double[64];

    /**
     * Paint axes and grid.
     *
     * @param IG
     */
    void paintGrid(final MyGraphics IG) {

        int GridThickness=Global.getParameter("axis_thickness", 0);
        int GridColor=Global.getParameter("axis_color", 0);
        boolean GridLabels=Global.getParameter("axis_labels", false);
        boolean GridBold=Global.getParameter("grid.bold", false);
        boolean GridLarge=Global.getParameter("grid.large", false);

        if (GridThickness==ConstructionObject.THIN) {
            IG.setColor(ZirkelFrame.LightColors[GridColor]);
        } else {
            IG.setColor(ZirkelFrame.Colors[GridColor]);
        }
        IG.setFont(GridLarge, GridBold);
        final double gridsize=getGridSize();
//        int dd=IH/200;
//        if (dd<1) {
//            dd=1;
//        }

        double dd=3*C.getOne();


        double x1=Math.floor((C.getX()-C.getW())/gridsize-1)*gridsize;
//        System.out.println("x1="+x1);
        int xi=0;
        while (x1<(C.getX()+C.getW())&&xi<64) {
            xg[xi++]=col(x1);
            x1+=gridsize;
        }
        double y1=Math.floor((C.getY()-C.getH())/gridsize-1)*gridsize;
//System.out.println("y1="+y1);
        int yi=0;
        while (y1<(C.getY()+C.getH())&&yi<64) {
            yg[yi++]=row(y1);
            y1+=gridsize;
        }

        for (int i=0; i<xi; i++) {
            IG.drawAxisLine(xg[i], 0, xg[i], IH, (float) (0.01f*gridopacity));
        }
        for (int j=0; j<yi; j++) {
            IG.drawAxisLine(0, yg[j], IW, yg[j], (float) (0.01f*gridopacity));
        }

        // for (int i=0; i<xi; i++) {
        // for (int j=0; j<yi; j++) {
        // IG.drawLine(xg[i],yg[j]-dd,xg[i],yg[j]+dd);
        // IG.drawLine(xg[i]-dd,yg[j],xg[i]+dd,yg[j]);
        // }
        // }

        if (GridThickness!=ConstructionObject.INVISIBLE) {
            if (0<C.getX()+C.getW()&&0>C.getX()-C.getW()) {
                double col=col(0);
                if (GridThickness!=ConstructionObject.THICK) {
                    IG.drawLine(col, 0, col, IH);
                } else {
                    IG.drawThickLine(col, 0, col, IH);
                }
            }
            if (0<C.getY()+C.getW()&&0>C.getY()-C.getW()) {
                double r=row(0);
                if (GridThickness!=ConstructionObject.THICK) {
                    IG.drawLine(0, r, IW, r);
                } else {
                    IG.drawThickLine(0, r, IW, r);
                }
            }
        }
        dd=dd*2;
        double labelsize=Math.pow(10, Math.floor(Math.log(C.getW()*2)/Math.log(10)))/10;
        while (C.getW()*2/labelsize>=10) {
            labelsize*=10;
        }
        if (C.getW()*2/labelsize<5) {
            labelsize/=2;
        }
        final FontMetrics fm=IG.getFontMetrics();
        final int FH=fm.getHeight();
        x1=Math.floor((C.getX()-C.getW())/labelsize-1)*labelsize;
        double lh=row(0);
        double bottomshift=FH+2*C.getOne();
        if (lh<0||lh>IH) {
            lh=IH;
            bottomshift=-7*C.getOne();
        }

        while (x1<(C.getX()+C.getW())) {
            double col=col(x1);
            final String s=format(x1);
            if (s.length()>0) {

                if (GridLabels) {
                    IG.drawString(s, col-fm.stringWidth(s)/2, lh+bottomshift);
                }
                IG.drawLine(col, lh-dd, col, lh+dd);
            }
            x1+=labelsize;
        }

        boolean left=true;
        double lw=col(0);
        if (lw<0||lw>IW-20) {
            lw=0;
            left=false;
        }

        y1=Math.floor((C.getY()-C.getW())/labelsize-1)*labelsize;
        while (y1<(C.getY()+C.getW())) {
            double r=row(y1);
            final String s=format(y1);
            if (s.length()>0) {
                double leftshift=(left)?-fm.stringWidth(s)-10*C.getOne():8*C.getOne();
                if (GridLabels) {
                    IG.drawString(s, lw+leftshift, r+(FH/2-2)*C.getOne());
                }
                IG.drawLine(lw-dd, r, lw+dd, r);
            }
            y1+=labelsize;
        }
    }

    public boolean getAxis_show() {
        return Global.getParameter("axis_show", false);
    }

    public void setAxis_show(boolean b) {
        axis_show=b;
        Global.setParameter("axis_show", b);
    }

    public void setAxis_labels(boolean b) {
        axis_labels=b;
        Global.setParameter("axis_labels", b);
    }

    public void setAxis_with_grid(boolean b) {
        axis_with_grid=b;
        Global.setParameter("axis_with_grid", b);
    }

    public void setAxis_color(int i) {
        axis_color=i;
        Global.setParameter("axis_color", i);
    }

    public void setAxis_thickness(int i) {
        axis_thickness=i;
        Global.setParameter("axis_thickness", i);
    }

    /**
     * Paint only the coordinate axes (no grid)
     *
     * @param IG
     */
    void paintAxes(final MyGraphics IG) {
//        System.out.println("paintAxes");
//        IG.setAntialiasing(false);

        int GridThickness=Global.getParameter("axis_thickness", 0);
        int GridColor=Global.getParameter("axis_color", 0);
        boolean GridLabels=Global.getParameter("axis_labels", false);
        boolean GridBold=Global.getParameter("grid.bold", false);
        boolean GridLarge=Global.getParameter("grid.large", false);

        if (GridThickness==ConstructionObject.THIN) {
            IG.setColor(ZirkelFrame.LightColors[GridColor]);
        } else {
            IG.setColor(ZirkelFrame.Colors[GridColor]);
        }
        IG.setFont(GridLarge, GridBold);
        final double gridsize=getGridSize();
        double x1=Math.floor((C.getX()-C.getW())/gridsize-1)*gridsize;
        double r0=row(0);
//        int dd=IH/200;
//        if (dd<1) {
//            dd=1;
//        }

        double dd=3*C.getOne();


        while (x1<(C.getX()+C.getW())) {
            double col=col(x1);
            IG.drawLine(col, r0-dd, col, r0+dd);
            x1+=gridsize;
        }
        double y1=Math.floor((C.getY()-C.getW())/gridsize-1)*gridsize;
        double c0=col(0);
        while (y1<(C.getY()+C.getW())) {
            double r=row(y1);
            IG.drawLine(c0-dd, r, c0+dd, r);
            y1+=gridsize;
        }




        if (GridThickness!=ConstructionObject.INVISIBLE) {
            if (0<C.getX()+C.getW()&&0>C.getX()-C.getW()) {
                double col=col(0);
                if (GridThickness!=ConstructionObject.THICK) {
                    IG.drawLine(col, 0, col, IH);
                } else {
                    IG.drawThickLine(col, 0, col, IH);
                }
            }
            if (0<C.getY()+C.getW()&&0>C.getY()-C.getW()) {
                double r=row(0);
                if (GridThickness!=ConstructionObject.THICK) {
                    IG.drawLine(0, r, IW, r);
                } else {
                    IG.drawThickLine(0, r, IW, r);
                }
            }
        }



        dd=dd*2;
        double labelsize=Math.pow(10, Math.floor(Math.log(C.getW()*2)/Math.log(10)))/10;
        while (C.getW()*2/labelsize>=10) {
            labelsize*=10;
        }
        if (C.getW()*2/labelsize<5) {
            labelsize/=2;
        }
        final FontMetrics fm=IG.getFontMetrics();
        final int FH=fm.getHeight();

        x1=Math.floor((C.getX()-C.getW())/labelsize-1)*labelsize;
        double lh=row(0);
        double bottomshift=FH+2*C.getOne();
        if (lh<0||lh>IH) {
            lh=IH;
            bottomshift=-7*C.getOne();
        }

        while (x1<(C.getX()+C.getW())) {
            double col=col(x1);
            final String s=format(x1);
            if (s.length()>0) {
                if (GridLabels) {
                    IG.drawString(s, col-fm.stringWidth(s)/2, lh+bottomshift);
                }
                IG.drawLine(col, lh-dd, col, lh+dd);
            }
            x1+=labelsize;
        }

        boolean left=true;
        double lw=col(0);
        if (lw<0||lw>IW-20) {
            lw=0;
            left=false;
        }

        y1=Math.floor((C.getY()-C.getW())/labelsize-1)*labelsize;
        while (y1<(C.getY()+C.getW())) {
            double r=row(y1);
            final String s=format(y1);
            if (s.length()>0) {
                double leftshift=(left)?-fm.stringWidth(s)-10*C.getOne():8*C.getOne();
                if (GridLabels) {
                    IG.drawString(s, lw+leftshift, r+FH/2-2*C.getOne());
                }
                IG.drawLine(lw-dd, r, lw+dd, r);
            }
            y1+=labelsize;
        }

//        IG.setAntialiasing(true);
    }

    public double pointSize() {
        return minpointsize;
    }

    public double fontSize() {
        return minfontsize;
    }

    public double lineSize() {
        return minlinesize;
    }
//    public double SelectionPointFactor=Global.getParameter("selectionsize",
//            1.5);
    public double SelectionPointFactor=3;

//    public double selectionSize() {
//        return SelectionPointFactor*PointSize;
//    }
    public double selectionSize() {
        return selectionsize;
    }

    public double monkeySpeed() {
        return monkeyspeed;
    }
    static char c[]=new char[20];
    int nc;

    public String format(double x) { // double xx=x;
        nc=0;
        boolean minus=false;
        if (x<-1e-12) {
            minus=true;
            x=-x;
        }
        x+=1e-12;
        final double h=x-Math.floor(x);
        if (rekformat(h, 8)) {
            c[nc++]='.';
        }
        while (x>=1) {
            final double s=Math.floor(x/10);
            c[nc++]=(char) ('0'+(int) (x-s*10));
            x=s;
        }
        if (nc>0&&minus) {
            c[nc++]='-';
        }
        // revert c:
        for (int i=0; i<nc/2; i++) {
            final char hc=c[nc-1-i];
            c[nc-1-i]=c[i];
            c[i]=hc;
        }
        // System.out.println(xx+" -> "+new String(c,0,nc));
        return new String(c, 0, nc);
    }

    boolean rekformat(double h, final int k) {
        h=h*10;
        final double x=Math.floor(h);
        if (k==0) {
            final int i=(int) x;
            if (i>0) {
                c[nc++]=(char) ('0'+i);
                return true;
            } else {
                return false;
            }
        } else {
            final int i=(int) x;
            if (rekformat(h-x, k-1)||i>0) {
                c[nc++]=(char) ('0'+i);
                return true;
            } else {
                return false;
            }
        }
    }

    @Override
    public void update(final Graphics g) {
        paint(g);
    }

    public void paintUntil(final ConstructionObject o) {
        if (LastPaint==o) {
            return;
        }
        LastPaint=o;
        repaint();
    }

    // validate all elements
    @Override
    public void validate() {
        dovalidate();
        if (OC instanceof TrackPainter) {
            ((TrackPainter) OC).validate(this);
        }
    }

    /**
     * Synchronized update of the construction to avoid a repaint during update.
     */
    synchronized public void dovalidate() {
        C.dovalidate();
    }
    // selection routines:
    // This vector is used by ALL selection and indication routines for
    // performance reasons.
    MyVector V=new MyVector();

    public void sort(final MyVector V) {
        if (V.size()<2) {
            return;
        }
        Sorter.QuickSort(V.getArray(), 0, V.size()-1);
    }

    /**
     * Slow function to resort a vector by the number of each element in the
     * construction.
     */
    public void sortRow(final MyVector V) {
        final ConstructionObject o[]=new ConstructionObject[V.size()];
        V.copyInto(o);
        V.removeAllElements();
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject oo=(ConstructionObject) e.nextElement();
            for (final ConstructionObject element : o) {
                if (element==oo) {
                    V.addElement(oo);
                    break;
                }
            }
        }
    }

    /**
     * select a circle or a line.
     **/
    public ConstructionObject selectCircleLine(final int x, final int y,
            final boolean multiple) // select a circle or a line
    {
        final MyVector v=selectCircleLineObjects(x, y, multiple, false);
        return select(v);
    }

    public ConstructionObject selectCircleLine(final int x, final int y) // select
    // a
    // circle
    // or
    // a
    // line
    {
        return selectCircleLine(x, y, true);
    }

    /**
     * select a circle or a line.
     **/
    public ConstructionObject selectPointonObject(final int x, final int y,
            final boolean multiple) // select a circle or a line
    {
        final MyVector v=selectPointonObjects(x, y, multiple, false);
        return select(v);
    }

    public ConstructionObject selectPointonObject(final int x, final int y) // select
    // a
    // circle
    // or
    // a
    // line
    {
        return selectPointonObject(x, y, true);
    }

    /**
     * Select all possible circles or lines at x,y. If a matching non-filled
     * object is found above a filled object, the latter is never selected.
     *
     * @param multiple
     *            determines, if it is possible to select selected objects.
     * @param testlocal
     *            determines, if objects that look locally like already selected
     *            objects should be omitted.
     */
    public MyVector selectCircleLineObjects(final int x, final int y,
            final boolean multiple, final boolean testlocal) // select a circle
    // or a line
    {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&(o instanceof PrimitiveLineObject||o instanceof PrimitiveCircleObject)&&o.nearto(x, y, true, this)&&(multiple||!o.selected())) {
                if (testlocal) {
                    final Enumeration ev=V.elements();
                    while (ev.hasMoreElements()) {
                        final ConstructionObject ov=(ConstructionObject) ev.nextElement();
                        if (o.locallyLike(ov)) {
                            o=null;
                            break;
                        }
                    }
                }
                if (o!=null) {
                    V.addElement(o);
                }
            }
        }
        return V;
    }

    public MyVector selectCircleLineObjects(final int x, final int y,
            final boolean multiple) {
        return selectCircleLineObjects(x, y, multiple, false);
    }

    public MyVector selectCircleLineObjects(final int x, final int y) {
        return selectCircleLineObjects(x, y, true, false);
    }

    /**
     * Select all possible object at x,y that can carry a point.
     *
     * @param multiple
     *            determines, if it is possible to select selected objects.
     * @param testlocal
     *            determines, if objects that look locally like already selected
     *            objects should be omitted.
     */
    public MyVector selectPointonObjects(final int x, final int y,
            final boolean multiple, final boolean testlocal) // select a circle
    // or a line
    {

        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&(o instanceof PointonObject)&&o.nearto(x, y, true, this)&&(multiple||!o.selected())) {
                if (testlocal) {
                    final Enumeration ev=V.elements();
                    while (ev.hasMoreElements()) {
                        final ConstructionObject ov=(ConstructionObject) ev.nextElement();
                        if (o.locallyLike(ov)) {
                            o=null;
                            break;
                        }
                    }
                }
                if (o!=null) {
                    V.addElement(o);
                }
            }
        }
        return V;
    }

    public MyVector selectPointonObjects(final int x, final int y,
            final boolean multiple) {
        return selectPointonObjects(x, y, multiple, false);
    }

    public MyVector selectPointonObjects(final int x, final int y) {
        return selectPointonObjects(x, y, true, false);
    }

    /**
     * Select all selectable objects near to x,y.
     *
     * @param multiple
     *            allows multiple selections.
     **/
    public MyVector selectObjects(final int x, final int y,
            final boolean multiple) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
        }
        return V;
    }

    public MyVector selectObjects(final int x, final int y) {
        return selectObjects(x, y, true);
    }

    /**
     * Select a single object near x,y.
     **/
    public ConstructionObject selectObject(final int x, final int y,
            final boolean multiple) {
        final MyVector v=selectObjects(x, y, multiple);
        return select(v, x, y);
    }

    public ConstructionObject selectObject(final int x, final int y) {
        return selectObject(x, y, true);
    }

    /**
     * select all constructable objects near x,y.
     **/
    public MyVector selectConstructableObjects(final int x, final int y,
            final boolean multiple) // select an enumeration of objects
    {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.valid()&&o.isFlag()&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
        }
        return V;
    }

    public MyVector selectConstructableObjects(final int x, final int y) {
        return selectConstructableObjects(x, y, true);
    }

    /**
     * select a single constructable object near x,y.
     **/
    public ConstructionObject selectConstructableObject(final int x, final int y) {
        final MyVector v=selectConstructableObjects(x, y, true);
        return select(v, x, y); // user determines
    }

    /**
     * Select a single line, segment, ray, perp., paral. or fixed angle near
     * x,y.
     **/
    public PrimitiveLineObject selectLine(final int x, final int y, final boolean multiple) {
        selectLineObjects(x, y, multiple);
        return (PrimitiveLineObject) select(V);
    }

    public void selectLineObjects(final int x, final int y, final boolean multiple) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&(o instanceof PrimitiveLineObject)&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
        }
    }

    public PrimitiveLineObject selectLine(final int x, final int y) {
        return selectLine(x, y, true);
    }

    /**
     * Select a point or a line (for the object tracker).
     */
    public void selectPointsOrLines(final int x, final int y,
            final boolean multiple) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&(o instanceof PrimitiveLineObject||o instanceof PointObject)&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
        }
    }

    /**
     * Select a single line, segment, or ray near x,y
     **/
    public TwoPointLineObject selectTwoPointLine(final int x, final int y,
            final boolean multiple) {
        selectTwoPointLines(x, y, multiple);
        return (TwoPointLineObject) select(V);
    }

    public void selectTwoPointLines(final int x, final int y,
            final boolean multiple) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&(o instanceof TwoPointLineObject)&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
        }
    }

    public TwoPointLineObject selectTwoPointLine(final int x, final int y) {
        return selectTwoPointLine(x, y, true);
    }

    /**
     * Select a single segment near x,y
     **/
    public SegmentObject selectSegment(final int x, final int y,
            final boolean multiple) {
        selectSegments(x, y, multiple);
        return (SegmentObject) select(V);
    }

    public void selectSegments(final int x, final int y, final boolean multiple) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&(o instanceof SegmentObject)&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
        }
    }

    public SegmentObject selectSegment(final int x, final int y) {
        return selectSegment(x, y, true);
    }

    public boolean isMultipleAcceptedObject(final ConstructionObject o) {
        boolean b=o instanceof AreaObject;
        b=b||o instanceof PrimitiveLineObject;
        b=b||o instanceof PrimitiveCircleObject;
        b=b||o instanceof QuadricObject;
        b=b||o instanceof JLocusTrackObject;
        return b;
    }

    public void selectMultipleFinals(final int x, final int y,
            final boolean multiple) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&(o instanceof PointObject)&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.removeAllElements();
                return;
            }
            if (o.isSelectable()&&(isMultipleAcceptedObject(o))&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
        }
        if (V.size()>1) {
            V.removeAllElements();
        }
    }

    public ConstructionObject selectMultipleFinal(final int x, final int y,
            final boolean multiple) {
        selectMultipleFinals(x, y, multiple);
        return (ConstructionObject) select(V);
    }

    /**
     * Select a single ray near x,y
     **/
    public RayObject selectRay(final int x, final int y, final boolean multiple) {
        selectRays(x, y, multiple);
        return (RayObject) select(V);
    }

    public void selectRays(final int x, final int y, final boolean multiple) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&(o instanceof RayObject)&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
        }
    }

    public RayObject selectRay(final int x, final int y) {
        return selectRay(x, y, true);
    }

    /**
     * Select a circle near x,y. A non-filled object is preferred before a
     * filled object.
     **/
    public PrimitiveCircleObject selectCircle(final int x, final int y,
            final boolean multiple) {
        selectCircles(x, y, multiple);
        return (PrimitiveCircleObject) select(V);
    }

    public void selectCircles(final int x, final int y, final boolean multiple) {
        Enumeration e=C.elements();
        V.removeAllElements();
        boolean haveNotFilled=false;
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&(o instanceof PrimitiveCircleObject)&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
                if (!o.isFilledForSelect()) {
                    haveNotFilled=true;
                }
            }
        }
        if (haveNotFilled) {
            e=V.elements();
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                if (o.isFilledForSelect()) {
                    V.removeElement(o);
                }
            }
        }
    }

    public PrimitiveCircleObject selectCircle(final int x, final int y) {
        return selectCircle(x, y, true);
    }

    public ConstructionObject selectAnimationObject(final int x, final int y) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&o.nearto(x, y, this)&&(o instanceof PointObject)) {
                PointObject pt=(PointObject) o;
                if (pt.isPointOn()) {
                    V.addElement(o);
                }
            } else if (o.isSelectable()&&o.nearto(x, y, this)&&(o instanceof ExpressionObject)) {
                V.addElement(o);
            }
        }
        return select(V);
    }

    /**
     * Select a point near x,y.
     **/
    public PointObject selectPoint(final int x, final int y,
            final boolean multiple) {
        selectPointObjects(x, y, multiple);
        return (PointObject) select(V);
    }

    public void selectPointObjects(final int x, final int y,
            final boolean multiple) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&o instanceof PointObject&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
        }
    }

    public PointObject selectPoint(final int x, final int y) {
        return selectPoint(x, y, true);
    }

    public PointObject selectPoint(final int x, final int y,
            final boolean multiple, final ConstructionObject until) {
        selectPointObjects(x, y, multiple, until);
        return (PointObject) select(V);
    }

    public void selectPointObjects(final int x, final int y,
            final boolean multiple, final ConstructionObject until) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o==until) {
                break;
            }
            if (o.isSelectable()&&o instanceof PointObject&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
        }
    }

    public PointObject selectPoint(final int x, final int y,
            final ConstructionObject until) {
        return selectPoint(x, y, true, until);
    }
    boolean NewPoint=false;
    
    public void setNewPoint(final boolean isNewPoint){
        NewPoint=isNewPoint;
    }

    public boolean isNewPoint() {
        return NewPoint;
    }

    /**
     * Select a point, and create a new point, if necessary. Even creates an
     * intersection, or a point bound to an object, if possible. If enabled, the
     * user is asked for confirmation in these cases. The variable NewPoint is
     * set to true, if the point was indeed created.
     *
     * @param multiple
     *            determines, if multiple selections are possible.
     * @param any
     *            determines, if the first point should be used.
     **/
    public PointObject selectCreatePoint(final int x, final int y,
            final boolean multiple, final boolean any, boolean altdown) {

        if ((isDP())&&(!isEuclidian())&&(!PaletteManager.isSelected("bi_distance"))) {
//        if ((isDP())) {
            if (C!=null) {
                double xx=x(x), yy=y(y);
                PrimitiveCircleObject Hz=(PrimitiveCircleObject) C.find("Hz");
                if (Math.sqrt(xx*xx+yy*yy)>Hz.getR()) {
                    return null;
                }
            }
        }
        NewPoint=false;
        if (Preview&&!is3D()) { // modified by Dibs for 3D 	
        	final PointObject p=new PointObject(C, x(x), y(y));
        	addObject(p);
        	p.setSuperHidden(true);
        	PreviewObject=p;
        	return p;
        }
        if (Preview&&(PaletteManager.isSelected("segment3D")||PaletteManager.isSelected("line3D")||PaletteManager.isSelected("ray3D")||PaletteManager.isSelected("midpoint3D")||PaletteManager.isSelected("vector3D"))) {
        	final PointObject p=new PointObject(C, x(x), y(y));
                p.setIs3D(true);
                p.setXYZ(NaN,NaN,NaN);
        	addObject(p);
        	p.setSuperHidden(true);
        	PreviewObject=p;
        	return p;
        }
        // User selects a known point:
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&o instanceof PointObject&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
            sort(V);
        }
        if (V.size()>0) {
            if (any) {
                return (PointObject) (V.elementAt(0));
            }
            final ConstructionObject o=select(V, Control||!Global.getParameter("options.indicate", true));
            if (o!=null) {
                return (PointObject) o;
            }
            return null;
        }
        if (Preview) return null;
        // User creates a new point:
        ConstructionObject oc=tryCreateIntersection(x, y, true);
        if (oc!=null) {
            final PointObject o=(PointObject) oc;
            o.edit(this, false, false);


            // o.setDefaults();
            // if (o.showName()) renameABC(o,false,false);
            return o;
        }
        if (!IntersectionYes) {
            return null;
        }
        final MyVector w=selectPointonObjects(x, y, true, false);
        filter(w, x, y, true);
        if (w.size()>0) {
            oc=select(w, !Control);
            if (oc==null) {
                return null;
            }
            final PointObject o=new PointObject(C, x(x), y(y), oc);
            if (is3D()&&o instanceof PointObject&&((PointObject) o).getBound()!=null) { // Dibs : 2D -> 3D
            	validate();
            	if (((PointObject) o).getBound() instanceof TwoPointLineObject) {
        		TwoPointLineObject ligne= (TwoPointLineObject) ((PointObject) o).getBound();
    			if (((PointObject) ligne.getP1()).is3D()&&((PointObject) ligne.getP2()).is3D()) {
    				try {
    					double x1=((PointObject) ligne.getP1()).getX3D();
    					double y1=((PointObject) ligne.getP1()).getY3D();
        				double z1=((PointObject) ligne.getP1()).getZ3D();
        				double x2=((PointObject) ligne.getP2()).getX3D();
        				double y2=((PointObject) ligne.getP2()).getY3D();
        				double z2=((PointObject) ligne.getP2 ()).getZ3D();
        				double x_O=getConstruction().find("O").getX();
        				double x_X=getConstruction().find("X").getX();
        				double x_Y=getConstruction().find("Y").getX();
        				double x_Z=getConstruction().find("Z").getX();
        				double alpha1=(o.getX()-x_O-x1*(x_X-x_O)-y1*(x_Y-x_O)-z1*(x_Z-x_O))/((x2-x1)*(x_X-x_O)+(y2-y1)*(x_Y-x_O)+(z2-z1)*(x_Z-x_O));
        				if (x1==x2&&y1==y2) {
        					double y_O=getConstruction().find("O").getY();
            				double y_X=getConstruction().find("X").getY();
            				double y_Y=getConstruction().find("Y").getY();
            				double y_Z=getConstruction().find("Z").getY();
        					alpha1=(getY()-y_O-x1*(y_X-y_O)-y1*(y_Y-y_O)-z1*(y_Z-y_O))/((x2-x1)*(y_X-y_O)+(y2-y1)*(y_Y-y_O)+(z2-z1)*(y_Z-y_O));
        				}
        				((PointObject) o).setX3D(x1+alpha1*(x2-x1));
        				((PointObject) o).setY3D(y1+alpha1*(y2-y1));
        				((PointObject) o).setZ3D(z1+alpha1*(z2-z1));
        				((PointObject) o).setIs3D(true);
        				o.validate();
    	            	} catch (final Exception eBary) {
    	            		}
    					}
    			}
            	else if (((PointObject) o).getBound() instanceof QuadricObject) {
            		QuadricObject quadrique= (QuadricObject) ((PointObject) o).getBound();
            		if (quadrique.getP()[0].is3D()&&quadrique.getP()[1].is3D()&&quadrique.getP()[2].is3D()&&quadrique.getP()[3].is3D()&&quadrique.getP()[4].is3D()) {
            			try {
        					double x0=quadrique.getP()[0].getX3D();
            				double y0=quadrique.getP()[0].getY3D();
            				double z0=quadrique.getP()[0].getZ3D();
            				double x1=quadrique.getP()[1].getX3D();
            				double y1=quadrique.getP()[1].getY3D();
            				double z1=quadrique.getP()[1].getZ3D();
            				double x2=quadrique.getP()[2].getX3D();
            				double y2=quadrique.getP()[2].getY3D();
            				double z2=quadrique.getP()[2].getZ3D();
            				double x_O=getConstruction().find("O").getX();
            				double x_X=getConstruction().find("X").getX();
            				double x_Y=getConstruction().find("Y").getX();
            				double x_Z=getConstruction().find("Z").getX();
            				double y_O=getConstruction().find("O").getY();
            				double y_X=getConstruction().find("X").getY();
            				double y_Y=getConstruction().find("Y").getY();
            				double y_Z=getConstruction().find("Z").getY();
            				double coeffa=(x1-x0)*(x_X-x_O)+(y1-y0)*(x_Y-x_O)+(z1-z0)*(x_Z-x_O);
            				double coeffb=(x2-x0)*(x_X-x_O)+(y2-y0)*(x_Y-x_O)+(z2-z0)*(x_Z-x_O);
            				double coeffc=(x1-x0)*(y_X-y_O)+(y1-y0)*(y_Y-y_O)+(z1-z0)*(y_Z-y_O);
            				double coeffd=(x2-x0)*(y_X-y_O)+(y2-y0)*(y_Y-y_O)+(z2-z0)*(y_Z-y_O);
            				double coeffe=getX()-x_O-x0*(x_X-x_O)-y0*(x_Y-x_O)-z0*(x_Z-x_O);
            				double coefff=getY()-y_O-x0*(y_X-y_O)-y0*(y_Y-y_O)-z0*(y_Z-y_O);
            				double alpha1=(coeffe*coeffd-coefff*coeffb)/(coeffa*coeffd-coeffb*coeffc);
            				double beta1=(coeffa*coefff-coeffc*coeffe)/(coeffa*coeffd-coeffb*coeffc);
            				((PointObject) o).setX3D(x0+alpha1*(x1-x0)+beta1*(x2-x0));
            				((PointObject) o).setY3D(y0+alpha1*(y1-y0)+beta1*(y2-y0));
            				((PointObject) o).setZ3D(z0+alpha1*(z1-z0)+beta1*(z2-z0));
            				((PointObject) o).setIs3D(true);
            				o.validate();
        	            	} catch (final Exception eBary) {
        	            }
            			
            		}
            	}
            }
            o.edit(this, false, false);
            // if (ShowGrid&&!Global.getParameter("grid.leftsnap",
            // false)) {
            // o.snap(this);
            // }
            o.setUseAlpha(true);
            addObject(o);
            validate();
            o.setDefaults();
            NewPoint=true;
            repaint(); //fix a bug when we put a point on an object which is above an other
            return o;
        }
        final PointObject p=new PointObject(C, x(x), y(y));
        boolean macroIs3D=false;   // Dibs : si la macro de menu est dans le dossier 3D, elle créera un point 3D à la volée
        if (OC instanceof MacroRunner) {
        	try {
        		macroIs3D=((MacroRunner) OC).getM().getName().substring(0,3).equals("3D/");
        	}
        	catch (final Exception f) {}
        }
        if (PaletteManager.isSelected("bi_3Dcoords")||PaletteManager.isSelected("vector3D")||PaletteManager.isSelected("midpoint3D")||PaletteManager.isSelected("bi_3Dsymc")||PaletteManager.isSelected("bi_3Dproj")||PaletteManager.isSelected("bi_3Dsymp")||PaletteManager.isSelected("bi_3Dtrans")||PaletteManager.isSelected("area3D")||PaletteManager.isSelected("segment3D")||PaletteManager.isSelected("line3D")||PaletteManager.isSelected("ray3D")||PaletteManager.isSelected("bi_3Dsphererayon")
        	||PaletteManager.isSelected("bi_3Dspherepoint")||PaletteManager.isSelected("bi_3Dplandroite")||PaletteManager.isSelected("bi_3Dplanplan")||PaletteManager.isSelected("bi_3Dspheredroite")||PaletteManager.isSelected("bi_3Dsphereplan")||PaletteManager.isSelected("bi_3Dcube")||PaletteManager.isSelected("bi_3Dtetra")||PaletteManager.isSelected("bi_3Docta")||PaletteManager.isSelected("bi_3Disoc")||PaletteManager.isSelected("bi_3Ddode")||PaletteManager.isSelected("bi_3Dcircle1")||PaletteManager.isSelected("bi_3Dcircle2")||PaletteManager.isSelected("bi_3Dcircle3pts")||PaletteManager.isSelected("angle3D")
        	||macroIs3D) {// création d'un point 3D.
        	p.setIs3D(is3D());
        	try {
        		double xO=C.find("O").getX(), yO = C.find("O").getY();
        		double x3DO=getConstruction().find("O").getX();
        		double y3DO=getConstruction().find("O").getY();
        		double xx3D = Math.sin(Math.toRadians(getConstruction().find("E10").getValue()))*(p.getX()-xO)-Math.sin(Math.toRadians(getConstruction().find("E11").getValue()))*Math.cos(Math.toRadians(getConstruction().find("E10").getValue()))*(p.getY()-yO);
    			double yy3D = Math.cos(Math.toRadians(getConstruction().find("E10").getValue()))*(p.getX()-xO)+Math.sin(Math.toRadians(getConstruction().find("E11").getValue()))*Math.sin(Math.toRadians(getConstruction().find("E10").getValue()))*(p.getY()-yO);
    			double zz3D = Math.cos(Math.toRadians(getConstruction().find("E11").getValue()))*(p.getY()-yO);
                        if (Math.abs(xx3D)<1e-16) {
                            xx3D=0;
                        }
                        if (Math.abs(yy3D)<1e-16) {
                            yy3D=0;
                        }
                        if (Math.abs(zz3D)<1e-16) {
                            zz3D=0;
                        }
    			p.setX3D(xx3D);
    			p.setY3D(yy3D);
    			p.setZ3D(zz3D);
    			p.setFixed("x(O)+("+xx3D+")*(x(X)-x(O))+("+yy3D+")*(x(Y)-x(O))+("+zz3D+")*(x(Z)-x(O))", "y(O)+("+xx3D+")*(y(X)-y(O))+("+yy3D+")*(y(Y)-y(O))+("+zz3D+")*(y(Z)-y(O))");
        	}
        	catch (final Exception f) {}
        	
        }
        if (altdown&&getAxis_show()) {
            p.setHalfIncrement(this);
        }

//        if (getAxis_show()) {
//            p.snap(this);
//        }



        addObject(p);
        p.setDefaults();

        if ((isDP())&&(!isEuclidian())&&(!PaletteManager.isSelected("bi_distance"))) {

//        if (isDP()) {
            if (C!=null) {
                PrimitiveCircleObject HzBack=(PrimitiveCircleObject) C.find("HzBack");
                p.setBound(HzBack);
                p.setInside(true);
            }
        }
        // rename Point, if showname
        // if (p.showName()) renameABC(p,false,false);
        NewPoint=true;

        if (!PaletteManager.isSelected("bi_function_u")) {
            p.edit(this, false, false);
        }
        return p;
    }
    boolean IntersectionYes=false;

    public ConstructionObject tryCreateIntersection(final int x, final int y,
            final boolean ask) {
        final MyVector w=selectPointonObjects(x, y, true, true);
        sort(w);
        IntersectionYes=true;
        if (w.size()<2) {
            return null;
        }
        final ConstructionObject P1=(ConstructionObject) w.elementAt(0);
        final ConstructionObject P2=(ConstructionObject) w.elementAt(1);
        if (!(P1 instanceof PointonObject&&P2 instanceof PointonObject)) {
            return null;
        }
        if (!((PointonObject) P1).canInteresectWith(P2)||!((PointonObject) P2).canInteresectWith(P1)) {
            return null;
        }
        final IntersectionObject o[]=IntersectionConstructor.construct(P1,
                P2, C);
        if (o.length==1&&!o[0].valid()) {
            return null;
        }
        if (o.length==2&&!o[0].valid()&&!o[1].valid()) {
            return null;
        }
        if (o.length==4&&!o[0].valid()&&!o[1].valid()&&!o[2].valid()&&!o[3].valid()) {
            return null;
        }

        if (IntersectionYes) {
            IntersectionObject oc=o[0];
            if (o.length==2) {
                final double d0=o[0].valid()?o[0].distanceTo(x, y, this):Double.MAX_VALUE;
                final double d1=o[1].valid()?o[1].distanceTo(x, y, this):Double.MAX_VALUE;
                if (d0<d1) {
                    oc=o[0];
                } else {
                    oc=o[1];
                }
            }
            if (o.length==4) {
                final double d0=o[0].valid()?o[0].distanceTo(x, y, this):Double.MAX_VALUE;
                final double d1=o[1].valid()?o[1].distanceTo(x, y, this):Double.MAX_VALUE;
                final double d2=o[2].valid()?o[2].distanceTo(x, y, this):Double.MAX_VALUE;
                final double d3=o[3].valid()?o[3].distanceTo(x, y, this):Double.MAX_VALUE;
                double min=Math.min(d0, d1);
                min=Math.min(min, d2);
                min=Math.min(min, d3);
                if (d0==min) {
                    oc=o[0];
                } else if (d1==min) {
                    oc=o[1];
                } else if (d2==min) {
                    oc=o[2];
                } else if (d3==min) {
                    oc=o[3];
                }
            }
            if ((P1 instanceof TwoPointLineObject&&((TwoPointLineObject) P1).getP1().is3D())||(P1 instanceof TwoPointLineObject&&((TwoPointLineObject) P1).getP2().is3D())||(P2 instanceof TwoPointLineObject&&((TwoPointLineObject) P2).getP1().is3D())||(P2 instanceof TwoPointLineObject&&((TwoPointLineObject) P2).getP2().is3D())) {
            	try {	// Dibs intersection 3D
                	double a1=((TwoPointLineObject) P1).getP1().getX3D();
                	double b1=((TwoPointLineObject) P1).getP1().getY3D();
                	double c1=((TwoPointLineObject) P1).getP1().getZ3D();
                	double a2=((TwoPointLineObject) P1).getP2().getX3D();
                	double b2=((TwoPointLineObject) P1).getP2().getY3D();
                	double c2=((TwoPointLineObject) P1).getP2().getZ3D();
                	double a3=((TwoPointLineObject) P2).getP1().getX3D();
                	double b3=((TwoPointLineObject) P2).getP1().getY3D();
                	double c3=((TwoPointLineObject) P2).getP1().getZ3D();
                	double a4=((TwoPointLineObject) P2).getP2().getX3D();
                	double b4=((TwoPointLineObject) P2).getP2().getY3D();
                	double c4=((TwoPointLineObject) P2).getP2().getZ3D();
                	double det =(a2-a1)*(b3-b4)+(b2-b1)*(a4-a3);
                	double dets=(b3-b1)*(a4-a3)-(a3-a1)*(b4-b3);
                	double dett=(a2-a1)*(b3-b1)-(b2-b1)*(a3-a1);
                	double s=0.0;
                	double t=0.0;
                	if (Math.abs(det)<1e-12) {
                		det =(b2-b1)*(c3-c4)+(c2-c1)*(b4-b3);
                		dets=(c3-c1)*(b4-b3)-(b3-b1)*(c4-c3);
                		dett=(b2-b1)*(c3-c1)-(c2-c1)*(b3-b1);
                	}
                	s=dets/det;
            		t=dett/det;
                	//System.out.println(Math.abs((c2-c1)*s-(c4-c3)*t-c3+c1));
                	if (Math.abs((c2-c1)*s-(c4-c3)*t-c3+c1)<1e-12) {
                		oc.setX3D(a1+s*(a2-a1));
                		oc.setY3D(b1+s*(b2-b1));
                		oc.setZ3D(c1+s*(c2-c1));
        				oc.setIs3D(true);
                	}
                	else return null;
            	} catch (final Exception ex) {
            		return null;
                }	
            }
            addObject(oc);
            oc.autoAway();
            oc.validate(x(x), y(y));
            validate();
            oc.setDefaults();
            oc.setRestricted(getRestricted());
            NewPoint=true;
            return oc;

        } else {
            return null;
        }
    }

    public PointObject selectCreatePoint(final int x, final int y, boolean altdown) {
        return selectCreatePoint(x, y, true, false, altdown);
    }

    /**
     * Select a moveable point at x,y. Ask user, if necessary.
     **/
    public ConstructionObject selectMoveablePoint(final int x, final int y) {
        final ConstructionObject s=findSelectedObject();
        if (s instanceof PointObject&&((MoveableObject) s).moveable()&&s.nearto(x, y, this)) {
            return s;
        }
        V.removeAllElements();
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&(o instanceof PointObject)&&((MoveableObject) o).moveable()&&o.nearto(x, y, this)) {
                V.addElement(o);
            }
        }
        if (V.size()==1) {
            return (ConstructionObject) V.elementAt(0);
        }
        final ConstructionObject o=select(V);
        if (o!=null) {
            o.setSelected(true);
        }
        return null;
    }

    /**
     * Select a point with a selector, provided by the calling constructor.
     **/
    public ConstructionObject selectWithSelector(final int x, final int y,
            final Selector sel, final ConstructionObject until,
            final boolean choice) {
        V.removeAllElements();
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o==until) {
                break;
            }
            if (o.isSelectable()&&sel.isAdmissible(this, o)&&o.nearto(x, y, this)) {

                V.addElement(o);
            }
        }
        if (V.size()==1) {
            return (ConstructionObject) V.elementAt(0);
        }
        if (!choice) {
            if (V.size()>0) {
                return (ConstructionObject) V.elementAt(0);
            } else {
                return null;
            }
        }
        final ConstructionObject o=select(V, x, y);
        return o;
    }

    public ConstructionObject selectWithSelector(final int x, final int y,
            final Selector sel, final ConstructionObject until) {
        return selectWithSelector(x, y, sel, until, true);
    }

    public ConstructionObject selectWithSelector(final int x, final int y,
            final Selector sel, final boolean choice) {
        return selectWithSelector(x, y, sel, null, choice);
    }

    public ConstructionObject selectWithSelector(final int x, final int y,
            final Selector sel) {
        return selectWithSelector(x, y, sel, null, true);
    }

    /**
     * Select a moveable point at x,y. Don't ask user, take first.
     **/
    public ConstructionObject selectImmediateMoveablePoint(final int x,
            final int y) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&o instanceof PointObject&&((MoveableObject) o).moveable()&&o.nearto(x, y, this)) {
                V.addElement(o);
            }
        }
        if (V.size()==0) {
            return null;
        }
        return (ConstructionObject) V.elementAt(0);
    }

    /**
     * Select a moveable object at x,y.
     **/
    public ConstructionObject selectMoveableObject(final int x, final int y) {
        final ConstructionObject s=findSelectedObject();
        if (s instanceof MoveableObject&&((MoveableObject) s).moveable()&&s.nearto(x, y, this)) {
            return s;
        }
        selectMoveableObjects(x, y);
        if (V.size()==1) {
            return (ConstructionObject) V.elementAt(0);
        }
        final ConstructionObject o=select(V);
        if (o!=null) {
            if (!Global.getParameter("options.choice", true)) {
                return o;
            }
            o.setSelected(true);
        }
        return null;
    }

    public void selectMoveableObjects(final int x, final int y,
            final boolean control) {
        V.removeAllElements();
        final ConstructionObject s=findSelectedObject();
        if (s instanceof MoveableObject&&((MoveableObject) s).moveable()&&s.nearto(x, y, this)) {
            V.addElement(s);
            return;
        } else if (control&&s instanceof FixedCircleObject&&s.nearto(x, y, this)&&((FixedCircleObject) s).fixedByNumber()) {
            V.addElement(s);
            return;
        } else if (control&&s instanceof FixedAngleObject&&s.nearto(x, y, this)&&((FixedAngleObject) s).fixedByNumber()) {
            V.addElement(s);
            return;
        }
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (!control&&o.isSelectable()&&o instanceof MoveableObject&&((MoveableObject) o).moveable()&&o.nearto(x, y, this)) {
                V.addElement(o);
            } else if (control&&o instanceof FixedCircleObject&&o.nearto(x, y, this)&&((FixedCircleObject) o).fixedByNumber()) {
                V.addElement(o);
            } else if (control&&o instanceof FixedAngleObject&&o.nearto(x, y, this)&&((FixedAngleObject) o).fixedByNumber()) {
                V.addElement(o);
            }
        }
        filter(V, x, y);
    }

    public void selectMoveableObjects(final int x, final int y) {
        selectMoveableObjects(x, y, false);
    }

    /**
     * Select a moveable object at x,y. Don't ask user, take first.
     **/
    public ConstructionObject selectImmediateMoveableObject(final int x,
            final int y) {
        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&o instanceof MoveableObject&&((MoveableObject) o).moveable()&&o.nearto(x, y, this)) {
                V.addElement(o);
            }
        }
        filter(V, x, y);
        if (V.size()==0) {
            return null;
        }
        return (ConstructionObject) V.elementAt(0);
    }

    /**
     * try to determine the unique objects that are near coordinates x,y and
     * delete all others from the vector v.
     **/
    public void filter(final MyVector v, final int x, final int y,
            final boolean choice) {
        boolean HasPoints=false, HasNotFilled=false;
        Enumeration e=v.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o instanceof PointObject) {
                HasPoints=true;
            }
            if (!o.isFilledForSelect()) {
                HasNotFilled=true;
            }
        }
        if (HasPoints) {
            e=v.elements();
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                if (!o.onlynearto(x, y, this)) {
                    v.removeElement(o);
                }
            }
        } else if (HasNotFilled) {
            e=v.elements();
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                if (o.isFilledForSelect()) {
                    v.removeElement(o);
                }
            }
        }
        sort(v);
        if (!choice) {
            v.truncate(1);
        }
    }

    public void filter(final MyVector v, final int x, final int y) {
        filter(v, x, y, Global.getParameter("options.choice", true)||Control);
    }

    /**
     * user must select an object in the selection dialog, unless the selection
     * is unique anyway, or the filter determines that the selection is unique.
     **/
    public ConstructionObject select(final MyVector v, final int x,
            final int y, final boolean choice) {
        if (v.size()==0) {
            return null;
        }
        if (v.size()==1) {
            return (ConstructionObject) v.elementAt(0);
        }
        filter(v, x, y);
        if (v.size()==1) {
            return (ConstructionObject) v.elementAt(0);
        }
        if (!choice) {
            return (ConstructionObject) v.elementAt(0);
        }
        sortRow(V);
	new eric.JSelectPopup(this, v, RightClicked);
        RightClicked=false;
        //final eric.JSelectPopup d=new eric.JSelectPopup(this, v); //remonté d'une ligne et ajouté le booléen RightClicked
        return null;
    }

    public ConstructionObject select(final MyVector v, final int x, final int y) {
        return select(v, x, y, Global.getParameter("options.choice", true)||Control);
    }

    /**
     * user must select an object in the selection dialog, unless the selection
     * is unique anyway.
     **/
    public ConstructionObject select(final MyVector v, final boolean choice) {
        if (v.size()==0) {
            return null;
        }
        if (v.size()==1) {
            return (ConstructionObject) v.elementAt(0);
        }
        // if (!choice) return (ConstructionObject)v.elementAt(0);
        sortRow(V);
	new eric.JSelectPopup(this, v, RightClicked);
        RightClicked=false;
        //final eric.JSelectPopup d=new eric.JSelectPopup(this, v);
        return null;
    }

    public ConstructionObject select(final MyVector v) {
        return select(v, Global.getParameter("options.choice", true)||Control);
    }

    /**
     * select the label of a point, i.e. a point, which is set by the user
     *
     * @return the first label found
     **/
    public ConstructionObject selectLabel(final int x, final int y) {
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&o.textcontains(x, y, this)) {
                return o;
            }
        }
        return null;
    }

    public ConstructionObject findSelectedObject() {
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.selected()) {
                return o;
            }
        }
        return null;
    }
    // Indication functions
    MyVector Indicated=new MyVector();
    PointObject IndicatePoint=null;

    public void indicate(final MyVector v, final boolean showname) {
        if (v.size()==1) {
            if (showname) {
                setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
            } else if (v.elementAt(0) instanceof PointObject) {
                setCursor(new Cursor(Cursor.HAND_CURSOR));
            }
        } else {
            setCursor(Cursor.getDefaultCursor());
        }
        if (Indicated.equalsIdentical(v)) {
            return;
        }
        Enumeration e=Indicated.elements();
        while (e.hasMoreElements()) {
            ((ConstructionObject) e.nextElement()).setIndicated(false);
        }
        Indicated.removeAllElements();
        e=v.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            Indicated.addElement(o);
            o.setIndicated(true, showname);
        }
        repaint();

    }

    public void indicate(final MyVector v) {
        indicate(v, false);
    }

    public void clearIndicated() {
        IndicatePoint=null;
        if (Indicated.size()==0) {
            return;
        }
        final Enumeration e=Indicated.elements();
        while (e.hasMoreElements()) {
            ((ConstructionObject) e.nextElement()).setIndicated(false);
        }
        Indicated.removeAllElements();
        setCursor(Cursor.getDefaultCursor());
        repaint();
    }
    boolean Preview=false;
    ConstructionObject LastNonPreview=null;
    MoveableObject PreviewObject=null;

    public void movePreview(double x, double y) {
        if (PreviewObject!=null) {
            PreviewObject.move(x, y);
            validate();
            repaint();
        }
    }

    public void movePreview(final MouseEvent e) {
        if (PreviewObject!=null) {
            PreviewObject.move(x(e.getX()), y(e.getY()));
            validate();
            repaint();
        }
    }

    public void prepareForPreview(final MouseEvent e) {
        LastNonPreview=C.lastButN(0);
        Preview=true;
    }

    public boolean isPreview() {
        return Preview;
    }

    public void clearPreview() {
        if (!Preview) {
            return;
        }
        C.clearAfter(LastNonPreview);
        LastNonPreview=null;
        PreviewObject=null;
        Preview=false;
        Count.fixAll(false);
    }

    public void setPreviewObject(final MoveableObject o) {
        PreviewObject=o;
    }

    public MoveableObject getPreviewObject() {
        return PreviewObject;
    }

    public ConstructionObject indicateTryCreateIntersection(final int x,
            final int y, final boolean ask) {

        final MyVector w=selectPointonObjects(x, y, true, true);

        sort(w);
        IntersectionYes=true;
        if (w.size()<2) {
            return null;
        }

        final IntersectionObject o[]=IntersectionConstructor.construct(
                (ConstructionObject) w.elementAt(0), (ConstructionObject) w.elementAt(1), C);

//        System.out.println("indicateTryCreateIntersection : "+o.length);

        if (o.length==1&&!o[0].valid()) {
            return null;
        }
        if (o.length==2) {
            if (!o[1].valid()) {
                if (!o[0].valid()) {
                    return null;
                }
            } else {
//                System.out.println("indicateTryCreateIntersection");
                final IntersectionObject h=o[0];
                o[0]=o[1];
                o[1]=h;
            }
        }
        IntersectionObject oc=o[0];
        if (o.length==2&&o[1].valid()) {
            final double d0=o[0].distanceTo(x, y, this), d1=o[1].distanceTo(x, y, this);
            if (d1<d0) {
                oc=o[1];
            }
        }
        if (o.length==4) {
            final double d0=o[0].valid()?o[0].distanceTo(x, y, this):Double.MAX_VALUE;
            final double d1=o[1].valid()?o[1].distanceTo(x, y, this):Double.MAX_VALUE;
            final double d2=o[2].valid()?o[2].distanceTo(x, y, this):Double.MAX_VALUE;
            final double d3=o[3].valid()?o[3].distanceTo(x, y, this):Double.MAX_VALUE;
            double min=Math.min(d0, d1);
            min=Math.min(min, d2);
            min=Math.min(min, d3);
            if (d0==min) {
                oc=o[0];
            } else if (d1==min) {
                oc=o[1];
            } else if (d2==min) {
                oc=o[2];
            } else if (d3==min) {
                oc=o[3];
            }
        }
        // oc.setDefaults();
        oc.setIndicated(true);
        oc.setType(PointObject.CIRCLE);
        oc.setColorType(ConstructionObject.THICK);
        IndicatePoint=oc;
        indicate(w);
        oc.validate(x(x), y(y));
        return oc;

    }

//    public void indicateCreatePoint(final int x, final int y,
//            final boolean multiple) {
//
//        final Enumeration e=C.elements();
//        V.removeAllElements();
//        while (e.hasMoreElements()) {
//            final ConstructionObject o=(ConstructionObject) e.nextElement();
//            if (o.isSelectable()&&o instanceof PointObject&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
//                V.addElement(o);
//            }
//            sort(V);
//        }
//        if (V.size()>0) {
//            IndicatePoint=null;
//            filter(V, x, y, false);
//            indicate(V);
//            return;
//        }
//        PointObject oc=(PointObject) indicateTryCreateIntersection(x, y, true);
//        if (oc!=null) {
//            return;
//        }
//        final MyVector w=selectPointonObjects(x, y, true, false);
//        filter(w, x, y, true);
//
//        if (w.size()>=1) {
//            if (!w.equalsIdentical(Indicated)) {
//
//                oc=new PointObject(C, x(x), y(y), (ConstructionObject) w.elementAt(0));
//
//                if (getAxis_show()&&!Global.getParameter("grid.leftsnap", false)) {
//                    oc.snap(this);
//                }
//                oc.setUseAlpha(true);
//                oc.validate();
//                oc.setIndicated(true);
//                oc.setType(PointObject.CIRCLE);
//                oc.setColorType(ConstructionObject.THICK);
//                IndicatePoint=oc;
//                indicate(w);
//            } else if (IndicatePoint!=null) {
//                IndicatePoint.setType(PointObject.CIRCLE);
//                IndicatePoint.setColorType(ConstructionObject.THICK);
//                IndicatePoint.move(x(x), y(y));
//                IndicatePoint.project((ConstructionObject) w.elementAt(0));
//                repaint();
//            }
//        } else {
//            clearIndicated();
//        }
//    }
    public PointObject indicateCreatePoint(final int x, final int y,
            final boolean multiple) {

        final Enumeration e=C.elements();
        V.removeAllElements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isSelectable()&&o instanceof PointObject&&o.nearto(x, y, this)&&(multiple||!o.selected())) {
                V.addElement(o);
            }
            sort(V);
        }
        if (V.size()>0) {
            IndicatePoint=null;
            filter(V, x, y, false);
            indicate(V);
            return (PointObject) V.elementAt(0);
        }
        PointObject oc=(PointObject) indicateTryCreateIntersection(x, y, true);
        if (oc!=null) {
            return oc;
        }
        final MyVector w=selectPointonObjects(x, y, true, false);
        filter(w, x, y, true);

        if (w.size()>0) {
            if (!w.equalsIdentical(Indicated)) {

                oc=new PointObject(C, x(x), y(y), (ConstructionObject) w.elementAt(0));

                if (getAxis_show()&&!Global.getParameter("grid.leftsnap", false)) {
                    oc.snap(this);
                }
                oc.setUseAlpha(true);
                oc.validate();
                oc.setIndicated(true);
                oc.setType(PointObject.CIRCLE);
                oc.setColorType(ConstructionObject.THICK);
                IndicatePoint=oc;
                indicate(w);
                return IndicatePoint;
            } else if (IndicatePoint!=null) {
                IndicatePoint.setType(PointObject.CIRCLE);
                IndicatePoint.setColorType(ConstructionObject.THICK);
                IndicatePoint.move(x(x), y(y));
                IndicatePoint.project((ConstructionObject) w.elementAt(0));
                repaint();
                return IndicatePoint;
            }
        } else {
            clearIndicated();
        }
        return null;
    }

    public void indicateCircleLineObjects(final int x, final int y) {
        final MyVector w=selectCircleLineObjects(x, y);
        filter(V, x, y);
        indicate(w);
    }

    public void indicatePointonObjects(final int x, final int y) {

        final MyVector w=selectPointonObjects(x, y);
        filter(V, x, y);
        indicate(w);
    }

    public void indicateIntersectedObjects(final int x, final int y) {
        final MyVector w=selectPointonObjects(x, y);
        if (!w.equalsIdentical(Indicated)&&w.size()>=2) {
            final IntersectionObject o[]=IntersectionConstructor.construct(
                    (ConstructionObject) w.elementAt(0), (ConstructionObject) w.elementAt(1), C);
            IntersectionObject oc=o[0];
            if (o.length==2) {
                final double d0=o[0].distanceTo(x, y, this), d1=o[1].distanceTo(x, y, this);
                if (d1<d0) {
                    oc=o[1];
                }
                oc.autoAway();
            }
            if (o.length==4) {
                final double d0=o[0].valid()?o[0].distanceTo(x, y, this):Double.MAX_VALUE;
                final double d1=o[1].valid()?o[1].distanceTo(x, y, this):Double.MAX_VALUE;
                final double d2=o[2].valid()?o[2].distanceTo(x, y, this):Double.MAX_VALUE;
                final double d3=o[3].valid()?o[3].distanceTo(x, y, this):Double.MAX_VALUE;
                double min=Math.min(d0, d1);
                min=Math.min(min, d2);
                min=Math.min(min, d3);
                if (d0==min) {
                    oc=o[0];
                } else if (d1==min) {
                    oc=o[1];
                } else if (d2==min) {
                    oc=o[2];
                } else if (d3==min) {
                    oc=o[3];
                }
                oc.autoAway();
            }
            oc.validate();
            oc.setDefaults();
            oc.setIndicated(true);
            oc.setColorType(ConstructionObject.THICK);
            oc.setType(PointObject.CIRCLE);
            IndicatePoint=oc;
        } else {
            IndicatePoint=null;
        }
        indicate(w);
    }

    public void indicateLineObjects(final int x, final int y) {
        selectLineObjects(x, y, true);
        filter(V, x, y);
        indicate(V);
    }

    public void indicateAnimationObjects(final int x, final int y) {
        selectAnimationObject(x, y);
        filter(V, x, y);
        indicate(V);
    }

    public void indicatePointObjects(final int x, final int y) {
        selectPointObjects(x, y, true);
        filter(V, x, y);
        indicate(V);
    }

    public void indicatePointObjects(final int x, final int y,
            final ConstructionObject until) {
        selectPointObjects(x, y, true, until);
        filter(V, x, y);
        indicate(V);
    }

    public void indicatePointsOrLines(final int x, final int y) {
        selectPointsOrLines(x, y, true);
        filter(V, x, y);
        indicate(V);
    }

    public void indicateSegments(final int x, final int y) {
        selectSegments(x, y, true);
        filter(V, x, y);
        indicate(V);
    }

    public void indicateMultipleFinal(final int x, final int y) {
        // selectMultipleFinals(x, y, true);
        filter(V, x, y);
        indicate(V);
    }

    public void indicateRays(final int x, final int y) {
        selectRays(x, y, true);
        filter(V, x, y);
        indicate(V);
    }

    public void indicateTwoPointLines(final int x, final int y) {
        selectTwoPointLines(x, y, true);
        filter(V, x, y);
        indicate(V);
    }

    public void indicateCircles(final int x, final int y) {
        selectCircles(x, y, true);
        filter(V, x, y);
        indicate(V);
    }

    public void indicateMoveableObjects(final int x, final int y,
            final boolean control) {
        selectMoveableObjects(x, y, control);
        filter(V, x, y);
        indicate(V);
    }

    public void indicateMoveableObjects(final int x, final int y) {
        indicateMoveableObjects(x, y, false);
    }

    public void indicateWithSelector(final int x, final int y,
            final Selector sel) {
        selectWithSelector(x, y, sel, false);
        filter(V, x, y);
        indicate(V);
    }

    public void indicateWithSelector(final int x, final int y,
            final Selector sel, final ConstructionObject until) {
        selectWithSelector(x, y, sel, until, false);
        filter(V, x, y);
        indicate(V);
    }

    public void indicateConstructableObjects(final int x, final int y) {
        selectConstructableObjects(x, y);
        filter(V, x, y);
        indicate(V);
    }

    public void indicateObjects(final int x, final int y, final boolean showname) {
        selectObjects(x, y);
        filter(V, x, y);
        indicate(V, showname);
    }

    public void indicateObjects(final int x, final int y) {
        indicateObjects(x, y, false);
    }

    public void setTool(final ObjectConstructor oc) {
	if (!(oc instanceof ZoomerTool)||!(OC instanceof SelectTool)) {
            if (OC!=null) {
		/* do NOT invalidate JSmacroTool when user want to move an object
		** or the construction by right clic
		** invalidation of JSmacroTool occurs only when user clic on an icon in PaletteManager
		** or on a submenu, in JGeneralMenuBar
		 */
                if (!(OC instanceof JSmacroTool)) {
                    OC.invalidate(this);
                }
            }
        }

//not working when multiple choice

//	if (!(oc instanceof ZoomerTool)||!(OC instanceof SelectTool)) {
//            if (OC!=null) {
//                //do NOT invalidate JSmacroTool when user want to move an object or the construction
//                if (!(OC instanceof JSmacroTool)) {
//                    OC.invalidate(this);
//                } else if (!(oc instanceof ZoomerTool||oc instanceof MoverTool) || ((JSmacroTool) OC).getPreviousTool() instanceof MoverTool) {
//                    ((JSmacroTool) OC).invalidate_and_saveoc(this, oc);
//                }
//            }
//        }

// note PM : la suite est à décommenter
// si jamais le code précédent ne répond pas aux exigences
// (les lignes précédentes sont à supprimer)

//        if (!(oc instanceof ZoomerTool)||!(OC instanceof SelectTool)) {
//            if (OC!=null) {
//                //do NOT invalidate JSmacroTool when user want to move an object or the construction
////	    if(!((oc instanceof ZoomerTool || oc instanceof MoverTool) && OC instanceof JSmacroTool))
//		OC.invalidate(this);
//
//                if (OC instanceof JSmacroTool) {
//                    ((JSmacroTool) OC).invalidate_and_saveoc(this, oc);
//                }else{
//                    OC.invalidate(this);
//                }
//                // note Eric : La modif ci-dessous provoquait une boucle sans fin dans le thread
//                // du script lorsque l'utilisateur prenait ZoomerTool ou MoverTool. InteractiveInput
//                // n'est pas un outil comme les autres : on tue le thread si l'utilisateur
//                // ne fait pas ce que le script veut qu'il fasse (montrer l'objet).
//                //
////                if (!(OC instanceof JSmacroTool)) {
////                    OC.invalidate(this);
////                } else if (!(oc instanceof ZoomerTool||oc instanceof MoverTool)) {
////                    ((JSmacroTool) OC).invalidate_and_saveoc(this, oc);
////                }
//            }
//        }


        OC=oc;
        OC.showStatus(this);
        clearIndicated();
        clearPreview();
    }

    public void setSuddenTool(final ObjectConstructor oc) // called from
    // ZirkelFrame
    {
        OC=oc;
    }

    public void reset() {
        clearPreview();
        clearIndicated();
        OC.reset(this);
    }

    public void clearSelected() // called from ObjectConstructor
    {
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            o.setSelected(false);
        }
        repaint();
    }

    public final synchronized void clear() // delete complete construction
    {
        if (OC!=null) {
            OC.invalidate(this);
        }
        C.clear();
        if (JCM!=null) {
            JCM.removeAllControls();
        }
        if (Animations!=null) {
            removeAllAnimations();
        }
        recompute();
        setDefaultColor(0);
        setDefaultColorType(0);
        //setDefaultType(2); //after restoring construction throw JS, point type is always "circle"(2) !
        reloadCD();
    }

    /**
     * Delete last construction step done by user (highest number) and all
     * non-visible steps before it.
     */
    public synchronized void back() {
        reset();
        ConstructionObject O=C.lastByNumber();
        if (O==null) {
            return;
        }
        if (O.isKeep()) {
            return;
        }
        delete(O);
//        System.out.println("entrée");
//        while (true) {
//            O=C.lastByNumber();
//            if (O==null) {
//                break;
//            } else if (!O.mustHide(this)||O.isHideBreak()||O.isKeep()||O.isOwnedByControl()) {
//                break;
//            }
//            delete(O);
//        }
//        System.out.println("sortie");
        validate();
    }

    public synchronized void undo() {
        reset();
        C.undo();
    }

    public void delete(final ConstructionObject o) {

        if (C.lastButN(0)==null) {
            return;
        }
        if (o.isKeep()) {
            return;
        }
        JControlsManager.removeOwnerControl(this, o);

        if (!(o instanceof AxisObject)) {
            C.clearConstructables();
            o.setFlag(true);
            C.determineChildren();
            C.delete(false);
	    update_distant(o, 0);
            reloadCD();
            repaintCD();
        }
    }

    /**
     * Delete a vector of construction objects.
     *
     * @param v
     */
    public void delete(final Vector v) {
        if (C.lastButN(0)==null) {
            return;
        }
        C.clearConstructables();
        final Enumeration e=v.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isKeep()) {
                return;
            }
            JControlsManager.removeOwnerControl(this, o);
            o.setFlag(true);
        }
        C.determineChildren();
        C.delete(false);
    }

    public boolean depends(final ConstructionObject o,
            final ConstructionObject on) {
        C.clearConstructables();
        on.setFlag(true);
        C.determineChildren();
        return o.isFlag();
    }

    public void addStatusListener(final StatusListener sl) {
        SL=sl;
    }

    public void showStatus(final String s) {
        if (SL!=null) {
            SL.showStatus(s);
        }
    }

    public void showStatus() {
        if (OC!=null) {
            OC.showStatus(this);
        }
    }

    public boolean showHidden() {
        return ShowHidden;
    }

    public void setDefaultColor(final int c) {
        C.DefaultColor=c;
    }

    public int getDefaultColor() {
        return C.DefaultColor;
    }

    public void setDefaultType(final int c) {
        C.DefaultType=c;
    }

    public int getDefaultType() {
        return C.DefaultType;
    }

    public void setPartial(final boolean flag) {
        C.Partial=flag;
    }

    public boolean getPartial() {
        return C.Partial;
    }

    public void setRestricted(final boolean flag) {
        C.Restricted=flag;
    }

    public boolean getRestricted() {
        return C.Restricted;
    }

    public void setPartialLines(final boolean flag) {
        C.PartialLines=flag;
    }

    public boolean getPartialLines() {
        return C.PartialLines;
    }

    public void setVectors(final boolean flag) {
        C.Vectors=flag;
    }

    public boolean getVectors() {
        return C.Vectors;
    }

    public void setLongNames(final boolean flag) {
        C.LongNames=flag;
    }

    public boolean getLongNames() {
        return C.LongNames;
    }

    public void setLargeFont(final boolean flag) {
        C.LargeFont=flag;
    }

    public boolean getLargeFont() {
        return C.LargeFont;
    }

    public void setBoldFont(final boolean flag) {
        C.BoldFont=flag;
    }

    public boolean getBoldFont() {
        return C.BoldFont;
    }

    public void setObtuse(final boolean flag) {
        C.Obtuse=flag;
    }

    public boolean getObtuse() {
        return C.Obtuse;
    }

    public void setSolid(final boolean flag) {
        C.Solid=flag;
    }

    public boolean getSolid() {
        return C.Solid;
    }

    public void setShowNames(final boolean flag) {
        C.ShowNames=flag;
    }

    public boolean getShowNames() {
        return C.ShowNames;
    }

    public void setShowValues(final boolean flag) {
        C.ShowValues=flag;
    }

    public boolean getShowValues() {
        return C.ShowValues;
    }

    public void setDefaultColorType(final int c) {
        C.DefaultColorType=c;
    }

    public int getDefaultColorType() {
        return C.DefaultColorType;
    }

    public void setShowHidden(final boolean flag) {
        ShowHidden=flag;
        repaint();
    }

    public boolean getShowHidden() {
        return ShowHidden;
    }

    public void setHidden(final boolean flag) {
        C.Hidden=flag;
    }

    /**
     * With this it is possible to hide all non-constructable items. This
     * function is called from any object to see if it has to hide.
     */
    public boolean hides(final ConstructionObject o) {
        if (OC instanceof SetTargetsTool) {
            return !o.isFlag();
        } else {
            return false;
        }
    }

    // Get xml file source :
    public String getFileSource() throws Exception {
        final ByteArrayOutputStream out=new ByteArrayOutputStream();
        save(out, true, true, false, true, getMacros(), "");
        return out.toString("utf-8");
    }

    public void setFileSource(String s) throws Exception {
        final ByteArrayOutputStream bout=new ByteArrayOutputStream();
        final PrintWriter out=new PrintWriter(new OutputStreamWriter(
                bout, "utf-8"));
        out.print(s);
        out.close();
        final byte b[]=bout.toByteArray();
        final InputStream in=new ByteArrayInputStream(b);
        clear();
        removeAllScripts();
        Count.resetAll();
        clearDrawings();
        load(in, true, true);
        validate();
        repaint();
        SwingUtilities.invokeLater(new Runnable() {

	    @Override
            public void run() {
                JCM.readXmlTags();
            }
        });
    }

    /**
     * Save the construction in this canvas in XML form to the specified output
     * stream. This function will create the complete XML file, including
     * headers.
     */
    public void save(OutputStream o, boolean construction,
            boolean macros, boolean protectedmacros, boolean script,
            Vector Macros, String Restrict) throws IOException {
        final boolean utf=Global.getParameter("options.utf", true);
        XmlWriter xml;
        if (utf) {
            xml=new XmlWriter(new PrintWriter(new OutputStreamWriter(o,
                    "UTF8"), true));
            xml.printEncoding(utf?"utf-8":"iso-8859-1");
        } else {
            xml=new XmlWriter(
                    new PrintWriter(new OutputStreamWriter(o), true));
            xml.printXml();
        }
        // xml.printXls("zirkel.xsl");
        // xml.printDoctype("CaR","zirkel.dtd");
        xml.startTagNewLine("CaR");
        if (macros) {
            // Sorter.sort(Macros);
            final Enumeration e=Macros.elements();
            while (e.hasMoreElements()) {
                final Macro m=((MacroItem) e.nextElement()).M;
                if (protectedmacros||!m.isProtected()) {
                    m.saveMacro(xml);
                }
            }
        }
        if (script) {
            for (ScriptItem myscript : Scripts.getScripts()) {
                myscript.saveScript(xml);
            }
        }

        if (construction) {
            xml.startTagStart("Construction");
            xml.startTagEndNewLine();
            xml.startTagStart("Window");
            xml.printArg("x", ""+C.getX());
            xml.printArg("y", ""+C.getY());
            xml.printArg("w", ""+C.getW());
            if (axis_show) {
                xml.printArg("showgrid", "true");
            }
            xml.finishTagNewLine();

            XmlTagWriter(xml);

            eric.controls.JControlsManager.PrintXmlTags(this, xml);



            if (axis_show) {
                xml.startTagStart("Grid");
                if (!axis_with_grid) {
                    xml.printArg("axesonly", "true");
                }
                xml.printArg("color", ""+(int) axis_color);
                xml.printArg("thickness", ""+(int) axis_thickness);
                if (!axis_labels) {
                    xml.printArg("nolabels", "true");
                } else {
                    if (Global.getParameter("grid.large", false)) {
                        xml.printArg("large", "true");
                    }
                    if (Global.getParameter("grid.bold", false)) {
                        xml.printArg("bold", "true");
                    }
                }
                xml.finishTagNewLine();
            }




            if (getConstruction().BackgroundFile!=null&&!getConstruction().BackgroundFile.equals("")) {
                xml.startTagStart("Background");
                xml.printArg("file", getConstruction().BackgroundFile);
                if (getConstruction().ResizeBackground) {
                    xml.printArg("resize", "true");
                }

                xml.printArg("usesize", Global.getParameter("background.usesize", "false"));
                xml.printArg("tile", Global.getParameter("background.tile", "false"));
                xml.printArg("center", Global.getParameter("background.center", "false"));

                xml.finishTagNewLine();
            }



            if (!C.getComment().equals("")) {
                xml.startTagNewLine("Comment");
                xml.printParagraphs(C.getComment(), 60);
                xml.endTagNewLine("Comment");
            }
            if (!C.getJobComment().equals("")) {
                xml.startTagNewLine("Assignment");
                xml.printParagraphs(C.getJobComment(), 60);
                xml.endTagNewLine("Assignment");
            }
            if (!Restrict.equals("")) {
                xml.finishTag("Restrict", "icons", Restrict);
            }

            Animations.printArgs(xml);
            Restrict_items.printArgs(xml);
            Exercise.printArgs(xml);

            if (OC instanceof ObjectTracker) {
                ((ObjectTracker) OC).save(xml);
            } else if (OC instanceof Tracker) {
                ((Tracker) OC).save(xml);
            } //            else if (OC instanceof AnimatorTool) {
            //                ((AnimatorTool) OC).save(xml);
            //            }
            else if (OC instanceof BreakpointAnimator) {
                ((BreakpointAnimator) OC).save(xml);
            }
            saveDrawings(xml);
            xml.startTagNewLine("Objects");
            C.save(xml);
            xml.endTagNewLine("Objects");
            xml.endTagNewLine("Construction");
        }
        xml.endTagNewLine("CaR");
    }

    public void load(final InputStream in, final boolean construction,
            final boolean macros) throws Exception { // System.out.println("read file");
        try {



            C.magnet.removeAllElements();
            if (construction) {
                C.clear();
                All=false;
                paint(getGraphics());
            } else {
                All=true;
            }
            final XmlReader xml=new XmlReader();
            xml.init(in);
            XmlTree tree=xml.scan();
            if (tree==null) {
                throw new ConstructionException("XML file not recognized");
            }
            Enumeration e=tree.getContent();
            while (e.hasMoreElements()) {
                tree=(XmlTree) e.nextElement();
                if (tree.getTag() instanceof XmlTagPI) {
                    continue;
                }
                if (!tree.getTag().name().equals("CaR")) {
                    throw new ConstructionException("CaR tag not found");
                } else {
                    break;
                }
            }
            e=tree.getContent();
            while (e.hasMoreElements()) {
                tree=(XmlTree) e.nextElement();
                final XmlTag tag=tree.getTag();
                if (tag.name().equals("Macro")) {
                    if (macros) {
                        try {
                            Count.setAllAlternate(true);
                            final Macro m=new Macro(this, tree);
                            int i;
                            for (i=0; i<Macros.size(); i++) {
                                if (((MacroItem) Macros.elementAt(i)).M.getName().equals(m.getName())) {
                                    break;
                                }
                            }
                            if (i>=Macros.size()) {
                                appendMacro(m);
                            }
                        } catch (final ConstructionException ex) {
                            Count.setAllAlternate(false);
                            throw ex;
                        }
                        Count.setAllAlternate(false);
                    }
                } else if (tag.name().equals("Script")) {
                    Scripts.addScript(tree);
                } else if (tag.name().equals("Construction")) {
                    if (construction) {
                        boolean job=false;
                        if (tag.hasParam("job")) {
                            job=true;
                            Last=tag.getValue("last");
                            if (Last==null) {
                                throw new ConstructionException(Global.name("exception.job"));
                            }
                            final String Target=tag.getValue("target");
                            if (Target==null) {
                                Targets=new Vector();
                                int i=1;
                                while (true) {
                                    final String s=tag.getValue("target"+i);
                                    i++;
                                    if (s==null) {
                                        break;
                                    }
                                    Targets.addElement(s);
                                }
                            } else {
                                Targets=new Vector();
                                Targets.addElement(Target);
                            }
                            if (Targets.isEmpty()) {
                                throw new ConstructionException(Global.name("exception.job"));
                            }
                        }
                        C.load(tree, this);
                        if (job) {
                            if (C.find(Last)==null) {
                                throw new ConstructionException(Global.name("exception.job"));
                            }
                            final Enumeration et=Targets.elements();
                            while (et.hasMoreElements()) {
                                final String s=(String) et.nextElement();
                                if (C.find(s)==null&&(!s.startsWith("~")||C.find(s.substring(1))==null)) {
                                    throw new ConstructionException(Global.name("exception.job"));
                                }
                            }
                            Job=true;
                        }
                        break;
                    }
                } else {
                    throw new ConstructionException("Construction not found");
                }
            }
            recompute();
            C.translateOffsets(this);
            resetSum();
            validate();
            repaint();
        } catch (final Exception e) {
            throw e;
        }
        reloadCD();
        repaint();
        // Give the magnet object list to the point
        // see PointConstructor.construct :
        Enumeration e=C.magnet.elements();
        while (e.hasMoreElements()) {
            final PointObject p=(PointObject) e.nextElement();
            p.setMagnetObjects((String) e.nextElement());
        }

        Scripts.fixMouseTargets();
        Animations.run();

        // System.out.println("finished reading file");
    }

    public void resetSum() {
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o instanceof ExpressionObject) {
                ((ExpressionObject) o).reset();
            }
        }
    }

    public void warning(final String s, final String help) {
        final Warning w=new Warning(getFrame(), s, Global.name("warning"), true, help);
        w.center(getFrame());
        w.setVisible(true);
    }

    public void warning(final String s) {
        warning(s, "");
    }

    public void load(final InputStream in) throws Exception {
        try {
            C.Loading=true;
            load(in, true, true);
            C.Loading=false;
        } catch (final Exception e) {
            C.Loading=false;
            throw e;
        }
    }

    public String getComment() {
        return C.getComment();
    }

    public void setComment(final String s) {
        C.setComment(s);
    }

    public String getJobComment() {
        return C.getJobComment();
    }

    public void setJobComment(final String s) {
        C.setJobComment(s);
    }

    /**
     * This can be used to set a frame window for the error dialogs that the
     * canvas my display.
     * @param f
     */
    public void setFrame(final Frame f) {
    }

    public Frame getFrame() {
        return pipe_tools.getFrame();
    }

    /**
     * Maginify the view by the specified factor.
     */
    public void magnify(final double f) {
        ZoomerTool.initNonDraggableObjects(C);
        C.setXYW(C.getX(), C.getY(), C.getW()*f);
        ZoomerTool.zoomNonDraggableObjectsBy(C, f);
        recompute();
        validate();
        repaint();
    }

    /**
     * Shift the view with these deltas.
     */
    public void shift(final double dx, final double dy) {
        ZoomerTool.initNonDraggableObjects(C);
        C.setXYW(C.getX()+dx*C.getW(), C.getY()+dy*C.getW(), C.getW());
        ZoomerTool.shiftNonDraggableObjectsBy(C, dx*C.getW(), dy*C.getW());
        recompute();
        validate();
        repaint();
    }

    /**
     * Tracker routines: Call the OC (must be a TrackPainter) to paint the
     * object track.
     */
    public void paintTrack(final MyGraphics g) {
        if (!(OC instanceof TrackPainter)) {
            return;
        }
        ((TrackPainter) OC).paint(g, this);
    }

    /**
     * Run through the construction to update all object texts. This should be
     * called, whenever the name of an item was changed. It will recreate only
     * those texts, which contain the old name.
     */
    public void updateTexts(final ConstructionObject o, final String oldname) {
        C.updateTexts(o, oldname);
    }

    public Construction getConstruction() {
        return C;
    }

    public boolean isEmpty() {
        return ((C.lastButN(0)==null)&&(!isScript()));
    }

    /* Job part for CaRMetal 3.5 and later :
     *
     */
    public String job_getMessageOk() {
        return Exercise.getMessage_ok();
    }

    public void job_setMessageOk(String m) {
        Exercise.setMessage_ok(m);
    }

    public String job_getMessageFailed() {
        return Exercise.getMessage_failed();
    }

    public void job_setMessageFailed(String m) {
        Exercise.setMessage_failed(m);
    }

    public void job_setHideFinals(boolean b) {
        Exercise.setHidefinals(b);
    }

    public void job_setStaticJob(boolean b) {
        Exercise.setStaticJob(b);
    }

    public boolean job_isStaticJob() {
        return Exercise.isStaticJob();
    }

    public void job_setBackup(String s) {
        Exercise.setBackup(s);
    }

    public void job_addTarget(ConstructionObject target) {
        Exercise.addTarget(target);
    }

    public void job_removeTarget(ConstructionObject target) {
        Exercise.removeTarget(target);
    }

    /*Two following methods only for loading process :
     *
     */
    public void job_setTargetNames(String t) {
        Exercise.setTargetNames(t);
    }

    public void job_setTargets() {
        Exercise.setTargets();
    }

    public boolean job_isTargets() {
        return (Exercise.getTargets().size()>0);
    }

    public ArrayList<ConstructionObject> job_getTargets() {
        return Exercise.getTargets();
    }

    public void job_showDialog() {
        Exercise.showControlDialog();
    }

    public void job_setTargetsField() {
        Exercise.setTargetsField();
    }

    public void initJobCreationDialog() {
        if (Exercise!=null) {
            Exercise.init();
        }
    }

    // The following functions change the default values of some objects.
    public void setShowColor(final int i) {
        ShowColor=i;
        repaint();
    }

    public int getShowColor() {
        return ShowColor;
    }
//    public ObjectConstructor getOC() {
//        return OC;
//    }
    // Macros:
    private Vector Macros=new Vector();

    public Vector getMacros() {
        return Macros;
    }

    public boolean haveMacros() {
        return Macros.size()>0;
    }

    public boolean haveNonprotectedMacros() {
        final Enumeration e=Macros.elements();
        while (e.hasMoreElements()) {
            final MacroItem m=(MacroItem) e.nextElement();
            if (!m.M.isProtected()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Define a macro. There must be parameters (but not necessarily targets).
     * The function will display the macro dialog. It will create a new macro,
     * when the dialog was not aborted. The macro is kept in the Macros vector
     * by name.
     */
    boolean defineMacro() {

        return true;
    }

    /**
     * Copy a macro with fixed parameters from another macro.
     */
    public Macro copyMacro(final Macro m, final String name,
            final boolean fixed[]) {
        try {
            final Macro macro=(Macro) (m.clone());
            macro.Name=name;
            final boolean f[]=new boolean[fixed.length];
            for (int i=0; i<f.length; i++) {
                f[i]=fixed[i];
            }
            macro.Fixed=f;
            storeMacro(macro, true);
            return macro;
        } catch (final Exception e) {
            return m;
        }
    }

    /**
     * Define a macro based on a construction in c and the targets and
     * parameters in this construction. Store the macro in m.
     *
     * @param
     */
    public void defineMacro(final Construction c, final Macro m,
            final boolean targetsonly, final boolean superhide,
            final String prompt[], final boolean hideduplicates)
            throws ConstructionException {
        final Vector P=c.getParameters(), T=c.getTargets();
        c.setTranslation(m); // for construction expressions only (windoww etc.)
        c.clearTranslations();
        if (T.isEmpty()) // got no targets
        {
            c.determineConstructables();
        } else // got targets
        {
            c.clearConstructables();
            c.setParameterAsConstructables();
            for (int i=0; i<T.size(); i++) {
                c.determineConstructables((ConstructionObject) T.elementAt(i));
                ((ConstructionObject) T.elementAt(i)).setMacroFinalIndex(i); // Dibs for ExecuteMacroAsBuilt
            }
        }
        // Make sure the counter for the macro object names starts
        // fresh (P1, P2, ...)
        Count.setAllAlternate(true);
        // Walk through the construction and copy all marked objects
        // to the macro definition.
        m.clearTranslators();
        Enumeration e=c.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o instanceof TwoPointLineObject && !(o instanceof VectorObject) && canConvert(c, (TwoPointLineObject) o) && o.isMainParameter()) {
                ((TwoPointLineObject) o).getP1().setFlag(false);
                ((TwoPointLineObject) o).getP2().setFlag(false);
            }
        }
        e=c.elements();
        while (e.hasMoreElements()) {
            ConstructionObject oc;
            final ConstructionObject o=(ConstructionObject) e.nextElement();
	    //System.out.println(o.getName()+" ; flag = "+o.isFlag());
	    //System.out.println(o.getName()+" ; param = "+o.isParameter());
	    //System.out.println(o.getName()+" ; mainparam = "+o.isMainParameter());
	    //System.out.println(o.getName()+" ; needed = "+needed(c,o,null));
	    //System.out.println(o.getName()+" ; "+!( o.isParameter() && !o.isMainParameter() && !needed(c, o, null)));
            if (o.isFlag()&&!( o.isParameter() && !o.isMainParameter() && !needed(c, o, null)) ) {
                // Now copy to the macro, but make sure that parameters
                // are simplified to their object type. E.g., an
                // objectpoint becomes a point, if it is a parameter.
                if (o instanceof PointObject&&o.isParameter()) {
                    final PointObject p=(PointObject) o;
                    if (p.isSpecialParameter()&&p.dependsOnParametersOnly()) {
                        oc=(ConstructionObject) p.copy(0, 0);
                    } else {
                        oc=new PointObject(c, p.getX(), p.getY());
                    }
                } else if (o instanceof FunctionObject&&o.isParameter()) {
                    final FunctionObject fo=new FunctionObject(c);
                    fo.setExpressions("x", "0", "0");
                    fo.setRange("-10", "10", "0.1");
                    oc=fo;
                } else if (o instanceof UserFunctionObject&&o.isParameter()) {
                    final UserFunctionObject fo=new UserFunctionObject(c);
                    fo.setExpressions("x", "0");
                    oc=fo;
                } else if (o instanceof ExpressionObject&&o.isParameter()) {
                    oc=new ExpressionObject(c, 0, 0);
                    ((ExpressionObject) oc).setExpression(o.getValue()+"", o.getConstruction());
                    ((ExpressionObject) oc).setCurrentValue(o.getValue());
                } else if (o instanceof TwoPointLineObject && !(o instanceof VectorObject) && canConvert(c, (TwoPointLineObject) o) && o.isParameter()) {
                    oc=new PrimitiveLineObject(c);
                } else if (o instanceof PrimitiveLineObject && !(o instanceof TwoPointLineObject) && !(o instanceof FixedAngleObject) && o.isParameter()) {
                    oc=new PrimitiveLineObject(c);
                } else if (o instanceof PrimitiveCircleObject&&o.isParameter()) {
                    oc=new PrimitiveCircleObject(c, ((PrimitiveCircleObject) o).getP1());
                    oc.translateConditionals();
                    oc.translate();
                } else if (o instanceof AreaObject&&o.isParameter()) {
                    oc=new AreaObject(c, new Vector());
                    oc.translateConditionals();
                    oc.translate();
                } else {
                    oc=(ConstructionObject) o.copy(0, 0);
                }

                if (oc!=null) {
                    m.add(oc);
                    if (o.isMainParameter()) {
                        oc.setName(o.getName());
                    }
                    if (targetsonly && !o.isTarget() && !o.isParameter()) {
                        if (superhide) {
                            oc.setSuperHidden(true);
                        } else {
                            oc.setHidden(true);
                        }
                    }
                    if (o.isParameter()&&o.isHidden()) {
                        oc.setHidden(true);
                    }
                    // All parameters in the constructions translate to
                    // the paramters in the macro definition.
                    o.setTranslation(oc);
                }
            } else {
                o.setTranslation(null);
            }
        }

        e=c.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isFlag()&&!o.isParameter()) {
                o.laterTranslate(c);
            }
        }
        Count.setAllAlternate(false);
        // translate the @... forward declarations in FindObjectExpression.
        c.clearErrors();
        m.runTranslators(c);
        // see if any errors occured (@.. to nonexisting object, generated
        // by the FindObjectExpression on translation.
        e=c.getErrors();
        if (e.hasMoreElements()) {
            warning((String) e.nextElement(), "macroerror");
        }
        // Now set the paramter array and make sure it is translated
        // to the objects in the macro definition.
        final ConstructionObject Params[]=new ConstructionObject[P.size()];
        for (int i=0; i<P.size(); i++) {
            Params[i]=((ConstructionObject) P.elementAt(i)).getTranslation();
        }

        m.setParams(Params);
        final String p[]=new String[prompt.length];
        for (int j=0; j<prompt.length; j++) {
            final ConstructionObject o=c.find(prompt[j]);
            if (o==null||o.getTranslation()==null||!(o instanceof FixedCircleObject||o instanceof FixedAngleObject||o instanceof ExpressionObject)) {
                throw new ConstructionException(Global.name("exception.prompt"));
            }
            for (int i=0; i<P.size(); i++) {
                final ConstructionObject op=(ConstructionObject) P.elementAt(i);
                if (op==o) {
                    throw new ConstructionException(Global.name("exception.prompt.parameter"));
                }
            }
            p[j]=o.getTranslation().getName();
        }
        m.setPromptFor(p);
        for (int i=0; i<prompt.length; i++) {
            m.setPromptName(i, prompt[i]);
        }
        m.hideDuplicates(hideduplicates);
    }

    /**
     * See, if this secondary parameter "o" is needed in the construction "c" by
     * either a constructable object, or a parameter different from "besides".
     */
    public boolean needed(final Construction c, final ConstructionObject o,
            final ConstructionObject besides) {
        final Enumeration e=c.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject u=(ConstructionObject) e.nextElement();
            if (!u.isFlag()||u==besides) {
                continue;
            }
            if (c.dependsDirectlyOn(u, o)) {
                return true;
            }
        }
        return false;
    }

    /**
     * See, if this two point line can be converted to a primitive line.
     */
    public boolean canConvert(final Construction c, final TwoPointLineObject o) {
        final PointObject p1=o.getP1(), p2=o.getP2();
        if (p1.isMainParameter()||p2.isMainParameter()) {
            return false;
        }
        return !(needed(c, p1, o)||needed(c, p2, o));
    }

    /*
     * Méthodes permettant de gérer les scripts
     */

    public void runOnLoadScripts() {
        if (firstLoad) {
            firstLoad=false;
            SwingUtilities.invokeLater(new Runnable() {

		@Override
                public void run() {
                    Scripts.runOnLoadScripts();
                }
            });
        }
    }

    public void stopAllScripts() {
	//Scripts.stopAllScripts();
	for(ScriptItem item : Scripts.getScripts()){
	    item.stopme();
	}
	for(ScriptItem item : JZirkelCanvas.getScriptsLeftPanel().getScripts()){
	    item.stopme();
	}
    }

    public void restartAllScripts() {
        //Scripts.restartAllScripts();
	for(ScriptItem item : Scripts.getScripts()){
	    item.restartme();
	}
	for(ScriptItem item : JZirkelCanvas.getScriptsLeftPanel().getScripts()){
	    item.restartme();
	}
    }

    public boolean isThereAnyScriptRunning() {
	//return Scripts.isThereAnyScriptRunning();
	for (ScriptItem item : Scripts.getScripts()) {
            if (item.isRunning()) {
                return true;
            }
        }
	for (ScriptItem item : JZirkelCanvas.getScriptsLeftPanel().getScripts()) {
            if (item.isRunning()) {
                return true;
            }
        }
	return false;
    }

    public boolean isThereAnyStoppedScripts() {
        for (ScriptItem item : Scripts.getScripts()) {
            if (item.isStopped()) {
                return true;
            }
        }
	for (ScriptItem item : JZirkelCanvas.getScriptsLeftPanel().getScripts()) {
            if (item.isStopped()) {
                return true;
            }
        }
        return false;
    }

    public void killAllScripts(){
	for (ScriptItem item : Scripts.getScripts()) {
            item.killme();
        }
	for (ScriptItem item : JZirkelCanvas.getScriptsLeftPanel().getScripts()) {
            item.killme();
        }
    }

    public void openScriptFile(String filename, boolean open) {
        Scripts.openScriptFile(filename, open);
    }

    //pas d'occ
    public void newScript() {
        Scripts.newScript();
    }
    //pas d'occ
    public void saveScript(String s, String script) {
        Scripts.saveScript(s, script);
    }

    public boolean isScript() {
        return (Scripts.getScripts().size()>0);
    }

    public ScriptItemsArray getScripts() {
        return Scripts.getScripts();
    }

    public ScriptPanel getScriptsPanel() {
	return Scripts;
    }
    //pas d'occ
    public void removeScript(String s) {
        Scripts.removeScript(s);
    }

    public void removeAllScripts() {
        Scripts.removeAllScripts();
    }

    public void runControlScripts(JCanvasPanel jp) {
        if (Scripts!=null) {
            Scripts.runControlScripts(jp);
        }
    }

//    public void pauseControlScripts(ConstructionObject o){
//        if (Scripts!=null) {
//            Scripts.pauseControlScripts(o);
//        }
//    }
    public void prepareDragActionScripts(ConstructionObject o) {
        if (Scripts!=null) {
            Scripts.prepareDragActionScript(o);
        }
    }

    public void runDragAction() {
        if (Scripts!=null) {
            Scripts.runDragAction();
        }
    }
    public void runUpAction(ConstructionObject o){
	if(Scripts!=null){
	    Scripts.runUpAction(o);
	}
    }

    public void stopDragAction() {
        if (Scripts!=null) {
            Scripts.stopDragAction();
        }
    }

    public AnimationPanel getAnimations() {
        return Animations;
    }

    public void removeAllAnimations() {
        remove(Animations);
        Animations=null;
        Animations=new AnimationPanel(this);
    }

    public boolean isAnimated(ConstructionObject o) {
        return Animations.isAnimated(o);
    }

    public void removeAnimation(ConstructionObject o) {
        Animations.removeAnimation(o);
    }

    public void addAnimation(ConstructionObject o) {
        Animations.addAnimation(o);
    }

    public void addAnimation(String objectname) {
        Animations.addAnimation(objectname);
    }

    public void selectAnimationPoints() {
        Animations.setObjectSelected(true);
    }

    public void setAnimationNegative(String objectname, boolean negative) {
        Animations.setAnimationNegative(objectname, negative);
    }

    public void setAnimationDelay(double delay) {
        Animations.setDelay(delay);
    }

    public RestrictContainer getRestrict() {
        return RestrictDialog;
    }

    public void initRestrictDialog() {
        if (RestrictDialog!=null) {
            RestrictDialog.init();
        }
    }

    public void getNewRestrictedDialog() {
        closeRestrictDialog();
        RestrictDialog=new RestrictContainer(this);
        add(RestrictDialog);
        RestrictDialog.init();
        RestrictDialog.revalidate();
        RestrictDialog.repaint();
        repaint();
    }

    public void closeRestrictDialog() {
        if (RestrictDialog!=null) {
            remove(RestrictDialog);
            repaint();
            RestrictDialog=null;
        }
    }

    public void addHiddenItem(String s) {
        Restrict_items.add(s);
    }

    public void removeHiddenItem(String s) {
        Restrict_items.remove(s);
    }

    public boolean isHiddenItem(String s) {
        return Restrict_items.isHidden(s);
    }

    public boolean isRestricted() {
        return Restrict_items.isRestricted();
    }

    public ArrayList<String> getHiddenItems() {
        return Restrict_items.get();
    }

    public void setHiddenItems(String items) {
        Restrict_items.set(items);
    }

    public void setHiddenItems(ArrayList<String> items) {
        Restrict_items.set(items);
    }

    public void initRestrictedHiddenItemsFromFactorySettings() {
        Restrict_items.initRestrictedHiddenItemsFromFactorySettings();
    }

    public void initStandardRestrictedHiddenItems() {
        Restrict_items.initRestrictedHiddenItems();
    }

    public void setStandardRestrictedItems() {
        Restrict_items.setStandardRestrictedItems();
    }

    public void setLibraryMacrosVisible(boolean b) {
        if (b!=islibrarymacrovisible) {
            islibrarymacrovisible=b;
            JZirkelCanvas.ActualiseMacroPanel();
        }
    }

    public boolean isLibraryMacrosVisible() {
        return islibrarymacrovisible;
    }

    /**
     * Define a macro from the information stored in the construction c, and
     * store it to the macros in this ZirkelCanvas object.
     */
    public void defineMacro(final String name, final String comment,
            final Construction c) throws ConstructionException {
        final Vector T=c.getTargets();
        final String Prompts[]=new String[c.Prompts.size()];
        for (int i=0; i<Prompts.length; i++) {
            Prompts[i]=(String) c.Prompts.elementAt(i);
        }
        final Macro m=new Macro(this, name, comment, Prompts);
        defineMacro(c, m, T.size()>0&&!c.ShowAll, c.SuperHide, c.getPromptFor(), true);
        storeMacro(m, true);
    }

    /*
     * Store the macro in the macro list (or replace the old macro with the same
     * name
     *
     * @param all Replace the macro without asking.
     */
    public void storeMacro(final Macro m, final boolean all) {
        int i;
        for (i=0; i<Macros.size(); i++) {
            if (((MacroItem) Macros.elementAt(i)).M.getName().equals(
                    m.getName())) {
                All=replaceMacro(m, i, all); // ask user if All=false
                break;
            }
        }
        if (i>=Macros.size()) {
            appendMacro(m);
        }
    }
    public boolean ProtectMacros=false;
    public MacroMenu MM=null;

    public void appendMacro(final Macro m) {
        if (!ReadOnly) {
            if (ProtectMacros) {
                m.setProtected(true);
            }
            if (MM==null) {
                MM=new MacroMenu(PM, "", null);
            }
            final MacroItem mi=MM.add(m, m.getName());
            if (mi.I!=null) {
                mi.I.addActionListener(this);
            }
            Macros.addElement(mi);
        } else {
            if (MM==null) {
                MM=new MacroMenu(null, "", null);
            }
            final MacroItem mi=MM.add(m, m.getName());
            if (mi.I!=null) {
                mi.I.addActionListener(this);
            }
            Macros.addElement(mi);
        }
    }

    /**
     * Replace the macro item number i with m.
     *
     * @return User wants to replace all subsequent macros.
     */
    public boolean replaceMacro(final Macro m, final int i, final boolean all) {
        return true;
    }
    public String MacroCurrentComment;

    /**
     * The user has to choose from a list of macros (for running).
     */
    public Macro chooseMacro() {
        return null;
    }

    /**
     * The user can choose from a list of macros (for saving).
     *
     * @return A vector of selected Macros.
     */
    public Vector chooseMacros() {
        return null;
    }

    /**
     * Run a macro by name.
     */
    public Macro chooseMacro(final String name) {
        Macro m=null;
        Enumeration e=Macros.elements();
        while (e.hasMoreElements()) {
            m=((MacroItem) e.nextElement()).M;
            if (name.equals(m.getName())) {
                return m;
            }
        }
        e=MacroTools.getBuiltinMacros().elements();
        while (e.hasMoreElements()) {
            m=((MacroItem) e.nextElement()).M;
            if (name.equals(m.getName())) {
                return m;
            }
        }
        return null;
    }

    public void deleteMacros(final Vector v) {
        final Enumeration e=v.elements();
        while (e.hasMoreElements()) {
            final MacroItem m=(MacroItem) e.nextElement();
            deleteMacro(m);
        }
    }

    public void deleteMacro(final MacroItem m) {
        Macros.removeElement(m);
        if (m.I!=null) {
            m.I.removeActionListener(this);
            MM.remove(m);
        }
    }

    public void clearMacros() {
        final Enumeration e=Macros.elements();
        while (e.hasMoreElements()) {
            final MacroItem m=(MacroItem) e.nextElement();
            if (m.I!=null) {
                m.I.removeActionListener(this);
                MM.remove(m);
            }
        }
        Macros.removeAllElements();
    }

    public void clearNonprotectedMacros() {
        final Vector Vec=new Vector();
        final Enumeration e=Macros.elements();
        while (e.hasMoreElements()) {
            final MacroItem m=(MacroItem) e.nextElement();
            if (!m.M.isProtected()) {
                Vec.addElement(m);
            }
        }
        deleteMacros(Vec);
    }

    public void clearProtectedMacros() {
        final Vector Vec=new Vector();
        final Enumeration e=Macros.elements();
        while (e.hasMoreElements()) {
            final MacroItem m=(MacroItem) e.nextElement();
            if (m.M.isProtected()) {
                Vec.addElement(m);
            }
        }
        deleteMacros(Vec);
    }

    public void protectMacros() {
        final Enumeration e=Macros.elements();
        while (e.hasMoreElements()) {
            final MacroItem m=(MacroItem) e.nextElement();
            m.M.setProtected(true);
        }
    }

    public void renameMacro(final Macro macro, final String name) {
        final Enumeration e=Macros.elements();
        while (e.hasMoreElements()) {
            final MacroItem m=(MacroItem) e.nextElement();
            if (m.I!=null&&m.M==macro) {
                deleteMacro(m);
                break;
            }
        }
        macro.setName(name);
        appendMacro(macro);
    }
    MacroBar MBar;

    public void setMacroBar(final MacroBar m) {
        MBar=m;
    }

    public void updateMacroBar() {
        // if (MBar!=null) {
        // MBar.update(Macros);
        // }
    }
    // For the prompt in the status line:
    TextField TF;

    public void setTextField(final TextField t) {
        TF=t;
    }

    public void setPrompt(final String s) {
        if (TF!=null) {
            TF.setText(s);
        }
    }

    // Loading:
    public void loadRun(final InputStream is) {
        final BufferedReader in=new BufferedReader(new InputStreamReader(is));
        String s="", comment="";
        while (true) {
            try {
                s=in.readLine();
                if (s==null) {
                    break;
                }
                int n;
                if ((n=s.indexOf("//"))>=0) {
                    comment=s.substring(n+2).trim();
                    s=s.substring(0, n);
                } else {
                    comment="";
                }
                s=s.trim();
                int k=0;
                if ((k=Interpreter.startTest("macro", s))>=0) {
                    loadMacro(in, s.substring(k).trim());
                } else if (!s.equals("")) {
                    C.interpret(this, s, comment);
                }
            } catch (final ConstructionException e) {
                warning(e.getDescription()+" --- "+s);
                break;
            } catch (final Exception e) {
                warning(e.toString()+" --- "+s);
                e.printStackTrace();
                break;
            }
        }
        C.updateCircleDep();
    }

    public void loadMacro(final BufferedReader in, final String name)
            throws ConstructionException {
        final Construction construction=new Construction();
        construction.clear();
        String s="", comment="", macrocomment="";
        boolean inComment=true, newLine=true;
        while (true) {
            try {
                s=in.readLine();
                if (s==null) {
                    throw new ConstructionException(Global.name("exception.macroend"));
                }
                s=s.trim();
                final int n=s.indexOf("//");
                if (inComment&&n==0) {
                    final String h=s.substring(n+2).trim();
                    if (newLine) {
                        macrocomment=macrocomment+h;
                        newLine=false;
                    } else {
                        if (h.equals("")) {
                            macrocomment=macrocomment+"\n";
                            newLine=true;
                        } else {
                            macrocomment=macrocomment+" "+h;
                            newLine=false;
                        }
                    }
                    continue;
                }
                inComment=false;
                if (n>=0) {
                    comment=s.substring(n+2).trim();
                    s=s.substring(0, n);
                } else {
                    comment="";
                }
                s=s.trim();
                if (s.equals(Global.name("end"))) {
                    break;
                }
                if (s.toLowerCase().equals("end")) {
                    break;
                }
                if (!s.equals("")) {
                    construction.interpret(this, s, comment);
                }
            } catch (final InvalidException e) {
            } catch (final ConstructionException e) {
                throw new ConstructionException(e.getDescription()+" --- "+s);
            } catch (final IOException e) {
                warning(e.toString());
                return;
            }
        }
        defineMacro(name, macrocomment, construction);
    }

    public double getGridSize() {
        double gridsize=Math.pow(10, Math.floor(Math.log(C.getW()*2)/Math.log(10)))/10;
        if (C.getW()*2/gridsize>=30) {
            gridsize*=5;
        }
        if (C.getW()*2/gridsize<10) {
            gridsize/=2;
        }
        return gridsize;
    }

    public LatexOutput createBB(final String filename, final int w,
            final int h, final double dpi) {
        try {
            String path="";
            if (Global.getParameter("options.fullpath", true)) {
                path=FileName.pathAndSeparator(filename);
            }
            PrintWriter out=new PrintWriter(new FileOutputStream(path+FileName.purefilename(filename)+".bb"));
            out.println("%%BoundingBox: 0 0 "+w+" "+h);
            out.close();
            out=new PrintWriter(new FileOutputStream(path+FileName.purefilename(filename)+".ztx"));
            final LatexOutput lout=new LatexOutput(out);
            lout.open(w, h, dpi, path+FileName.filename(filename));
            return lout;
        } catch (final Exception e) {
            warning(e.toString());
        }
        return null;
    }

    /**
     * Return pressed.
     */
    public void returnPressed() {
        if (OC instanceof MacroRunner) {
            ((MacroRunner) OC).returnPressed(this);
        }
    }

    public void setMagnetTool(final PointObject p) {
        setTool(new MagnetTool(this, p, OC));
        p.setStrongSelected(true);
        final Enumeration e=p.getMagnetObjects().elements();
        while (e.hasMoreElements()) {
            final MagnetObj mo=(MagnetObj) e.nextElement();
            mo.setSelected(true);
        }
        repaint();
    }

    public void setJSTool(ScriptThread th, String msg, String type) {
        if (OC!=null) {
            OC.invalidate(this);
        }
        OC=new JSmacroTool(this, th, msg, type, OC);
        OC.showStatus(this);
        clearIndicated();
        clearPreview();
    }

    public void setNullTool() {
        if (OC!=null) {
            OC.invalidate(this);
        }
        OC=new nullTool(this, OC);
        OC.showStatus(this);
        clearIndicated();
        clearPreview();
    }

//    private SelectTool SelectionTool=new SelectTool(this);
    public void setSelectTool() {
        if (OC!=null) {
            OC.invalidate(this);
        }
        OC=new SelectTool(this);
        OC.showStatus(this);
        clearIndicated();
        clearPreview();
    }

    public void bind(final PointObject p) {
        setTool(new BinderTool(this, p, OC));
    }

    public void setAway(final IntersectionObject p, final boolean away) {
        setTool(new SetAwayTool(this, p, away, OC));
    }

    public void setCurveCenter(final FunctionObject p) {
        setTool(new SetCurveCenterTool(this, p, OC));
    }

    public void range(final PrimitiveCircleObject c) {
        setTool(new SetRangeTool(this, c, OC));
    }

    public void set(final FixedAngleObject a) {
        setTool(new SetFixedAngle(this, a, OC));
    }

    public void set(final FixedCircleObject c) {
        setTool(new SetFixedCircle(this, c, OC));
    }

    public boolean enabled(final String function) {
        if (ZCI!=null) {
            return ZCI.enabled(function);
        } else {
            return true;
        }
    }

    public void pause(final boolean flag) {
        OC.pause(flag);
    }

    public void setReadOnly(final boolean flag) {
        ReadOnly=flag;
    }

    public void allowRightMouse(final boolean flag) {
        AllowRightMouse=flag;
    }

    public boolean changed() {
        return C.changed();
    }
    Image OldBackground=null;

    /**
     * Create a background image for the Movertool, consisting of the current
     * construction. This is called when moving with the control key is called.
     *
     * @param flag
     */
    public void grab(final boolean flag) {
        if (flag) {
            OldBackground=Background;
            Background=createImage(IW, IH);
            final Graphics G=Background.getGraphics();
            G.drawImage(I, 0, 0, this);
        } else {
            Background=OldBackground;
            OldBackground=null;
        }
        repaint();
    }

    public void setBackground(final Image i) {
        Background=i;
        repaint();
    }

    public void setInteractive(final boolean flag) {
        Interactive=flag;
    }

// Utilisé par le JPopup de selection des objets (ambiguité) et
// pour l'application d'outils à la liste de construction (ConstructionDisplayPanel)
    public void setConstructionObject(ConstructionObject o) {
        if (OC!=null) {
            NewPoint=false;
            OC.setConstructionObject(o, this);
        }
    }

    public ObjectConstructor getTool() {
        return OC;
    }
    MyVector Drawings=new MyVector();

    public synchronized void addDrawing(final Drawing d) {
        Drawings.addElement(d);
    }

    public synchronized void clearDrawings() {
        Drawings.removeAllElements();
        repaint();
    }

    public synchronized void paintDrawings(final MyGraphics g) {
        final Enumeration e=Drawings.elements();
        while (e.hasMoreElements()) {
            final Drawing d=(Drawing) e.nextElement();
            final Enumeration ec=d.elements();
            if (ec.hasMoreElements()) {
                g.setColor(ZirkelFrame.Colors[d.getColor()]);
                CoordinatesXY xy=(CoordinatesXY) ec.nextElement();
                int col=(int) col(xy.X), r=(int) row(xy.Y);
                while (ec.hasMoreElements()) {
                    xy=(CoordinatesXY) ec.nextElement();
                    final int c1=(int) col(xy.X), r1=(int) row(xy.Y);
                    g.drawLine(col, r, c1, r1);
                    col=c1;
                    r=r1;
                }
            }
        }
    }

    public void saveDrawings(final XmlWriter xml) {
        final Enumeration e=Drawings.elements();
        while (e.hasMoreElements()) {
            final Drawing d=(Drawing) e.nextElement();
            final Enumeration ec=d.elements();
            if (ec.hasMoreElements()) {
                xml.startTagNewLine("Draw", "color", ""+d.getColor());
                while (ec.hasMoreElements()) {
                    final CoordinatesXY xy=(CoordinatesXY) ec.nextElement();
                    xml.startTagStart("Point");
                    xml.printArg("x", ""+xy.X);
                    xml.printArg("y", ""+xy.Y);
                    xml.finishTagNewLine();
                }
                xml.endTagNewLine("Draw");
            }
        }
    }

    public void loadDrawings(final XmlTree tree) throws ConstructionException {
        XmlTag tag=tree.getTag();
        if (!tag.name().equals("Draw")) {
            return;
        }
        final Drawing d=new Drawing();
        try {
            if (tag.hasParam("color")) {
                d.setColor(Integer.parseInt(tag.getValue("color")));
            }
        } catch (final Exception e) {
            throw new ConstructionException("Illegal Draw Parameter");
        }
        final Enumeration e=tree.getContent();
        while (e.hasMoreElements()) {
            final XmlTree t=(XmlTree) e.nextElement();
            tag=t.getTag();
            if (tag.name().equals("Point")) {
                try {
                    final double x=new Double(tag.getValue("x")).doubleValue();
                    final double y=new Double(tag.getValue("y")).doubleValue();
                    d.addXY(x, y);
                } catch (final Exception ex) {
                    throw new ConstructionException("Illegal Draw Parameter");
                }
            }
        }
        Drawings.addElement(d);
    }
    int PointLast, LineLast, AngleLast;

    public void renameABC(final ConstructionObject o, final boolean enforce,
            final boolean reset) {
        if (!enforce) {
            if (o instanceof PointObject) {
                for (int i='A'; i<='Z'; i++) {
                    final ConstructionObject h=C.find(""+(char) i);
                    if (h==null) {
                        o.setName(""+(char) i);
                        o.setShowName(true);
                        repaint();
                        break;
                    }
                }
            } else if (o instanceof AngleObject||o instanceof FixedAngleObject) {
                for (int i='a'; i<='z'; i++) {
                    final ConstructionObject h=C.find("\\"+(char) i);
                    if (h==null) {
                        o.setName("\\"+(char) i);
                        o.setShowName(true);
                        repaint();
                        break;
                    }
                }
            } else if (o instanceof PrimitiveLineObject) {
                for (int i='a'; i<='z'; i++) {
                    final ConstructionObject h=C.find(""+(char) i);
                    if (h==null) {
                        o.setName(""+(char) i);
                        o.setShowName(true);
                        repaint();
                        break;
                    }
                }
            }
        } else {
            if (reset) {
                PointLast=0;
                LineLast=0;
                AngleLast=0;
            }
            if (o instanceof PointObject) {
                final String name=""+(char) ('A'+PointLast);
                final ConstructionObject h=C.find(name);
                if (h!=null&&h!=o) {
                    h.setName("***temp***");
                    final String s=o.getName();
                    o.setName(name);
                    h.setName(s);
                } else {
                    o.setName(name);
                }
                PointLast++;
            } else if (o instanceof AngleObject||o instanceof FixedAngleObject) {
                final String name="\\"+(char) ('a'+AngleLast);
                final ConstructionObject h=C.find(name);
                if (h!=null&&h!=o) {
                    h.setName("***temp***");
                    final String s=o.getName();
                    o.setName(name);
                    h.setName(s);
                } else {
                    o.setName(name);
                }
                AngleLast++;
            } else if (o instanceof PrimitiveLineObject) {
                final String name=""+(char) ('a'+LineLast);
                final ConstructionObject h=C.find(name);
                if (h!=null&&h!=o) {
                    h.setName("***temp***");
                    final String s=o.getName();
                    o.setName(name);
                    h.setName(s);
                } else {
                    o.setName(name);
                }
                LineLast++;
            }
        }
    }

    public void selectAllMoveableVisibleObjects() {
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o instanceof MoveableObject&&((MoveableObject) o).moveable()&&!o.mustHide(this)) {
                o.setStrongSelected(true);
            }
        }
    }

    public void hideDuplicates(final ConstructionObject from) {
        final Enumeration e=C.elements();
        if (from!=null) {
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                if (o==from) {
                    break;
                }
            }
        }
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.mustHide(this)||o.isKeep()) {
                continue;
            }
            final Enumeration ex=C.elements();
            while (ex.hasMoreElements()) {
                final ConstructionObject o1=(ConstructionObject) ex.nextElement();
                if (o1==o) {
                    break;
                }
                if (o1.mustHide(this)||o1.isKeep()) {
                    continue;
                }
                if (o.equals(o1)) {
                    o.setHidden(true);
                    break;
                }
            }
        }
    }

    public void hideDuplicates() {
        hideDuplicates(null);
    }

    public void createEquationXY() {
        final EquationXYObject f=new EquationXYObject(C,
                "(x^2+y^2)^2-3*(x^2-y^2)");
        // EquationXYObject f=new EquationXYObject(C,"x+y");
        f.setDefaults();
        C.add(f);
        f.edit(this, true, false);
        repaint();
        reloadCD();
    }

    public void createCurve() {
        final FunctionObject f=new FunctionObject(C);
        f.setDefaults();
        C.add(f);
        f.setExpressions("x", "x", "x+1");
        f.edit(this, true, true);
        if (f.EditAborted) {
            delete(f);
        }
        repaint();
        reloadCD();
	update_distant(f, 1);
    }

    public void editMultipleSelection() {
        if (MultipleSelection.size()>0) {
            eric.bar.JPropertiesBar.EditObjects(MultipleSelection);
        }
    }

    public void editLast() {
        if (C.lastButN(0)==null) {
            return;
        }
        C.lastButN(0).edit(this, true, false);
    }

    public void breakpointLast(final boolean flag, final boolean hiding) {
        final ConstructionObject o=C.lastButN(0);
        if (o==null) {
            return;
        }
        if (hiding) {
            o.setHideBreak(flag);
        } else {
            o.setBreak(flag);
        }
    }

    @Override
    public void notifyChanged() {
        if (!C.Loading) {
            reloadCD();
            pipe_tools.TabHaveChanged(true);
        }
    }

    public void startWaiting() {
        Interactive=false;
        showMessage(Global.name("message.saving"));
    }

    public void endWaiting() {
        Interactive=true;
        hideMessage();
    }

    public void showMessage(final String s) {
    }

    public void hideMessage() {
    }
    // HotEqn stuff, requires the HotEqn classes:
//    sHotEqn HE=null;
//
//    public void setHotEqn(final String s) {
//        if (HE==null) {
//            HE=new sHotEqn(this);
//        }
//        HE.setEquation(s);
//    }
//
//    public int paintHotEqn(final int c, final int r, final Graphics g) {
//        if (HE==null) {
//            return 0;
//        }
//        return HE.paint(c, r, g);
//    }
    // Stuff for the permanent construction display
    private ConstructionDisplayPanel CDP=null;

    public void reloadCD() {
        if (CDP!=null&&C!=null) {
            CDP.reload();
        }
    }

    public void repaintCD() {
        if (CDP!=null&&C!=null) {
            CDP.updateDisplay();
        }
    }

    public ConstructionDisplayPanel getNewCDP() {
        CDP=null;
        CDP=new ConstructionDisplayPanel(this);
        reloadCD();
        return CDP;
    }

    public void removeCDP() {
        CDP=null;
    }

    public ConstructionDisplayPanel getCDP() {
        return CDP;
    }

    @Override
    public void mouseWheelMoved(final MouseWheelEvent e) {
        if (Global.getParameter("options.nomousezoom", false)) {
            return;
        }
        if (e.getScrollType()==MouseWheelEvent.WHEEL_BLOCK_SCROLL) {
            if (e.getWheelRotation()<0) {
                magnify(1/Math.sqrt(Math.sqrt(2)));
            } else {
                magnify(Math.sqrt(Math.sqrt(2)));
            }
        } else {
            final int n=e.getScrollAmount();
            if (e.getWheelRotation()<0) {
                magnify(1/Math.pow(2, n/12.));
            } else {
                magnify(Math.pow(2, n/12.));
            }
        }
    }

    public Dimension getOwnerWindowDim() {
        return new Dimension(OwnerWindowWidth, OwnerWindowHeight);
    }

    /**
     * @return the paintCalled
     */
    public boolean isPaintCalled() {
        return paintCalled;
    }

    /**
     * @param paintCalled the paintCalled to set
     */
    public void setPaintCalled(boolean b) {
        this.paintCalled=b;
    }

    /******************
     * PARTIE RESEAU  *
     ******************/

    public void set_cnt(pm.Client.ClientNetworkTools cnt) {
        this.cnt = cnt;
    }

    public pm.Client.ClientNetworkTools get_cnt(){
        return cnt;
    }

    /*
     * Pour que le panneau reste toujours au centre
     * de la fenêtre lors d'un redimensionnement
     */
    public void init_cnt(){
        if(cnt!=null) {
            cnt.init(this.getSize().width, this.getSize().height);
        }
    }
    
    public ZDialog updatable(){
	if(cnt!=null && cnt.get_real_time()){
	    return (pm.Client.ClientNetworkTools) cnt;
	} else if(JGeneralMenuBar.get_scp()!=null && JGeneralMenuBar.get_scp().get_collaboration()){
	    return (pm.Server.ServerControlPanel) JGeneralMenuBar.get_scp();
	}
        return null;
    }

    /**
     * @param i is :
     *	    0 to delete an object
     *	    1 to add
     *	    2 to update object by its parents (points)
     *	    3 to update object itself
     */
    public synchronized void update_distant(final ConstructionObject o, final int i) {
	final ZDialog ZD;
        if((ZD=updatable())==null) {
            return;
        }

	SwingUtilities.invokeLater(new Runnable() {
	    private String msg;
	    private Object obj;

	    @Override
	    public void run() {
	        try {
		    ByteArrayOutputStream bout = new ByteArrayOutputStream();
		    XmlWriter xml = new XmlWriter(new PrintWriter(new OutputStreamWriter(bout), true));
		    //xml.printXml();  //problème dans les unités d'angle
		    xml.printEncoding();
		    xml.startTagNewLine("Objects");

		    if(i==1) {
		        o.save(xml);
		        xml.endTagNewLine("Objects");
		        msg = "<To add>\n"+bout.toString();
		    } else if(i==0) {
		        o.save(xml);
		        xml.endTagNewLine("Objects");
		        msg = "<To delete>\n"+bout.toString();
		    } else if(i==2) {

		        if(o instanceof PointObject){
			    o.save(xml);
			} else {
			    Enumeration e = o.depending();
			    while(e.hasMoreElements()) {
			        if((obj = e.nextElement()) instanceof PointObject) {
				    ((PointObject) obj).save(xml);
				}
			    }
			}

			xml.endTagNewLine("Objects");
			msg = "<To update>\n"+bout.toString();
		    } else if(i==3) {
			o.save(xml);
			xml.endTagNewLine("Objects");
			msg = "<To update>\n"+bout.toString();
		    }
		    ZD.send(msg);
		} catch(Exception ex) {
		    System.err.println("BUG ZC : "+ex);
		}
	    }
	});
    }
    
    public synchronized void update_distant(final String s) {
        final ZDialog ZD;
        if((ZD=updatable())==null) {
            return;
        }
        
        SwingUtilities.invokeLater(new Runnable() {
            @Override
	    public void run() {
                ZD.send("<To interpret>\n"+s+"\n");
            }
        });
    }

    public synchronized void update_distant(final String old_name, final String new_name) {
	final ZDialog ZD;
        if((ZD=updatable())==null) {
            return;
        }

	ZD.send("<To change name>\n"+old_name+";"+new_name+"\n"+"");
    }

    public void update_local(String src) {
	try {
	    if(src.startsWith("<To change name>")) {
		src = src.replace("<To change name>\n", "");
		String name[] = src.split(";");
		this.getConstruction().find(name[0]).setName(name[1].replace("\n", ""));
	    } else if (src.startsWith("<To interpret>")) {
                C.interpret(this, src.replace("<To interpret>\n", ""), "");
            } else {
		final ByteArrayOutputStream bout = new ByteArrayOutputStream();
		final PrintWriter out = new PrintWriter(new OutputStreamWriter(bout, "utf-8"));
		out.print(src);
		out.close();
		final byte b[] = bout.toByteArray();
		final InputStream IN = new ByteArrayInputStream(b);

		final XmlReader xml = new XmlReader();
		xml.init(IN);
		XmlTree tree = xml.scan();

		Enumeration root = tree.getContent();
		do {
		    tree = (XmlTree) root.nextElement();
		} while(root.hasMoreElements() && !tree.getTag().name().equals("Objects"));

		if(src.startsWith("<To add>") || src.startsWith("<Global>")) {
		    this.getConstruction().readConstruction(tree, false);
		} else { //delete, update
		    tree = (XmlTree) tree.getContent();
		    while(tree.hasMoreElements()) {
			XmlTree t = (XmlTree) tree.nextElement();
			String name = t.getTag().getValue("name");
			ConstructionObject o = this.getConstruction().find(name);
			if(src.startsWith("<To delete>")){
			    this.delete(o);
			} else { //update
                            XmlTag tag = t.getTag();
                            
			    o.setHidden(tag.hasParam("hidden"));
			    o.setShowName(tag.hasParam("showname"));
			    o.setShowValue(tag.hasParam("showvalue"));
			    o.setTracked(tag.hasParam("tracked"));
			    o.setColor(tag.hasParam("color")?Integer.parseInt(tag.getValue("color")):0);
			    o.setFixed(tag.hasParam("fixed"));
                            
                            //for Text
                            Enumeration e = t.getContent();
                            while(e.hasMoreElements()) {
                                XmlTree xt = (XmlTree) e.nextElement();
                                if(xt.getTag() instanceof XmlTagText) {
                                    o.setLines(((XmlTagText) xt.getTag()).getContent());
                                    o.setText(((XmlTagText) xt.getTag()).getContent(), true);
                                }
                            }
                            
                            // Conditionals
                            o.clearConditionals();
                            int i = 0;
                            while(tag.hasParam("ctag"+i) && tag.hasParam("cexpr"+i)) {
                                o.addConditional(tag.getValue("ctag"+i), new Expression(tag.getValue("cexpr"+i), C, o));
                                i++;
                            }
                            
			    if(o instanceof FunctionObject){
				((FunctionObject) o).setExpressions(tag.getValue("var"), tag.getValue("x"), tag.getValue("y"));
			    } else {
				if(o.fixed()) {
					o.setFixed(tag.getValue("x"), tag.getValue("y"));
				    } else {
					o.move(Double.valueOf(tag.getValue("x")), Double.valueOf(tag.getValue("y")));
				    }
			    }
			    this.dovalidate();
			}
		    }
		}
	    }
	    this.repaint();
	} catch (Exception e){}
    }
}
