/* -*- 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/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; css::uno::Reference& LokChartHelper::GetXController() { if(!mxController.is() && mpViewShell) { SfxInPlaceClient* pIPClient = mpViewShell->GetIPClient(); if (pIPClient) { const css::uno::Reference< ::css::embed::XEmbeddedObject >& xEmbObj = pIPClient->GetObject(); if( xEmbObj.is() ) { ::css::uno::Reference< ::css::chart2::XChartDocument > xChart( xEmbObj->getComponent(), uno::UNO_QUERY ); if( xChart.is() ) { ::css::uno::Reference< ::css::frame::XController > xChartController = xChart->getCurrentController(); if( xChartController.is() ) { mxController = std::move(xChartController); } } } } } return mxController; } css::uno::Reference& LokChartHelper::GetXDispatcher() { if( !mxDispatcher.is() ) { ::css::uno::Reference< ::css::frame::XController >& xChartController = GetXController(); if( xChartController.is() ) { ::css::uno::Reference< ::css::frame::XDispatch > xDispatcher( xChartController, uno::UNO_QUERY ); if( xDispatcher.is() ) { mxDispatcher = std::move(xDispatcher); } } } return mxDispatcher; } vcl::Window* LokChartHelper::GetWindow() { if (!mpWindow) { ::css::uno::Reference< ::css::frame::XController >& xChartController = GetXController(); if( xChartController.is() ) { ::css::uno::Reference< ::css::frame::XFrame > xFrame = xChartController->getFrame(); if (xFrame.is()) { ::css::uno::Reference< ::css::awt::XWindow > xDockerWin = xFrame->getContainerWindow(); vcl::Window* pParent = VCLUnoHelper::GetWindow( xDockerWin ); if (pParent) { sal_uInt16 nTotChildren = pParent->GetChildCount(); while (nTotChildren > 0) { --nTotChildren; vcl::Window* pChildWin = pParent->GetChild(nTotChildren); if (pChildWin && pChildWin->IsChart()) { mpWindow = pChildWin; break; } } } } } } return mpWindow.get(); } tools::Rectangle LokChartHelper::GetChartBoundingBox() { tools::Rectangle aBBox; if (mpViewShell) { SfxInPlaceClient* pIPClient = mpViewShell->GetIPClient(); if (pIPClient) { vcl::Window* pRootWin = pIPClient->GetEditWin(); if (pRootWin) { vcl::Window* pWindow = GetWindow(); if (pWindow) { // In all cases, the following code fragment // returns the chart bounding box in twips. const MapMode& aCWMapMode = pWindow->GetMapMode(); constexpr auto p = o3tl::getConversionMulDiv(o3tl::Length::px, o3tl::Length::twip); const auto& scaleX = aCWMapMode.GetScaleX(); const auto& scaleY = aCWMapMode.GetScaleY(); const auto nXNum = p.first * scaleX.GetDenominator(); const auto nXDen = p.second * scaleX.GetNumerator(); const auto nYNum = p.first * scaleY.GetDenominator(); const auto nYDen = p.second * scaleY.GetNumerator(); Point aOffset = pWindow->GetOffsetPixelFrom(*pRootWin); if (mbNegativeX && AllSettings::GetLayoutRTL()) { // If global RTL flag is set, vcl-window X offset of chart window is // mirrored w.r.t parent window rectangle. This needs to be reverted. aOffset.setX(pRootWin->GetOutOffXPixel() + pRootWin->GetSizePixel().Width() - pWindow->GetOutOffXPixel() - pWindow->GetSizePixel().Width()); } aOffset = aOffset.scale(nXNum, nXDen, nYNum, nYDen); Size aSize = pWindow->GetSizePixel().scale(nXNum, nXDen, nYNum, nYDen); aBBox = tools::Rectangle(aOffset, aSize); } } } } return aBBox; } void LokChartHelper::Invalidate() { mpWindow = nullptr; mxDispatcher.clear(); mxController.clear(); } bool LokChartHelper::Hit(const Point& aPos) { if (mpViewShell) { vcl::Window* pChartWindow = GetWindow(); if (pChartWindow) { tools::Rectangle rChartBBox = GetChartBoundingBox(); return rChartBBox.Contains(aPos); } } return false; } bool LokChartHelper::HitAny(const Point& aPos, bool bNegativeX) { SfxViewShell* pCurView = SfxViewShell::Current(); int nPartForCurView = pCurView ? pCurView->getPart() : -1; SfxViewShell* pViewShell = SfxViewShell::GetFirst(); while (pViewShell) { if (pCurView && pViewShell->GetDocId() == pCurView->GetDocId() && pViewShell->getPart() == nPartForCurView) { LokChartHelper aChartHelper(pViewShell, bNegativeX); if (aChartHelper.Hit(aPos)) return true; } pViewShell = SfxViewShell::GetNext(*pViewShell); } return false; } void LokChartHelper::PaintTile(VirtualDevice& rRenderContext, const tools::Rectangle& rTileRect) { if (!mpViewShell) return; vcl::Window* pChartWindow = GetWindow(); if (!pChartWindow) return; tools::Rectangle aChartRect = GetChartBoundingBox(); tools::Rectangle aTestRect = rTileRect; aTestRect.Intersection( aChartRect ); if (aTestRect.IsEmpty()) return; Point aOffset( aChartRect.Left() - rTileRect.Left(), aChartRect.Top() - rTileRect.Top() ); Point aOffsetFromTile = convertTwipToMm100(aOffset); Size aSize = convertTwipToMm100(aChartRect.GetSize()); tools::Rectangle aRectangle(Point(0,0), aSize); bool bEnableMapMode = !pChartWindow->IsMapModeEnabled(); pChartWindow->EnableMapMode(); bool bRenderContextEnableMapMode = !rRenderContext.IsMapModeEnabled(); rRenderContext.EnableMapMode(); rRenderContext.Push(vcl::PushFlags::MAPMODE); MapMode aCWMapMode = pChartWindow->GetMapMode(); aCWMapMode.SetScaleX(rRenderContext.GetMapMode().GetScaleX()); aCWMapMode.SetScaleY(rRenderContext.GetMapMode().GetScaleY()); aCWMapMode.SetOrigin(aOffsetFromTile); rRenderContext.SetMapMode(aCWMapMode); pChartWindow->Paint(rRenderContext, aRectangle); rRenderContext.Pop(); if (bRenderContextEnableMapMode) rRenderContext.EnableMapMode(false); if (bEnableMapMode) pChartWindow->EnableMapMode(false); } void LokChartHelper::PaintAllChartsOnTile(VirtualDevice& rDevice, int nOutputWidth, int nOutputHeight, int nTilePosX, int nTilePosY, tools::Long nTileWidth, tools::Long nTileHeight, bool bNegativeX) { if (comphelper::LibreOfficeKit::isTiledAnnotations()) return; // Resizes the virtual device so to contain the entries context rDevice.SetOutputSizePixel(Size(nOutputWidth, nOutputHeight)); rDevice.Push(vcl::PushFlags::MAPMODE); MapMode aMapMode(rDevice.GetMapMode()); // Scaling. Must convert from pixels to twips. We know // that VirtualDevices use a DPI of 96. const Fraction scale = conversionFract(o3tl::Length::px, o3tl::Length::twip); Fraction scaleX = Fraction(nOutputWidth, nTileWidth) * scale; Fraction scaleY = Fraction(nOutputHeight, nTileHeight) * scale; aMapMode.SetScaleX(scaleX); aMapMode.SetScaleY(scaleY); rDevice.SetMapMode(aMapMode); SfxViewShell* pCurView = SfxViewShell::Current(); int nPartForCurView = pCurView ? pCurView->getPart() : -1; tools::Long nTileRectLeft = bNegativeX ? -nTilePosX - nTileWidth : nTilePosX; tools::Rectangle aTileRect(Point(nTileRectLeft, nTilePosY), Size(nTileWidth, nTileHeight)); SfxViewShell* pViewShell = SfxViewShell::GetFirst(); while (pViewShell) { if (pCurView && pViewShell->GetDocId() == pCurView->GetDocId() && pViewShell->getPart() == nPartForCurView) { LokChartHelper aChartHelper(pViewShell, bNegativeX); aChartHelper.PaintTile(rDevice, aTileRect); } pViewShell = SfxViewShell::GetNext(*pViewShell); } rDevice.Pop(); } bool LokChartHelper::postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier, double fScaleX, double fScaleY) { Point aMousePos(nX, nY); vcl::Window* pChartWindow = GetWindow(); if (pChartWindow) { tools::Rectangle rChartBBox = GetChartBoundingBox(); if (rChartBBox.Contains(aMousePos)) { int nChartWinX = nX - rChartBBox.Left(); int nChartWinY = nY - rChartBBox.Top(); // chart window expects pixels, but the conversion factor // can depend on the client zoom Point aPos(nChartWinX * fScaleX, nChartWinY * fScaleY); LokMouseEventData aMouseEventData(nType, aPos, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier); SfxLokHelper::postMouseEventAsync(pChartWindow, aMouseEventData); return true; } } return false; } bool LokChartHelper::setTextSelection(int nType, int nX, int nY) { tools::Rectangle rChartBBox = GetChartBoundingBox(); if (rChartBBox.Contains(Point(nX, nY))) { css::uno::Reference xDispatcher = GetXDispatcher(); if (xDispatcher.is()) { int nChartWinX = nX - rChartBBox.Left(); int nChartWinY = nY - rChartBBox.Top(); // no scale here the chart controller expects twips // that are converted to hmm util::URL aURL; aURL.Path = "LOKSetTextSelection"; uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue({}, static_cast(nType)), // Why no name? comphelper::makePropertyValue({}, static_cast(nChartWinX)), comphelper::makePropertyValue({}, static_cast(nChartWinY)) }; xDispatcher->dispatch(aURL, aArgs); } return true; } return false; } bool LokChartHelper::setGraphicSelection(int nType, int nX, int nY, double fScaleX, double fScaleY) { tools::Rectangle rChartBBox = GetChartBoundingBox(); if (rChartBBox.Contains(Point(nX, nY))) { int nChartWinX = nX - rChartBBox.Left(); int nChartWinY = nY - rChartBBox.Top(); vcl::Window* pChartWindow = GetWindow(); Point aPos(nChartWinX * fScaleX, nChartWinY * fScaleY); switch (nType) { case LOK_SETGRAPHICSELECTION_START: { MouseEvent aClickEvent(aPos, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); pChartWindow->MouseButtonDown(aClickEvent); MouseEvent aMoveEvent(aPos, 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); pChartWindow->MouseMove(aMoveEvent); } break; case LOK_SETGRAPHICSELECTION_END: { MouseEvent aMoveEvent(aPos, 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); pChartWindow->MouseMove(aMoveEvent); MouseEvent aClickEvent(aPos, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); pChartWindow->MouseButtonUp(aClickEvent); } break; default: assert(false); break; } return true; } return false; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */