/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
 
#include <svx/dlgctl3d.hxx>
#include <svx/strings.hrc>
#include <svx/view3d.hxx>
#include <svx/fmmodel.hxx>
#include <svl/itempool.hxx>
#include <svx/fmpage.hxx>
#include <svx/sphere3d.hxx>
#include <svx/cube3d.hxx>
#include <svx/scene3d.hxx>
#include <vcl/svapp.hxx>
#include <svx/helperhittest3d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <polygn3d.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xflclit.hxx>
#include <svx/xlineit0.hxx>
#include <svx/xlnclit.hxx>
#include <svx/xlnwtit.hxx>
#include <helpids.h>
#include <svx/dialmgr.hxx>
#include <tools/helpers.hxx>
#include <vcl/settings.hxx>
 
using namespace com::sun::star;
 
Svx3DPreviewControl::Svx3DPreviewControl()
    : mnObjectType(SvxPreviewObjectType::SPHERE)
{
}
 
void Svx3DPreviewControl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
{
    Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(80, 100), MapMode(MapUnit::MapAppFont)));
    pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
    CustomWidgetController::SetDrawingArea(pDrawingArea);
    SetOutputSizePixel(aSize);
 
    Construct();
}
 
void Svx3DPreviewControl::ClearPageView()
{
    mp3DView->ClearPageView();
}
 
Svx3DPreviewControl::~Svx3DPreviewControl()
{
    mp3DObj.clear();
    mxFmPage.clear();
    mpScene.clear();
    mp3DView.reset();
    mpModel.reset();
}
 
void Svx3DPreviewControl::Construct()
{
    // Do never mirror the preview window.  This explicitly includes right
    // to left writing environments.
    EnableRTL (false);
    OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
    rDevice.SetMapMode(MapMode(MapUnit::Map100thMM));
 
    // Model
    mpModel.reset(new FmFormModel());
 
    // Page
    mxFmPage = new FmFormPage( *mpModel );
    mpModel->InsertPage( mxFmPage.get(), 0 );
 
    // 3D View
    mp3DView.reset(new E3dView(*mpModel, &rDevice));
    mp3DView->SetBufferedOutputAllowed(true);
    mp3DView->SetBufferedOverlayAllowed(true);
 
    // 3D Scene
    mpScene = new E3dScene(*mpModel);
 
    // initially create object
    SetObjectType(SvxPreviewObjectType::SPHERE);
 
    // camera and perspective
    Camera3D rCamera  = mpScene->GetCamera();
    const basegfx::B3DRange& rVolume = mpScene->GetBoundVolume();
    double fW = rVolume.getWidth();
    double fH = rVolume.getHeight();
    double fCamZ = rVolume.getMaxZ() + ((fW + fH) / 2.0);
 
    rCamera.SetAutoAdjustProjection(false);
    rCamera.SetViewWindow(- fW / 2, - fH / 2, fW, fH);
    basegfx::B3DPoint aLookAt;
    double fDefaultCamPosZ = mp3DView->GetDefaultCamPosZ();
    basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ);
    rCamera.SetPosAndLookAt(aCamPos, aLookAt);
    double fDefaultCamFocal = mp3DView->GetDefaultCamFocal();
    rCamera.SetFocalLength(fDefaultCamFocal);
 
    mpScene->SetCamera( rCamera );
    mxFmPage->InsertObject( mpScene.get() );
 
    basegfx::B3DHomMatrix aRotation;
    aRotation.rotate(basegfx::deg2rad( 25 ), 0.0, 0.0);
    aRotation.rotate(0.0, basegfx::deg2rad( 40 ), 0.0);
    mpScene->SetTransform(aRotation * mpScene->GetTransform());
 
    // invalidate SnapRects of objects
    mpScene->SetBoundAndSnapRectsDirty();
 
    SfxItemSetFixed<XATTR_LINESTYLE, XATTR_LINESTYLE,
        XATTR_FILL_FIRST, XATTR_FILLBITMAP> aSet( mpModel->GetItemPool() );
    aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
    aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) );
    aSet.Put( XFillColorItem( u""_ustr, COL_WHITE ) );
 
    mpScene->SetMergedItemSet(aSet);
 
    // PageView
    SdrPageView* pPageView = mp3DView->ShowSdrPage( mxFmPage.get() );
    mp3DView->hideMarkHandles();
 
    // mark scene
    mp3DView->MarkObj( mpScene.get(), pPageView );
}
 
void Svx3DPreviewControl::Resize()
{
    // size of page
    Size aSize(GetOutputSizePixel());
    aSize = GetDrawingArea()->get_ref_device().PixelToLogic(aSize);
    mxFmPage->SetSize(aSize);
 
    // set size
    Size aObjSize( aSize.Width()*5/6, aSize.Height()*5/6 );
    Point aObjPoint( (aSize.Width() - aObjSize.Width()) / 2,
        (aSize.Height() - aObjSize.Height()) / 2);
    tools::Rectangle aRect( aObjPoint, aObjSize);
    mpScene->SetSnapRect( aRect );
}
 
