/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 A. Stebich (librecad@mail.lordofbikes.de)
** 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<climits>
#include<cmath>

#include <QApplication>
#include <QDesktopWidget>
#include <QAction>
#include <QMouseEvent>
#include <QtAlgorithms>

#include "rs_graphicview.h"

#include "rs_line.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"
#include "rs_math.h"
#include "rs_debug.h"
#include "rs_color.h"

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

/**
 * Constructor.
 */
RS_GraphicView::RS_GraphicView(QWidget* parent, Qt::WindowFlags f)
    :QWidget(parent, f)
	,eventHandler{new RS_EventHandler{this}}
	,gridColor(Qt::gray)
	,metaGridColor{64, 64, 64}
	,grid{new RS_Grid{this}}
	,drawingMode(RS2::ModeFull)
	,savedViews(16)
    ,previousViewTime(QDateTime::currentDateTime())
    ,panning(false)
{
    RS_SETTINGS->beginGroup("Colors");
    setBackground(QColor(RS_SETTINGS->readEntry("/background", Colors::background)));
    setGridColor(QColor(RS_SETTINGS->readEntry("/grid", Colors::grid)));
    setMetaGridColor(QColor(RS_SETTINGS->readEntry("/meta_grid", Colors::meta_grid)));
    setSelectedColor(QColor(RS_SETTINGS->readEntry("/select", Colors::select)));
    setHighlightedColor(QColor(RS_SETTINGS->readEntry("/highlight", Colors::highlight)));
    setStartHandleColor(QColor(RS_SETTINGS->readEntry("/start_handle", Colors::start_handle)));
    setHandleColor(QColor(RS_SETTINGS->readEntry("/handle", Colors::handle)));
    setEndHandleColor(QColor(RS_SETTINGS->readEntry("/end_handle", Colors::end_handle)));
    RS_SETTINGS->endGroup();
}

RS_GraphicView::~RS_GraphicView()
{
    qDeleteAll(overlayEntities);
}

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

/**
 * 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() const{
	if (container) {
		RS_Graphic* g = container->getGraphic();
		if (g) {
			return g->isGridOn();
		}
	}
	return true;
}

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


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

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

/**
 * Centers the drawing in x-direction.
 */
