/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
**
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file gpl-2.0.txt included in the
** packaging of this file.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
** This copyright notice MUST APPEAR in all copies of the script!
**
**********************************************************************/

#include <QApplication>
#include <QDesktopWidget>
#include <QAction>
#include <limits.h>
#include "qc_applicationwindow.h"
#include "rs_graphicview.h"

#include "rs_linetypepattern.h"
#include "rs_eventhandler.h"
#include "rs_graphic.h"
#include "rs_grid.h"
#include "rs_painter.h"
#include "rs_mtext.h"
#include "rs_text.h"
#include "rs_settings.h"
#include "rs_dialogfactory.h"
#include "rs_layer.h"

#ifdef EMU_C99
#include "emu_c99.h"
#endif

/**
 * Constructor.
 */
RS_GraphicView::RS_GraphicView()
    : background(), foreground(),
      savedViews(16), savedViewIndex(0),savedViewCount(0),previousViewTime(QDateTime::currentDateTime())
    ,m_bIsCleanUp(false)
{
    drawingMode = RS2::ModeFull;
    printing = false;
    deleteMode = false;
    factor = RS_Vector(1.0,1.0);
    offsetX = 0;
    offsetY = 0;
    container = NULL;
    eventHandler = new RS_EventHandler(this);
    gridColor = Qt::gray;
    metaGridColor = RS_Color(64,64,64);
    grid = new RS_Grid(this);
    zoomFrozen = false;
    //gridVisible = true;
    draftMode = false;

    borderLeft = 0;
    borderTop = 0;
    borderRight = 0;
    borderBottom = 0;

    relativeZero = RS_Vector(false);
    relativeZeroLocked=false;

    mx = my = 0;

    RS_SETTINGS->beginGroup("/Appearance");
    setBackground(QColor(RS_SETTINGS->readEntry("/BackgroundColor", "#000000")));
    setGridColor(QColor(RS_SETTINGS->readEntry("/GridColor", "#7F7F7F")));
    setMetaGridColor(QColor(RS_SETTINGS->readEntry("/MetaGridColor", "#3F3F3F")));
    setSelectedColor(QColor(RS_SETTINGS->readEntry("/SelectedColor", "#A54747")));
	setHighlightedColor(QColor(RS_SETTINGS->readEntry("/HighlightedColor", "#739373")));
	setStartHandleColor(QColor(RS_SETTINGS->readEntry("/StartHandleColor", "#00FFFF")));
	setHandleColor(QColor(RS_SETTINGS->readEntry("/HandleColor", "#0000FF")));
	setEndHandleColor(QColor(RS_SETTINGS->readEntry("/EndHandleColor", "#0000FF")));

    RS_SETTINGS->endGroup();

    printPreview = false;

    QC_ApplicationWindow::getAppWindow()->setPreviousZoomEnable(false);
    //currentInsert = NULL;
}



/**
 * Destructor.
 */
RS_GraphicView::~RS_GraphicView() {
    /*
    //@write default snap mode from prefrences.
    defaultSnapMode.restriction=defaultSnapRes;
    unsigned int snapFlags=RS_Snapper::snapModeToInt(defaultSnapMode);
    RS_SETTINGS->beginGroup("/Snap");
    RS_SETTINGS->writeEntry("/SnapMode",QString::number(snapFlags));
    RS_SETTINGS->endGroup();
    // no need to delete child widgets, Qt does it all for us
    */
    delete grid;
}



/**
 * Must be called by any derrived class in the destructor.
 */
void RS_GraphicView::cleanUp() {
    m_bIsCleanUp=true;
    //delete eventHandler;
    delete eventHandler;
}

/**
 * Sets the pointer to the graphic which contains the entities
 * which are visualized by this widget.
 */
void RS_GraphicView::setContainer(RS_EntityContainer* container) {
    this->container = container;
    //adjustOffsetControls();
}



/**
 * Sets the zoom factor in X for this visualization of the graphic.
 */
void RS_GraphicView::setFactorX(double f) {
    if (!zoomFrozen) {
        factor.x = fabs(f);
    }
}



/**
 * Sets the zoom factor in Y for this visualization of the graphic.
 */
void RS_GraphicView::setFactorY(double f) {
    if (!zoomFrozen) {
        factor.y = fabs(f);
    }
}

void RS_GraphicView::setOffset(int ox, int oy) {
//    DEBUG_HEADER();
//    RS_DEBUG->print(/*RS_Debug::D_WARNING, */"set offset from (%d, %d) to (%d, %d)", getOffsetX(), getOffsetY(), ox, oy);
    setOffsetX(ox);
    setOffsetY(oy);
}

/**
 * @return true if the grid is switched on.
 */
bool RS_GraphicView::isGridOn() {
    if (container!=NULL) {
        RS_Graphic* g = container->getGraphic();
        if (g!=NULL) {
            return g->isGridOn();
        }
    }
    return true;
}

/**
 * @return true if the grid is isometric
 *
 *@Author: Dongxu Li
 */
bool RS_GraphicView::isGridIsometric() {
    return grid->isIsometric();
}


void RS_GraphicView::setCrosshairType(RS2::CrosshairType chType){
    grid->setCrosshairType(chType);
}

RS2::CrosshairType RS_GraphicView::getCrosshairType(){
    return grid->getCrosshairType();
}


/**
 * Centers the drawing in x-direction.
 */
void RS_GraphicView::centerOffsetX() {
    if (container!=NULL && !zoomFrozen) {
        offsetX = (int)(((getWidth()-borderLeft-borderRight)
                         - (container->getSize().x*factor.x))/2.0
                        - (container->getMin().x*factor.x)) + borderLeft;
    }
}



/**
 * Centers the drawing in y-direction.
 */
void RS_GraphicView::centerOffsetY() {
    if (container!=NULL && !zoomFrozen) {
        offsetY = (int)((getHeight()-borderTop-borderBottom
                         - (container->getSize().y*factor.y))/2.0
                        - (container->getMin().y*factor.y)) + borderBottom;
    }
}



/**
 * Centers the given coordinate in the view in x-direction.
 */
void RS_GraphicView::centerX(double v) {
    if (!zoomFrozen) {
        offsetX = (int)((v*factor.x)
                        - (double)(getWidth()-borderLeft-borderRight)/2.0);
    }
}



/**
 * Centers the given coordinate in the view in y-direction.
 */
void RS_GraphicView::centerY(double v) {
    if (!zoomFrozen) {
        offsetY = (int)((v*factor.y)
                        - (double)(getHeight()-borderTop-borderBottom)/2.0);
    }
}

