/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2010-2011 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 "qg_graphicview.h"

#include <QGridLayout>
#include <QLabel>
#include <QMenu>
#include <QDebug>
#include <QNativeGestureEvent>

#include "rs_actionzoomin.h"
#include "rs_actionzoompan.h"
#include "rs_actionzoomscroll.h"
#include "rs_actionzoomauto.h"
#include "rs_actionmodifydelete.h"
#include "rs_actionselectsingle.h"
#include "rs_settings.h"
#include "rs_painterqt.h"
#include "rs_dialogfactory.h"
#include "qg_dialogfactory.h"
#include "rs_eventhandler.h"
#include "rs_actiondefault.h"


#include "qg_scrollbar.h"
#include "rs_modification.h"
#include "rs_debug.h"
#include "rs_graphic.h"

#ifdef Q_OS_WIN32
#define CURSOR_SIZE 16
#else
#define CURSOR_SIZE 15
#endif

/**
 * Constructor.
 */
QG_GraphicView::QG_GraphicView(QWidget* parent, Qt::WindowFlags f, RS_Document* doc)
    :RS_GraphicView(parent, f)
    ,device("Mouse")
    ,curCad(new QCursor(QPixmap(":ui/cur_cad_bmp.png"), CURSOR_SIZE, CURSOR_SIZE))
    ,curDel(new QCursor(QPixmap(":ui/cur_del_bmp.png"), CURSOR_SIZE, CURSOR_SIZE))
    ,curSelect(new QCursor(QPixmap(":ui/cur_select_bmp.png"), CURSOR_SIZE, CURSOR_SIZE))
    ,curMagnifier(new QCursor(QPixmap(":ui/cur_glass_bmp.png"), CURSOR_SIZE, CURSOR_SIZE))
    ,curHand(new QCursor(QPixmap(":ui/cur_hand_bmp.png"), CURSOR_SIZE, CURSOR_SIZE))
    ,redrawMethod(RS2::RedrawAll)
    ,isSmoothScrolling(false)
{
    RS_DEBUG->print("QG_GraphicView::QG_GraphicView()..");

    if (doc)
    {
        setContainer(doc);
        doc->setGraphicView(this);
        setDefaultAction(new RS_ActionDefault(*doc, *this));
    }

    setFactorX(4.0);
    setFactorY(4.0);
    setBorders(10, 10, 10, 10);

    setMouseTracking(true);
    setFocusPolicy(Qt::NoFocus);

    // SourceForge issue 45 (Left-mouse drag shrinks window)
    setAttribute(Qt::WA_NoMousePropagation);

    view_rect = LC_Rect(toGraph(0, 0), toGraph(getWidth(), getHeight()));
}



/**
 * Destructor
 */
QG_GraphicView::~QG_GraphicView() {
	cleanUp();
}



/**
 * @return width of widget.
 */
int QG_GraphicView::getWidth() const
{
    if (scrollbars)
        return width() - vScrollBar->sizeHint().width();
    else
        return width();
}



/**
 * @return height of widget.
 */
int QG_GraphicView::getHeight() const
{
    if (scrollbars)
        return height() - hScrollBar->sizeHint().height();
    else
        return height();
}


/**
 * Changes the current background color of this view.
 */
void QG_GraphicView::setBackground(const RS_Color& bg) {
    RS_GraphicView::setBackground(bg);

    QPalette palette;
    palette.setColor(backgroundRole(), bg);
    setPalette(palette);
}


/**
 * Sets the mouse cursor to the given type.
 */
