/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 */

#include "UBGraphicsScene.h"

#include <QtGui>
#include <QtWebKit>
#include <QtSvg>
#include <QGraphicsView>

#include "frameworks/UBGeometryUtils.h"
#include "frameworks/UBPlatformUtils.h"

#include "core/UBApplication.h"
#include "core/UBSettings.h"
#include "core/UBApplicationController.h"
#include "core/UBDisplayManager.h"
#include "core/UBPersistenceManager.h"

#include "gui/UBMagnifer.h"

#include "tools/UBGraphicsRuler.h"
#include "tools/UBGraphicsProtractor.h"
#include "tools/UBGraphicsCompass.h"
#include "tools/UBGraphicsTriangle.h"
#include "tools/UBGraphicsCurtainItem.h"
#include "tools/UBGraphicsCache.h"

#include "document/UBDocumentProxy.h"

#include "board/UBBoardController.h"
#include "board/UBDrawingController.h"

#include "UBGraphicsItemUndoCommand.h"
#include "UBGraphicsTextItemUndoCommand.h"
#include "UBGraphicsProxyWidget.h"
#include "UBGraphicsPixmapItem.h"
#include "UBGraphicsSvgItem.h"
#include "UBGraphicsPolygonItem.h"
#include "UBGraphicsVideoItem.h"
#include "UBGraphicsAudioItem.h"
#include "UBGraphicsWidgetItem.h"
#include "UBGraphicsPDFItem.h"
#include "UBGraphicsTextItem.h"

#include "UBAppleWidget.h"
#include "UBW3CWidget.h"
#include "UBGraphicsStroke.h"

#include "core/memcheck.h"

qreal UBGraphicsScene::backgroundLayerStart = -20000000.0;
qreal UBGraphicsScene::objectLayerStart = -10000000.0;
qreal UBGraphicsScene::drawingLayerStart = 0.0;
qreal UBGraphicsScene::toolLayerStart = 10000000.0;

qreal UBGraphicsScene::toolOffsetRuler = 100;
qreal UBGraphicsScene::toolOffsetProtractor = 100;
qreal UBGraphicsScene::toolOffsetTriangle = 100;
qreal UBGraphicsScene::toolOffsetCompass = 100;
qreal UBGraphicsScene::toolOffsetEraser = 200;

qreal UBGraphicsScene::toolOffsetCurtain = 1000;
qreal UBGraphicsScene::toolOffsetPointer = 1100;

qreal UBGraphicsScene::toolOffsetCache = 1000;//Didier please define offset you want

UBGraphicsScene::UBGraphicsScene(UBDocumentProxy* parent)
    : UBCoreGraphicsScene(parent)
    , mEraser(0)
    , mPointer(0)
    , mDocument(parent)
    , mDarkBackground(false)
    , mCrossedBackground(false)
    , mIsModified(true)
    , mBackgroundObject(0)
    , mPreviousWidth(0)
    , mInputDeviceIsPressed(false)
    , mArcPolygonItem(0)
    , mRenderingContext(Screen)
    , mCurrentStroke(0)
    , mShouldUseOMP(true)
    , mItemCount(0)
    , enableUndoRedoStack(true)
    , magniferControlViewWidget(0)
    , magniferDisplayViewWidget(0)

{

#ifdef __ppc__
    mShouldUseOMP = false;
#elif defined(Q_WS_MAC)
    mShouldUseOMP = QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5;
#endif

    setDocument(parent);

    mDrawingZIndex = drawingLayerStart;
    mObjectZIndex = objectLayerStart;

    mEraser = new QGraphicsEllipseItem(); // mem : owned and destroyed by the scene
    mEraser->setRect(QRect(0, 0, 0, 0));
    mEraser->setVisible(false);

    mEraser->setZValue(/*toolLayerStart + toolOffsetEraser*/2);
    mEraser->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control));

    mTools << mEraser;
    addItem(mEraser);

    mPointer = new QGraphicsEllipseItem();  // mem : owned and destroyed by the scene
    mPointer->setRect(QRect(0, 0, 20, 20));
    mPointer->setVisible(false);

    mPointer->setPen(Qt::NoPen);
    mPointer->setBrush(QBrush(QColor(255, 0, 0, 186)));

    mPointer->setZValue( /*toolLayerStart + toolOffsetPointer*/ 2);
    mPointer->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));

    mTools << mPointer;
    addItem(mPointer);

    if (UBApplication::applicationController)
    {
        setViewState(SceneViewState(1,
            UBApplication::applicationController->initialHScroll(),
            UBApplication::applicationController->initialVScroll()));
    }

    connect(this, SIGNAL(selectionChanged()), this, SLOT(selectionChangedProcessing()));
}

UBGraphicsScene::~UBGraphicsScene()
{
    DisposeMagnifierQWidgets();

    if (mCurrentStroke)
        if (mCurrentStroke->polygons().empty())
            delete mCurrentStroke;
}

void UBGraphicsScene::selectionChangedProcessing()
{
    if (selectedItems().count())
        UBApplication::showMessage("ZValue is " + QString::number(selectedItems().first()->zValue(), 'f'));


    QList<QGraphicsItem *> allItemsList = items();
    qreal maxZ = 0.;
    for( int i = 0; i < allItemsList.size(); i++ ) {
        QGraphicsItem *nextItem = allItemsList.at(i);
        //Temporary stub. Due to ugly z-order implementation I need to do this (sankore 360)
        //z-order behavior should be reimplemented and this stub should be deleted
        if (nextItem == mBackgroundObject)
            continue;
        //Temporary stub end (sankore 360)
        if (nextItem->zValue() > maxZ)
            maxZ = nextItem->zValue();
        nextItem->setZValue(nextItem->data(UBGraphicsItemData::ItemOwnZValue).toReal());
//        nextItem->setZValue(qreal(1));
    }
    QList<QGraphicsItem *> selItemsList = selectedItems();
    for( int i = 0; i < selItemsList.size(); i++ ) {
        QGraphicsItem *nextItem = selItemsList.at(i);
        nextItem->setZValue(maxZ + 0.0001);
    }
}

// MARK: -
// MARK: Mouse/Tablet events handling

bool UBGraphicsScene::inputDevicePress(const QPointF& scenePos, const qreal& pressure)
{
    //mMesure1Ms = 0;
    //mMesure2Ms = 0;

    bool accepted = false;

    if (mInputDeviceIsPressed)
    {
        qWarning() << "scene received input device pressed, without input device release, muting event as input device move";
        accepted = inputDeviceMove(scenePos, pressure);
    }
    else
    {
        mInputDeviceIsPressed = true;

        UBStylusTool::Enum currentTool = (UBStylusTool::Enum)UBDrawingController::drawingController()->stylusTool();

        if (UBDrawingController::drawingController()->isDrawingTool())
        {
            qreal width = 0;

            // delete current stroke, if not assigned to any polygon
            if (mCurrentStroke)
                if (mCurrentStroke->polygons().empty())
                    delete mCurrentStroke;

            mCurrentStroke = new UBGraphicsStroke();

            if (currentTool != UBStylusTool::Line)
                width = UBDrawingController::drawingController()->currentToolWidth() * pressure;
            else
                width = UBDrawingController::drawingController()->currentToolWidth(); //ignore pressure for line tool

            width /= UBApplication::boardController->systemScaleFactor();
            width /= UBApplication::boardController->currentZoom();

            mAddedItems.clear();
            mRemovedItems.clear();

			if (UBDrawingController::drawingController()->mActiveRuler)
			{
				UBDrawingController::drawingController()->mActiveRuler->StartLine(
					scenePos, width);
			}
			else
			{
				moveTo(scenePos);
				drawLineTo(scenePos, width,
					UBDrawingController::drawingController()->stylusTool() == UBStylusTool::Line);
			}
            accepted = true;
        }
        else if (currentTool == UBStylusTool::Eraser)
        {
            mAddedItems.clear();
            mRemovedItems.clear();
            moveTo(scenePos);

            qreal eraserWidth = UBSettings::settings()->currentEraserWidth();
            eraserWidth /= UBApplication::boardController->systemScaleFactor();
            eraserWidth /= UBApplication::boardController->currentZoom();

            eraseLineTo(scenePos, eraserWidth);
            drawEraser(scenePos, true);

            accepted = true;
        }
        else if (currentTool == UBStylusTool::Pointer)
        {
            drawPointer(scenePos, true);
            accepted = true;
        }
    }

    return accepted;
}


bool UBGraphicsScene::inputDeviceMove(const QPointF& scenePos, const qreal& pressure)
{
    bool accepted = false;

    UBDrawingController *dc = UBDrawingController::drawingController();
    UBStylusTool::Enum currentTool = (UBStylusTool::Enum)dc->stylusTool();

    QPointF position = QPointF(scenePos);

    if (currentTool == UBStylusTool::Eraser)
    {
        drawEraser(position);
        accepted = true;
    }

    if (mInputDeviceIsPressed)
    {
        if (dc->isDrawingTool())
        {
            qreal width = 0;

            if (currentTool != UBStylusTool::Line)
                width = dc->currentToolWidth() * pressure;
            else
                width = dc->currentToolWidth();//ignore pressure for line tool

            width /= UBApplication::boardController->systemScaleFactor();
            width /= UBApplication::boardController->currentZoom();

			if (dc->mActiveRuler)
			{
				dc->mActiveRuler->DrawLine(position, width);
			}
			else
			{
	            if (currentTool == UBStylusTool::Line)
		        {
                            // TODO:    Verify this beautiful implementation and check if
                            //          it is possible to optimize it
			        QLineF radius(mPreviousPoint, position);
				    qreal angle = radius.angle();
					angle = qRound(angle / 45) * 45;
	                qreal radiusLength = radius.length();
		            QPointF newPosition(
			            mPreviousPoint.x() + radiusLength * cos((angle * PI) / 180),
				        mPreviousPoint.y() - radiusLength * sin((angle * PI) / 180));
					QLineF chord(position, newPosition);
                                        if (chord.length() < qMin((int)16, (int)(radiusLength / 20)))
						position = newPosition;
				}

				drawLineTo(position, width,
					UBDrawingController::drawingController()->stylusTool() == UBStylusTool::Line);
			}
        }
        else if (currentTool == UBStylusTool::Eraser)
        {
            qreal eraserWidth = UBSettings::settings()->currentEraserWidth();
            eraserWidth /= UBApplication::boardController->systemScaleFactor();
            eraserWidth /= UBApplication::boardController->currentZoom();

            eraseLineTo(position, eraserWidth);
        }
        else if (currentTool == UBStylusTool::Pointer)
        {
            drawPointer(position);
        }

        accepted = true;
    }

    return accepted;
}


bool UBGraphicsScene::inputDeviceRelease()
{
    /*
    if (mMesure1Ms > 0 ||  mMesure2Ms > 0)
    {
        qWarning() << "---------------------------";
        qWarning() << "mMesure1Ms: " << mMesure1Ms;
        qWarning() << "mMesure2Ms: " << mMesure2Ms;

        mMesure1Ms = 0;
        mMesure2Ms = 0;
    }
    */

    bool accepted = false;



    if (mPointer)
    {
        mPointer->hide();
        accepted = true;
    }

    UBDrawingController *dc = UBDrawingController::drawingController();
    if (dc->isDrawingTool()) 
    {
        if (mCurrentStroke)
        {
            if (mCurrentStroke->polygons().empty())
                delete mCurrentStroke;
            mCurrentStroke = 0;
        } 
    } 
   
    if (mRemovedItems.size() > 0 || mAddedItems.size() > 0)
    {

        if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
            UBGraphicsItemUndoCommand* udcmd = new UBGraphicsItemUndoCommand(this, mRemovedItems, mAddedItems); //deleted by the undoStack

            if(UBApplication::undoStack)
                UBApplication::undoStack->push(udcmd);
        }

        mRemovedItems.clear();
        mAddedItems.clear();
        accepted = true;
    }

    mInputDeviceIsPressed = false;

    setDocumentUpdated();

    return accepted;
}

// MARK: -

void UBGraphicsScene::drawEraser(const QPointF &pPoint, bool isFirstDraw)
{
    qreal eraserWidth = UBSettings::settings()->currentEraserWidth();
    eraserWidth /= UBApplication::boardController->systemScaleFactor();
    eraserWidth /= UBApplication::boardController->currentZoom();

    qreal eraserRadius = eraserWidth / 2;

    // TODO UB 4.x optimize - no need to do that every time we move it
    if (mEraser)
    {
        mEraser->setRect(QRectF(pPoint.x() - eraserRadius, pPoint.y() - eraserRadius, eraserWidth, eraserWidth));

        if(isFirstDraw)
        {
            qreal maxZ = 0.;
            QList<QGraphicsItem *> allItemsList = items();
            for( int i = 0; i < allItemsList.size(); i++ )
            {
                QGraphicsItem *nextItem = allItemsList.at(i);
                qreal zValue = nextItem->zValue();
                if (zValue > maxZ)
                    maxZ = zValue;
                nextItem->setZValue(nextItem->data(UBGraphicsItemData::ItemOwnZValue).toReal());
            }

            mEraser->setZValue(maxZ + 0.0001);
            mEraser->show();
        }
    }
}

void UBGraphicsScene::drawPointer(const QPointF &pPoint, bool isFirstDraw)
{
    qreal pointerDiameter = UBSettings::pointerDiameter / UBApplication::boardController->currentZoom();
    qreal pointerRadius = pointerDiameter / 2;

    // TODO UB 4.x optimize - no need to do that every time we move it
    if (mPointer)
    {
        mPointer->setRect(QRectF(pPoint.x() - pointerRadius,
                pPoint.y() - pointerRadius,
                pointerDiameter,
                pointerDiameter));

        if(isFirstDraw)
        {
            qreal maxZ = 0.;
            QList<QGraphicsItem *> allItemsList = items();
            for( int i = 0; i < allItemsList.size(); i++ )
            {
                QGraphicsItem *nextItem = allItemsList.at(i);
                qreal zValue = nextItem->zValue();
                if (zValue > maxZ)
                    maxZ = zValue;
                nextItem->setZValue(nextItem->data(UBGraphicsItemData::ItemOwnZValue).toReal());
            }

            mPointer->setZValue(maxZ + 0.0001);
            mPointer->show();
        }
    }
}

// call this function when user release mouse button in Magnifier mode
void UBGraphicsScene::DisposeMagnifierQWidgets()
{
    if(magniferControlViewWidget) 
    {
        magniferControlViewWidget->hide();
        magniferControlViewWidget->setParent(0);
        delete magniferControlViewWidget;
        magniferControlViewWidget = NULL;
    }

    if(magniferDisplayViewWidget) 
    {
        magniferDisplayViewWidget->hide();
        magniferDisplayViewWidget->setParent(0);
        delete magniferDisplayViewWidget;
        magniferDisplayViewWidget = NULL;
    }

    // some time have crash here on access to app (when call from destructor when close sankore app)
    // so i just add try/catch section here
    try
    {
        UBApplication::app()->restoreOverrideCursor();
    }
    catch (...)
    {
    }
    
}

void UBGraphicsScene::moveTo(const QPointF &pPoint)
{
    mPreviousPoint = pPoint;
    mPreviousWidth = -1.0;
    mPreviousPolygonItems.clear();
    mArcPolygonItem = 0;
}