/**
 * @return Current action or NULL.
 */
RS_ActionInterface* RS_GraphicView::getDefaultAction() {
    if (eventHandler!=NULL) {
        return eventHandler->getDefaultAction();
    } else {
        return NULL;
    }
}



/**
 * Sets the default action of the event handler.
 */
void RS_GraphicView::setDefaultAction(RS_ActionInterface* action) {
    if (eventHandler!=NULL) {
        eventHandler->setDefaultAction(action);
    }
}



/**
 * @return Current action or NULL.
 */
RS_ActionInterface* RS_GraphicView::getCurrentAction() {
    if (eventHandler!=NULL) {
        return eventHandler->getCurrentAction();
    } else {
        return NULL;
    }
}



/**
 * Sets the current action of the event handler.
 */
void RS_GraphicView::setCurrentAction(RS_ActionInterface* action) {
    RS_DEBUG->print("RS_GraphicView::setCurrentAction");
    if (eventHandler!=NULL) {
        eventHandler->setCurrentAction(action);
    }
    RS_DEBUG->print("RS_GraphicView::setCurrentAction: OK");
}


/**
 * Kills all running selection actions. Called when a selection action
 * is launched to reduce confusion.
 */
void RS_GraphicView::killSelectActions() {
    if (eventHandler!=NULL) {
        eventHandler->killSelectActions();
    }
}



/**
 * Kills all running actions.
 */
void RS_GraphicView::killAllActions() {
    if (eventHandler!=NULL) {
        eventHandler->killAllActions();
    }
}



/**
 * Go back in menu or current action.
 */
void RS_GraphicView::back() {
    if (eventHandler!=NULL && eventHandler->hasAction()) {
        eventHandler->back();
    } else {
        if (RS_DIALOGFACTORY!=NULL) {
            RS_DIALOGFACTORY->requestPreviousMenu();
        }
    }
}



/**
 * Go forward with the current action.
 */
void RS_GraphicView::enter() {
    if (eventHandler!=NULL && eventHandler->hasAction()) {
        eventHandler->enter();
    }
}



/**
 * Called by the actual GUI class which implements the RS_GraphicView
 * interface to notify LibreCAD about mouse events.
 */
void RS_GraphicView::mousePressEvent(QMouseEvent* e) {
    if (eventHandler!=NULL) {
        eventHandler->mousePressEvent(e);
    }
}



/**
 * Called by the actual GUI class which implements the RS_GraphicView
 * interface to notify LibreCAD about mouse events.
 */
void RS_GraphicView::mouseReleaseEvent(QMouseEvent* e) {
    RS_DEBUG->print("RS_GraphicView::mouseReleaseEvent");
    if (eventHandler!=NULL) {
        if (e->button()!=Qt::RightButton ||
                eventHandler->hasAction()) {

            eventHandler->mouseReleaseEvent(e);
            //e->accept();
        }
        else {
            back();

            e->accept();
        }
    }
    RS_DEBUG->print("RS_GraphicView::mouseReleaseEvent: OK");
}


/*	*
 *	Function name:
 *
 *	Description:		- Called by the actual GUI class which implements the
 *							  RS_GraphicView interface to notify LibreCAD about
 *							  mouse events.
 *
 *	Author(s):			..., Claude Sylvain
 *	Created:				?
 *	Last modified:		23 July 2011
 *
 *	Parameters:			QMouseEvent* e:
 *								...
 *
 *	Returns:				void
 *	*/

void RS_GraphicView::mouseMoveEvent(QMouseEvent* e) {
    RS_DEBUG->print("RS_GraphicView::mouseMoveEvent begin");

    RS_Graphic* graphic = NULL;

    if (container->rtti()==RS2::EntityGraphic) {
        graphic = (RS_Graphic*)container;
    }

    RS_DEBUG->print("RS_GraphicView::mouseMoveEvent 001");

    if (e!=NULL) {
        mx = e->x();
        my = e->y();
    }

    RS_DEBUG->print("RS_GraphicView::mouseMoveEvent 002");

    if (eventHandler!=NULL) {
        eventHandler->mouseMoveEvent(e);
    }

    RS_DEBUG->print("RS_GraphicView::mouseMoveEvent 003");

    if (	((eventHandler == NULL) || !eventHandler->hasAction()) &&
                (graphic != NULL))
    {
        RS_Vector	mouse		= toGraph(RS_Vector(mx, my));
        RS_Vector	relMouse	= mouse - getRelativeZero();

        if (RS_DIALOGFACTORY!=NULL)
            RS_DIALOGFACTORY->updateCoordinateWidget(mouse, relMouse);
    }

    RS_DEBUG->print("RS_GraphicView::mouseMoveEvent end");
}



/**
 * Called by the actual GUI class which implements the RS_GraphicView
 * interface to notify LibreCAD about mouse events.
 */
void RS_GraphicView::mouseLeaveEvent() {
    if (eventHandler!=NULL) {
        eventHandler->mouseLeaveEvent();
    }
}



/**
 * Called by the actual GUI class which implements the RS_GraphicView
 * interface to notify LibreCAD about mouse events.
 */
void RS_GraphicView::mouseEnterEvent() {
    if (eventHandler!=NULL) {
        eventHandler->mouseEnterEvent();
    }
}



/**
 * Called by the actual GUI class which implements the RS_GraphicView
 * interface to notify LibreCAD about key events.
 */
void RS_GraphicView::keyPressEvent(QKeyEvent* e) {
    if (eventHandler!=NULL) {
        eventHandler->keyPressEvent(e);
    }
}



/**
 * Called by the actual GUI class which implements the RS_GraphicView
 * interface to notify LibreCAD about key events.
 */
void RS_GraphicView::keyReleaseEvent(QKeyEvent* e) {
    if (eventHandler!=NULL) {
        eventHandler->keyReleaseEvent(e);
    }
}



/**
 * Called by the actual GUI class which implements a command line.
 */
void RS_GraphicView::commandEvent(RS_CommandEvent* e) {
    if (eventHandler!=NULL) {
        eventHandler->commandEvent(e);
    }
}



/**
 * Enables coordinate input in the command line.
 */
void RS_GraphicView::enableCoordinateInput() {
    if (eventHandler!=NULL) {
        eventHandler->enableCoordinateInput();
    }
}



/**
 * Disables coordinate input in the command line.
 */
void RS_GraphicView::disableCoordinateInput() {
    if (eventHandler!=NULL) {
        eventHandler->disableCoordinateInput();
    }
}



/**
 * zooms in by factor f
 */
void RS_GraphicView::zoomIn(double f, const RS_Vector& center) {

    if (f<1.0e-6) {
        RS_DEBUG->print(RS_Debug::D_WARNING,
                        "RS_GraphicView::zoomIn: invalid factor");
        return;
    }


    RS_Vector c = center;
    if (c.valid==false) {
        c = toGraph(RS_Vector(getWidth()/2, getHeight()/2));
    }

    zoomWindow(
                toGraph(RS_Vector(0,0))
                .scale(c, RS_Vector(1.0/f,1.0/f)),
                toGraph(RS_Vector(getWidth(),getHeight()))
                .scale(c, RS_Vector(1.0/f,1.0/f)));

    //adjustOffsetControls();
    //adjustZoomControls();
    // updateGrid();
    redraw();
}



/**
 * zooms in by factor f in x
 */
void RS_GraphicView::zoomInX(double f) {
    factor.x*=f;
    offsetX=(int)((offsetX-getWidth()/2)*f)+getWidth()/2;
    adjustOffsetControls();
    adjustZoomControls();
    // updateGrid();
    redraw();
}



/**
 * zooms in by factor f in y
 */
void RS_GraphicView::zoomInY(double f) {
    factor.y*=f;
    offsetY=(int)((offsetY-getHeight()/2)*f)+getHeight()/2;
    adjustOffsetControls();
    adjustZoomControls();
    //    updateGrid();
    redraw();
}



/**
 * zooms out by factor f
 */
void RS_GraphicView::zoomOut(double f, const RS_Vector& center) {
    if (f<1.0e-6) {
        RS_DEBUG->print(RS_Debug::D_WARNING,
                        "RS_GraphicView::zoomOut: invalid factor");
        return;
    }
    zoomIn(1/f, center);
}



/**
 * zooms out by factor f in x
 */
void RS_GraphicView::zoomOutX(double f) {
    if (f<1.0e-6) {
        RS_DEBUG->print(RS_Debug::D_WARNING,
                        "RS_GraphicView::zoomOutX: invalid factor");
        return;
    }
    factor.x/=f;
    offsetX=(int)(offsetX/f);
    adjustOffsetControls();
    adjustZoomControls();
    //    updateGrid();
    redraw();
}



/**
 * zooms out by factor f y
 */
void RS_GraphicView::zoomOutY(double f) {
    if (f<1.0e-6) {
        RS_DEBUG->print(RS_Debug::D_WARNING,
                        "RS_GraphicView::zoomOutY: invalid factor");
        return;
    }
    factor.y/=f;
    offsetY=(int)(offsetY/f);
    adjustOffsetControls();
    adjustZoomControls();
    //    updateGrid();
    redraw();
}

/**
 * performs autozoom
 *
 * @param axis include axis in zoom
 * @param keepAspectRatio true: keep aspect ratio 1:1
 *                        false: factors in x and y are stretched to the max
 */
void RS_GraphicView::zoomAuto(bool axis, bool keepAspectRatio) {

    RS_DEBUG->print("RS_GraphicView::zoomAuto");


    if (container!=NULL) {
        container->calculateBorders();

        double sx, sy;
        if (axis) {
            sx = std::max(container->getMax().x, 0.0)
                    - std::min(container->getMin().x, 0.0);
            sy = std::max(container->getMax().y, 0.0)
                    - std::min(container->getMin().y, 0.0);
        } else {
            sx = container->getSize().x;
            sy = container->getSize().y;
        }
        //    std::cout<<" RS_GraphicView::zoomAuto("<<axis<<","<<keepAspectRatio<<")"<<std::endl;

        double fx=1., fy=1.;
        unsigned short fFlags=0;

        if (sx>RS_TOLERANCE) {
            fx = (getWidth()-borderLeft-borderRight) / sx;
        } else {
            fFlags += 1; //invalid x factor
        }

        if (sy>RS_TOLERANCE) {
            fy = (getHeight()-borderTop-borderBottom) / sy;
        } else {
            fFlags += 2; //invalid y factor
        }
        //    std::cout<<"0: fx= "<<fx<<"\tfy="<<fy<<std::endl;

        RS_DEBUG->print("f: %f/%f", fx, fy);

        switch(fFlags){
        case 1:
            fx=fy;
            break;
        case 2:
            fy=fx;
            break;
        case 3:
            return; //do not do anything, invalid factors
        default:
            if (keepAspectRatio) {
                fx = fy = std::min(fx, fy);
            }
            //                break;
        }
        //    std::cout<<"1: fx= "<<fx<<"\tfy="<<fy<<std::endl;

        RS_DEBUG->print("f: %f/%f", fx, fy);
        //exclude invalid factors
        fFlags=0;
        if (fx<RS_TOLERANCE||fx>RS_MAXDOUBLE) {
            fx=1.0;
            fFlags += 1;
        }
        if (fy<RS_TOLERANCE||fy>RS_MAXDOUBLE) {
            fy=1.0;
            fFlags += 2;
        }
        if(fFlags == 3 ) return;
        saveView();
        //        std::cout<<"2: fx= "<<fx<<"\tfy="<<fy<<std::endl;
        setFactorX(fx);
        setFactorY(fy);

        RS_DEBUG->print("f: %f/%f", fx, fy);


//        RS_DEBUG->print("adjustZoomControls");
        adjustZoomControls();
//        RS_DEBUG->print("centerOffsetX");
        centerOffsetX();
//        RS_DEBUG->print("centerOffsetY");
        centerOffsetY();
//        RS_DEBUG->print("adjustOffsetControls");
        adjustOffsetControls();
//        RS_DEBUG->print("updateGrid");
        //    updateGrid();

        redraw();
    }
    RS_DEBUG->print("RS_GraphicView::zoomAuto OK");
}



/**
 * Shows previous view.
 */
void RS_GraphicView::zoomPrevious() {

    RS_DEBUG->print("RS_GraphicView::zoomPrevious");

    if (container!=NULL) {
        restoreView();
    }
}



/**
 * Saves the current view as previous view to which we can
 * switch back later with @see restoreView().
 */
void RS_GraphicView::saveView() {
    QDateTime noUpdateWindow=QDateTime::currentDateTime().addMSecs(-500);
    //do not update view within 500 milliseconds
    if(previousViewTime > noUpdateWindow) return;
    previousViewTime = QDateTime::currentDateTime();
    savedViews[savedViewIndex]=std::make_tuple(offsetX,offsetY,factor);
    savedViewIndex = (savedViewIndex+1)%savedViews.size();
    if(savedViewCount<savedViews.size()) savedViewCount++;

    if(savedViewCount==1){
        QC_ApplicationWindow::getAppWindow()->setPreviousZoomEnable(true);
    }
}