void QG_GraphicView::setMouseCursor(RS2::CursorType c) {

    switch (c) {
    default:
    case RS2::ArrowCursor:
        setCursor(Qt::ArrowCursor);
        break;
    case RS2::UpArrowCursor:
        setCursor(Qt::UpArrowCursor);
        break;
    case RS2::CrossCursor:
        setCursor(Qt::CrossCursor);
        break;
    case RS2::WaitCursor:
        setCursor(Qt::WaitCursor);
        break;
    case RS2::IbeamCursor:
        setCursor(Qt::IBeamCursor);
        break;
    case RS2::SizeVerCursor:
        setCursor(Qt::SizeVerCursor);
        break;
    case RS2::SizeHorCursor:
        setCursor(Qt::SizeHorCursor);
        break;
    case RS2::SizeBDiagCursor:
        setCursor(Qt::SizeBDiagCursor);
        break;
    case RS2::SizeFDiagCursor:
        setCursor(Qt::SizeFDiagCursor);
        break;
    case RS2::SizeAllCursor:
        setCursor(Qt::SizeAllCursor);
        break;
    case RS2::BlankCursor:
        setCursor(Qt::BlankCursor);
        break;
    case RS2::SplitVCursor:
        setCursor(Qt::SplitVCursor);
        break;
    case RS2::SplitHCursor:
        setCursor(Qt::SplitHCursor);
        break;
    case RS2::PointingHandCursor:
        setCursor(Qt::PointingHandCursor);
        break;
    case RS2::ForbiddenCursor:
        setCursor(Qt::ForbiddenCursor);
        break;
    case RS2::WhatsThisCursor:
        setCursor(Qt::WhatsThisCursor);
        break;
    case RS2::OpenHandCursor:
        setCursor(Qt::OpenHandCursor);
        break;
    case RS2::ClosedHandCursor:
        setCursor(Qt::ClosedHandCursor);
        break;
    case RS2::CadCursor:
        cursor_hiding
            ? setCursor(Qt::BlankCursor)
            : setCursor(*curCad);
        break;
    case RS2::DelCursor:
        setCursor(*curDel);
        break;
    case RS2::SelectCursor:
        setCursor(*curSelect);
        break;
    case RS2::MagnifierCursor:
        setCursor(*curMagnifier);
        break;
    case RS2::MovingHandCursor:
        setCursor(*curHand);
        break;

    }
}


/**
 * Sets the text for the grid status widget in the left bottom corner.
 */
void QG_GraphicView::updateGridStatusWidget(const QString& text)
{
   emit gridStatusChanged(text);
}


/**
 * Redraws the widget.
 */
void QG_GraphicView::redraw(RS2::RedrawMethod method) {
        redrawMethod=(RS2::RedrawMethod ) (redrawMethod | method);
        update(); // Paint when reeady to pain
//	repaint(); //Paint immediate
}


void QG_GraphicView::resizeEvent(QResizeEvent* /*e*/) {
    RS_DEBUG->print("QG_GraphicView::resizeEvent begin");
    adjustOffsetControls();
    adjustZoomControls();
//     updateGrid();
        // Small hack, delete the snapper during resizes
        getOverlayContainer(RS2::Snapper)->clear();
        redraw();
    RS_DEBUG->print("QG_GraphicView::resizeEvent end");
}

void QG_GraphicView::mousePressEvent(QMouseEvent* event)
{
    // pan zoom with middle mouse button
    if (event->button()==Qt::MiddleButton)
    {
        setCurrentAction(new RS_ActionZoomPan(*container, *this));
    }
    eventHandler->mousePressEvent(event);
}

void QG_GraphicView::mouseDoubleClickEvent(QMouseEvent* e)
{
    switch(e->button())
    {
        default:
            break;
        case Qt::MiddleButton:
            setCurrentAction(new RS_ActionZoomAuto(*container, *this));
            break;
        case Qt::LeftButton:
            if (menus.contains("Double-Click"))
            {
                killAllActions();
                menus["Double-Click"]->popup(mapToGlobal(e->pos()));
            }
            break;
    }
    e->accept();
}


void QG_GraphicView::mouseReleaseEvent(QMouseEvent* event)
{
    RS_DEBUG->print("QG_GraphicView::mouseReleaseEvent");

    event->accept();

    switch (event->button())
    {
    case Qt::RightButton:
        if (event->modifiers()==Qt::ControlModifier)
        {
            if (menus.contains("Ctrl+Right-Click"))
            {
                menus["Ctrl+Right-Click"]->popup(mapToGlobal(event->pos()));
                break;
            }
        }
        if (event->modifiers()==Qt::ShiftModifier)
        {
            if (menus.contains("Shift+Right-Click"))
            {
                menus["Shift+Right-Click"]->popup(mapToGlobal(event->pos()));
                break;
            }
        }

        if (!eventHandler->hasAction())
        {
            if (menus.contains("Right-Click"))
            {
                menus["Right-Click"]->popup(mapToGlobal(event->pos()));
            }
            else if (!recent_actions.isEmpty())
            {
                QMenu* context_menu = new QMenu(this);
                context_menu->setAttribute(Qt::WA_DeleteOnClose);
                context_menu->addActions(recent_actions);
                context_menu->exec(mapToGlobal(event->pos()));
            }
        }
        else back();
        break;

    case Qt::XButton1:
        enter();
        emit xbutton1_released();
        break;

    default:
        eventHandler->mouseReleaseEvent(event);
        break;
    }
    RS_DEBUG->print("QG_GraphicView::mouseReleaseEvent: OK");
}


void QG_GraphicView::mouseMoveEvent(QMouseEvent* event)
{
    event->accept();
    eventHandler->mouseMoveEvent(event);
}

bool QG_GraphicView::event(QEvent *event)
{
    if (event->type() == QEvent::NativeGesture) {
        QNativeGestureEvent *nge = static_cast<QNativeGestureEvent *>(event);

        if (nge->gestureType() == Qt::ZoomNativeGesture) {
            double v = nge->value();
            RS2::ZoomDirection direction;
            double factor;

            if (v < 0) {
                direction = RS2::Out;
                factor = 1-v;
            } else {
                direction = RS2::In;
                factor = 1+v;
            }

            // It seems the NativeGestureEvent::pos() incorrectly reports global coordinates
            QPoint g = mapFromGlobal(nge->globalPos());
            RS_Vector mouse = toGraph(g.x(), g.y());
            setCurrentAction(new RS_ActionZoomIn(*container, *this, direction,
												 RS2::Both, &mouse, factor));
        }

        return true;
    }
    return QWidget::event(event);
}

/**
 * support for the wacom graphic tablet.
 */
void QG_GraphicView::tabletEvent(QTabletEvent* e) {
    if (testAttribute(Qt::WA_UnderMouse)) {
        switch (e->device()) {
        case QTabletEvent::Eraser:
            if (e->type()==QEvent::TabletRelease) {
                if (container) {

                    RS_ActionSelectSingle* a =
                        new RS_ActionSelectSingle(*container, *this);
                    setCurrentAction(a);
                    QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(),
                                   Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);//RLZ
                    mouseReleaseEvent(&ev);
                    a->finish();

                    if (container->countSelected()>0) {
                        setCurrentAction(
                            new RS_ActionModifyDelete(*container, *this));
                    }
                }
            }
            break;

        case QTabletEvent::Stylus:
        case QTabletEvent::Puck:
            if (e->type()==QEvent::TabletPress) {
                QMouseEvent ev(QEvent::MouseButtonPress, e->pos(),
                               Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);//RLZ
                mousePressEvent(&ev);
            } else if (e->type()==QEvent::TabletRelease) {
                QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(),
                               Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);//RLZ
                mouseReleaseEvent(&ev);
            } else if (e->type()==QEvent::TabletMove) {
                QMouseEvent ev(QEvent::MouseMove, e->pos(),
                               Qt::NoButton, 0, Qt::NoModifier);//RLZ
                mouseMoveEvent(&ev);
            }
            break;

        default:
            break;
        }
    }

    // a 'mouse' click:
    /*if (e->pressure()>10 && lastPressure<10) {
        QMouseEvent e(QEvent::MouseButtonPress, e->pos(),
           Qt::LeftButton, Qt::LeftButton);
        mousePressEvent(&e);
}
    else if (e->pressure()<10 && lastPressure>10) {
        QMouseEvent e(QEvent::MouseButtonRelease, e->pos(),
           Qt::LeftButton, Qt::LeftButton);
        mouseReleaseEvent(&e);
}	else if (lastPos!=e->pos()) {
        QMouseEvent e(QEvent::MouseMove, e->pos(),
           Qt::NoButton, 0);
        mouseMoveEvent(&e);
}

    lastPressure = e->pressure();
    lastPos = e->pos();
    */
}