void Svx3DPreviewControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    mp3DView->CompleteRedraw(&rRenderContext, vcl::Region(rRect));
}
 
bool Svx3DPreviewControl::MouseButtonDown(const MouseEvent& rMEvt)
{
    if (rMEvt.IsShift() && rMEvt.IsMod1())
    {
        if(SvxPreviewObjectType::SPHERE == GetObjectType())
        {
            SetObjectType(SvxPreviewObjectType::CUBE);
        }
        else
        {
            SetObjectType(SvxPreviewObjectType::SPHERE);
        }
    }
    return false;
}
 
void Svx3DPreviewControl::SetObjectType(SvxPreviewObjectType nType)
{
    if(mnObjectType == nType && mp3DObj)
        return;
 
    SfxItemSetFixed<SDRATTR_START, SDRATTR_END> aSet(mpModel->GetItemPool());
    mnObjectType = nType;
 
    if( mp3DObj )
    {
        aSet.Put(mp3DObj->GetMergedItemSet());
        mpScene->RemoveObject( mp3DObj->GetOrdNum() );
        mp3DObj.clear();
    }
 
    switch( nType )
    {
        case SvxPreviewObjectType::SPHERE:
        {
            mp3DObj = new E3dSphereObj(
                *mpModel,
                mp3DView->Get3DDefaultAttributes(),
                basegfx::B3DPoint( 0, 0, 0 ),
                basegfx::B3DVector( 5000, 5000, 5000 ));
        }
        break;
 
        case SvxPreviewObjectType::CUBE:
        {
            mp3DObj = new E3dCubeObj(
                *mpModel,
                mp3DView->Get3DDefaultAttributes(),
                basegfx::B3DPoint( -2500, -2500, -2500 ),
                basegfx::B3DVector( 5000, 5000, 5000 ));
        }
        break;
    }
 
    if (mp3DObj)
    {
        mpScene->InsertObject( mp3DObj.get() );
        mp3DObj->SetMergedItemSet(aSet);
    }
 
    Invalidate();
}
 
SfxItemSet const & Svx3DPreviewControl::Get3DAttributes() const
{
    return mp3DObj->GetMergedItemSet();
}
 
void Svx3DPreviewControl::Set3DAttributes( const SfxItemSet& rAttr )
{
    mp3DObj->SetMergedItemSet(rAttr, true);
    Resize();
    Invalidate();
}
 
#define RADIUS_LAMP_PREVIEW_SIZE    (4500.0)
#define RADIUS_LAMP_SMALL           (600.0)
#define RADIUS_LAMP_BIG             (1000.0)
#define NO_LIGHT_SELECTED           (0xffffffff)
#define MAX_NUMBER_LIGHTS              (8)
 
const sal_Int32 g_nInteractionStartDistance = 5 * 5 * 2;
 
Svx3DLightControl::Svx3DLightControl()
:   maSelectedLight(NO_LIGHT_SELECTED),
    maLightObjects(MAX_NUMBER_LIGHTS, nullptr),
    mfRotateX(-20.0),
    mfRotateY(45.0),
    mfRotateZ(0.0),
    mfSaveActionStartHor(0.0),
    mfSaveActionStartVer(0.0),
    mfSaveActionStartRotZ(0.0),
    mbMouseMoved(false),
    mbMouseCaptured(false),
    mbGeometrySelected(false)
{
}
 
void Svx3DLightControl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
{
    Svx3DPreviewControl::SetDrawingArea(pDrawingArea);
    Construct2();
}
 