/**
 * Restores the view previously saved with
 * @see saveView().
 */
void RS_GraphicView::restoreView() {
    if(savedViewCount == 0) return;
    savedViewCount --;
    if(savedViewCount==0){
        QC_ApplicationWindow::getAppWindow()->setPreviousZoomEnable(false);
    }
    savedViewIndex = (savedViewIndex + savedViews.size() - 1)%savedViews.size();

    offsetX = std::get<0>(savedViews[savedViewIndex]);
    offsetY = std::get<1>(savedViews[savedViewIndex]);
    factor = std::get<2>(savedViews[savedViewIndex]);

    adjustOffsetControls();
    adjustZoomControls();
    //    updateGrid();

    redraw();
}


/*	*
 *	Function name:
 *	Description:		Performs autozoom in Y axis only.
 *	Author(s):			..., Claude Sylvain
 *	Created:				?
 *	Last modified:		23 July 2011
 *
 *	Parameters:			bool axis:
 *								Axis in zoom.
 *
 *	Returns:				void
 *	*/

void RS_GraphicView::zoomAutoY(bool axis) {
    if (container!=NULL) {
        double visibleHeight = 0.0;
        double minY = RS_MAXDOUBLE;
        double maxY = RS_MINDOUBLE;
        bool noChange = false;

        for (RS_Entity* e=container->firstEntity(RS2::ResolveNone);
             e!=NULL;
             e = container->nextEntity(RS2::ResolveNone)) {

            if (e->rtti()==RS2::EntityLine) {
                RS_Line* l = (RS_Line*)e;
                double x1, x2;
                x1 = toGuiX(l->getStartpoint().x);
                x2 = toGuiX(l->getEndpoint().x);

                if (	((x1 > 0.0) && (x1 < (double) getWidth())) ||
                        ((x2 > 0.0) && (x2 < (double) getWidth())))
                {
                    minY = std::min(minY, l->getStartpoint().y);
                    minY = std::min(minY, l->getEndpoint().y);
                    maxY = std::max(maxY, l->getStartpoint().y);
                    maxY = std::max(maxY, l->getEndpoint().y);
                }
            }
        }

        if (axis) {
            visibleHeight = std::max(maxY, 0.0) - std::min(minY, 0.0);
        } else {
            visibleHeight = maxY-minY;
        }

        if (visibleHeight<1.0) {
            noChange = true;
        }

        double fy = 1.0;
        if (visibleHeight>1.0e-6) {
            fy = (getHeight()-borderTop-borderBottom)
                    / visibleHeight;
            if (factor.y<0.000001) {
                noChange = true;
            }
        }

        if (noChange==false) {
            setFactorY(fy);
            //centerOffsetY();
            offsetY = (int)((getHeight()-borderTop-borderBottom
                             - (visibleHeight*factor.y))/2.0
                            - (minY*factor.y)) + borderBottom;
            adjustOffsetControls();
            adjustZoomControls();
            //    updateGrid();

        }
        RS_DEBUG->print("Auto zoom y ok");
    }
}



/**
 * Zooms the area given by v1 and v2.
 *
 * @param keepAspectRatio true: keeps the aspect ratio 1:1
 *                        false: zooms exactly the selected range to the
 *                               current graphic view
 */
void RS_GraphicView::zoomWindow(RS_Vector v1, RS_Vector v2,
                                bool keepAspectRatio) {



    double zoomX=480.0;    // Zoom for X-Axis
    double zoomY=640.0;    // Zoom for Y-Axis   (Set smaller one)
    int zoomBorder = 0;

    // Switch left/right and top/bottom is necessary:
    if(v1.x>v2.x) {
        std::swap(v1.x,v2.x);
    }
    if(v1.y>v2.y) {
        std::swap(v1.y,v2.y);
    }

    // Get zoom in X and zoom in Y:
    if(v2.x-v1.x>1.0e-6) {
        zoomX = getWidth() / (v2.x-v1.x);
    }
    if(v2.y-v1.y>1.0e-6) {
        zoomY = getHeight() / (v2.y-v1.y);
    }

    // Take smaller zoom:
    if (keepAspectRatio) {
        if(zoomX<zoomY) {
            if(getWidth()!=0) {
                zoomX = zoomY = ((double)(getWidth()-2*zoomBorder)) /
                        (double)getWidth()*zoomX;
            }
        } else {
            if(getHeight()!=0) {
                zoomX = zoomY = ((double)(getHeight()-2*zoomBorder)) /
                        (double)getHeight()*zoomY;
            }
        }
    }

    zoomX=fabs(zoomX);
    zoomY=fabs(zoomY);

    // Borders in pixel after zoom
    int pixLeft  =(int)(v1.x*zoomX);
    int pixTop   =(int)(v2.y*zoomY);
    int pixRight =(int)(v2.x*zoomX);
    int pixBottom=(int)(v1.y*zoomY);
    if(  pixLeft == INT_MIN || pixLeft== INT_MAX ||
         pixRight == INT_MIN || pixRight== INT_MAX ||
         pixTop == INT_MIN || pixTop== INT_MAX ||
         pixBottom == INT_MIN || pixBottom== INT_MAX ) {
        RS_DIALOGFACTORY->commandMessage("Requested zooming factor out of range. Zooming not changed");
        return;
    }
    saveView();

    // Set new offset for zero point:
    offsetX = - pixLeft + (getWidth() -pixRight +pixLeft)/2;
    offsetY = - pixTop + (getHeight() -pixBottom +pixTop)/2;
    factor.x = zoomX;
    factor.y = zoomY;

    adjustOffsetControls();
    adjustZoomControls();
    //    updateGrid();

    redraw();
}



/**
 * Centers the point v1.
 */
void RS_GraphicView::zoomPan(int dx, int dy) {
    //offsetX+=(int)toGuiDX(v1.x);
    //offsetY+=(int)toGuiDY(v1.y);

    offsetX += dx;
    offsetY -= dy;

    adjustOffsetControls();
    //adjustZoomControls();
    //    updateGrid();

    redraw();
}



/**
 * Scrolls in the given direction.
 */
void RS_GraphicView::zoomScroll(RS2::Direction direction) {
    switch (direction) {
    case RS2::Up:
        offsetY-=50;
        break;
    case RS2::Down:
        offsetY+=50;
        break;
    case RS2::Right:
        offsetX+=50;
        break;
    case RS2::Left:
        offsetX-=50;
        break;
    }
    adjustOffsetControls();
    adjustZoomControls();
    //    updateGrid();

    redraw();
}