void UBGraphicsScene::drawLineTo(const QPointF &pEndPoint, const qreal &pWidth, bool bLineStyle)
{
    if (mPreviousWidth == -1.0)
        mPreviousWidth = pWidth;

    UBGraphicsPolygonItem *polygonItem = lineToPolygonItem(QLineF(mPreviousPoint, pEndPoint), pWidth);

    if (!polygonItem->brush().isOpaque())
    {
        for (int i = 0; i < mPreviousPolygonItems.size(); i++)
        {
            UBGraphicsPolygonItem* previous = mPreviousPolygonItems.value(i);
            polygonItem->subtract(previous);
        }
    }

	if (bLineStyle)
    {
        QSetIterator<QGraphicsItem*> itItems(mAddedItems);

        while (itItems.hasNext())
        {
            QGraphicsItem* item = itItems.next();
            removeItem(item);
        }
        mAddedItems.clear();
    }

    mAddedItems.insert(polygonItem);

    if (mCurrentStroke)
    {
        polygonItem->setStroke(mCurrentStroke);
    }

    addItem(polygonItem);

    mPreviousPolygonItems.append(polygonItem);

	if (!bLineStyle)
    {
        mPreviousPoint = pEndPoint;
        mPreviousWidth = pWidth;
    }
}


void UBGraphicsScene::eraseLineTo(const QPointF &pEndPoint, const qreal &pWidth)
{
    const QLineF line(mPreviousPoint, pEndPoint);

    const QPolygonF eraserPolygon = UBGeometryUtils::lineToPolygon(line, pWidth);
    const QRectF eraserBoundingRect = eraserPolygon.boundingRect();
    const QRectF eraserInnerRect = UBGeometryUtils::lineToInnerRect(line, pWidth);

    QPainterPath eraserPathVar;
    eraserPathVar.addPolygon(eraserPolygon);
    const QPainterPath eraserPath = eraserPathVar;

    QList<QGraphicsItem*> collidItems = items(eraserBoundingRect, Qt::IntersectsItemBoundingRect);

    QSet<QGraphicsItem*> toBeAddedItems;
    QSet<QGraphicsItem*> toBeRemovedItems;

    int collidItemsSize = collidItems.size();

    toBeAddedItems.reserve(collidItemsSize);
    toBeRemovedItems.reserve(collidItemsSize);

    if (mShouldUseOMP)
    {
#pragma omp parallel for
        for (int i = 0; i < collidItemsSize; i++)
        {
            UBGraphicsPolygonItem *collidingPolygonItem
                = qgraphicsitem_cast<UBGraphicsPolygonItem*> (collidItems.at(i));

            if (collidingPolygonItem)
            {
                if(eraserInnerRect.contains(collidingPolygonItem->boundingRect()))
                {
#pragma omp critical
                    toBeRemovedItems << collidingPolygonItem;
                }
                else
                {
                    QPolygonF collidingPolygon = collidingPolygonItem->polygon();
                    QPainterPath collidingPath;
                    collidingPath.addPolygon(collidingPolygon);

                    QPainterPath croppedPath = collidingPath.subtracted(eraserPath);
                    QPainterPath croppedPathSimplified = croppedPath.simplified();

                    if (croppedPath == collidingPath)
                    {
                        // NOOP
                    }
                    else if (croppedPathSimplified.isEmpty())
                    {
#pragma omp critical
                        toBeRemovedItems << collidingPolygonItem;
                    }
                    else
                    {
                        foreach(const QPolygonF &pol, croppedPathSimplified.toFillPolygons())
                        {
                            UBGraphicsPolygonItem* croppedPolygonItem = collidingPolygonItem->deepCopy(pol);
#pragma omp critical
                            toBeAddedItems << croppedPolygonItem;
                        }
#pragma omp critical
                        toBeRemovedItems << collidingPolygonItem;
                    }
                }
            }
        }
    }
    else
    {
        for (int i = 0; i < collidItemsSize; i++)
        {
            UBGraphicsPolygonItem *collidingPolygonItem
                = qgraphicsitem_cast<UBGraphicsPolygonItem*> (collidItems.at(i));

            if (collidingPolygonItem)
            {
                if(eraserInnerRect.contains(collidingPolygonItem->boundingRect()))
                {
                    toBeRemovedItems << collidingPolygonItem;
                }
                else
                {
                    QPolygonF collidingPolygon = collidingPolygonItem->polygon();
                    QPainterPath collidingPath;
                    collidingPath.addPolygon(collidingPolygon);

                    QPainterPath croppedPath = collidingPath.subtracted(eraserPath);
                    QPainterPath croppedPathSimplified = croppedPath.simplified();

                    if (croppedPath == collidingPath)
                    {
                        // NOOP
                    }
                    else if (croppedPathSimplified.isEmpty())
                    {
                        toBeRemovedItems << collidingPolygonItem;
                    }
                    else
                    {
                        foreach(const QPolygonF &pol, croppedPathSimplified.toFillPolygons())
                        {
                            UBGraphicsPolygonItem* croppedPolygonItem = collidingPolygonItem->deepCopy(pol);
                            toBeAddedItems << croppedPolygonItem;
                        }

                        toBeRemovedItems << collidingPolygonItem;
                    }
                }
            }
        }
    }

    addItems(toBeAddedItems);
    mAddedItems += toBeAddedItems;

    removeItems(toBeRemovedItems);
    mRemovedItems += toBeRemovedItems;

    mPreviousPoint = pEndPoint;
}


void UBGraphicsScene::drawArcTo(const QPointF& pCenterPoint, qreal pSpanAngle)
{
    if (mArcPolygonItem)
    {
        mAddedItems.remove(mArcPolygonItem);
        removeItem(mArcPolygonItem);
        mArcPolygonItem = 0;
    }
    qreal penWidth = UBSettings::settings()->currentPenWidth();
    penWidth /= UBApplication::boardController->systemScaleFactor();
    penWidth /= UBApplication::boardController->currentZoom();

    mArcPolygonItem = arcToPolygonItem(QLineF(pCenterPoint, mPreviousPoint), pSpanAngle, penWidth);
    mAddedItems.insert(mArcPolygonItem);
    addItem(mArcPolygonItem);

    setDocumentUpdated();
}


qreal UBGraphicsScene::getNextDrawingZIndex()
{
    mDrawingZIndex = mDrawingZIndex + 1.0;
    return mDrawingZIndex;
}


qreal UBGraphicsScene::getNextObjectZIndex()
{
    mObjectZIndex = mObjectZIndex + 1.0;
    return mObjectZIndex;
}


void UBGraphicsScene::setBackground(bool pIsDark, bool pIsCrossed)
{
    bool needRepaint = false;

    if (mDarkBackground != pIsDark)
    {
        mDarkBackground = pIsDark;

        if (mEraser)
        {
            if (mDarkBackground)
            {
                mEraser->setBrush(UBSettings::eraserBrushDarkBackground);
                mEraser->setPen(UBSettings::eraserPenDarkBackground);
            }
            else
            {
                mEraser->setBrush(UBSettings::eraserBrushLightBackground);
                mEraser->setPen(UBSettings::eraserPenLightBackground);
            }
        }

        recolorAllItems();

        needRepaint = true;
        setModified(true);
    }

    if (mCrossedBackground != pIsCrossed)
    {
        mCrossedBackground = pIsCrossed;
        needRepaint = true;
        setModified(true);
    }

    if (needRepaint)
    {
        foreach(QGraphicsView* view, views())
        {
            view->resetCachedContent();
        }
    }
}


void UBGraphicsScene::recolorAllItems()
{
    QMap<QGraphicsView*, QGraphicsView::ViewportUpdateMode> previousUpdateModes;
    foreach(QGraphicsView* view, views())
    {
        previousUpdateModes.insert(view, view->viewportUpdateMode());
        view->setViewportUpdateMode(QGraphicsView::NoViewportUpdate);
    }

    for(int i = 0; i < mFastAccessItems.size(); i++)
    {
        UBGraphicsPolygonItem *polygonItem = qgraphicsitem_cast<UBGraphicsPolygonItem*> (mFastAccessItems.at(i));

        if (polygonItem)
        {
            QColor color;

            if (mDarkBackground)
            {
                color = polygonItem->colorOnDarkBackground();
            }
            else
            {
                color = polygonItem->colorOnLightBackground();
            }

            polygonItem->setColor(color);
            continue;
        }
    }

    foreach(QGraphicsView* view, views())
    {
        view->setViewportUpdateMode(previousUpdateModes.value(view));
    }
}