void Svx3DLightControl::Construct2()
{
    {
        // hide all page stuff, use control background (normally gray)
        const Color aDialogColor(Application::GetSettings().GetStyleSettings().GetDialogColor());
        mp3DView->SetPageVisible(false);
        mp3DView->SetApplicationBackgroundColor(aDialogColor);
        mp3DView->SetApplicationDocumentColor(aDialogColor);
    }
 
    {
        // create invisible expansion object
        const double fMaxExpansion(RADIUS_LAMP_BIG + RADIUS_LAMP_PREVIEW_SIZE);
        mpExpansionObject = new E3dCubeObj(
            *mpModel,
            mp3DView->Get3DDefaultAttributes(),
            basegfx::B3DPoint(-fMaxExpansion, -fMaxExpansion, -fMaxExpansion),
            basegfx::B3DVector(2.0 * fMaxExpansion, 2.0 * fMaxExpansion, 2.0 * fMaxExpansion));
        mpScene->InsertObject( mpExpansionObject.get() );
        SfxItemSet aSet(mpModel->GetItemPool());
        aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
        aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
        mpExpansionObject->SetMergedItemSet(aSet);
    }
 
    {
        // create lamp control object (Yellow lined object)
        // base circle
        const basegfx::B2DPolygon a2DCircle(basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), RADIUS_LAMP_PREVIEW_SIZE));
        basegfx::B3DPolygon a3DCircle(basegfx::utils::createB3DPolygonFromB2DPolygon(a2DCircle));
        basegfx::B3DHomMatrix aTransform;
 
        aTransform.rotate(M_PI_2, 0.0, 0.0);
        aTransform.translate(0.0, -RADIUS_LAMP_PREVIEW_SIZE, 0.0);
        a3DCircle.transform(aTransform);
 
        // create object for it
        mpLampBottomObject = new E3dPolygonObj(
            *mpModel,
            basegfx::B3DPolyPolygon(a3DCircle));
        mpScene->InsertObject( mpLampBottomObject.get() );
 
        // half circle with stand
        basegfx::B2DPolygon a2DHalfCircle;
        a2DHalfCircle.append(basegfx::B2DPoint(RADIUS_LAMP_PREVIEW_SIZE, 0.0));
        a2DHalfCircle.append(basegfx::B2DPoint(RADIUS_LAMP_PREVIEW_SIZE, -RADIUS_LAMP_PREVIEW_SIZE));
        a2DHalfCircle.append(basegfx::utils::createPolygonFromEllipseSegment(
            basegfx::B2DPoint(0.0, 0.0), RADIUS_LAMP_PREVIEW_SIZE, RADIUS_LAMP_PREVIEW_SIZE, 2 * M_PI - M_PI_2, M_PI_2));
        basegfx::B3DPolygon a3DHalfCircle(basegfx::utils::createB3DPolygonFromB2DPolygon(a2DHalfCircle));
 
        // create object for it
        mpLampShaftObject = new E3dPolygonObj(
            *mpModel,
            basegfx::B3DPolyPolygon(a3DHalfCircle));
        mpScene->InsertObject( mpLampShaftObject.get() );
 
        // initially invisible
        SfxItemSet aSet(mpModel->GetItemPool());
        aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
        aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
 
        mpLampBottomObject->SetMergedItemSet(aSet);
        mpLampShaftObject->SetMergedItemSet(aSet);
    }
 
    {
        // change camera settings
        Camera3D rCamera  = mpScene->GetCamera();
        const basegfx::B3DRange& rVolume = mpScene->GetBoundVolume();
        double fW = rVolume.getWidth();
        double fH = rVolume.getHeight();
        double fCamZ = rVolume.getMaxZ() + ((fW + fH) / 2.0);
 
        rCamera.SetAutoAdjustProjection(false);
        rCamera.SetViewWindow(- fW / 2, - fH / 2, fW, fH);
        basegfx::B3DPoint aLookAt;
        double fDefaultCamPosZ = mp3DView->GetDefaultCamPosZ();
        basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ);
        rCamera.SetPosAndLookAt(aCamPos, aLookAt);
        double fDefaultCamFocal = mp3DView->GetDefaultCamFocal();
        rCamera.SetFocalLength(fDefaultCamFocal);
 
        mpScene->SetCamera( rCamera );
 
        basegfx::B3DHomMatrix aNeutral;
        mpScene->SetTransform(aNeutral);
    }
 
    // invalidate SnapRects of objects
    mpScene->SetBoundAndSnapRectsDirty();
}
 
void Svx3DLightControl::ConstructLightObjects()
{
    for(sal_uInt32 a(0); a < MAX_NUMBER_LIGHTS; a++)
    {
        // get rid of possible existing light object
        if(maLightObjects[a])
        {
            mpScene->RemoveObject(maLightObjects[a]->GetOrdNum());
            maLightObjects[a] = nullptr;
        }
 
        if(GetLightOnOff(a))
        {
            const bool bIsSelectedLight(a == maSelectedLight);
            basegfx::B3DVector aDirection(GetLightDirection(a));
            aDirection.normalize();
            aDirection *= RADIUS_LAMP_PREVIEW_SIZE;
 
            const double fLampSize(bIsSelectedLight ? RADIUS_LAMP_BIG : RADIUS_LAMP_SMALL);
            rtl::Reference<E3dObject> pNewLight = new E3dSphereObj(
                *mpModel,
                mp3DView->Get3DDefaultAttributes(),
                basegfx::B3DPoint( 0, 0, 0 ),
                basegfx::B3DVector( fLampSize, fLampSize, fLampSize));
            mpScene->InsertObject(pNewLight.get());
 
            basegfx::B3DHomMatrix aTransform;
            aTransform.translate(aDirection.getX(), aDirection.getY(), aDirection.getZ());
            pNewLight->SetTransform(aTransform);
 
            SfxItemSet aSet(mpModel->GetItemPool());
            aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
            aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) );
            aSet.Put( XFillColorItem(OUString(), GetLightColor(a)));
            pNewLight->SetMergedItemSet(aSet);
 
            maLightObjects[a] = pNewLight.get();
        }
    }
}
 
void Svx3DLightControl::AdaptToSelectedLight()
{
    if(NO_LIGHT_SELECTED == maSelectedLight)
    {
        // make mpLampBottomObject/mpLampShaftObject invisible
        SfxItemSet aSet(mpModel->GetItemPool());
        aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
        aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
        mpLampBottomObject->SetMergedItemSet(aSet);
        mpLampShaftObject->SetMergedItemSet(aSet);
    }
    else
    {
        basegfx::B3DVector aDirection(GetLightDirection(maSelectedLight));
        aDirection.normalize();
 
        // make mpLampBottomObject/mpLampShaftObject visible (yellow hairline)
        SfxItemSet aSet(mpModel->GetItemPool());
        aSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) );
        aSet.Put( XLineColorItem(OUString(), COL_YELLOW));
        aSet.Put( XLineWidthItem(0));
        aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
        mpLampBottomObject->SetMergedItemSet(aSet);
        mpLampShaftObject->SetMergedItemSet(aSet);
 
        // adapt transformation of mpLampShaftObject
        basegfx::B3DHomMatrix aTransform;
        double fRotateY(0.0);
 
        if(!basegfx::fTools::equalZero(aDirection.getZ()) || !basegfx::fTools::equalZero(aDirection.getX()))
        {
            fRotateY = atan2(-aDirection.getZ(), aDirection.getX());
        }
 
        aTransform.rotate(0.0, fRotateY, 0.0);
        mpLampShaftObject->SetTransform(aTransform);
 
        // adapt transformation of selected light
        E3dObject* pSelectedLight = maLightObjects[sal_Int32(maSelectedLight)];
 
        if(pSelectedLight)
        {
            aTransform.identity();
            aTransform.translate(
                aDirection.getX() * RADIUS_LAMP_PREVIEW_SIZE,
                aDirection.getY() * RADIUS_LAMP_PREVIEW_SIZE,
                aDirection.getZ() * RADIUS_LAMP_PREVIEW_SIZE);
            pSelectedLight->SetTransform(aTransform);
        }
    }
}
 
void Svx3DLightControl::TrySelection(Point aPosPixel)
{
    if(!mpScene)
        return;
 
    const Point aPosLogic(GetDrawingArea()->get_ref_device().PixelToLogic(aPosPixel));
    const basegfx::B2DPoint aPoint(aPosLogic.X(), aPosLogic.Y());
    std::vector< const E3dCompoundObject* > aResult;
    getAllHit3DObjectsSortedFrontToBack(aPoint, *mpScene, aResult);
 
    if(aResult.empty())
        return;
 
    // exclude expansion object which will be part of
    // the hits. It's invisible, but for HitTest, it's included
    const E3dCompoundObject* pResult = nullptr;
 
    for(auto const & b: aResult)
    {
        if(b && b != mpExpansionObject.get())
        {
            pResult = b;
            break;
        }
    }
 
    if(pResult == mp3DObj.get())
    {
        if(!mbGeometrySelected)
        {
            mbGeometrySelected = true;
            maSelectedLight = NO_LIGHT_SELECTED;
            ConstructLightObjects();
            AdaptToSelectedLight();
            Invalidate();
 
            if(maSelectionChangeCallback.IsSet())
            {
                maSelectionChangeCallback.Call(this);
            }
        }
    }
    else
    {
        sal_uInt32 aNewSelectedLight(NO_LIGHT_SELECTED);
 
        for(sal_uInt32 a(0); a < MAX_NUMBER_LIGHTS; a++)
        {
            if(maLightObjects[a] && maLightObjects[a] == pResult)
            {
                aNewSelectedLight = a;
            }
        }
 
        if(aNewSelectedLight != maSelectedLight)
        {
            SelectLight(aNewSelectedLight);
 
            if(maSelectionChangeCallback.IsSet())
            {
                maSelectionChangeCallback.Call(this);
            }
        }
    }
}
 
void Svx3DLightControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    Svx3DPreviewControl::Paint(rRenderContext, rRect);
}
 
tools::Rectangle Svx3DLightControl::GetFocusRect()
{
    if (!HasFocus())
        return tools::Rectangle();
    Size aFocusSize = GetOutputSizePixel();
    aFocusSize.AdjustWidth( -4 );
    aFocusSize.AdjustHeight( -4 );
    return tools::Rectangle(Point(2, 2), aFocusSize);
}
 
bool Svx3DLightControl::MouseButtonDown( const MouseEvent& rMEvt )
{
    bool bCallParent(true);
 
    // switch state
    if(rMEvt.IsLeft())
    {
        if(IsSelectionValid() || mbGeometrySelected)
        {
            mbMouseMoved = false;
            bCallParent = false;
            maActionStartPoint = rMEvt.GetPosPixel();
            CaptureMouse();
            mbMouseCaptured = true;
        }
        else
        {
            // Single click without moving much trying to do a selection
            TrySelection(rMEvt.GetPosPixel());
            bCallParent = false;
        }
    }
 
    // call parent
    if (bCallParent)
        return Svx3DPreviewControl::MouseButtonDown(rMEvt);
    return true;
}
 
bool Svx3DLightControl::MouseMove(const MouseEvent& rMEvt)
{
    if (!mbMouseCaptured)
        return false;
 
    Point aDeltaPos = rMEvt.GetPosPixel() - maActionStartPoint;
 
    if(!mbMouseMoved)
    {
        if(sal_Int32(aDeltaPos.X() * aDeltaPos.X() + aDeltaPos.Y() * aDeltaPos.Y()) > g_nInteractionStartDistance)
        {
            if(mbGeometrySelected)
            {
                GetRotation(mfSaveActionStartVer, mfSaveActionStartHor, mfSaveActionStartRotZ);
            }
            else
            {
                // interaction start, save values
                GetPosition(mfSaveActionStartHor, mfSaveActionStartVer);
            }
 
            mbMouseMoved = true;
        }
    }
 
    if(mbMouseMoved)
    {
        if(mbGeometrySelected)
        {
            double fNewRotX = mfSaveActionStartVer - basegfx::deg2rad(aDeltaPos.Y());
            double fNewRotY = mfSaveActionStartHor + basegfx::deg2rad(aDeltaPos.X());
 
            // cut horizontal
            while(fNewRotY < 0.0)
            {
                fNewRotY += 2 * M_PI;
            }
 
            while(fNewRotY >= 2 * M_PI)
            {
                fNewRotY -= 2 * M_PI;
            }
 
            // cut vertical
            if(fNewRotX < -M_PI_2)
            {
                fNewRotX = -M_PI_2;
            }
 
            if(fNewRotX > M_PI_2)
            {
                fNewRotX = M_PI_2;
            }
 
            SetRotation(fNewRotX, fNewRotY, mfSaveActionStartRotZ);
 
            if(maChangeCallback.IsSet())
            {
                maChangeCallback.Call(this);
            }
        }
        else
        {
            // interaction in progress
            double fNewPosHor = mfSaveActionStartHor + static_cast<double>(aDeltaPos.X());
            double fNewPosVer = mfSaveActionStartVer - static_cast<double>(aDeltaPos.Y());
 
            // cut horizontal
            fNewPosHor = NormAngle360(fNewPosHor);
 
            // cut vertical
            if(fNewPosVer < -90.0)
            {
                fNewPosVer = -90.0;
            }
 
            if(fNewPosVer > 90.0)
            {
                fNewPosVer = 90.0;
            }
 
            SetPosition(fNewPosHor, fNewPosVer);
 
            if(maChangeCallback.IsSet())
            {
                maChangeCallback.Call(this);
            }
        }
    }
    return true;
}
 
bool Svx3DLightControl::MouseButtonUp(const MouseEvent& rMEvt)
{
    if (!mbMouseCaptured)
        return false;
    ReleaseMouse();
    mbMouseCaptured = false;
 
    if (mbMouseMoved)
    {
        // was change interactively
    }
    else
    {
        // simple click without much movement, try selection
        TrySelection(rMEvt.GetPosPixel());
    }
 
    return true;
}
 
void Svx3DLightControl::Resize()
{
    // set size of page
    const Size aSize(GetDrawingArea()->get_ref_device().PixelToLogic(GetOutputSizePixel()));
    mxFmPage->SetSize(aSize);
 
    // set position and size of scene
    mpScene->SetSnapRect(tools::Rectangle(Point(0, 0), aSize));
}
 
void Svx3DLightControl::SetObjectType(SvxPreviewObjectType nType)
{
    // call parent
    Svx3DPreviewControl::SetObjectType(nType);
 
    // apply object rotation
    if(mp3DObj)
    {
        basegfx::B3DHomMatrix aObjectRotation;
        aObjectRotation.rotate(mfRotateX, mfRotateY, mfRotateZ);
        mp3DObj->SetTransform(aObjectRotation);
    }
}
 
bool Svx3DLightControl::IsSelectionValid()
{
    return (NO_LIGHT_SELECTED != maSelectedLight) && GetLightOnOff(maSelectedLight);
}
 
void Svx3DLightControl::GetPosition(double& rHor, double& rVer)
{
    if(IsSelectionValid())
    {
        basegfx::B3DVector aDirection(GetLightDirection(maSelectedLight));
        aDirection.normalize();
        rHor = basegfx::rad2deg(atan2(-aDirection.getX(), -aDirection.getZ()) + M_PI); // 0..360.0
        rVer = basegfx::rad2deg(atan2(aDirection.getY(), aDirection.getXZLength())); // -90.0..90.0
    }
    if(IsGeometrySelected())
    {
        rHor = basegfx::rad2deg(mfRotateY); // 0..360.0
        rVer = basegfx::rad2deg(mfRotateX); // -90.0..90.0
    }
}
 