/**
 * Zooms to page extends.
 */
void RS_GraphicView::zoomPage() {

    RS_DEBUG->print("RS_GraphicView::zoomPage");
    if (container==NULL) {
        return;
    }

    RS_Graphic* graphic = container->getGraphic();
    if (graphic==NULL) {
        return;
    }

    RS_Vector s = graphic->getPaperSize()/graphic->getPaperScale();

    double fx, fy;

    if (s.x>RS_TOLERANCE) {
        fx = (getWidth()-borderLeft-borderRight) / s.x;
    } else {
        fx = 1.0;
    }

    if (s.y>RS_TOLERANCE) {
        fy = (getHeight()-borderTop-borderBottom) / s.y;
    } else {
        fy = 1.0;
    }

    RS_DEBUG->print("f: %f/%f", fx, fy);

    fx = fy = std::min(fx, fy);

    RS_DEBUG->print("f: %f/%f", fx, fy);

    if (fx<RS_TOLERANCE) {
        fx=fy=1.0;
    }

    setFactorX(fx);
    setFactorY(fy);

    RS_DEBUG->print("f: %f/%f", fx, fy);

    centerOffsetX();
    centerOffsetY();
    adjustOffsetControls();
    adjustZoomControls();
    //    updateGrid();

    redraw();
}



/**
 * Draws the entities within the given range.
 */
void RS_GraphicView::drawWindow_DEPRECATED(RS_Vector v1, RS_Vector v2) {
    RS_DEBUG->print("RS_GraphicView::drawWindow() begin");
    if (container!=NULL) {
        for (RS_Entity* se=container->firstEntity(RS2::ResolveNone);
             se!=NULL;
             se = container->nextEntity(RS2::ResolveNone)) {
            if (se->isInWindow(v1, v2)) {
                drawEntity(NULL, se);
            }
        }
    }
    RS_DEBUG->print("RS_GraphicView::drawWindow() end");
}

/**
 * Draws the entities.
 * This function can only be called from within the paint event
 *
 */
void RS_GraphicView::drawLayer1(RS_Painter *painter) {

    // drawing paper border:
    if (isPrintPreview()) {
        drawPaper(painter);
    }

    // drawing meta grid:
    if (!isPrintPreview()) {

        //increase grid point size on for DPI>96
        int dpiX = qApp->desktop()->logicalDpiX();
//        DEBUG_HEADER();
//        RS_DEBUG->print(RS_Debug::D_ERROR, "dpiX=%d\n",dpiX);
        const RS_Pen penSaved=painter->getPen();
        if(dpiX>96) {
            RS_Pen pen=penSaved;
            pen.setWidth(RS2::Width01);
            painter->setPen(pen);
        }

        //only drawGrid updates the grid layout (updatePointArray())
        drawMetaGrid(painter);
        //draw grid after metaGrid to avoid overwriting grid points by metaGrid lines
        //bug# 3430258
        drawGrid(painter);

        if(dpiX>96) painter->setPen(penSaved);

    }

}


/*	*
 *	Function name:
 *	Description: 		Do the drawing, step 2/3.
 *	Author(s):			..., Claude Sylvain
 *	Created:				?
 *	Last modified:		23 July 2011
 *
 *	Parameters:			RS_Painter *painter:
 *								...
 *
 *	Returns:				void
 *	*/

void RS_GraphicView::drawLayer2(RS_Painter *painter)
{
    drawEntity(painter, container);	//	Draw all entities.

    //	If not in print preview, draw the absolute zero reference.
    //	----------------------------------------------------------
    if (!isPrintPreview())
        drawAbsoluteZero(painter);
}


void RS_GraphicView::drawLayer3(RS_Painter *painter) {
    // drawing zero points:
    if (!isPrintPreview()) {
        drawRelativeZero(painter);
        drawOverlay(painter);
    }
}


/*	*
 *	Function name:
 *
 *	Description:	- Sets the pen of the painter object to the suitable pen
 *						  for the given entity.
 *
 *	Author(s):		..., Claude Sylvain
 *	Created:			?
 *	Last modified:	17 November 2011
 *
 *	Parameters:		RS_Painter *painter:
 *							...
 *
 *						RS_Entity *e:
 *							...
 *
 *	Returns:			void
 */

void RS_GraphicView::setPenForEntity(RS_Painter *painter,RS_Entity *e)
{
    if (draftMode) {
        painter->setPen(RS_Pen(foreground,
                               RS2::Width00, RS2::SolidLine));
    }

    // Getting pen from entity (or layer)
    RS_Pen pen = e->getPen(true);

    int w = pen.getWidth();
    if (w<0) {
        w = 0;
    }

#if 1 /*TRUE*/
	// - Scale pen width.
	// - Notes: pen width is not scaled on print and print preview.
        //   This is the standard (AutoCAD like) behaviour.
    // bug# 3437941
    // ------------------------------------------------------------
    if (!draftMode)
    {
        double	uf = 1.0;	// Unit factor.
        double	wf = 1.0;	// Width factor.

        RS_Graphic* graphic = container->getGraphic();

        if (graphic != NULL)
        {
            uf = RS_Units::convert(1.0, RS2::Millimeter, graphic->getUnit());

            if (	(isPrinting() || isPrintPreview()) &&
                                        graphic->getPaperScale() > RS_TOLERANCE )
            {
                wf = 1.0 / graphic->getPaperScale();
            }
        }

        pen.setScreenWidth(toGuiDX(w / 100.0 * uf * wf));
    }
    else
    {
//		pen.setWidth(RS2::Width00);
        pen.setScreenWidth(0);
    }

#else

    // - Scale pen width.
    // - Notes: pen width is scaled on print and print preview.
    //   This is not the standard (AutoCAD like) behaviour.
    // --------------------------------------------------------
    if (!draftMode)
    {
        double	uf = 1.0;	//	Unit factor.

        RS_Graphic* graphic = container->getGraphic();

        if (graphic != NULL)
            uf = RS_Units::convert(1.0, RS2::Millimeter, graphic->getUnit());

        pen.setScreenWidth(toGuiDX(w / 100.0 * uf));
    }
    else
        pen.setScreenWidth(0);
#endif

    // prevent drawing with 1-width which is slow:
    if (RS_Math::round(pen.getScreenWidth())==1) {
        pen.setScreenWidth(0.0);
    }

    // prevent background color on background drawing:
    if (pen.getColor().stripFlags()==background.stripFlags()) {
        pen.setColor(foreground);
    }

    // this entity is selected:
    if (e->isSelected()) {
        pen.setLineType(RS2::DotLine);
        pen.setColor(selectedColor);
    }

    // this entity is highlighted:
    if (e->isHighlighted()) {
        pen.setColor(highlightedColor);
    }

    // deleting not drawing:
    if (getDeleteMode()) {
        pen.setColor(background);
    }

    painter->setPen(pen);
}