UBGraphicsPolygonItem* UBGraphicsScene::lineToPolygonItem(const QLineF &pLine, const qreal &pWidth)
{
    UBGraphicsPolygonItem *polygonItem = new UBGraphicsPolygonItem(pLine, pWidth);

    initPolygonItem(polygonItem);

    return polygonItem;
}


void UBGraphicsScene::initPolygonItem(UBGraphicsPolygonItem* polygonItem)
{
    QColor colorOnDarkBG;
    QColor colorOnLightBG;

    if (UBDrawingController::drawingController()->stylusTool() == UBStylusTool::Marker)
    {
        colorOnDarkBG = UBApplication::boardController->markerColorOnDarkBackground();
        colorOnLightBG = UBApplication::boardController->markerColorOnLightBackground();
    }
    else // settings->stylusTool() == UBStylusTool::Pen + failsafe
    {
        colorOnDarkBG = UBApplication::boardController->penColorOnDarkBackground();
        colorOnLightBG = UBApplication::boardController->penColorOnLightBackground();
    }

    if (mDarkBackground)
    {
        polygonItem->setColor(colorOnDarkBG);
    }
    else
    {
        polygonItem->setColor(colorOnLightBG);
    }

    polygonItem->setColorOnDarkBackground(colorOnDarkBG);
    polygonItem->setColorOnLightBackground(colorOnLightBG);

    polygonItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Graphic));

//    polygonItem->setZValue(getNextDrawingZIndex());
    UBGraphicsItem::assignZValue(polygonItem, getNextDrawingZIndex());
}


UBGraphicsPolygonItem* UBGraphicsScene::arcToPolygonItem(const QLineF& pStartRadius, qreal pSpanAngle, qreal pWidth)
{
    QPolygonF polygon = UBGeometryUtils::arcToPolygon(pStartRadius, pSpanAngle, pWidth);

    return polygonToPolygonItem(polygon);
}


UBGraphicsPolygonItem* UBGraphicsScene::polygonToPolygonItem(const QPolygonF pPolygon)
{
    UBGraphicsPolygonItem *polygonItem = new UBGraphicsPolygonItem(pPolygon);

    initPolygonItem(polygonItem);

    return polygonItem;
}


void UBGraphicsScene::hideEraser()
{
    if (mEraser)
        mEraser->hide();
}


void UBGraphicsScene::leaveEvent(QEvent * event)
{
    Q_UNUSED(event);
    hideEraser();
}


UBGraphicsScene* UBGraphicsScene::sceneDeepCopy() const
{
    UBGraphicsScene* copy = new UBGraphicsScene(this->document());

    copy->setBackground(this->isDarkBackground(), this->isCrossedBackground());

    copy->mDrawingZIndex = this->mDrawingZIndex;
    copy->mObjectZIndex = this->mObjectZIndex;
    copy->setSceneRect(this->sceneRect());

    if (this->mNominalSize.isValid())
        copy->setNominalSize(this->mNominalSize);

    QListIterator<QGraphicsItem*> itItems(this->mFastAccessItems);

    QMap<UBGraphicsStroke*, UBGraphicsStroke*> groupClone;

    while (itItems.hasNext())
    {
        QGraphicsItem* item = itItems.next();
        QGraphicsItem* cloneItem = 0;

        UBItem* ubItem = dynamic_cast<UBItem*>(item);
        UBGraphicsStroke* stroke = dynamic_cast<UBGraphicsStroke*>(item);

        if (ubItem && !stroke)
        {
            cloneItem = dynamic_cast<QGraphicsItem*>(ubItem->deepCopy());
        }

        if (cloneItem)
        {
            copy->addItem(cloneItem);

            if (isBackgroundObject(item))
            {
                copy->setAsBackgroundObject(cloneItem);
            }

            if (this->mTools.contains(item))
            {
                copy->mTools << cloneItem;
            }

            UBGraphicsPolygonItem* polygon = dynamic_cast<UBGraphicsPolygonItem*>(item);

            if(polygon)
            {
                UBGraphicsStroke* stroke = dynamic_cast<UBGraphicsStroke*>(item->parentItem());

                if (stroke)
                {
                    UBGraphicsStroke* cloneStroke = groupClone.value(stroke);

                    if (!cloneStroke)
                    {
                        cloneStroke = stroke->deepCopy();
                        groupClone.insert(stroke, cloneStroke);
                    }

                    polygon->setStroke(cloneStroke);
                }
            }
        }
    }

    // TODO UB 4.7 ... complete all members ?

    return copy;
}


UBItem* UBGraphicsScene::deepCopy() const
{
    return sceneDeepCopy();
}


void UBGraphicsScene::clearItemsAndAnnotations()
{
    deselectAllItems();

    QSet<QGraphicsItem*> emptyList;
    QSet<QGraphicsItem*> removedItems;

    QListIterator<QGraphicsItem*> itItems(mFastAccessItems);

    while (itItems.hasNext())
    {
        QGraphicsItem* item = itItems.next();

        if(!mTools.contains(item) && !isBackgroundObject(item))
        {
            removeItem(item);
            removedItems << item;
        }
    }

    // force refresh, QT is a bit lazy and take a lot of time (nb item ^2 ?) to trigger repaint
    update(sceneRect());

    if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
        UBGraphicsItemUndoCommand* uc = new UBGraphicsItemUndoCommand(this, removedItems, emptyList);
        UBApplication::undoStack->push(uc);
    }

    setDocumentUpdated();
}


void UBGraphicsScene::clearItems()
{
    deselectAllItems();

    QSet<QGraphicsItem*> emptyList;
    QSet<QGraphicsItem*> removedItems;

    QListIterator<QGraphicsItem*> itItems(mFastAccessItems);

    while (itItems.hasNext())
    {
        QGraphicsItem* item = itItems.next();

        if (!item->parentItem())
        {
            UBGraphicsPolygonItem* pi = qgraphicsitem_cast<UBGraphicsPolygonItem*>(item);

            if(!pi && !mTools.contains(item) && !isBackgroundObject(item))
            {
                removeItem(item);
                removedItems << item;
            }
        }
    }

    // force refresh, QT is a bit lazy and take a lot of time (nb item ^2 ?) to trigger repaint
    update(sceneRect());


    if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
        UBGraphicsItemUndoCommand* uc = new UBGraphicsItemUndoCommand(this, removedItems, emptyList);
        UBApplication::undoStack->push(uc);
    }

    setDocumentUpdated();
}


void UBGraphicsScene::clearAnnotations()
{
    QSet<QGraphicsItem*> emptyList;
    QSet<QGraphicsItem*> removedItems;

    QListIterator<QGraphicsItem*> itItems(mFastAccessItems);

    while (itItems.hasNext())
    {
        QGraphicsItem* item = itItems.next();
        UBGraphicsPolygonItem* pi = qgraphicsitem_cast<UBGraphicsPolygonItem*>(item);
        if (pi)
        {
            removeItem(item);
            removedItems << item;
        }
    }

    // force refresh, QT is a bit lazy and take a lot of time (nb item ^2 ?) to trigger repaint
    update(sceneRect());


    if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
        UBGraphicsItemUndoCommand* uc = new UBGraphicsItemUndoCommand(this, removedItems, emptyList);
        UBApplication::undoStack->push(uc);
    }

    setDocumentUpdated();
}


