/* 

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 java.awt.CheckboxMenuItem;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.TextField;
import java.awt.datatransfer.Clipboard;
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.io.BufferedOutputStream;
import java.io.BufferedReader;
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 java.util.Enumeration;
import java.util.Vector;
import java.util.zip.GZIPOutputStream;

import rene.dialogs.Warning;
import rene.gui.Global;
import rene.gui.MyCheckboxMenuItem;
import rene.gui.MyMenuItem;
import rene.util.FileName;
import rene.util.ImageSelection;
import rene.util.MyVector;
import rene.util.PngEncoder;
import rene.util.sort.Sorter;
import rene.util.xml.XmlReader;
import rene.util.xml.XmlTag;
import rene.util.xml.XmlTagPI;
import rene.util.xml.XmlTree;
import rene.util.xml.XmlWriter;
import rene.zirkel.construction.ChangedListener;
import rene.zirkel.construction.Construction;
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.MyGraphics;
import rene.zirkel.graphics.MyGraphics13;
import rene.zirkel.graphics.MyGraphicsEPS;
import rene.zirkel.graphics.MyGraphicsFig;
import rene.zirkel.graphics.MyGraphicsPDF;
import rene.zirkel.graphics.MyGraphicsSvg;
import rene.zirkel.graphics.PolygonDrawer;
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.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.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.JLocusTrackObject;
import rene.zirkel.structures.CoordinatesXY;
import rene.zirkel.tools.AnimatorTool;
import rene.zirkel.tools.BinderTool;
import rene.zirkel.tools.BreakpointAnimator;
import rene.zirkel.tools.EditTool;
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.SaveJob;
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 atp.sHotEqn;

//import eric.JUndo;
//import eric.applet.dock.dock;
import eric.GUI.palette.JIcon;
import eric.GUI.palette.JIconMouseAdapter;
import eric.GUI.palette.PaletteManager;
import eric.GUI.pipe_tools;
import eric.GUI.themes;
import eric.JSprogram.ScriptItem;
import eric.JSprogram.ScriptPanel;
import eric.JZirkelCanvas;
import eric.animations.AnimationPanel;
import eric.bar.JPropertiesBar;
import eric.controls.JControlsManager;
import eric.jobs.JobManager;
import eric.macros.MacroTools;
import eric.restrict.RestrictContainer;
import eric.restrict.RestrictItems;
import java.awt.Point;
import java.awt.Toolkit;
import java.util.ArrayList;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import rene.util.parser.StringParser;
import rene.zirkel.construction.ConstructionDisplayPanel;
import rene.zirkel.macro.MacroContextualPopupMenu;
import rene.zirkel.tools.DefineJobTool;
import rene.zirkel.tools.JSmacroTool;
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.
 */