void QG_GraphicView::leaveEvent(QEvent* e) {
    eventHandler->mouseLeaveEvent();
    QWidget::leaveEvent(e);
}


void QG_GraphicView::enterEvent(QEvent* e) {
    eventHandler->mouseEnterEvent();
    QWidget::enterEvent(e);
}


void QG_GraphicView::focusOutEvent(QFocusEvent* e) {
    QWidget::focusOutEvent(e);
}


void QG_GraphicView::focusInEvent(QFocusEvent* e) {
    eventHandler->mouseEnterEvent();
    QWidget::focusInEvent(e);
}

/**
 * mouse wheel event. zooms in/out or scrolls when
 * shift or ctrl is pressed.
 */
void QG_GraphicView::wheelEvent(QWheelEvent *e) {
    //RS_DEBUG->print("wheel: %d", e->delta());

    //printf("state: %d\n", e->state());
    //printf("ctrl: %d\n", Qt::ControlButton);

    if (container==NULL) {
        return;
    }

    RS_Vector mouse = toGraph(e->x(), e->y());

    if (device == "Trackpad")
    {
        QPoint numPixels = e->pixelDelta();

        // high-resolution scrolling triggers Pan instead of Zoom logic
        isSmoothScrolling |= !numPixels.isNull();

        if (isSmoothScrolling)
        {
            if (e->phase() == Qt::ScrollEnd) isSmoothScrolling = false;
        }
        else // Trackpads that without high-resolution scrolling
             // e.g. libinput-XWayland trackpads
        {
            numPixels = e->angleDelta() / 4;
        }

        if (!numPixels.isNull())
        {
            if (e->modifiers()==Qt::ControlModifier)
            {
                RS_SETTINGS->beginGroup("/Defaults");
                bool invZoom = (RS_SETTINGS->readNumEntry("/InvertZoomDirection", 0) == 1);
                RS_SETTINGS->endGroup();

                // Hold ctrl to zoom. 1 % per pixel
                double v = (invZoom) ? (numPixels.y() / 100.) : (-numPixels.y() / 100.);
                RS2::ZoomDirection direction;
                double factor;

                if (v < 0) {
                    direction = RS2::Out; factor = 1-v;
                } else {
                    direction = RS2::In;  factor = 1+v;
                }

                setCurrentAction(new RS_ActionZoomIn(*container, *this, direction,
                                                     RS2::Both, &mouse, factor));
            }
            else
            {
                RS_SETTINGS->beginGroup("/Defaults");
                bool inv_h = (RS_SETTINGS->readNumEntry("/WheelScrollInvertH", 0) == 1);
                bool inv_v = (RS_SETTINGS->readNumEntry("/WheelScrollInvertV", 0) == 1);
                RS_SETTINGS->endGroup();

                int hDelta = (inv_h) ? -numPixels.x() : numPixels.x();
                int vDelta = (inv_v) ? -numPixels.y() : numPixels.y();

                // scroll by scrollbars: issue #479 (it has its own issues)
                if (scrollbars)
                {
                    hScrollBar->setValue(hScrollBar->value() - hDelta);
                    vScrollBar->setValue(vScrollBar->value() - vDelta);
                }
                else
                {
                    setCurrentAction(new RS_ActionZoomScroll(hDelta, vDelta,
                                                             *container, *this));
                }
            }
            redraw();
        }
        e->accept();
        return;
    }

    if (e->delta() == 0) {
        // A zero delta event occurs when smooth scrolling is ended. Ignore this
        e->accept();
        return;
    }

    bool scroll = false;
    RS2::Direction direction = RS2::Up;

    // scroll up / down:
    if (e->modifiers()==Qt::ControlModifier) {
        scroll = true;
        switch(e->orientation()){
        case Qt::Horizontal:
            direction=(e->delta()>0)?RS2::Left:RS2::Right;
            break;
        default:
        case Qt::Vertical:
            direction=(e->delta()>0)?RS2::Up:RS2::Down;
        }
    }

    // scroll left / right:
    else if	(e->modifiers()==Qt::ShiftModifier) {
        scroll = true;
        switch(e->orientation()){
        case Qt::Horizontal:
            direction=(e->delta()>0)?RS2::Up:RS2::Down;
            break;
        default:
        case Qt::Vertical:
            direction=(e->delta()>0)?RS2::Left:RS2::Right;
        }
    }

    if (scroll && scrollbars) {
		//scroll by scrollbars: issue #479

        RS_SETTINGS->beginGroup("/Defaults");
        bool inv_h = (RS_SETTINGS->readNumEntry("/WheelScrollInvertH", 0) == 1);
        bool inv_v = (RS_SETTINGS->readNumEntry("/WheelScrollInvertV", 0) == 1);
        RS_SETTINGS->endGroup();

        int delta;

		switch(direction){
		case RS2::Left:
		case RS2::Right:
            delta = (inv_h) ? -e->delta() : e->delta();
			hScrollBar->setValue(hScrollBar->value()+delta);
			break;
		default:
            delta = (inv_v) ? -e->delta() : e->delta();
			vScrollBar->setValue(vScrollBar->value()+delta);
		}

//        setCurrentAction(new RS_ActionZoomScroll(direction,
//                         *container, *this));
    }

    // zoom in / out:
    else if (e->modifiers()==0) {

        /*
         * The zoomFactor effects how quickly the scroll wheel will zoom in & out.
         *
         * Benchmarks:
         * 1.250 - the original; fast & usable, but seems a choppy & a bit 'jarring'
         * 1.175 - still a bit choppy
         * 1.150 - smoother than the original, but still 'quick' enough for good navigation.
         * 1.137 - seems to work well for me
         * 1.125 - about the lowest that would be acceptable and useful, a tad on the slow side for me
         * 1.100 - a very slow & deliberate zooming, but feels very "cautious", "controlled", "safe", and "precise".
         * 1.000 - goes nowhere. :)
         */
		const double zoomFactor=1.137;

		RS_Vector mainViewCenter = toGraph(getWidth()/2, getHeight()/2);

		RS_SETTINGS->beginGroup("/Defaults");
		bool invZoom = (RS_SETTINGS->readNumEntry("/InvertZoomDirection", 0) == 1);
		RS_SETTINGS->endGroup();

		if ((e->delta()>0 && !invZoom) || (e->delta()<0 && invZoom)) {
			const double zoomInOvershoot=1.20;

			RS_Vector effect{mouse};
			{
				effect-=mainViewCenter;
				effect.scale(zoomInOvershoot);
				effect+=mainViewCenter;
			}

			setCurrentAction(new RS_ActionZoomIn(*container, *this,
												 RS2::In, RS2::Both,
												 &effect,
												 zoomFactor
												));
		} else {
			const double zoomOutUndershoot=0.30;

			RS_Vector effect{mouse};
			{
				effect-=mainViewCenter;
				effect.scale(zoomOutUndershoot);
				effect+=mainViewCenter;
			}

			setCurrentAction(new RS_ActionZoomIn(*container, *this,
												 RS2::Out, RS2::Both,
												 &effect,
												 zoomFactor
												));
		}
    }
    redraw();

    QMouseEvent* event = new QMouseEvent(QEvent::MouseMove,
                                         QPoint(e->x(), e->y()),
                                         Qt::NoButton, Qt::NoButton,
                                         Qt::NoModifier);
    eventHandler->mouseMoveEvent(event);
    delete event;

    e->accept();
}