UBGraphicsPixmapItem* UBGraphicsScene::addPixmap(const QPixmap& pPixmap, QGraphicsItem* replaceFor, const QPointF& pPos, qreal pScaleFactor, bool pUseAnimation)
{
    UBGraphicsPixmapItem* pixmapItem = new UBGraphicsPixmapItem();

    pixmapItem->setFlag(QGraphicsItem::ItemIsMovable, true);
    pixmapItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
//    pixmapItem->setZValue(getNextObjectZIndex());
    UBGraphicsItem::assignZValue(pixmapItem, getNextObjectZIndex());

    pixmapItem->setPixmap(pPixmap);

    QPointF half(pPixmap.width() * pScaleFactor / 2, pPixmap.height()  * pScaleFactor / 2);
    pixmapItem->setPos(pPos - half);

    addItem(pixmapItem);

    if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
        UBGraphicsItemUndoCommand* uc = new UBGraphicsItemUndoCommand(this, replaceFor, pixmapItem);
        UBApplication::undoStack->push(uc);
    }

    pixmapItem->scale(pScaleFactor, pScaleFactor);

    if (pUseAnimation)
    {
        pixmapItem->setOpacity(0);

        QPropertyAnimation *animation = new QPropertyAnimation(pixmapItem, "opacity");
        animation->setDuration(1000);
        animation->setStartValue(0.0);
        animation->setEndValue(1.0);

        animation->start();
    }

    pixmapItem->show();
    setDocumentUpdated();

    return pixmapItem;
}


void UBGraphicsScene::textUndoCommandAdded(UBGraphicsTextItem *textItem)
{
    if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
        UBGraphicsTextItemUndoCommand* uc = new UBGraphicsTextItemUndoCommand(textItem);
        UBApplication::undoStack->push(uc);
    }
}


UBGraphicsVideoItem* UBGraphicsScene::addVideo(const QUrl& pVideoFileUrl, bool shouldPlayAsap, const QPointF& pPos)
{
    UBGraphicsVideoItem* videoItem = new UBGraphicsVideoItem(pVideoFileUrl);

    videoItem->setPos(pPos);

    videoItem->setFlag(QGraphicsItem::ItemIsMovable, true);
    videoItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
//    videoItem->setZValue(getNextObjectZIndex());
    UBGraphicsItem::assignZValue(videoItem, getNextObjectZIndex());

    addItem(videoItem);

    videoItem->show();

    if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
        UBGraphicsItemUndoCommand* uc = new UBGraphicsItemUndoCommand(this, 0, videoItem);
        UBApplication::undoStack->push(uc);
    }

    videoItem->mediaObject()->play();

    if (!shouldPlayAsap)
    {
        videoItem->mediaObject()->pause();
        videoItem->mediaObject()->seek(0);
    }

    setDocumentUpdated();

    return videoItem;
}

UBGraphicsAudioItem* UBGraphicsScene::addAudio(const QUrl& pAudioFileUrl, bool shouldPlayAsap, const QPointF& pPos)
{
    UBGraphicsAudioItem* audioItem = new UBGraphicsAudioItem(pAudioFileUrl);

    audioItem->setPos(pPos);

    audioItem->setFlag(QGraphicsItem::ItemIsMovable, true);
    audioItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
//    audioItem->setZValue(getNextObjectZIndex());
    UBGraphicsItem::assignZValue(audioItem, getNextObjectZIndex());

    addItem(audioItem);

    audioItem->show();

    if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
        UBGraphicsItemUndoCommand* uc = new UBGraphicsItemUndoCommand(this, 0, audioItem);
        UBApplication::undoStack->push(uc);
    }

    audioItem->mediaObject()->play();

    if (!shouldPlayAsap)
    {
        audioItem->mediaObject()->pause();
        audioItem->mediaObject()->seek(0);
    }

    setDocumentUpdated();

    return audioItem;
}


UBGraphicsWidgetItem* UBGraphicsScene::addWidget(const QUrl& pWidgetUrl, const QPointF& pPos)
{
    int widgetType = UBAbstractWidget::widgetType(pWidgetUrl);

    if(widgetType == UBWidgetType::Apple)
    {
        return addAppleWidget(pWidgetUrl, pPos);
    }
    else if(widgetType == UBWidgetType::W3C)
    {
        return addW3CWidget(pWidgetUrl, pPos);
    }
    else
    {
        qDebug() << "UBGraphicsScene::addWidget: Unknown widget Type";
        return 0;
    }
}


UBGraphicsAppleWidgetItem* UBGraphicsScene::addAppleWidget(const QUrl& pWidgetUrl, const QPointF& pPos)
{
    UBGraphicsAppleWidgetItem *appleWidget = new UBGraphicsAppleWidgetItem(pWidgetUrl);

    addGraphicsWidget(appleWidget, pPos);

    return appleWidget;
}


UBGraphicsW3CWidgetItem* UBGraphicsScene::addW3CWidget(const QUrl& pWidgetUrl, const QPointF& pPos, int widgetType)
{
    UBGraphicsW3CWidgetItem *w3CWidget = new UBGraphicsW3CWidgetItem(pWidgetUrl, 0, widgetType);

    addGraphicsWidget(w3CWidget, pPos);

    return w3CWidget;
}

void UBGraphicsScene::addGraphicsWidget(UBGraphicsWidgetItem* graphicsWidget, const QPointF& pPos)
{
    graphicsWidget->setFlag(QGraphicsItem::ItemIsSelectable, true);
//    graphicsWidget->setZValue(getNextObjectZIndex());
    UBGraphicsItem::assignZValue(graphicsWidget, getNextObjectZIndex());

    addItem(graphicsWidget);

    qreal ssf = 1 / UBApplication::boardController->systemScaleFactor();

    graphicsWidget->scale(ssf, ssf);

    graphicsWidget->setPos(QPointF(pPos.x() - graphicsWidget->boundingRect().width() / 2,
        pPos.y() - graphicsWidget->boundingRect().height() / 2));

    if (graphicsWidget->widgetWebView()->canBeContent())
    {
//        graphicsWidget->widgetWebView()->loadMainHtml();

        graphicsWidget->setSelected(true);
        if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
            UBGraphicsItemUndoCommand* uc = new UBGraphicsItemUndoCommand(this, 0, graphicsWidget);
            UBApplication::undoStack->push(uc);
        }

        setDocumentUpdated();
    }
    else
    {
        UBApplication::boardController->moveGraphicsWidgetToControlView(graphicsWidget);
    }
}


UBGraphicsW3CWidgetItem* UBGraphicsScene::addOEmbed(const QUrl& pContentUrl, const QPointF& pPos)
{
    QStringList widgetPaths = UBPersistenceManager::persistenceManager()->allWidgets(UBSettings::settings()->applicationApplicationsLibraryDirectory());

    UBGraphicsW3CWidgetItem *widget = 0;

    foreach(QString widgetPath, widgetPaths)
    {
        if (widgetPath.contains("VideoPicker"))
        {
            widget = addW3CWidget(QUrl::fromLocalFile(widgetPath), pPos);

            if (widget)
            {
                widget->setPreference("oembedUrl", pContentUrl.toString());
                setDocumentUpdated();
                break;
            }
        }
    }

    return widget;
}

UBGraphicsSvgItem* UBGraphicsScene::addSvg(const QUrl& pSvgFileUrl, const QPointF& pPos)
{
    QString path = pSvgFileUrl.toLocalFile();

    UBGraphicsSvgItem *svgItem = new UBGraphicsSvgItem(path);

    svgItem->setFlag(QGraphicsItem::ItemIsMovable, true);
    svgItem->setFlag(QGraphicsItem::ItemIsSelectable, true);
//    svgItem->setZValue(getNextObjectZIndex());
    UBGraphicsItem::assignZValue(svgItem, getNextObjectZIndex());

    qreal sscale = 1 / UBApplication::boardController->systemScaleFactor();
    svgItem->scale(sscale, sscale);

    QPointF half(svgItem->boundingRect().width() / 2, svgItem->boundingRect().height() / 2);
    svgItem->setPos(pPos - half);
    svgItem->show();

    addItem(svgItem);

    if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
        UBGraphicsItemUndoCommand* uc = new UBGraphicsItemUndoCommand(this, 0, svgItem);
        UBApplication::undoStack->push(uc);
    }

    setDocumentUpdated();

    return svgItem;
}