void RS_GraphicView::centerOffsetX() {
	if (container && !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 && !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 nullptr.
 */
RS_ActionInterface* RS_GraphicView::getDefaultAction() {
	if (eventHandler) {
		return eventHandler->getDefaultAction();
	} else {
        return nullptr;
	}
}



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



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



/**
 * Sets the current action of the event handler.
 */
void RS_GraphicView::setCurrentAction(RS_ActionInterface* action) {
    RS_DEBUG->print("RS_GraphicView::setCurrentAction");
	if (eventHandler) {
		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) {
		eventHandler->killSelectActions();
	}
}



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



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

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

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

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



/**
 * Disables coordinate input in the command line.
 */
void RS_GraphicView::disableCoordinateInput() {
	if (eventHandler) {
		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) {
        //find mouse position
        c= getMousePosition();
    }

	zoomWindow(
				toGraph(0, 0)
				.scale(c, RS_Vector(1.0/f,1.0/f)),
				toGraph(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
 */
#include <iostream>
void RS_GraphicView::zoomAuto(bool axis, bool keepAspectRatio) {

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


	if (container) {
        container->calculateBorders();

		double sx, sy;
		if (axis) {
			auto const dV = container->getMax() - container->getMin();
			sx = std::max(dV.x, 0.);
			sy = std::max(dV.y, 0.);
		} else {
			sx = container->getSize().x;
			sy = container->getSize().y;
		}
//		    std::cout<<" RS_GraphicView::zoomAuto("<<sx<<","<<sy<<")"<<std::endl;
//			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) {
		restoreView();
	}
}



/**
 * Saves the current view as previous view to which we can
 * switch back later with @see restoreView().
 */
void RS_GraphicView::saveView() {
	if(getGraphic()) getGraphic()->setModified(true);
	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){
		emit previous_zoom_state(true);
	}
}



/**
 * Restores the view previously saved with
 * @see saveView().
 */
void RS_GraphicView::restoreView() {
	if(savedViewCount == 0) return;
	savedViewCount --;
	if(savedViewCount==0){
		emit previous_zoom_state(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) {
		double visibleHeight = 0.0;
		double minY = RS_MAXDOUBLE;
		double maxY = RS_MINDOUBLE;
		bool noChange = false;

		for(auto e: *container){

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

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

	RS_Vector s = graphic->getPrintAreaSize()/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) {
		for(auto se: *container){
			if (se->isInWindow(v1, v2)) {
                drawEntity(nullptr, 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;
	}

	// - Scale pen width.
	// - By default 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)
		{
			uf = RS_Units::convert(1.0, RS2::Millimeter, graphic->getUnit());

			if ((isPrinting() || isPrintPreview()) &&
					graphic->getPaperScale() > RS_TOLERANCE )
			{
				if (scaleLineWidth)
				{
					wf = graphic->getVariableDouble("$DIMSCALE", 1.0);
				}
				else
				{
					wf = 1.0 / graphic->getPaperScale();
				}

			}
		}

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

	// 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
    // and enhance visibility of black lines on dark backgrounds
    RS_Color    penColor {pen.getColor().stripFlags()};
    if ( penColor == background.stripFlags()
         || (penColor.toIntColor() == RS_Color::Black
             && penColor.colorDistance( background) < RS_Color::MinColorDistance)) {
        pen.setColor( foreground);
    }

	if (!isPrinting() && !isPrintPreview())
	{
		// 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 recursively called e.g. for polylines.
 * If the class wide painter is nullptr 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 the draw routine will only 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 until we are going to optimize 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 the draw routine will only 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 until we are going to optimize 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 disabled:
    // given entity is nullptr:
	if (!e) {
		return;
	}

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

    // test if the entity is in the viewport
    if (!isPrinting() &&
        e->rtti() != RS2::EntityGraphic &&
        e->rtti() != RS2::EntityLine &&
       (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()) {
        switch(e->rtti()){
        case RS2::EntityMText:
        case RS2::EntityText:
            painter->drawRect(toGui(e->getMin()), toGui(e->getMax()));
            break;
        case RS2::EntityImage:
            // all images as rectangles:
            painter->drawRect(toGui(e->getMin()), toGui(e->getMax()));
            break;
        case RS2::EntityHatch:
            //skip hatches
            break;
        default:
            drawEntityPlain(painter, e, patternOffset);
        }
	} else {
		drawEntityPlain(painter, e, patternOffset);
	}

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

			for (size_t 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) {
		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) {
		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 recursively called e.g. for polylines.
 */
void RS_GraphicView::deleteEntity(RS_Entity* e) {

	// RVT_PORT When we delete a single entity, 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 nullptr.
 */
const RS_LineTypePattern* RS_GraphicView::getPattern(RS2::LineType t) {
	switch (t) {
	case RS2::SolidLine:
		return &RS_LineTypePattern::patternSolidLine;
		break;

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

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

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

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

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

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

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



/**
 * This virtual method can be overwritten to draw the absolute
 * zero. It's called from within drawIt(). The default implementation
 * draws a simple red cross on the zero of the 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 const zr = 20;

    RS_Pen p(RS_Color(255,0,0), RS2::Width00, RS2::SolidLine);
	p.setScreenWidth(0);
	painter->setPen(p);
	//painter->setBrush(Qt::NoBrush);
    auto vp=toGui(RS_Vector(0,0));
    if( vp.x +zr<0 || vp.x-zr>getWidth()) return;
    if( vp.y +zr<0 || vp.y-zr>getHeight()) return;

    painter->drawLine(RS_Vector(vp.x-zr, vp.y),
                      RS_Vector(vp.x+zr, vp.y)
                      );
    painter->drawLine(RS_Vector(vp.x, vp.y-zr),
                      RS_Vector(vp.x, vp.y+zr)
                      );
}

/**
 * This virtual method can be overwritten to draw the relative
 * zero point. It's called from within drawIt(). The default implementation
 * 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) {
		return;
	}

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

    int const zr=5;
    auto vp=toGui(relativeZero);
    if( vp.x +zr<0 || vp.x-zr>getWidth()) return;
    if( vp.y +zr<0 || vp.y-zr>getHeight()) return;

    painter->drawLine(RS_Vector(vp.x-zr, vp.y),
                      RS_Vector(vp.x+zr, vp.y)
                      );
    painter->drawLine(RS_Vector(vp.x, vp.y-zr),
                      RS_Vector(vp.x, vp.y+zr)
                      );

    painter->drawCircle(vp, 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) {
		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 printAreaSize = graphic->getPrintAreaSize();
	double scale = graphic->getPaperScale();

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

	int marginLeft = (int)(graphic->getMarginLeftInUnits() * factor.x / scale);
	int marginTop = (int)(graphic->getMarginTopInUnits() * factor.y / scale);
	int marginRight = (int)(graphic->getMarginRightInUnits() * factor.x / scale);
	int marginBottom = (int)(graphic->getMarginBottomInUnits() * factor.y / scale);

	int printAreaW = (int)(v2.x-v1.x);
	int printAreaH = (int)(v2.y-v1.y);

	int paperX1 = (int)v1.x;
	int paperY1 = (int)v1.y;
	// Don't show margins between neighbor pages.
	int paperW = printAreaW + marginLeft + marginRight;
	int paperH = printAreaH - marginTop - marginBottom;

	int numX = graphic->getPagesNumHoriz();
	int numY = graphic->getPagesNumVert();

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

	// shadow:
	painter->fillRect(paperX1+6, paperY1+6, paperW, paperH,
					  RS_Color(64,64,64));

	// border:
	painter->fillRect(paperX1, paperY1, paperW, paperH,
					  RS_Color(64,64,64));

	// paper:
	painter->fillRect(paperX1+1, paperY1-1, paperW-2, paperH+2,
					  RS_Color(180,180,180));

	// print area:
	painter->fillRect(paperX1+1+marginLeft, paperY1-1-marginBottom,
					  printAreaW-2, printAreaH+2,
					  RS_Color(255,255,255));

	// don't paint boundaries if zoom is to small
	if (qMin(fabs(printAreaW/numX), fabs(printAreaH/numY)) > 2) {
		// boundaries between pages:
		for (int pX = 1; pX < numX; pX++) {
			double offset = ((double)printAreaW*pX)/numX;
			painter->fillRect(paperX1+marginLeft+offset, paperY1,
							  1, paperH,
							  RS_Color(64,64,64));
		}
		for (int pY = 1; pY < numY; pY++) {
			double offset = ((double)printAreaH*pY)/numY;
			painter->fillRect(paperX1, paperY1-marginBottom+offset,
							  paperW, 1,
							  RS_Color(64,64,64));
		}
	}
}


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

	if (!(grid && isGridOn())) return;


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

	//grid->updatePointArray();
	auto const& pts = grid->getPoints();
	for(auto const& v: pts){
		painter->drawGridPoint(toGui(v));
	}

	// 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 && isGridOn()) /*|| 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:
	auto mx = grid->getMetaX();
	for(auto const& x: mx){
		painter->drawLine(RS_Vector(toGuiX(x), 0),
						  RS_Vector(toGuiX(x), getHeight()));
		if(grid->isIsometric()){
			painter->drawLine(RS_Vector(toGuiX(x)+0.5*dx, 0),
							  RS_Vector(toGuiX(x)+0.5*dx, getHeight()));
		}
	}
	auto my = grid->getMetaY();
	if(grid->isIsometric()){//isometric metaGrid
		dx=fabs(dx);
		dy=fabs(dy);
		if(!my.size()|| 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

		for(auto const& y: my){
			painter->drawLine(RS_Vector(0, toGuiY(y)),
							  RS_Vector(getWidth(), toGuiY(y)));
		}
	}


}

void RS_GraphicView::drawOverlay(RS_Painter *painter)
{
    double patternOffset(0.);

    foreach (auto ec, overlayEntities)
    {
        foreach (auto e, ec->getEntityList())
        {
            setPenForEntity(painter, e);
            e->draw(painter, this, patternOffset);
        }
    }
}

RS2::SnapRestriction RS_GraphicView::getSnapRestriction() const
{
	return defaultSnapRes;
}

RS_SnapMode RS_GraphicView::getDefaultSnapMode() const
{
	return defaultSnapMode;
}

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

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

	if (eventHandler) {
		eventHandler->setSnapRestriction(sr);
	}
}

/**
 * Translates a vector in real coordinates to a vector in screen coordinates.
 */
RS_Vector RS_GraphicView::toGui(RS_Vector v) const{
	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) const{
	return x*factor.x + offsetX;
}



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



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



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



/**
 * Translates a vector in screen coordinates to a vector in real coordinates.
 */
RS_Vector RS_GraphicView::toGraph(RS_Vector v) const{
	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) const{
	return RS_Vector(toGraphX(x), toGraphY(y));
}


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



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



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



/**
 * Translates a screen coordinate distance to a real coordinate distance.
 */
double RS_GraphicView::toGraphDY(int d) const{
	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;
        emit relative_zero_changed(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);
}


/**
 * Gets the specified overlay container.
 */
RS_EntityContainer* RS_GraphicView::getOverlayContainer(RS2::OverlayGraphics position)
{
	if (overlayEntities[position]) {
		return overlayEntities[position];
	}
    overlayEntities[position]=new RS_EntityContainer(nullptr);

	return overlayEntities[position];

}

RS_Grid* RS_GraphicView::getGrid() const{
	return grid.get();
}

RS_EventHandler* RS_GraphicView::getEventHandler() const{
    return eventHandler;
}

void RS_GraphicView::setBackground(const RS_Color& bg) {
	background = bg;

    RS_Color black(0,0,0);
    if (black.colorDistance( bg) >= RS_Color::MinColorDistance) {
        foreground = black;
    }
    else {
        foreground = RS_Color(255,255,255);
    }
}

void RS_GraphicView::setBorders(int left, int top, int right, int bottom) {
	borderLeft = left;
	borderTop = top;
	borderRight = right;
	borderBottom = bottom;
}

RS_Graphic* RS_GraphicView::getGraphic() const{
	if (container && container->rtti()==RS2::EntityGraphic) {
		return static_cast<RS_Graphic *>(container);
	} else {
		return nullptr;
	}
}

RS_EntityContainer* RS_GraphicView::getContainer() const{
	return container;
}

void RS_GraphicView::setFactor(double f) {
	setFactorX(f);
	setFactorY(f);
}

RS_Vector RS_GraphicView::getFactor() const{
	return factor;
}
int RS_GraphicView::getBorderLeft() const{
	return borderLeft;
}
int RS_GraphicView::getBorderTop() const{
	return borderTop;
}
int RS_GraphicView::getBorderRight() const{
	return borderRight;
}
int RS_GraphicView::getBorderBottom() const{
	return borderBottom;
}

void RS_GraphicView::freezeZoom(bool freeze) {
	zoomFrozen=freeze;
}
bool RS_GraphicView::isZoomFrozen() const{
	return zoomFrozen;
}
void RS_GraphicView::setOffsetX(int ox) {
	offsetX = ox;
}
void RS_GraphicView::setOffsetY(int oy) {
	offsetY = oy;
}
int RS_GraphicView::getOffsetX() const{
	return offsetX;
}
int RS_GraphicView::getOffsetY() const{
	return offsetY;
}
void RS_GraphicView::lockRelativeZero(bool lock) {
	relativeZeroLocked=lock;
}

bool RS_GraphicView::isRelativeZeroLocked() const{
	return relativeZeroLocked;
}

RS_Vector const& RS_GraphicView::getRelativeZero() const{
	return relativeZero;
}

void RS_GraphicView::setPrintPreview(bool pv) {
	printPreview = pv;
}

bool RS_GraphicView::isPrintPreview() const{
	return printPreview;
}

void RS_GraphicView::setPrinting(bool p) {
	printing = p;
}

bool RS_GraphicView::isPrinting() const{
	return printing;
}

bool RS_GraphicView::isDraftMode() const{
	return draftMode;
}

void RS_GraphicView::setDraftMode(bool dm) {
	draftMode=dm;
}

bool RS_GraphicView::isCleanUp(void) const
{
	return m_bIsCleanUp;
}

bool RS_GraphicView::isPanning() const {
    return panning;
}

void RS_GraphicView::setPanning(bool state) {
    panning = state;
}