/**
 * Draws an entity. Might be recusively called e.g. for polylines.
 * If the class wide painter is NULL a new painter will be created
 * and destroyed afterwards.
 *
 * @param patternOffset Offset of line pattern (used for connected
 *        lines e.g. in splines).
 * @param db Double buffering on (recommended) / off
 */
void RS_GraphicView::drawEntity(RS_Entity* /*e*/, double& /*patternOffset*/) {
    RS_DEBUG->print("RS_GraphicView::drawEntity(RS_Entity*,patternOffset) not supported anymore");
    // RVT_PORT this needs to be optimized
    // ONe way to do is to send a RS2::RedrawSelected, then teh draw routine will onyl draw all selected entities
    // Dis-advantage is that we still need to iterate over all entities, but
    // this might be very fast
    // For now we just redraw the drawing untill we are going to optmize drawing
    redraw(RS2::RedrawDrawing);
}
void RS_GraphicView::drawEntity(RS_Entity* /*e*/ /*patternOffset*/) {
    RS_DEBUG->print("RS_GraphicView::drawEntity(RS_Entity*,patternOffset) not supported anymore");
    // RVT_PORT this needs to be optimized
    // ONe way to do is to send a RS2::RedrawSelected, then teh draw routine will onyl draw all selected entities
    // Dis-advantage is that we still need to iterate over all entities, but
    // this might be very fast
    // For now we just redraw the drawing untill we are going to optmize drawing
    redraw(RS2::RedrawDrawing);
}
void RS_GraphicView::drawEntity(RS_Painter *painter, RS_Entity* e) {
    double offset(0.);
    drawEntity(painter,e,offset);
}
void RS_GraphicView::drawEntity(RS_Painter *painter, RS_Entity* e, double& patternOffset) {

    // update is diabled:
    // given entity is NULL:
    if (e==NULL) {
        return;
    }

    // entity is not visible:
    if (!e->isVisible()) {
        return;
    }
    if( isPrintPreview() || isPrinting() ) {
        //do not draw construction layer on print preview or print
            if(e->isConstructionLayer()) return;
    }

    // test if the entity is in the viewport
    /* temporary disabled so rs_overlaylien can be drawn
    if (!e->isContainer() && !isPrinting() &&
            (painter==NULL || !painter->isPreviewMode()) &&
            (toGuiX(e->getMax().x)<0 || toGuiX(e->getMin().x)>getWidth() ||
             toGuiY(e->getMin().y)<0 || toGuiY(e->getMax().y)>getHeight())) {
        return;
    } */

    // set pen (color):
    setPenForEntity(painter, e );

    //RS_DEBUG->print("draw plain");
    if (isDraftMode()) {
        // large mtexts as rectangles:
        if (e->rtti()==RS2::EntityMText) {
            if (toGuiDX(((RS_MText*)e)->getHeight())<4 || e->countDeep()>100) {
                painter->drawRect(toGui(e->getMin()), toGui(e->getMax()));
            } else {
                drawEntityPlain(painter, e, patternOffset);
            }
        }
        // large texts as rectangles:
        else if (e->rtti()==RS2::EntityText) {
            if (toGuiDX(((RS_Text*)e)->getHeight())<4 || e->countDeep()>100) {
                painter->drawRect(toGui(e->getMin()), toGui(e->getMax()));
            } else {
                drawEntityPlain(painter, e, patternOffset);
            }
        }

        // all images as rectangles:
        else if (e->rtti()==RS2::EntityImage) {
            painter->drawRect(toGui(e->getMin()), toGui(e->getMax()));
        }

        // hide hatches:
        else if (e->rtti()==RS2::EntityHatch) {
            // nothing
        }

        else {
            drawEntityPlain(painter, e, patternOffset);
        }
    } else {
        drawEntityPlain(painter, e, patternOffset);
    }

    // draw reference points:
    if (e->isSelected()) {
        if (!e->isParentSelected()) {
            RS_VectorSolutions s = e->getRefPoints();

            for (int i=0; i<s.getNumber(); ++i) {
                int sz = -1;
				RS_Color col = handleColor;
				if (i == 0) {
					col = startHandleColor;
				}
				else if (i == s.getNumber() - 1) {
					col = endHandleColor;
				}
                if (getDeleteMode()) {
                    painter->drawHandle(toGui(s.get(i)), background, sz);
                } else {
                    painter->drawHandle(toGui(s.get(i)), col, sz);
                }
            }
        }
    }

    //RS_DEBUG->print("draw plain OK");


    //RS_DEBUG->print("RS_GraphicView::drawEntity() end");
}


/**
 * Draws an entity.
 * The painter must be initialized and all the attributes (pen) must be set.
 */
void RS_GraphicView::drawEntityPlain(RS_Painter *painter, RS_Entity* e, double& patternOffset) {
    if (e==NULL) {
        return;
    }

    if (!e->isContainer() && (e->isSelected()!=painter->shouldDrawSelected())) {
        return;
    }

    e->draw(painter, this, patternOffset);

}
void RS_GraphicView::drawEntityPlain(RS_Painter *painter, RS_Entity* e) {
    if (e==NULL) {
        return;
    }

    if (!e->isContainer() && (e->isSelected()!=painter->shouldDrawSelected())) {
        return;
    }
    double patternOffset(0.);
    e->draw(painter, this, patternOffset);

}
/**
 * Deletes an entity with the background color.
 * Might be recusively called e.g. for polylines.
 */
void RS_GraphicView::deleteEntity(RS_Entity* e) {

    // RVT_PORT When we delete a single entoty, we can do this but we need to remove this then also from containerEntities
    RS_DEBUG->print("RS_GraphicView::deleteEntity will for now redraw the whole screen instead of just deleting the entity");
    setDeleteMode(true);
    drawEntity(e);
    setDeleteMode(false);
    redraw(RS2::RedrawDrawing);
}




/**
 * @return Pointer to the static pattern struct that belongs to the
 * given pattern type or NULL.
 */