public class ZirkelCanvas extends javax.swing.JPanel implements MouseListener,
        MouseMotionListener, ItemListener, AddEventListener, ActionListener,
        ChangedListener, MouseWheelListener // the canvas to do the construction
{

    /**
     *
     */
    private int minfontsize, minpointsize, minlinesize, arrowsize, 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 boolean is3D=false;
    private int OwnerWindowWidth, OwnerWindowHeight;
    private boolean islibrarymacrovisible=true;
//    public JUndo JU=new JUndo();
//    private dock DOCK;
    private boolean paintCalled=false;

    /**
     * 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 void getLocalPreferences() {
        minfontsize=Global.getParameter("minfontsize", 12);
        minpointsize=Global.getParameter("minpointsize", 3);
        minlinesize=Global.getParameter("minlinesize", 1);
        arrowsize=Global.getParameter("arrowsize", 10);
        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);
    }

    public void setLocalPreferences() {
        Global.setParameter("minfontsize", minfontsize);
        Global.setParameter("minpointsize", minpointsize);
        Global.setParameter("minlinesize", minlinesize);
        Global.setParameter("arrowsize", arrowsize);
        Global.setParameter("digits.lengths", digits_lengths);
        Global.setParameter("digits.edit", digits_edit);
        Global.setParameter("digits.angles", digits_angles);
        Global.setParameter("colorbackground", getColor(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", ""+minfontsize);
        xml.printArg("minpointsize", ""+minpointsize);
        xml.printArg("minlinesize", ""+minlinesize);
        xml.printArg("arrowsize", ""+arrowsize);
        xml.printArg("digits.lengths", ""+digits_lengths);
        xml.printArg("digits.edit", ""+digits_edit);
        xml.printArg("digits.angles", ""+digits_angles);
        xml.printArg("colorbackground", ""+colorbackground);
        xml.printArg("colorbackgroundx", ""+colorbackgroundx);
        xml.printArg("colorbackgroundy", ""+colorbackgroundy);
        xml.printArg("colorbackgroundPal", ""+colorbackgroundPal);
        xml.printArg("fig3D", String.valueOf(is3D()));
        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("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());
            }
        }
    }

    // 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 set3D(boolean b) {
        is3D=b;
    }

    public boolean is3D() {
        return is3D;
    }

    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 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));
    }

    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);
    }

    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
    {

        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.
     */
    public void added(final Construction c, final ConstructionObject o) {

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

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

    }

    public boolean check(final Construction c) {
        final int n=TargetO.length;
        for (int i=0; i<n; i++) {
            TargetS[i]=null;
        }
        int found=0;
        final Enumeration e=c.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            if (o.isKeep()) {
                continue;
            }
            for (int i=0; i<n; i++) {
                if (TargetS[i]!=null) {
                    continue;
                }
                if (o.equals(TargetO[i])) {
                    TargetS[i]=o;
                    found++;
                    break;
                }
            }
        }
        return found==n;
    }

    /**
     * Check if the solution to the current problem has been found.
     */
    public void check() {
        if (displayJob()&&TargetO.length>0) {
            boolean olddone=true;
            for (int i=0; i<TargetO.length; i++) {
                if (TargetS[i]!=null&&!TargetS[i].isInConstruction()) {
                    TargetS[i]=null;
                }
                if (TargetS[i]!=null) {
                    continue;
                } else {
                    olddone=false;
                }
            }
            final boolean done=check(C);
            if (done&&DL!=null&&!olddone) {
                repaint();
                DL.notifyDone();
                freeJob();
            }
        }
    }
    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:
    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;
            }
        }
    }

    public synchronized void mouseReleased(final MouseEvent e) {
        if (!Interactive) {
            return;
        }
        if (DT!=null) {
            DT.waitReady();
        }
        if (RightClicked) {
            RightClicked=false;
            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);
                        check();
                        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.size()!=0)) {
                    PM.show(e.getComponent(), e.getX(), e.getY());
                }
            }
            repaintCD();
            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();
        check();
        Dragging=false;
    }

    public synchronized void mouseClicked(final MouseEvent e) {
    }

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

    public void mouseEntered(final MouseEvent e) {
    }

    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;

    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
    @Override
    public synchronized void paint(final Graphics g) {

        if (g==null) {
            return;
        }
        if (C.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);
            if (!Global.getParameter("printscalepreview", false)||!Global.getParameter("printscale.sizes", false)) {
                IG=new MyGraphics13(I.getGraphics(), this);
                IG.setSize(IW, IH);
                // PointSize=IH/PointSizeFactor;
                // FontSize=PointSize*4;

                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;

                IG.setDefaultFont((int) FontSize, Global.getParameter(
                        "font.large", false), Global.getParameter("font.bold",
                        false));

                UniversalTrack.createTrackImage();
            } else {
                final double dpi=IW/Global.getParameter("printscale.width", 5.0);
                IG=new MyGraphics13(I.getGraphics(), Global.getParameter(
                        "printscale.linewidth", 0.02)*dpi, this, null);
                IG.setSize(IW, IH);
                PointSize=Global.getParameter("printscale.pointsize", 0.07)*dpi;
                FontSize=Global.getParameter("printscale.textsize", 0.3)*dpi;
                IG.setDefaultFont((int) FontSize, Global.getParameter(
                        "font.large", false), Global.getParameter("font.bold",
                        false));
            }
            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);
            }
        }



        C.computeHeavyObjects(this);
        UniversalTrack.draw();
        dopaint(IG);



        if (Global.getParameter("printscale.middle", false)&&Global.getParameter("printscalepreview", false)) {
            IG.setColor(Color.red);
            final int PW=Global.getParameter("printscale.w", IW);
            final int PH=Global.getParameter("printscale.h", IH);
            IG.drawRect(IW/2-PW/2-3, IH/2-PH/2-3, PW+7,
                    PH+7);
        }