void QG_GraphicView::keyPressEvent(QKeyEvent* e)
{
    if (container==NULL) {
        return;
    }

    bool scroll = false;
    RS2::Direction direction = RS2::Up;

    switch (e->key()) {
    case Qt::Key_Left:
        scroll = true;
        direction = RS2::Right;
        break;
    case Qt::Key_Right:
        scroll = true;
        direction = RS2::Left;
        break;
    case Qt::Key_Up:
        scroll = true;
        direction = RS2::Up;
        break;
    case Qt::Key_Down:
        scroll = true;
        direction = RS2::Down;
        break;
    default:
        scroll = false;
        break;
    }

    if (scroll) {
        setCurrentAction(new RS_ActionZoomScroll(direction,
                         *container, *this));
    }
    eventHandler->keyPressEvent(e);
}


void QG_GraphicView::keyReleaseEvent(QKeyEvent* e)
{
    eventHandler->keyReleaseEvent(e);
}

/**
 * Called whenever the graphic view has changed.
 * Adjusts the scrollbar ranges / steps.
 */
void QG_GraphicView::adjustOffsetControls()
{
    if (scrollbars)
    {
        static bool running = false;

        if (running) {
                return;
        }

        running = true;

        if (container==NULL || hScrollBar==NULL || vScrollBar==NULL) {
            return;
        }

        int ox = getOffsetX();
        int oy = getOffsetY();

        RS_Vector min = container->getMin();
        RS_Vector max = container->getMax();

        // no drawing yet - still allow to scroll
        if (max.x < min.x+1.0e-6 ||
                max.y < min.y+1.0e-6 ||
                    max.x > RS_MAXDOUBLE ||
                    max.x < RS_MINDOUBLE ||
                    min.x > RS_MAXDOUBLE ||
                    min.x < RS_MINDOUBLE ||
                    max.y > RS_MAXDOUBLE ||
                    max.y < RS_MINDOUBLE ||
                    min.y > RS_MAXDOUBLE ||
                    min.y < RS_MINDOUBLE ) {
            min = RS_Vector(-10,-10);
            max = RS_Vector(100,100);
        }

        auto factor = getFactor();

        int minVal = (int)(-getWidth()*0.75
                           + std::min(min.x, 0.)*factor.x);
        int maxVal = (int)(-getWidth()*0.25
                           + std::max(max.x, 0.)*factor.x);

        if (minVal<=maxVal) {
            hScrollBar->setRange(minVal, maxVal);
        }

        minVal = (int)(+getHeight()*0.25
                       - std::max(max.y, 0.)*factor.y);
        maxVal = (int)(+getHeight()*0.75
                       - std::min(min.y, 0.)*factor.y);

        if (minVal<=maxVal) {
            vScrollBar->setRange(minVal, maxVal);
        }

        hScrollBar->setPageStep(getWidth());
        vScrollBar->setPageStep(getHeight());

        hScrollBar->setValue(-ox);
        vScrollBar->setValue(oy);


        slotHScrolled(-ox);
        slotVScrolled(oy);


//        RS_DEBUG->print("H min: %d / max: %d / step: %d / value: %d\n",
//                        hScrollBar->minimum(), hScrollBar->maximum(),
//                        hScrollBar->pageStep(), ox);

//        RS_DEBUG->print(/*RS_Debug::D_WARNING, */"V min: %d / max: %d / step: %d / value: %d\n",
//                        vScrollBar->minimum(), vScrollBar->maximum(),
//                        vScrollBar->pageStep(), oy);


        running = false;
    }
}