RS_LineTypePattern* RS_GraphicView::getPattern(RS2::LineType t) {
    switch (t) {
    case RS2::SolidLine:
        return &patternSolidLine;
        break;

    case RS2::DotLine:
        return &patternDotLine;
        break;
    case RS2::DotLine2:
        return &patternDotLine2;
        break;
    case RS2::DotLineX2:
        return &patternDotLineX2;
        break;

    case RS2::DashLine:
        return &patternDashLine;
        break;
    case RS2::DashLine2:
        return &patternDashLine2;
        break;
    case RS2::DashLineX2:
        return &patternDashLineX2;
        break;

    case RS2::DashDotLine:
        return &patternDashDotLine;
        break;
    case RS2::DashDotLine2:
        return &patternDashDotLine2;
        break;
    case RS2::DashDotLineX2:
        return &patternDashDotLineX2;
        break;

    case RS2::DivideLine:
        return &patternDivideLine;
        break;
    case RS2::DivideLine2:
        return &patternDivideLine2;
        break;
    case RS2::DivideLineX2:
        return &patternDivideLineX2;
        break;

    case RS2::CenterLine:
        return &patternCenterLine;
        break;
    case RS2::CenterLine2:
        return &patternCenterLine2;
        break;
    case RS2::CenterLineX2:
        return &patternCenterLineX2;
        break;

    case RS2::BorderLine:
        return &patternBorderLine;
        break;
    case RS2::BorderLine2:
        return &patternBorderLine2;
        break;
    case RS2::BorderLineX2:
        return &patternBorderLineX2;
        break;

    case RS2::LineByLayer:
        return &patternBlockLine;
        break;
    case RS2::LineByBlock:
        return &patternBlockLine;
        break;
    default:
        break;
    }
    return NULL;
}



/**
 * This virtual method can be overwritten to draw the absolute
 * zero. It's called from within drawIt(). The default implemetation
 * draws a simple red cross on the zero of thge sheet
 * THis function can ONLY be called from within a paintEvent because it will
 * use the painter
 *
 * @see drawIt()
 */
void RS_GraphicView::drawAbsoluteZero(RS_Painter *painter) {

    int zr = 20;

    RS_Pen p(QColor(255,0,0), RS2::Width00, RS2::SolidLine);
    p.setScreenWidth(0);
    painter->setPen(p);
    //painter->setBrush(Qt::NoBrush);

    painter->drawLine(RS_Vector(toGuiX(0.0)-zr,
                                toGuiY(0.0)),
                      RS_Vector(toGuiX(0.0)+zr,
                                toGuiY(0.0)));

    painter->drawLine(RS_Vector(toGuiX(0.0),
                                toGuiY(0.0)-zr),
                      RS_Vector(toGuiX(0.0),
                                toGuiY(0.0)+zr));

}



/**
 * This virtual method can be overwritten to draw the relative
 * zero point. It's called from within drawIt(). The default implemetation
 * draws a simple red round zero point. This is the point that was last created by the user, end of a line for example
 * THis function can ONLY be called from within a paintEvent because it will
 * use the painter
 *
 * @see drawIt()
 */
void RS_GraphicView::drawRelativeZero(RS_Painter *painter) {

    if (relativeZero.valid==false) {
        return;
    }

    RS_Pen p(RS_Color(255, 0, 0), RS2::Width00, RS2::SolidLine);
    p.setScreenWidth(0);
    painter->setPen(p);

    int zr=5;

    painter->drawLine(RS_Vector(toGuiX(relativeZero.x)-zr,
                                toGuiY(relativeZero.y)),
                      RS_Vector(toGuiX(relativeZero.x)+zr,
                                toGuiY(relativeZero.y)));

    painter->drawLine(RS_Vector(toGuiX(relativeZero.x),
                                toGuiY(relativeZero.y)-zr),
                      RS_Vector(toGuiX(relativeZero.x),
                                toGuiY(relativeZero.y)+zr));

    painter->drawCircle(toGui(relativeZero), 5);
}



/**
 * Draws the paper border (for print previews).
 * This function can ONLY be called from within a paintEvent because it will
 * use the painter
 *
 * @see drawIt()
 */
void RS_GraphicView::drawPaper(RS_Painter *painter) {

    if (container==NULL) {
        return;
    }

    RS_Graphic* graphic = container->getGraphic();
    if (graphic->getPaperScale()<1.0e-6) {
        return;
    }

    // draw paper:
    // RVT_PORT rewritten from     painter->setPen(Qt::gray);
    painter->setPen(QColor(Qt::gray));

    RS_Vector pinsbase = graphic->getPaperInsertionBase();
    RS_Vector size = graphic->getPaperSize();
    double scale = graphic->getPaperScale();

    RS_Vector v1 = toGui((RS_Vector(0,0)-pinsbase)/scale);
    RS_Vector v2 = toGui((size-pinsbase)/scale);

    // gray background:
    painter->fillRect(0,0, getWidth(), getHeight(),
                      RS_Color(200,200,200));

    // shadow
    painter->fillRect(
                (int)(v1.x)+6, (int)(v1.y)+6,
                (int)((v2.x-v1.x)), (int)((v2.y-v1.y)),
                RS_Color(64,64,64));

    // border:
    painter->fillRect(
                (int)(v1.x), (int)(v1.y),
                (int)((v2.x-v1.x)), (int)((v2.y-v1.y)),
                RS_Color(64,64,64));

    // paper
    painter->fillRect(
                (int)(v1.x)+1, (int)(v1.y)-1,
                (int)((v2.x-v1.x))-2, (int)((v2.y-v1.y))+2,
                RS_Color(255,255,255));

}


/**
 * Draws the grid.
 *
 * @see drawIt()
 */
void RS_GraphicView::drawGrid(RS_Painter *painter) {

    if (grid==NULL || isGridOn()==false) {
        return;
    }


    // draw grid:
    //painter->setPen(Qt::gray);
    painter->setPen(gridColor);

//    grid->updatePointArray();
    RS_Vector* pts = grid->getPoints();
    if (pts!=NULL) {
        for (int i=0; i<grid->count(); ++i) {
            painter->drawGridPoint(toGui(pts[i]));
        }
    }

    // draw grid info:
    //painter->setPen(Qt::white);
    QString info = grid->getInfo();
    //info = QString("%1 / %2")
    //       .arg(grid->getSpacing())
    //       .arg(grid->getMetaSpacing());

    updateGridStatusWidget(info);


}



/**
 * Draws the meta grid.
 *
 * @see drawIt()
 */