//        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();

    }
    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.last()) {
                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) {
        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.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);
//         JMacrosTools.CurrentJZF.JCM.paintControls(IG.getGraphics());
    }

    void dopaintFig(final MyGraphicsFig IG) {
        if (getAxis_show()) {
            if (Global.getParameter("axis_with_grid", true)) {
                paintGrid(IG);
            } else {
                paintAxes(IG);
            }
        }
        IG.pushLayer(true);
        int layer=0;
        while (true) {
            IG.pushLayer();
            int l=0;
            Enumeration e=C.elements();
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                if (o==LastPaint) {
                    break;
                }
                if (l==layer&&o.isBack()) {
                    o.paint(IG, this);
                }
                if (o.isBreak()) {
                    l++;
                    if (l>layer) {
                        break;
                    }
                }
            }
            if (l<layer) {
                break;
            }
            l=0;
            e=C.elements();
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                if (o==LastPaint) {
                    break;
                }
                if (l==layer&&!o.isBack()&&!o.selected()) {
                    o.paint(IG, this);
                }
                if (o.isBreak()) {
                    l++;
                    if (l>layer) {
                        break;
                    }
                }
            }
            l=0;
            e=C.elements();
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                if (o==LastPaint) {
                    break;
                }
                if (l==layer&&!o.isBack()&&o.selected()) {
                    o.paint(IG, this);
                }
                if (o.isBreak()) {
                    l++;
                    if (l>layer) {
                        break;
                    }
                }
            }
            layer++;
        }
        IG.pushLayer(false);
        if (LastPaint==null) {
            paintTrack(IG);
        }
    }
    int xg[]=new int[64], yg[]=new int[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 x1=Math.floor((C.getX()-C.getW())/gridsize-1)*gridsize;
        int xi=0;
        while (x1<(C.getX()+C.getW())&&xi<64) {
            final int c=(int) col(x1);
            xg[xi++]=c;
            x1+=gridsize;
        }
        double y1=Math.floor((C.getY()-C.getW())/gridsize-1)*gridsize;
        int yi=0;
        while (y1<(C.getY()+C.getW())&&yi<64) {
            final int r=(int) row(y1);
            yg[yi++]=r;
            y1+=gridsize;
        }

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

        // 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()) {
                final int c=(int) col(0);
                if (GridThickness!=ConstructionObject.THICK) {
                    IG.drawLine(c, 0, c, IH);
                } else {
                    IG.drawThickLine(c, 0, c, IH);
                }
            }
            if (0<C.getY()+C.getW()&&0>C.getY()-C.getW()) {
                final int r=(int) 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;
        int lh=(int) row(0);
        int bottomshift=FH+2;
        if (lh<0||lh>IH) {
            lh=IH;
            bottomshift=-7;
        }

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

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

        boolean left=true;
        int lw=(int) 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())) {
            final int r=(int) row(y1);
            final String s=format(y1);
            if (s.length()>0) {
                final int leftshift=(left)?-fm.stringWidth(s)-5:8;
                if (GridLabels) {
                    IG.drawString(s, lw+leftshift, r+FH/2-2);
                }
                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;
        final int r0=(int) row(0);
        int dd=IH/200;
        if (dd<1) {
            dd=1;
        }
        while (x1<(C.getX()+C.getW())) {
            final int c=(int) col(x1);
            IG.drawLine(c, r0-dd, c, r0+dd);
            x1+=gridsize;
        }
        double y1=Math.floor((C.getY()-C.getW())/gridsize-1)*gridsize;
        final int c0=(int) col(0);
        while (y1<(C.getY()+C.getW())) {
            final int r=(int) 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()) {
                final int c=(int) col(0);
                if (GridThickness!=ConstructionObject.THICK) {
                    IG.drawLine(c, 0, c, IH);
//                    IG.drawRect(c, 0, 1, IH);
                } else {
                    IG.drawThickLine(c, 0, c, IH);
                }
            }
            if (0<C.getY()+C.getW()&&0>C.getY()-C.getW()) {
                final int r=(int) 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;
        int lh=(int) row(0);
        int bottomshift=FH+2;
        if (lh<0||lh>IH) {
            lh=IH;
            bottomshift=-7;
        }

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

        boolean left=true;
        int lw=(int) 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())) {
            final int r=(int) row(y1);
            final String s=format(y1);
            if (s.length()>0) {
                final int leftshift=(left)?-fm.stringWidth(s)-5:8;
                if (GridLabels) {
                    IG.drawString(s, lw+leftshift, r+FH/2-2);
                }
                IG.drawLine(lw-dd, r, lw+dd, r);
            }
            y1+=labelsize;
        }

        IG.setAntialiasing(true);
    }

    public double pointSize() {
        return PointSize;
    }
    public double SelectionPointFactor=Global.getParameter("selectionsize",
            1.5);

    public double selectionSize() {
        return SelectionPointFactor*PointSize;
    }
    public double Scale=1.0;

    public int scale(final int x) {
        return (int) (Scale*x);
    }
    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 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) {

        NewPoint=false;
        if (Preview) {
            final PointObject p=new PointObject(C, x(x), y(y));
            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;
        }

        // 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;
            }
            if (oc!=null) {
                boolean yes=true;

                if (yes) {
                    final PointObject o=new PointObject(C, x(x), y(y), oc);
                    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;
                    return o;
                } else {
                    return null;
                }
            }
        }
        final PointObject p=new PointObject(C, x(x), y(y));

        if (altdown&&getAxis_show()) {
            p.setHalfIncrement(this);
        }

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



        addObject(p);
        p.setDefaults();
        // 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 (IntersectionYes) {
            if (o.length==1||!o[1].valid()) {
                addObject(o[0]);
                o[0].autoAway();
                o[0].validate(x(x), y(y));
                validate();
                o[0].setDefaults();
                o[0].setRestricted(getRestricted());
                return o[0];
            }
            if (!o[0].valid()) {
                addObject(o[1]);
                o[1].autoAway();
                validate();
                o[1].setDefaults();
                o[1].setRestricted(getRestricted());
                return o[1];
            }
            IntersectionObject oc=o[0];
            final double d0=o[0].distanceTo(x, y, this), d1=o[1].distanceTo(x, y, this);
            if (d1<d0) {
                oc=o[1];
            }
            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);
        RightClicked=false;
        final eric.JSelectPopupDialog d=new eric.JSelectPopupDialog(getFrame(), this,
                v);
        final ConstructionObject o=d.getConstructionObject();
        d.doclose();
        return o;
    }

    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);
        RightClicked=false;
        final eric.JSelectPopupDialog d=new eric.JSelectPopupDialog(getFrame(), this,
                v);
        final ConstructionObject o=d.getConstructionObject();
        d.doclose();
        return o;
    }

    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(final MouseEvent e) {
        if (PreviewObject!=null) {
            PreviewObject.move(x(e.getX()), y(e.getY()));
            validate();
            repaint();
        }
    }

    public void prepareForPreview(final MouseEvent e) {
        LastNonPreview=C.last();
        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 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);

        if (o.length==1&&!o[0].valid()) {
            return null;
        }
        if (o.length==2) {
            if (!o[1].valid()) {
                if (!o[0].valid()) {
                    return null;
                }
            } else {
                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];
            }
        }
        // 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 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();
            }
            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!=null) {
            OC.invalidate(this);
        }
        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 synchronized void clear() // delete complete construction
    {
        displayJob(false);
        if (OC!=null) {
            OC.invalidate(this);
        }
        C.clear();
        if (JCM!=null) {
            JCM.removeAllControls();

        }
        if (Animations!=null) {
            removeAllAnimations();
        }
        recompute();
        setDefaultColor(0);
        setDefaultColorType(0);
        setDefaultType(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.last()==null) {
            return;
        }
        if (o.isKeep()) {
            return;
        }
        JControlsManager.removeOwnerControl(this, o);

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

    /**
     * Delete a vector of construction objects.
     *
     * @param v
     */
    public void delete(final Vector v) {
        if (C.last()==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;
        }
    }

    /**
     * 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) {
            final Enumeration e=Scripts.getScripts().elements();
            while (e.hasMoreElements()) {
                final ScriptItem s=(ScriptItem) e.nextElement();
                s.saveScript(xml);
            }
        }

        if (construction) {
            xml.startTagStart("Construction");
            if (isJob()) {
                xml.printArg("job", "true");
                xml.printArg("last", Last);
                int i=1;
                final Enumeration e=Targets.elements();
                while (e.hasMoreElements()) {
                    xml.printArg("target"+i, (String) e.nextElement());
                    i++;
                }
            }
            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", ""+axis_color);
                xml.printArg("thickness", ""+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=0;
                            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.size()==0) {
                                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());
        }

        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.last()==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_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();
        }
    }

    /**
     * Sets the job parameters for the current construction (the last item to
     * display and the target item).
     * @param last
     */
    public void setJob(final ConstructionObject last) {
        Job=true;
        Last=last.getName();
        Targets=new Vector();
    }

    public void addJobTarget(final ConstructionObject target,
            final boolean visible) {
        if (visible) {
            Targets.addElement("~"+target.getName());
        } else {
            Targets.addElement(target.getName());
        }
    }

    public void clearTargets() {
        Targets=new Vector();
    }

    /**
     * Cancel the job state o fthe current construction.
     */
    public void clearJob() {
        Job=false;
    }

    /**
     * Test, if the construction is a valid job.
     */
    public boolean isJob() {
        final Enumeration e=Targets.elements();
        while (e.hasMoreElements()) {
            final String s=(String) e.nextElement();
            if (C.find(s)==null&&(!s.startsWith("~")||C.find(s.substring(1))==null)) {
                return false;
            }
        }
        return Job&&C.find(Last)!=null&&Targets.size()>0;
    }

    public boolean inTargets(final String s) {
        final Enumeration e=Targets.elements();
        while (e.hasMoreElements()) {
            if (((String) e.nextElement()).equals(s)) {
                return true;
            }
        }
        return false;
    }

    /**
     *
     * This will display the construction as a job (if it is a job). The
     * construction is copied up to the last element. The copies are marked as
     * keep objects, so that they cannot be changed. The last object is marked
     * as the target object, so that it displays in a special color.
     *
     * The function can also flip back to the original construction
     * (flag=false).
     */
    public void displayJob(final boolean flag) {
        clearSelected();
        if (!(OC instanceof SaveJob)) {
            OC.reset(this);
        }
        if (flag) // diplay the job
        {
            C.setOriginalOrder(true);
            if (displayJob()||!isJob()) {
                return;
            }
            final Construction Cnew=new Construction();
            Cnew.setXYW(C.getX(), C.getY(), C.getW());
            Enumeration e=C.elements();
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                o.setJobTarget(true);
                o.setJobTarget(false);
            }
            e=C.elements();
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                Cnew.add(o);
                o.setKeep(true);
                if (inTargets(o.getName())||inTargets("~"+o.getName())) {
                    o.setJobTarget(true);
                }
                if (o.getName().equals(Last)) {
                    break;
                }
            }
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                if (inTargets(o.getName())||inTargets("~"+o.getName())) {
                    Cnew.add(o);
                    o.setKeep(true);
                    o.setJobTarget(true);
                }
            }
            int n=0;
            e=Targets.elements();
            while (e.hasMoreElements()) {
                final String s=(String) e.nextElement();
                if (!s.startsWith("~")) {
                    n++;
                }
            }
            TargetO=new ConstructionObject[n];
            TargetS=new ConstructionObject[n];
            n=0;
            e=Targets.elements();
            while (e.hasMoreElements()) {
                final String s=(String) e.nextElement();
                if (!s.startsWith("~")) {
                    TargetO[n]=Cnew.find(s);
                    TargetS[n]=null;
                    n++;
                }
            }
            COriginal=C;
            C=Cnew;
            C.addAddEventListener(this);
            C.setComment(COriginal.getJobComment());
            recompute();
            reloadCD();
            repaint();
        } else // display the orginal construction again
        {
            if (!displayJob()) {
                return;
            }
            C.removeAddEventListener(this);
            C=COriginal;
            COriginal=null;
            final Enumeration e=C.elements();
            while (e.hasMoreElements()) {
                final ConstructionObject o=(ConstructionObject) e.nextElement();
                o.setKeep(false);
                o.setJobTarget(false);
            }
            C.setOriginalOrder(false);
            recompute();
            reloadCD();
            repaint();
        }
    }

    /**
     * If the job is solved, this function removes all restrictions.
     */
    public void freeJob() {
        final Enumeration e=C.elements();
        while (e.hasMoreElements()) {
            final ConstructionObject o=(ConstructionObject) e.nextElement();
            o.setKeep(false);
            o.setJobTarget(false);
            repaint();
        }
    }

    /**
     * Test if the canvas displays a job right now.
     */
    public boolean displayJob() {
        return COriginal!=null;
    }

    // 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.size()==0) // 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));
            }
        }
        // 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&&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();
            if (o.isFlag()&&!(o.isParameter()&&!o.isMainParameter()&&!needed(c,
                    o, null))) { // System.out.println(o.getName()+", parameter: "+o.isParameter()+
                // ", main: "+o.isMainParameter()+", needed "+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&&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));
    }

    public void saveScript(String s, String script) {
        Scripts.saveScript(s, script);
    }

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

    public Vector<ScriptItem> getScripts() {
        return Scripts.getScripts();
    }

    public void removeScript(String s) {
        Scripts.removeScript(s);
    }

    public void removeAllScripts() {
        remove(Scripts);
        Scripts=null;
        Scripts=new ScriptPanel(this);
    }

    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=0;
        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 V=new Vector();
        final Enumeration e=Macros.elements();
        while (e.hasMoreElements()) {
            final MacroItem m=(MacroItem) e.nextElement();
            if (!m.M.isProtected()) {
                V.addElement(m);
            }
        }
        deleteMacros(V);
    }

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

    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 c=new Construction();
        c.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("")) {
                    c.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, c);
    }

    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;
    }

    /**
     * Save picture as PNG on file or copy to clipboard
     *
     * @param filename
     *            ("" for clipboard)
     * @return not failed
     */
    synchronized public boolean savePNG(final String filename,
            final boolean latex) {
        LatexOutput lout=null;
        if (IW==0||I==null) {
            return false;
        }
        if (Global.getParameter("printscale.middle", true)) {
            savePreviewAsPNG(filename);
            return true;
        }
        double dpi=Global.getParameter("printscale.dpi", 300);
        if (!Global.getParameter("printscale.sizes", false)) {
            final int w=Global.getParameter("printscale.w", IW);
            final int h=Global.getParameter("printscale.h", IH);
            if (latex) {
                lout=createBB(filename, w, h, dpi);
            }
            final int IWold=IW;
            IW=w;
            IH=h;
            Scale=((double) IW)/IWold;
            I=createImage(IW, IH);
            IG=new MyGraphics13(I.getGraphics(), Scale, this, lout);
            IG.setSize(w, h);
            double pointsize=PointSize*Scale;
            double fontsize=FontSize*Scale;
            if (Scale<1) {
                if (Global.getParameter("options.minpointsize", false)&&pointsize<MinPointSize) {
                    pointsize=MinPointSize;
                }
                if (Global.getParameter("options.minfontsize", false)&&fontsize<MinFontSize) {
                    fontsize=MinFontSize;
                }
            }
            IG.setDefaultFont((int) (fontsize), Global.getParameter(
                    "font.large", false), Global.getParameter("font.bold",
                    false));
            PointSize=pointsize;
        } else {
            final int IWOld=IW;
            dpi=Global.getParameter("printscale.dpi", 300.0);
            IW=(int) (Global.getParameter("printscale.width", 5.0)/2.54*dpi);
            IH=(int) (Global.getParameter("printscale.height", 5.0)/2.54*dpi);
            if (latex) {
                lout=createBB(filename, IW, IH, dpi);
            }
            I=createImage(IW, IH);
            IG=new MyGraphics13(I.getGraphics(), Global.getParameter(
                    "printscale.linewidth", 0.02)/2.54*dpi, this, lout);
            IG.setSize(IW, IH);
            PointSize=Global.getParameter("printscale.pointsize", 0.07)/2.54*dpi;
            IG.setDefaultFont((int) (Global.getParameter("printscale.textsize",
                    0.3)/2.54*dpi), Global.getParameter("font.large", false), Global.getParameter(
                    "font.bold", false));
            Scale=(double) IW/IWOld;
        }
        recompute();
        if (Global.getParameter("options.bitmapbackground", false)) {
            IG.clearRect(0, 0, IW, IH, getBackground());
        } else {
            IG.clearRect(0, 0, IW, IH, Color.white);
        }
        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);
        if (lout!=null) {
            lout.close();
        }
        Interactive=true;
        if (!filename.equals("")) {
            try {
                final BufferedOutputStream out=new BufferedOutputStream(
                        new FileOutputStream(filename));
                final PngEncoder png=new PngEncoder(I, PngEncoder.NO_ALPHA,
                        0, 9);
                png.setDPI(dpi);
                out.write(png.pngEncode());
                out.close();
            } catch (final Exception e) {
                warning(e.toString());
            }
        } else {
            try {
                final Clipboard clipboard=getToolkit().getSystemClipboard();
                final ImageSelection is=new ImageSelection(I);
                clipboard.setContents(is, null);
            } catch (final Exception e) {
                Scale=1;
                I=null;
                repaint();
                return false;
            }
        }
        Scale=1;
        I=null;
        repaint();
        return true;
    }

    public void savePreviewAsPNG(final String filename) {
        final int PW=Global.getParameter("printscale.w", IW);
        final int PH=Global.getParameter("printscale.h", IH);
        final Image i=createImage(PW, PH);
        final Graphics g=i.getGraphics();
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, PW, PH);
        g.drawImage(I, -(IW-PW)/2, -(IH-PH)/2, this);
        if (!filename.equals("")) {
            try {
                final FileOutputStream out=new FileOutputStream(filename);
                final PngEncoder png=new PngEncoder(i, PngEncoder.NO_ALPHA,
                        0, 9);
                out.write(png.pngEncode());
                out.close();
            } catch (final Exception e) {
                warning(e.toString());
            }
        } else {
            try {
                final Clipboard clipboard=getToolkit().getSystemClipboard();
                final ImageSelection is=new ImageSelection(I);
                clipboard.setContents(is, null);
            } catch (final Exception e) {
            }
        }
    }

    public void saveFIG(final String filename) {
        try {
            final PrintWriter out=new PrintWriter(new FileOutputStream(
                    filename));
            final MyGraphicsFig fig=new MyGraphicsFig(out, IW, IH);
            fig.setSize(IW, IH);
            dopaintFig(fig);
            fig.close();
            out.close();
        } catch (final Exception e) {
            warning(e.toString());
            e.printStackTrace();
        }
    }

    public void saveSVG(final String filename) {
        try {
            OutputStream o=new FileOutputStream(filename);
            if (ZirkelFrame.isCompressed(filename)) {
                o=new GZIPOutputStream(o);
            }
            final PrintWriter out=new PrintWriter(new OutputStreamWriter(o,
                    "UTF8"));
            final MyGraphicsSvg svg=new MyGraphicsSvg(out, IW, IH);
            svg.setSize(IW, IH);
            dopaint(svg);
            svg.close();
            out.close();
        } catch (final Exception e) {
            warning(e.toString());
            e.printStackTrace();
        }
    }

    public void savePDF(final String filename) {
        PrintWriter out=null;
        try {
            OutputStream o=new FileOutputStream(filename);
            if (ZirkelFrame.isCompressed(filename)) {
                o=new GZIPOutputStream(o);
            }
            out=new PrintWriter(new OutputStreamWriter(o, "CP1252"));
        } catch (final Exception e) {
            warning(e.toString());
            e.printStackTrace();
        }
        final int iw=IW, ih=IH;
        IW=(int) (Global.getParameter("printscale.width", IW*2)/2.54*72+0.5);
        IH=(int) (Global.getParameter("printscale.height", IH*2)/2.54*72+0.5);
        final double ps=PointSize;
        Scale=(double) IW/iw;
        double pointsize=PointSize*Scale;
        double fontsize=FontSize*Scale+1;
        double linewidth=Scale;
        if (Global.getParameter("printscale.sizes", false)) {
            pointsize=Global.getParameter("printscale.pointsize", 0.07)/2.54*72;
            fontsize=Global.getParameter("printscale.fontsize", 0.3)/2.54*72;
            linewidth=Global.getParameter("printscale.linewidth", 0.02)/2.54*72;
        }
        PointSize=pointsize;
        recompute();
        final MyGraphicsPDF pdf=new MyGraphicsPDF(out, IW, IH, linewidth);
        pdf.setSize(IW, IH);
        pdf.setDefaultFont((int) (fontsize), Global.getParameter("font.large",
                false), Global.getParameter("font.bold", false));
        try {
            dopaint(pdf);
            pdf.close();
            out.close();
        } catch (final Exception e) {
            warning(e.toString());
            e.printStackTrace();
        }
        IW=iw;
        IH=ih;
        Scale=1.0;
        PointSize=ps;
        I=null;
        repaint();
    }

    public void saveEPS(final String filename) {
        OutputStream o=null;
        try {
            o=new FileOutputStream(filename);
            if (ZirkelFrame.isCompressed(filename)) {
                o=new GZIPOutputStream(o);
            }
        } catch (final Exception e) {
            warning(e.toString());
            e.printStackTrace();
        }
        final int iw=IW, ih=IH;
        IW=(int) (Global.getParameter("printscale.width", IW*2)/2.54*72+0.5);
        IH=(int) (Global.getParameter("printscale.height", IH*2)/2.54*72+0.5);
        final double ps=PointSize;
        Scale=(double) IW/iw;
        double pointsize=PointSize*Scale;
        double fontsize=FontSize*Scale+1;
        double linewidth=Scale;
        if (Global.getParameter("printscale.sizes", false)) {
            pointsize=Global.getParameter("printscale.pointsize", 0.07)/2.54*72;
            fontsize=Global.getParameter("printscale.fontsize", 0.3)/2.54*72;
            linewidth=Global.getParameter("printscale.linewidth", 0.02)/2.54*72;
        }
        PointSize=pointsize;
        recompute();
        try {
            final MyGraphicsEPS eps=new MyGraphicsEPS(o, IW, IH);
            eps.setSize(IW, IH);
            eps.setLineWidth(linewidth);
            eps.setDefaultFont((int) (fontsize), Global.getParameter(
                    "font.large", false), Global.getParameter("font.bold",
                    false));
            dopaint(eps);
            eps.close();
            o.close();
        } catch (final Exception e) {
            warning(e.toString());
            e.printStackTrace();
        }
        IW=iw;
        IH=ih;
        Scale=1.0;
        PointSize=ps;
        I=null;
        repaint();
    }

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

    public void magnet(final PointObject p) {
        setTool(new MagnetTool(this, p, OC));
    }

    public void setJSTool(final String msg, String type) {
        if (OC!=null) {
            OC.invalidate(this);
        }
        OC=new JSmacroTool(this, 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();
    }

    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;
    }

    public ObjectConstructor getCurrentTool() {
        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 c=(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(c, r, c1, r1);
                    c=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", "0");
        f.edit(this, true, true);
        if (f.EditAborted) {
            delete(f);
        }
        repaint();
        reloadCD();
    }

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

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

    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;
    }

    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;
    }
}