UBGraphicsTextItem* UBGraphicsScene::addText(const QString& pString, const QPointF& pTopLeft)
{
    return addTextWithFont(pString, pTopLeft, UBSettings::settings()->fontPixelSize()
            , UBSettings::settings()->fontFamily(), UBSettings::settings()->isBoldFont()
            , UBSettings::settings()->isItalicFont());
}


UBGraphicsTextItem* UBGraphicsScene::addTextWithFont(const QString& pString, const QPointF& pTopLeft
            , int pointSize, const QString& fontFamily, bool bold, bool italic)
{
    UBGraphicsTextItem *textItem = new UBGraphicsTextItem();
    textItem->setPlainText(pString);
//    textItem->setZValue(getNextObjectZIndex());
    UBGraphicsItem::assignZValue(textItem, getNextObjectZIndex());

    QFont font = textItem->font();

    if (fontFamily == "")
    {
        font = QFont(UBSettings::settings()->fontFamily());
    }
    else
    {
        font = QFont(fontFamily);
    }

    if (pointSize < 1)
    {
        font.setPixelSize(UBSettings::settings()->fontPixelSize());
    }
    else
    {
        font.setPointSize(pointSize);
    }

    font.setBold(bold);
    font.setItalic(italic);
    textItem->setFont(font);

    QFontMetrics fi(font);
    QRect br = fi.boundingRect(pString);

    textItem->setTextWidth(qMax((qreal)br.width() + 50, (qreal)200));
    textItem->setTextHeight(br.height());

    addItem(textItem);

    textItem->setPos(pTopLeft);

    textItem->show();

    if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
        UBGraphicsItemUndoCommand* uc = new UBGraphicsItemUndoCommand(this, 0, textItem);
        UBApplication::undoStack->push(uc);
    }

    connect(textItem, SIGNAL(textUndoCommandAdded(UBGraphicsTextItem *)), this, SLOT(textUndoCommandAdded(UBGraphicsTextItem *)));

    textItem->setSelected(true);
    textItem->setFocus();

    setDocumentUpdated();

    return textItem;
}
UBGraphicsTextItem *UBGraphicsScene::addTextHtml(const QString &pString, const QPointF& pTopLeft)
{
    UBGraphicsTextItem *textItem = new UBGraphicsTextItem();
    textItem->setPlainText("");
    textItem->setHtml(pString);
//    textItem->setZValue(getNextObjectZIndex());
    UBGraphicsItem::assignZValue(textItem, getNextObjectZIndex());

    addItem(textItem);
    textItem->show();

    if (enableUndoRedoStack) { //should be deleted after scene own undo stack implemented
        UBGraphicsItemUndoCommand* uc = new UBGraphicsItemUndoCommand(this, 0, textItem);
        UBApplication::undoStack->push(uc);
    }

    connect(textItem, SIGNAL(textUndoCommandAdded(UBGraphicsTextItem *)),
            this,     SLOT(textUndoCommandAdded(UBGraphicsTextItem *)));

    textItem->setFocus();

    setDocumentUpdated();
    textItem->setPos(pTopLeft);

    return textItem;
}

void UBGraphicsScene::addItem(QGraphicsItem* item)
{
    setModified(true);
    UBCoreGraphicsScene::addItem(item);

    if (!mTools.contains(item))
      ++mItemCount;

    mFastAccessItems << item;
}

void UBGraphicsScene::addItems(const QSet<QGraphicsItem*>& items)
{
    setModified(true);

    foreach(QGraphicsItem* item, items)
        UBCoreGraphicsScene::addItem(item);

    mItemCount += items.size();

    mFastAccessItems += items.toList();
}

void UBGraphicsScene::removeItem(QGraphicsItem* item)
{
    setModified(true);
    UBCoreGraphicsScene::removeItem(item);

    if (!mTools.contains(item))
      --mItemCount;

    mFastAccessItems.removeAll(item);
}


void UBGraphicsScene::removeItems(const QSet<QGraphicsItem*>& items)
{
    setModified(true);

    foreach(QGraphicsItem* item, items)
        UBCoreGraphicsScene::removeItem(item);

    mItemCount -= items.size();

    foreach(QGraphicsItem* item, items)
        mFastAccessItems.removeAll(item);
}


void UBGraphicsScene::deselectAllItems()
{
    foreach(QGraphicsItem *gi, selectedItems ())
    {
        gi->setSelected(false);
    }
}

bool UBGraphicsScene::isEmpty() const
{
    return mItemCount == 0;
}

QGraphicsItem* UBGraphicsScene::setAsBackgroundObject(QGraphicsItem* item, bool pAdaptTransformation, bool pExpand)
{
    if (mBackgroundObject)
    {
        removeItem(mBackgroundObject);
        mBackgroundObject = 0;
    }

    if (item)
    {
        item->setFlag(QGraphicsItem::ItemIsSelectable, false);
        item->setFlag(QGraphicsItem::ItemIsMovable, false);
        item->setAcceptedMouseButtons(Qt::NoButton);
        item->setData(UBGraphicsItemData::ItemLayerType, UBItemLayerType::FixedBackground);

//        item->setZValue(backgroundLayerStart);
        UBGraphicsItem::assignZValue(item, backgroundLayerStart);

        if (pAdaptTransformation)
        {
            item = scaleToFitDocumentSize(item, true, 0, pExpand);
        }

        if (item->scene() != this)
            addItem(item);

        mBackgroundObject = item;

    }

    return item;
}


QRectF UBGraphicsScene::normalizedSceneRect(qreal ratio)
{

    QRectF normalizedRect(nominalSize().width() / -2, nominalSize().height() / -2,
        nominalSize().width(), nominalSize().height());

    foreach(QGraphicsItem* gi, mFastAccessItems)
    {
        if(gi && gi->isVisible() && !mTools.contains(gi))
        {
            normalizedRect = normalizedRect.united(gi->sceneBoundingRect());
        }
    }

    if (ratio > 0.0)
    {
        qreal normalizedRectRatio = normalizedRect.width() / normalizedRect.height();

        if (normalizedRectRatio > ratio)
        {
            //the normalized rect is too wide, we increase height
            qreal newHeight = normalizedRect.width() / ratio;
            qreal offset = (newHeight - normalizedRect.height()) / 2;
            normalizedRect.setY(normalizedRect.y() - offset);
            normalizedRect.setHeight(newHeight);
        }
        else if (normalizedRectRatio < ratio)
        {
            //the normalized rect is too high, we increase the width
            qreal newWidth = normalizedRect.height() * ratio;
            qreal offset = (newWidth - normalizedRect.width()) / 2;
            normalizedRect.setX(normalizedRect.x() - offset);
            normalizedRect.setWidth(newWidth);
        }
    }

    return normalizedRect;
}


void UBGraphicsScene::setDocument(UBDocumentProxy* pDocument)
{
    if (pDocument != mDocument)
    {
        if (mDocument)
        {
            setModified(true);
        }

        mDocument = pDocument;
        setParent(pDocument);
    }
}


QGraphicsItem* UBGraphicsScene::scaleToFitDocumentSize(QGraphicsItem* item, bool center, int margin, bool expand)
{
    int maxWidth = mNominalSize.width() - (margin * 2);
    int maxHeight = mNominalSize.height() - (margin * 2);

    QRectF size = item->sceneBoundingRect();

    if (expand || size.width() > maxWidth || size.height() > maxHeight)
    {
        qreal ratio = qMin(maxWidth / size.width(), maxHeight / size.height());

        item->scale(ratio, ratio);

        if(center)
        {
            item->setPos(item->sceneBoundingRect().width() / -2.0,
                item->sceneBoundingRect().height() / -2.0);
        }
    }

    return item;
}


