diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /svx/source/dialog/dlgctl3d.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | svx/source/dialog/dlgctl3d.cxx | 1173 |
1 files changed, 1173 insertions, 0 deletions
diff --git a/svx/source/dialog/dlgctl3d.cxx b/svx/source/dialog/dlgctl3d.cxx new file mode 100644 index 000000000..792485a06 --- /dev/null +++ b/svx/source/dialog/dlgctl3d.cxx @@ -0,0 +1,1173 @@ +/* -*- 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() + : mpScene(nullptr) + , mp3DObj(nullptr) + , 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(); +} + +Svx3DPreviewControl::~Svx3DPreviewControl() +{ + mxFmPage.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()); + mpModel->GetItemPool().FreezeIdRanges(); + + // 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 ); + + 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( "", COL_WHITE ) ); + + mpScene->SetMergedItemSet(aSet); + + // PageView + SdrPageView* pPageView = mp3DView->ShowSdrPage( mxFmPage.get() ); + mp3DView->hideMarkHandles(); + + // mark scene + mp3DView->MarkObj( mpScene, 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() ); + // always use SdrObject::Free(...) for SdrObjects (!) + SdrObject* pTemp(mp3DObj); + SdrObject::Free(pTemp); + } + + 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 ); + 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), + mpExpansionObject(nullptr), + mpLampBottomObject(nullptr), + mpLampShaftObject(nullptr), + 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 ); + 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 ); + + // 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 ); + + // 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()); + // always use SdrObject::Free(...) for SdrObjects (!) + SdrObject* pTemp(maLightObjects[a]); + SdrObject::Free(pTemp); + 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); + E3dObject* pNewLight = new E3dSphereObj( + *mpModel, + mp3DView->Get3DDefaultAttributes(), + basegfx::B3DPoint( 0, 0, 0 ), + basegfx::B3DVector( fLampSize, fLampSize, fLampSize)); + mpScene->InsertObject(pNewLight); + + 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; + } + } +} + +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) + { + pResult = b; + break; + } + } + + if(pResult == mp3DObj) + { + 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: */ |