/**
 * override this to adjust controls and widgets that
 * control the zoom factor of the graphic.
 */
void QG_GraphicView::adjustZoomControls() {}


/**
 * Slot for horizontal scroll events.
 */
void QG_GraphicView::slotHScrolled(int value) {
    // Scrollbar behaviour tends to change with every Qt version..
    // so let's keep old code in here for now

    //static int running = false;
    //if (!running) {
    //running = true;
    ////RS_DEBUG->print("value x: %d\n", value);
    if (hScrollBar->maximum()==hScrollBar->minimum()) {
        centerOffsetX();
    } else {
        setOffsetX(-value);
    }
    //if (isUpdateEnabled()) {
//         updateGrid();
    redraw();
}


/**
 * Slot for vertical scroll events.
 */
void QG_GraphicView::slotVScrolled(int value) {
    // Scrollbar behaviour tends to change with every Qt version..
    // so let's keep old code in here for now

    //static int running = false;
    //if (!running) {
    //running = true;
//    DEBUG_HEADER
//	RS_DEBUG->print(/*RS_Debug::D_WARNING,*/ "%s %s(): set vertical offset from %d to %d\n",
//                    __FILE__, __func__, getOffsetY(), value);
    if (vScrollBar->maximum()==vScrollBar->minimum()) {
        centerOffsetY();
    } else {
        setOffsetY(value);
    }
    //if (isUpdateEnabled()) {
  //  updateGrid();
    redraw();
}
/**
 * @brief setOffset
 * @param ox, offset X
 * @param oy, offset Y
 */