void UBGraphicsScene::addRuler(QPointF center)
{
    UBGraphicsRuler* ruler = new UBGraphicsRuler(); // mem : owned and destroyed by the scene
    mTools << ruler;
    QRectF rect = ruler->rect();
    ruler->setRect(center.x() - rect.width()/2, center.y() - rect.height()/2, rect.width(), rect.height());

//    ruler->setZValue(toolLayerStart + toolOffsetRuler);
    UBGraphicsItem::assignZValue(ruler, toolLayerStart + toolOffsetRuler);

    ruler->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));

    addItem(ruler);

    ruler->setVisible(true);
    setModified(true);
}


void UBGraphicsScene::addProtractor(QPointF center)
{
    // Protractor

    UBGraphicsProtractor* protractor = new UBGraphicsProtractor(); // mem : owned and destroyed by the scene
    mTools << protractor;

//    protractor->setZValue(toolLayerStart + toolOffsetProtractor);
    UBGraphicsItem::assignZValue(protractor, toolLayerStart + toolOffsetProtractor);

    protractor->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));

    addItem(protractor);

    QPointF itemSceneCenter = protractor->sceneBoundingRect().center();
    protractor->moveBy(center.x() - itemSceneCenter.x(), center.y() - itemSceneCenter.y());

    protractor->setVisible(true);
    setModified(true);
}

void UBGraphicsScene::addTriangle(QPointF center)
{
    // Triangle

    UBGraphicsTriangle* triangle = new UBGraphicsTriangle(); // mem : owned and destroyed by the scene
    mTools << triangle;

    UBGraphicsItem::assignZValue(triangle, toolLayerStart + toolOffsetTriangle);

    triangle->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));

    addItem(triangle);

    QPointF itemSceneCenter = triangle->sceneBoundingRect().center();
    triangle->moveBy(center.x() - itemSceneCenter.x(), center.y() - itemSceneCenter.y());

    triangle->setVisible(true);
    setModified(true);
}

void UBGraphicsScene::addMagnifier(UBMagnifierParams params)
{
    // can have only one magnifier at one time
    if(magniferControlViewWidget) return;

    QWidget *cContainer = (QWidget*)(UBApplication::boardController->controlContainer());
    QGraphicsView *cView = (QGraphicsView*)UBApplication::boardController->controlView();
    QGraphicsView *dView = (QGraphicsView*)UBApplication::boardController->displayView();

    QPoint dvZeroPoint = dView->mapToGlobal(QPoint(0,0));

    int cvW = cView->width();
    int dvW = dView->width();
    qreal wCoeff = (qreal)dvW / (qreal)cvW;

    int cvH = cView->height();
    int dvH = dView->height();
    qreal hCoeff = (qreal)dvH / (qreal)cvH;

    QPoint ccPoint(params.x,params.y);
    QPoint globalPoint = cContainer->mapToGlobal(ccPoint);
    QPoint cvPoint = cView->mapFromGlobal(globalPoint);
    QPoint dvPoint( cvPoint.x() * wCoeff + dvZeroPoint.x(), cvPoint.y() * hCoeff + dvZeroPoint.y());

    magniferControlViewWidget = new UBMagnifier((QWidget*)(UBApplication::boardController->controlContainer()), true);
    magniferControlViewWidget->setGrabView((QGraphicsView*)UBApplication::boardController->controlView());
    magniferControlViewWidget->setMoveView((QWidget*)(UBApplication::boardController->controlContainer()));
    magniferControlViewWidget->setSize(params.sizePercentFromScene);
    magniferControlViewWidget->setZoom(params.zoom);

    magniferDisplayViewWidget = new UBMagnifier((QWidget*)(UBApplication::boardController->displayView()), false);
    magniferDisplayViewWidget->setGrabView((QGraphicsView*)UBApplication::boardController->controlView());
    magniferDisplayViewWidget->setMoveView((QGraphicsView*)UBApplication::boardController->displayView());
    magniferDisplayViewWidget->setSize(params.sizePercentFromScene);
    magniferDisplayViewWidget->setZoom(params.zoom);

    magniferControlViewWidget->grabNMove(globalPoint, globalPoint, true);
    magniferDisplayViewWidget->grabNMove(globalPoint, dvPoint, true);
    magniferControlViewWidget->show();
    magniferDisplayViewWidget->show();

    connect(magniferControlViewWidget, SIGNAL(magnifierMoved_Signal(QPoint)), this, SLOT(moveMagnifier(QPoint)));
    connect(magniferControlViewWidget, SIGNAL(magnifierClose_Signal()), this, SLOT(closeMagnifier()));
    connect(magniferControlViewWidget, SIGNAL(magnifierZoomIn_Signal()), this, SLOT(zoomInMagnifier()));
    connect(magniferControlViewWidget, SIGNAL(magnifierZoomOut_Signal()), this, SLOT(zoomOutMagnifier()));
    connect(magniferControlViewWidget, SIGNAL(magnifierResized_Signal(qreal)), this, SLOT(resizedMagnifier(qreal)));

    setModified(true);
}

void UBGraphicsScene::moveMagnifier(QPoint newPos)
{
    QWidget *cContainer = (QWidget*)(UBApplication::boardController->controlContainer());
    QGraphicsView *cView = (QGraphicsView*)UBApplication::boardController->controlView();
    QGraphicsView *dView = (QGraphicsView*)UBApplication::boardController->displayView();

    QPoint dvZeroPoint = dView->mapToGlobal(QPoint(0,0));

    int cvW = cView->width();
    int dvW = dView->width();
    qreal wCoeff = (qreal)dvW / (qreal)cvW;

    int cvH = cView->height();
    int dvH = dView->height();
    qreal hCoeff = (qreal)dvH / (qreal)cvH;

    QPoint globalPoint = cContainer->mapToGlobal(newPos);
    QPoint cvPoint = cView->mapFromGlobal(globalPoint);
    QPoint dvPoint( cvPoint.x() * wCoeff + dvZeroPoint.x(), cvPoint.y() * hCoeff + dvZeroPoint.y());

    magniferControlViewWidget->grabNMove(globalPoint, globalPoint, false, false);
    magniferDisplayViewWidget->grabNMove(globalPoint, dvPoint, false, true);

    setModified(true);
}

void UBGraphicsScene::closeMagnifier()
{
    DisposeMagnifierQWidgets();
}

void UBGraphicsScene::zoomInMagnifier()
{
    if(magniferControlViewWidget->params.zoom < 8)
    {
        magniferControlViewWidget->setZoom(magniferControlViewWidget->params.zoom + 0.5);
        magniferDisplayViewWidget->setZoom(magniferDisplayViewWidget->params.zoom + 0.5);
    }
}

void UBGraphicsScene::zoomOutMagnifier()
{
    if(magniferControlViewWidget->params.zoom > 1)
    {
        magniferControlViewWidget->setZoom(magniferControlViewWidget->params.zoom - 0.5);
        magniferDisplayViewWidget->setZoom(magniferDisplayViewWidget->params.zoom - 0.5);
    }
}

void UBGraphicsScene::resizedMagnifier(qreal newPercent)
{
    if(newPercent > 18 && newPercent < 50)
    {
        magniferControlViewWidget->setSize(newPercent);
        magniferControlViewWidget->grabPoint();
        magniferDisplayViewWidget->setSize(newPercent);
        magniferDisplayViewWidget->grabPoint();
    }
}