void Svx3DLightControl::SetPosition(double fHor, double fVer)
{
    if(IsSelectionValid())
    {
        // set selected light's direction
        fHor = basegfx::deg2rad(fHor) - M_PI; // -PI..PI
        fVer = basegfx::deg2rad(fVer); // -PI2..PI2
        basegfx::B3DVector aDirection(cos(fVer) * -sin(fHor), sin(fVer), cos(fVer) * -cos(fHor));
        aDirection.normalize();
 
        if(!aDirection.equal(GetLightDirection(maSelectedLight)))
        {
            // set changed light direction at SdrScene
            SfxItemSet aSet(mpModel->GetItemPool());
 
            switch(maSelectedLight)
            {
                case 0: aSet.Put(makeSvx3DLightDirection1Item(aDirection)); break;
                case 1: aSet.Put(makeSvx3DLightDirection2Item(aDirection)); break;
                case 2: aSet.Put(makeSvx3DLightDirection3Item(aDirection)); break;
                case 3: aSet.Put(makeSvx3DLightDirection4Item(aDirection)); break;
                case 4: aSet.Put(makeSvx3DLightDirection5Item(aDirection)); break;
                case 5: aSet.Put(makeSvx3DLightDirection6Item(aDirection)); break;
                case 6: aSet.Put(makeSvx3DLightDirection7Item(aDirection)); break;
                default:
                case 7: aSet.Put(makeSvx3DLightDirection8Item(aDirection)); break;
            }
 
            mpScene->SetMergedItemSet(aSet);
 
            // correct 3D light's and LampFrame's geometries
            AdaptToSelectedLight();
            Invalidate();
        }
    }
    if(!IsGeometrySelected())
        return;
 
    if(mfRotateX == fVer && mfRotateY == fHor)
        return;
 
    mfRotateX = basegfx::deg2rad(fVer);
    mfRotateY = basegfx::deg2rad(fHor);
 
    if(mp3DObj)
    {
        basegfx::B3DHomMatrix aObjectRotation;
        aObjectRotation.rotate(mfRotateX, mfRotateY, mfRotateZ);
        mp3DObj->SetTransform(aObjectRotation);
 
        Invalidate();
    }
}
 
void Svx3DLightControl::SetRotation(double fRotX, double fRotY, double fRotZ)
{
    if(!IsGeometrySelected())
        return;
 
    if(fRotX == mfRotateX && fRotY == mfRotateY && fRotZ == mfRotateZ)
        return;
 
    mfRotateX = fRotX;
    mfRotateY = fRotY;
    mfRotateZ = fRotZ;
 
    if(mp3DObj)
    {
        basegfx::B3DHomMatrix aObjectRotation;
        aObjectRotation.rotate(mfRotateX, mfRotateY, mfRotateZ);
        mp3DObj->SetTransform(aObjectRotation);
 
        Invalidate();
    }
}
 
void Svx3DLightControl::GetRotation(double& rRotX, double& rRotY, double& rRotZ)
{
    rRotX = mfRotateX;
    rRotY = mfRotateY;
    rRotZ = mfRotateZ;
}
 
void Svx3DLightControl::Set3DAttributes( const SfxItemSet& rAttr )
{
    // call parent
    Svx3DPreviewControl::Set3DAttributes(rAttr);
 
    if(maSelectedLight != NO_LIGHT_SELECTED && !GetLightOnOff(maSelectedLight))
    {
        // selected light is no more active, select new one
        maSelectedLight = NO_LIGHT_SELECTED;
    }
 
    // local updates
    ConstructLightObjects();
    AdaptToSelectedLight();
    Invalidate();
}
 
void Svx3DLightControl::SelectLight(sal_uInt32 nLightNumber)
{
    if(nLightNumber > 7)
    {
        nLightNumber = NO_LIGHT_SELECTED;
    }
 
    if(NO_LIGHT_SELECTED != nLightNumber)
    {
        if(!GetLightOnOff(nLightNumber))
        {
            nLightNumber = NO_LIGHT_SELECTED;
        }
    }
 
    if(nLightNumber != maSelectedLight)
    {
        maSelectedLight = nLightNumber;
        mbGeometrySelected = false;
        ConstructLightObjects();
        AdaptToSelectedLight();
        Invalidate();
    }
}
 
bool Svx3DLightControl::GetLightOnOff(sal_uInt32 nNum) const
{
    if(nNum <= 7)
    {
        const SfxItemSet aLightItemSet(Get3DAttributes());
 
        switch(nNum)
        {
            case 0 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_1).GetValue();
            case 1 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_2).GetValue();
            case 2 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_3).GetValue();
            case 3 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_4).GetValue();
            case 4 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_5).GetValue();
            case 5 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_6).GetValue();
            case 6 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_7).GetValue();
            case 7 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_8).GetValue();
        }
    }
 
    return false;
}
 