void QG_GraphicView::setOffset(int ox, int oy) {
//    DEBUG_HEADER
//    qDebug()<<"adjusting offset from ("<<getOffsetX()<<","<<getOffsetY()<<") to ("<<ox<<" , "<<oy<<")";
    RS_GraphicView::setOffset(ox, oy);
    // need to adjust offset control for scrollbars when setting graphicview offset
    adjustOffsetControls();
}

RS_Vector QG_GraphicView::getMousePosition() const
{
    //find mouse position
    QPoint vp=mapFromGlobal(QCursor::pos());
    //if cursor is not on widget, return the widget center position
    if(!rect().contains(vp))
        vp=QPoint(width()/2, height()/2);
    return toGraph(vp.x(), vp.y());
}

void QG_GraphicView::getPixmapForView(std::unique_ptr<QPixmap>& pm)
{
	QSize const s0(getWidth(), getHeight());
	if(pm && pm->size()==s0)
		return;
	pm.reset(new QPixmap(getWidth(), getHeight()));
}

void QG_GraphicView::layerActivated(RS_Layer *layer) {
	RS_SETTINGS->beginGroup("/Modify");
	bool toActivated= (RS_SETTINGS->readNumEntry("/ModifyEntitiesToActiveLayer", 0)==1);
	RS_SETTINGS->endGroup();

	if(!toActivated) return;
    RS_EntityContainer *container = this->getContainer();
    RS_Graphic* graphic = this->getGraphic();
    QList<RS_Entity*> clones;

    if (graphic) {
        graphic->startUndoCycle();
    }

    for (auto en: *container) {
        if (!en) continue;
        if (!en->isSelected()) continue;

        RS_Entity* cl = en->clone();
        cl->setLayer(layer);
        this->deleteEntity(en);
        en->setSelected(false);
        cl->setSelected(false);
        clones << cl;

        if (!graphic) continue;

        en->setUndoState(true);
        graphic->addUndoable(en);
    }

    for (auto cl: clones) {
        container->addEntity(cl);
        this->drawEntity(cl);

        if (!graphic) continue;

        graphic->addUndoable(cl);
    }

    if (graphic) {
        graphic->endUndoCycle();
        graphic->updateInserts();
    }

    container->calculateBorders();
    container->setSelected(false);
    redraw(RS2::RedrawDrawing);
}


/**
 * Handles paint events by redrawing the graphic in this view.
 * usually that's very fast since we only paint the buffer we
 * have from the last call..
 */