void UBGraphicsScene::addCompass(QPointF center)
{
    UBGraphicsCompass* compass = new UBGraphicsCompass(); // mem : owned and destroyed by the scene
    mTools << compass;
    addItem(compass);

    QRectF rect = compass->rect();
    compass->setRect(center.x() - rect.width() / 2, center.y() - rect.height() / 2, rect.width(), rect.height());

//    compass->setZValue(toolLayerStart + toolOffsetCompass);
    UBGraphicsItem::assignZValue(compass, toolLayerStart + toolOffsetCompass);

    compass->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));

    compass->setVisible(true);
    setModified(true);
}

void UBGraphicsScene::addCache()
{
    UBGraphicsCache* cache = new UBGraphicsCache();
    mTools << cache;

    addItem(cache);

    cache->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));

    cache->setVisible(true);
    cache->setSelected(true);
    UBApplication::boardController->notifyCache(true);
    UBApplication::boardController->notifyPageChanged();
}

void UBGraphicsScene::addMask(const QPointF &center)
{ 
    UBGraphicsCurtainItem* curtain = new UBGraphicsCurtainItem(); // mem : owned and destroyed by the scene
    mTools << curtain;

	addItem(curtain);

    QRectF rect = UBApplication::boardController->activeScene()->normalizedSceneRect();
    rect.setRect(center.x() - rect.width()/4, center.y() - rect.height()/4, rect.width()/2 , rect.height()/2);
    curtain->setRect(rect);
    UBGraphicsItem::assignZValue(curtain, toolLayerStart + toolOffsetCurtain);

    curtain->setVisible(true);
    curtain->setSelected(true);
    setModified(true);
}


void UBGraphicsScene::setRenderingQuality(UBItem::RenderingQuality pRenderingQuality)
{
    QListIterator<QGraphicsItem*> itItems(mFastAccessItems);

    while (itItems.hasNext())
    {
        QGraphicsItem *gItem =  itItems.next();

        UBItem *ubItem = dynamic_cast<UBItem*>(gItem);

        if (ubItem)
        {
            ubItem->setRenderingQuality(pRenderingQuality);
        }
    }
}


QList<QUrl> UBGraphicsScene::relativeDependencies() const
{
    QList<QUrl> relativePathes;

    QListIterator<QGraphicsItem*> itItems(mFastAccessItems);

    while (itItems.hasNext())
    {
        UBGraphicsVideoItem *videoItem = qgraphicsitem_cast<UBGraphicsVideoItem*> (itItems.next());

        if (videoItem && videoItem->mediaFileUrl().isRelative())
        {
            relativePathes << videoItem->mediaFileUrl();
        }
    }

    return relativePathes;
}


QSize UBGraphicsScene::nominalSize()
{
    if (mDocument && !mNominalSize.isValid())
    {
        mNominalSize = mDocument->defaultDocumentSize();
    }

    return mNominalSize;
}


void UBGraphicsScene::setNominalSize(const QSize& pSize)
{
    if (nominalSize() != pSize)
    {
        mNominalSize = pSize;
        emit pageSizeChanged();

        if(mDocument)
            mDocument->setDefaultDocumentSize(pSize);

    }
}


void UBGraphicsScene::setNominalSize(int pWidth, int pHeight)
{
     setNominalSize(QSize(pWidth, pHeight));
}


QGraphicsItem* UBGraphicsScene::rootItem(QGraphicsItem* item) const
{
    QGraphicsItem* root = item;

    while (root->parentItem())
    {
        root = root->parentItem();
    }

    return root;
}


void UBGraphicsScene::drawItems (QPainter * painter, int numItems,
        QGraphicsItem * items[], const QStyleOptionGraphicsItem options[], QWidget * widget)
{
    if (mRenderingContext == NonScreen || mRenderingContext == PdfExport)
    {
        int count = 0;

        QGraphicsItem** itemsFiltered = new QGraphicsItem*[numItems];
        QStyleOptionGraphicsItem *optionsFiltered = new QStyleOptionGraphicsItem[numItems];

        for (int i = 0; i < numItems; i++)
        {
            if (!mTools.contains(rootItem(items[i])))
            {
                UBGraphicsPDFItem *pdfItem = qgraphicsitem_cast<UBGraphicsPDFItem*> (items[i]);
                if(!pdfItem || mRenderingContext == NonScreen)
                {
                    itemsFiltered[count] = items[i];
                    optionsFiltered[count] = options[i];
                    count++;
                }
            }
        }

        QGraphicsScene::drawItems(painter, count, itemsFiltered, optionsFiltered, widget);

        delete[] optionsFiltered;
        delete[] itemsFiltered;

    }
    else if (mRenderingContext == Podcast)
    {
        int count = 0;

        QGraphicsItem** itemsFiltered = new QGraphicsItem*[numItems];
        QStyleOptionGraphicsItem *optionsFiltered = new QStyleOptionGraphicsItem[numItems];

        for (int i = 0; i < numItems; i++)
        {
            bool ok;
            int itemLayerType = items[i]->data(UBGraphicsItemData::ItemLayerType).toInt(&ok);
            if (ok && (itemLayerType >= UBItemLayerType::FixedBackground && itemLayerType <= UBItemLayerType::Tool))
            {
                itemsFiltered[count] = items[i];
                optionsFiltered[count] = options[i];
                count++;
            }
        }

        QGraphicsScene::drawItems(painter, count, itemsFiltered, optionsFiltered, widget);

        delete[] optionsFiltered;
        delete[] itemsFiltered;

    }
    else
    {
        QGraphicsScene::drawItems(painter, numItems, items, options, widget);
    }
}


void UBGraphicsScene::keyReleaseEvent(QKeyEvent * keyEvent)
{

    QList<QGraphicsItem*> si = selectedItems();

    if ((si.size() > 0) && (keyEvent->isAccepted()))
    {
#ifdef Q_OS_MAC
        if (keyEvent->key() == Qt::Key_Backspace)
#else
        if (keyEvent->matches(QKeySequence::Delete))
#endif
        {
            foreach(QGraphicsItem* item, si)
            {
                switch (item->type())
                {
                case UBGraphicsW3CWidgetItem::Type:
                    {
                        UBGraphicsW3CWidgetItem *wc3_widget = dynamic_cast<UBGraphicsW3CWidgetItem*>(item);
                        if (0 != wc3_widget)
                        if (!wc3_widget->hasFocus())
                            wc3_widget->remove();                                                             
                        break;
                    }
                case UBGraphicsAppleWidgetItem::Type:
                    {
                        UBGraphicsAppleWidgetItem *Apple_widget = dynamic_cast<UBGraphicsAppleWidgetItem*>(item);
                        if (0 !=Apple_widget)
                        if (!Apple_widget->hasFocus())
                            Apple_widget->remove();                          
                        break;
                    }
                case UBGraphicsTextItem::Type:
                    {
                        UBGraphicsTextItem *text_item = dynamic_cast<UBGraphicsTextItem*>(item);
                        if (0 != text_item)
                        if (!text_item->hasFocus())
                            text_item->remove();                              
                        break;
                    }

                default:
                    {
                        UBGraphicsItem *ubgi = dynamic_cast<UBGraphicsItem*>(item);
                        if (0 != ubgi)
                            ubgi->remove();
                        else
                            UBCoreGraphicsScene::removeItem(item);      
                    }
                }
            }
        }

        keyEvent->accept();
    }

    QGraphicsScene::keyReleaseEvent(keyEvent);
}


void UBGraphicsScene::setDocumentUpdated()
{
    if (document())
        document()->setMetaData(UBSettings::documentUpdatedAt
                , UBStringUtils::toUtcIsoDateTime(QDateTime::currentDateTime()));
}


void UBGraphicsScene::setToolCursor(int tool)
{
    if (tool != (int)UBStylusTool::Selector
            && tool != (int)UBStylusTool::Text)
    {
        deselectAllItems();
    }

    if (tool != (int)UBStylusTool::Eraser)
    {
        hideEraser();
    }
}