Color Svx3DLightControl::GetLightColor(sal_uInt32 nNum) const
{
    if(nNum <= 7)
    {
        const SfxItemSet aLightItemSet(Get3DAttributes());
 
        switch(nNum)
        {
            case 0 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_1).GetValue();
            case 1 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_2).GetValue();
            case 2 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_3).GetValue();
            case 3 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_4).GetValue();
            case 4 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_5).GetValue();
            case 5 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_6).GetValue();
            case 6 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_7).GetValue();
            case 7 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_8).GetValue();
        }
    }
 
    return COL_BLACK;
}
 
basegfx::B3DVector Svx3DLightControl::GetLightDirection(sal_uInt32 nNum) const
{
    if(nNum <= 7)
    {
        const SfxItemSet aLightItemSet(Get3DAttributes());
 
        switch(nNum)
        {
            case 0 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_1).GetValue();
            case 1 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_2).GetValue();
            case 2 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_3).GetValue();
            case 3 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_4).GetValue();
            case 4 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_5).GetValue();
            case 5 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_6).GetValue();
            case 6 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_7).GetValue();
            case 7 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_8).GetValue();
        }
    }
 
    return basegfx::B3DVector();
}
 
SvxLightCtl3D::SvxLightCtl3D(Svx3DLightControl& rLightControl, weld::Scale& rHori,
                       weld::Scale& rVert, weld::Button& rSwitcher)
    : mrLightControl(rLightControl)
    , mrHorScroller(rHori)
    , mrVerScroller(rVert)
    , mrSwitcher(rSwitcher)
{
    // init members
    Init();
}
 
void SvxLightCtl3D::Init()
{
    Size aSize(mrLightControl.GetDrawingArea()->get_ref_device().LogicToPixel(Size(80, 100), MapMode(MapUnit::MapAppFont)));
    mrLightControl.set_size_request(aSize.Width(), aSize.Height());
 
    // #i58240# set HelpIDs for scrollbars and switcher
    mrHorScroller.set_help_id(HID_CTRL3D_HSCROLL);
    mrVerScroller.set_help_id(HID_CTRL3D_VSCROLL);
    mrSwitcher.set_help_id(HID_CTRL3D_SWITCHER);
    mrSwitcher.set_accessible_name(SvxResId(STR_SWITCH));
 
    // Light preview
    mrLightControl.Show();
    mrLightControl.SetChangeCallback( LINK(this, SvxLightCtl3D, InternalInteractiveChange) );
    mrLightControl.SetSelectionChangeCallback( LINK(this, SvxLightCtl3D, InternalSelectionChange) );
 
    // Horiz Scrollbar
    mrHorScroller.show();
    mrHorScroller.set_range(0, 36000);
    mrHorScroller.connect_value_changed( LINK(this, SvxLightCtl3D, ScrollBarMove) );
 
    // Vert Scrollbar
    mrVerScroller.show();
    mrVerScroller.set_range(0, 18000);
    mrVerScroller.connect_value_changed( LINK(this, SvxLightCtl3D, ScrollBarMove) );
 
    // Switch Button
    mrSwitcher.show();
    mrSwitcher.connect_clicked( LINK(this, SvxLightCtl3D, ButtonPress) );
 
    weld::DrawingArea* pArea = mrLightControl.GetDrawingArea();
    pArea->connect_key_press(Link<const KeyEvent&, bool>()); //acknowledge we first remove the old one
    pArea->connect_key_press(LINK(this, SvxLightCtl3D, KeyInput));
 
    pArea->connect_focus_in(Link<weld::Widget&, void>()); //acknowledge we first remove the old one
    pArea->connect_focus_in(LINK(this, SvxLightCtl3D, FocusIn));
 
    // check selection
    CheckSelection();
}
 
void SvxLightCtl3D::CheckSelection()
{
    const bool bSelectionValid(mrLightControl.IsSelectionValid() || mrLightControl.IsGeometrySelected());
    mrHorScroller.set_sensitive(bSelectionValid);
    mrVerScroller.set_sensitive(bSelectionValid);
 
    if (bSelectionValid)
    {
        double fHor(0.0), fVer(0.0);
        mrLightControl.GetPosition(fHor, fVer);
        mrHorScroller.set_value( sal_Int32(fHor * 100.0) );
        mrVerScroller.set_value( 18000 - sal_Int32((fVer + 90.0) * 100.0) );
    }
}
 
void SvxLightCtl3D::move( double fDeltaHor, double fDeltaVer )
{
    double fHor(0.0), fVer(0.0);
 
    mrLightControl.GetPosition(fHor, fVer);
    fHor += fDeltaHor;
    fVer += fDeltaVer;
 
    if( fVer > 90.0 )
        return;
 
    if ( fVer < -90.0 )
        return;
 
    mrLightControl.SetPosition(fHor, fVer);
    mrHorScroller.set_value( sal_Int32(fHor * 100.0) );
    mrVerScroller.set_value( 18000 - sal_Int32((fVer + 90.0) * 100.0) );
 
    if(maUserInteractiveChangeCallback.IsSet())
    {
        maUserInteractiveChangeCallback.Call(this);
    }
}
 