void QG_GraphicView::paintEvent(QPaintEvent *)
{

    // Re-Create or get the layering pixmaps
    getPixmapForView(PixmapLayer1);
    getPixmapForView(PixmapLayer2);
    getPixmapForView(PixmapLayer3);

    // Draw Layer 1
    if (redrawMethod & RS2::RedrawGrid)
    {
        PixmapLayer1->fill(background);
        RS_PainterQt painter1(PixmapLayer1.get());
        drawLayer1((RS_Painter*)&painter1);
        painter1.end();
    }

    if (redrawMethod & RS2::RedrawDrawing)
    {
        view_rect = LC_Rect(toGraph(0, 0),
                            toGraph(getWidth(), getHeight()));
        // DRaw layer 2
        PixmapLayer2->fill(Qt::transparent);
        RS_PainterQt painter2(PixmapLayer2.get());
        if (antialiasing)
        {
            painter2.setRenderHint(QPainter::Antialiasing);
        }
        painter2.setDrawingMode(drawingMode);
        painter2.setDrawSelectedOnly(false);
        drawLayer2((RS_Painter*)&painter2);
        painter2.setDrawSelectedOnly(true);
        drawLayer2((RS_Painter*)&painter2);
        painter2.end();
    }

    if (redrawMethod & RS2::RedrawOverlay)
    {
        PixmapLayer3->fill(Qt::transparent);
        RS_PainterQt painter3(PixmapLayer3.get());
        if (antialiasing)
        {
            painter3.setRenderHint(QPainter::Antialiasing);
        }
        drawLayer3((RS_Painter*)&painter3);
        painter3.end();
    }

    // Finally paint the layers back on the screen, bitblk to the rescue!
    RS_PainterQt wPainter(this);
    wPainter.drawPixmap(0,0,*PixmapLayer1);
    wPainter.drawPixmap(0,0,*PixmapLayer2);
    wPainter.drawPixmap(0,0,*PixmapLayer3);
    wPainter.end();

    redrawMethod=RS2::RedrawNone;
}

void QG_GraphicView::setAntialiasing(bool state)
{
	antialiasing = state;
}

void QG_GraphicView::addScrollbars()
{
    scrollbars = true;

    hScrollBar = new QG_ScrollBar(Qt::Horizontal, this);
    vScrollBar = new QG_ScrollBar(Qt::Vertical, this);
    layout = new QGridLayout(this);

    setOffset(50, 50);

    layout->setMargin(0);
    layout->setSpacing(0);
    layout->setColumnStretch(0, 1);
    layout->setColumnStretch(1, 0);
    layout->setColumnStretch(2, 0);
    layout->setRowStretch(0, 1);
    layout->setRowStretch(1, 0);

    hScrollBar->setSingleStep(50);
    hScrollBar->setCursor(Qt::ArrowCursor);
    layout->addWidget(hScrollBar, 1, 0);
    connect(hScrollBar, SIGNAL(valueChanged(int)),
            this, SLOT(slotHScrolled(int)));

    vScrollBar->setSingleStep(50);
    vScrollBar->setCursor(Qt::ArrowCursor);
    layout->addWidget(vScrollBar, 0, 1);
    connect(vScrollBar, SIGNAL(valueChanged(int)),
            this, SLOT(slotVScrolled(int)));
}

bool QG_GraphicView::hasScrollbars()
{
    return scrollbars;
}

void QG_GraphicView::setCursorHiding(bool state)
{
    cursor_hiding = state;
}

void QG_GraphicView::setCurrentQAction(QAction* q_action)
{
    eventHandler->setQAction(q_action);

    if (recent_actions.contains(q_action))
    {
        recent_actions.removeOne(q_action);
    }
    recent_actions.prepend(q_action);
}

void QG_GraphicView::destroyMenu(const QString& activator)
{
    if (menus.contains(activator))
    {
        auto menu = menus.take(activator);
        delete menu;
    }
}

void QG_GraphicView::setMenu(const QString& activator, QMenu* menu)
{
    destroyMenu(activator);
    menus[activator] = menu;
}