void RS_GraphicView::drawMetaGrid(RS_Painter *painter) {

    if (grid==NULL || isGridOn()==false /*|| grid->getMetaSpacing()<0.0*/) {
        return;
    }

    //draw grid after metaGrid to avoid overwriting grid points by metaGrid lines
    //bug# 3430258
    grid->updatePointArray();
    RS_Pen pen(metaGridColor,
               RS2::Width00,
               RS2::DotLine);
    painter->setPen(pen);

        RS_Vector dv=grid->getMetaGridWidth().scale(factor);
        double dx=fabs(dv.x);
        double dy=fabs(dv.y); //potential bug, need to recover metaGrid.width
    // draw meta grid:
        double* mx = grid->getMetaX();
        if (mx!=NULL) {
            for (int i=0; i<grid->countMetaX(); ++i) {
                painter->drawLine(RS_Vector(toGuiX(mx[i]), 0),
                                  RS_Vector(toGuiX(mx[i]), getHeight()));
                if(grid->isIsometric()){
                    painter->drawLine(RS_Vector(toGuiX(mx[i])+0.5*dx, 0),
                                      RS_Vector(toGuiX(mx[i])+0.5*dx, getHeight()));
                }
            }
        }
    double* my = grid->getMetaY();
    if(grid->isIsometric()){//isometric metaGrid
        dx=fabs(dx);
        dy=fabs(dy);
        if(my==NULL || dx<1||dy<1) return;
        RS_Vector baseMeta(toGui(RS_Vector(mx[0],my[0])));
        // x-x0=k*dx, x-remainder(x-x0,dx)
        RS_Vector vp0(-remainder(-baseMeta.x,dx)-dx,getHeight()-remainder(getHeight()-baseMeta.y,dy)+dy);
        RS_Vector vp1(vp0);
        RS_Vector vp2(getWidth()-remainder(getWidth()-baseMeta.x,dx)+dx,vp0.y);
        RS_Vector vp3(vp2);
        int cmx = round((vp2.x - vp0.x)/dx);
        int cmy = round((vp0.y +remainder(-baseMeta.y,dy)+dy)/dy);
        for(int i=cmx+cmy+2;i>=0;i--){
            if ( i <= cmx ) {
                vp0.x += dx;
                vp2.y -= dy;
            }else{
                vp0.y -= dy;
                vp2.x -= dx;
            }
            if ( i <= cmy ) {
                vp1.y -= dy;
                vp3.x -= dx;
            }else{
                vp1.x += dx;
                vp3.y -= dy;
            }
            painter->drawLine(vp0,vp1);
            painter->drawLine(vp2,vp3);
        }

    }else{//orthogonal

        if (my!=NULL) {
            for (int i=0; i<grid->countMetaY(); ++i) {
                painter->drawLine(RS_Vector(0, toGuiY(my[i])),
                                  RS_Vector(getWidth(), toGuiY(my[i])));
            }
        }
    }


}

void RS_GraphicView::drawOverlay(RS_Painter *painter) {
    QList<int> keys=overlayEntities.keys();
    for (int i = 0; i < keys.size(); ++i) {
        if (overlayEntities[i] != NULL) {
            setPenForEntity(painter, overlayEntities[i] );
            drawEntityPlain(painter, overlayEntities[i]);
        }
    }
}

/**
 * Sets the default snap mode used by newly created actions.
 */
void RS_GraphicView::setDefaultSnapMode(RS_SnapMode sm) {
    defaultSnapMode = sm;
    if (eventHandler!=NULL) {
        eventHandler->setSnapMode(sm);
    }
}



/**
 * Sets a snap restriction (e.g. orthogonal).
 */
void RS_GraphicView::setSnapRestriction(RS2::SnapRestriction sr) {
    defaultSnapRes = sr;

    if (eventHandler!=NULL) {
        eventHandler->setSnapRestriction(sr);
    }
}



/**
 * Translates a vector in real coordinates to a vector in screen coordinates.
 */
RS_Vector RS_GraphicView::toGui(RS_Vector v) {
    return RS_Vector(toGuiX(v.x), toGuiY(v.y));
}



/**
 * Translates a real coordinate in X to a screen coordinate X.
 * @param visible Pointer to a boolean which will contain true
 * after the call if the coordinate is within the visible range.
 */
double RS_GraphicView::toGuiX(double x) {
    return x*factor.x + offsetX;
}



/**
 * Translates a real coordinate in Y to a screen coordinate Y.
 */
double RS_GraphicView::toGuiY(double y) {
    return -y*factor.y + getHeight() - offsetY;
}



/**
 * Translates a real coordinate distance to a screen coordinate distance.
 */
double RS_GraphicView::toGuiDX(double d) {
    return d*factor.x;
}



/**
 * Translates a real coordinate distance to a screen coordinate distance.
 */
double RS_GraphicView::toGuiDY(double d) {
    return d*factor.y;
}



/**
 * Translates a vector in screen coordinates to a vector in real coordinates.
 */
RS_Vector RS_GraphicView::toGraph(RS_Vector v) {
    return RS_Vector(toGraphX(RS_Math::round(v.x)),
                     toGraphY(RS_Math::round(v.y)));
}



/**
 * Translates two screen coordinates to a vector in real coordinates.
 */
RS_Vector RS_GraphicView::toGraph(int x, int y) {
    return RS_Vector(toGraphX(x), toGraphY(y));
}


/**
 * Translates a screen coordinate in X to a real coordinate X.
 */
double RS_GraphicView::toGraphX(int x) {
    return (x - offsetX)/factor.x;
}



/**
 * Translates a screen coordinate in Y to a real coordinate Y.
 */
double RS_GraphicView::toGraphY(int y) {
    return -(y - getHeight() + offsetY)/factor.y;
}



/**
 * Translates a screen coordinate distance to a real coordinate distance.
 */
double RS_GraphicView::toGraphDX(int d) {
    return d/factor.x;
}



/**
 * Translates a screen coordinate distance to a real coordinate distance.
 */
double RS_GraphicView::toGraphDY(int d) {
    return d/factor.y;
}


/**
 * Sets the relative zero coordinate (if not locked)
 * without deleting / drawing the point.
 */
void RS_GraphicView::setRelativeZero(const RS_Vector& pos) {
    if (relativeZeroLocked==false) {
        relativeZero = pos;
    }
}



/**
 * Sets the relative zero coordinate, deletes the old position
 * on the screen and draws the new one.
 */
void RS_GraphicView::moveRelativeZero(const RS_Vector& pos) {
    setRelativeZero(pos);
    redraw(RS2::RedrawGrid);
}


/**
 * Remove all overlay entities
 */
RS_EntityContainer* RS_GraphicView::getOverlayContainer(RS2::OverlayGraphics position)
{
    if (overlayEntities[position]!=NULL) {
        return overlayEntities[position];
    }
    overlayEntities[position]=new RS_EntityContainer(NULL);

    return overlayEntities[position];

}