IMPL_LINK(SvxLightCtl3D, KeyInput, const KeyEvent&, rKEvt, bool)
{
    const vcl::KeyCode aCode(rKEvt.GetKeyCode());
 
    if (aCode.GetModifier())
        return false;
 
    bool bHandled = true;
 
    switch ( aCode.GetCode() )
    {
        case KEY_SPACE:
        {
            break;
        }
        case KEY_LEFT:
        {
            move(  -4.0,  0.0 ); // #i58242# changed move direction in X
            break;
        }
        case KEY_RIGHT:
        {
            move( 4.0,  0.0 ); // #i58242# changed move direction in X
            break;
        }
        case KEY_UP:
        {
            move(  0.0,  4.0 );
            break;
        }
        case KEY_DOWN:
        {
            move(  0.0, -4.0 );
            break;
        }
        case KEY_PAGEUP:
        {
            sal_Int32 nLight(mrLightControl.GetSelectedLight() - 1);
 
            while((nLight >= 0) && !mrLightControl.GetLightOnOff(nLight))
            {
                nLight--;
            }
 
            if(nLight < 0)
            {
                nLight = 7;
 
                while((nLight >= 0) && !mrLightControl.GetLightOnOff(nLight))
                {
                    nLight--;
                }
            }
 
            if(nLight >= 0)
            {
                mrLightControl.SelectLight(nLight);
                CheckSelection();
 
                if(maUserSelectionChangeCallback.IsSet())
                {
                    maUserSelectionChangeCallback.Call(this);
                }
            }
 
            break;
        }
        case KEY_PAGEDOWN:
        {
            sal_Int32 nLight(mrLightControl.GetSelectedLight() - 1);
 
            while(nLight <= 7 && !mrLightControl.GetLightOnOff(nLight))
            {
                nLight++;
            }
 
            if(nLight > 7)
            {
                nLight = 0;
 
                while(nLight <= 7 && !mrLightControl.GetLightOnOff(nLight))
                {
                    nLight++;
                }
            }
 
            if(nLight <= 7)
            {
                mrLightControl.SelectLight(nLight);
                CheckSelection();
 
                if(maUserSelectionChangeCallback.IsSet())
                {
                    maUserSelectionChangeCallback.Call(this);
                }
            }
 
            break;
        }
        default:
        {
            bHandled = false;
            break;
        }
    }
    return bHandled;
}
 
IMPL_LINK_NOARG(SvxLightCtl3D, FocusIn, weld::Widget&, void)
{
    if (mrLightControl.IsEnabled())
    {
        CheckSelection();
    }
}
 
IMPL_LINK_NOARG(SvxLightCtl3D, ScrollBarMove, weld::Scale&, void)
{
    const sal_Int32 nHor(mrHorScroller.get_value());
    const sal_Int32 nVer(mrVerScroller.get_value());
 
    mrLightControl.SetPosition(
        static_cast<double>(nHor) / 100.0,
        static_cast<double>((18000 - nVer) - 9000) / 100.0);
 
    if (maUserInteractiveChangeCallback.IsSet())
    {
        maUserInteractiveChangeCallback.Call(this);
    }
}
 
IMPL_LINK_NOARG(SvxLightCtl3D, ButtonPress, weld::Button&, void)
{
    if(SvxPreviewObjectType::SPHERE == GetSvx3DLightControl().GetObjectType())
    {
        GetSvx3DLightControl().SetObjectType(SvxPreviewObjectType::CUBE);
    }
    else
    {
        GetSvx3DLightControl().SetObjectType(SvxPreviewObjectType::SPHERE);
    }
}
 
IMPL_LINK_NOARG(SvxLightCtl3D, InternalInteractiveChange, Svx3DLightControl*, void)
{
    double fHor(0.0), fVer(0.0);
 
    mrLightControl.GetPosition(fHor, fVer);
    mrHorScroller.set_value( sal_Int32(fHor * 100.0) );
    mrVerScroller.set_value( 18000 - sal_Int32((fVer + 90.0) * 100.0) );
 
    if(maUserInteractiveChangeCallback.IsSet())
    {
        maUserInteractiveChangeCallback.Call(this);
    }
}
 
IMPL_LINK_NOARG(SvxLightCtl3D, InternalSelectionChange, Svx3DLightControl*, void)
{
    CheckSelection();
 
    if(maUserSelectionChangeCallback.IsSet())
    {
        maUserSelectionChangeCallback.Call(this);
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'normalize' is required to be utilized.

V530 The return value of function 'normalize' is required to be utilized.

V530 The return value of function 'normalize' is required to be utilized.

V530 The return value of function 'normalize' is required to be utilized.