diff options
Diffstat (limited to 'sw/source/uibase/docvw/HeaderFooterWin.cxx')
-rw-r--r-- | sw/source/uibase/docvw/HeaderFooterWin.cxx | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/sw/source/uibase/docvw/HeaderFooterWin.cxx b/sw/source/uibase/docvw/HeaderFooterWin.cxx new file mode 100644 index 000000000..bce5cfcde --- /dev/null +++ b/sw/source/uibase/docvw/HeaderFooterWin.cxx @@ -0,0 +1,521 @@ +/* -*- 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 <strings.hrc> + +#include <doc.hxx> +#include <drawdoc.hxx> +#include <cmdid.h> +#include <DashedLine.hxx> +#include <docsh.hxx> +#include <edtwin.hxx> +#include <fmthdft.hxx> +#include <HeaderFooterWin.hxx> +#include <pagedesc.hxx> +#include <pagefrm.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <wrtsh.hxx> +#include <IDocumentDrawModelAccess.hxx> + +#include <basegfx/color/bcolortools.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/vector/b2dsize.hxx> +#include <drawinglayer/attribute/fillgradientattribute.hxx> +#include <drawinglayer/attribute/fontattribute.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <editeng/boxitem.hxx> +#include <svx/hdft.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/metric.hxx> +#include <vcl/menubtn.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <memory> + +#define TEXT_PADDING 5 +#define BOX_DISTANCE 10 +#define BUTTON_WIDTH 18 + +using namespace basegfx; +using namespace basegfx::utils; +using namespace drawinglayer::attribute; + +namespace +{ + basegfx::BColor lcl_GetFillColor(const basegfx::BColor& rLineColor) + { + basegfx::BColor aHslLine = basegfx::utils::rgb2hsl(rLineColor); + double nLuminance = aHslLine.getZ() * 2.5; + if ( nLuminance == 0 ) + nLuminance = 0.5; + else if ( nLuminance >= 1.0 ) + nLuminance = aHslLine.getZ() * 0.4; + aHslLine.setZ( nLuminance ); + return basegfx::utils::hsl2rgb( aHslLine ); + } + + basegfx::BColor lcl_GetLighterGradientColor(const basegfx::BColor& rDarkColor) + { + basegfx::BColor aHslDark = basegfx::utils::rgb2hsl(rDarkColor); + double nLuminance = aHslDark.getZ() * 255 + 20; + aHslDark.setZ( nLuminance / 255.0 ); + return basegfx::utils::hsl2rgb( aHslDark ); + } + + B2DPolygon lcl_GetPolygon( const ::tools::Rectangle& rRect, bool bOnTop ) + { + const double nRadius = 3; + const double nKappa((M_SQRT2 - 1.0) * 4.0 / 3.0); + + B2DPolygon aPolygon; + aPolygon.append( B2DPoint( rRect.Left(), rRect.Top() ) ); + + { + B2DPoint aCorner( rRect.Left(), rRect.Bottom() ); + B2DPoint aStart( rRect.Left(), rRect.Bottom() - nRadius ); + B2DPoint aEnd( rRect.Left() + nRadius, rRect.Bottom() ); + aPolygon.append( aStart ); + aPolygon.appendBezierSegment( + interpolate( aStart, aCorner, nKappa ), + interpolate( aEnd, aCorner, nKappa ), + aEnd ); + } + + { + B2DPoint aCorner( rRect.Right(), rRect.Bottom() ); + B2DPoint aStart( rRect.Right() - nRadius, rRect.Bottom() ); + B2DPoint aEnd( rRect.Right(), rRect.Bottom() - nRadius ); + aPolygon.append( aStart ); + aPolygon.appendBezierSegment( + interpolate( aStart, aCorner, nKappa ), + interpolate( aEnd, aCorner, nKappa ), + aEnd ); + } + + aPolygon.append( B2DPoint( rRect.Right(), rRect.Top() ) ); + + if ( !bOnTop ) + { + B2DRectangle aBRect = vcl::unotools::b2DRectangleFromRectangle(rRect); + B2DHomMatrix aRotation = createRotateAroundPoint( + aBRect.getCenterX(), aBRect.getCenterY(), M_PI ); + aPolygon.transform( aRotation ); + } + + return aPolygon; + } +} + +void SwFrameButtonPainter::PaintButton(drawinglayer::primitive2d::Primitive2DContainer& rSeq, + const tools::Rectangle& rRect, bool bOnTop) +{ + rSeq.clear(); + B2DPolygon aPolygon = lcl_GetPolygon(rRect, bOnTop); + + // Colors + basegfx::BColor aLineColor = SwViewOption::GetHeaderFooterMarkColor().getBColor(); + basegfx::BColor aFillColor = lcl_GetFillColor(aLineColor); + basegfx::BColor aLighterColor = lcl_GetLighterGradientColor(aFillColor); + + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + if (rSettings.GetHighContrastMode()) + { + aFillColor = rSettings.GetDialogColor().getBColor(); + aLineColor = rSettings.GetDialogTextColor().getBColor(); + + rSeq.push_back(drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(B2DPolyPolygon(aPolygon), aFillColor))); + } + else + { + B2DRectangle aGradientRect = vcl::unotools::b2DRectangleFromRectangle(rRect); + double nAngle = M_PI; + if (bOnTop) + nAngle = 0; + FillGradientAttribute aFillAttrs(drawinglayer::attribute::GradientStyle::Linear, 0.0, 0.0, 0.0, nAngle, aLighterColor, aFillColor, 10); + rSeq.push_back(drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::FillGradientPrimitive2D(aGradientRect, aFillAttrs))); + } + + // Create the border lines primitive + rSeq.push_back(drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aPolygon, aLineColor))); +} + +SwHeaderFooterWin::SwHeaderFooterWin( SwEditWin* pEditWin, const SwFrame *pFrame, bool bHeader ) : + SwFrameMenuButtonBase( pEditWin, pFrame ), + m_aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/swriter/ui/headerfootermenu.ui", ""), + m_bIsHeader( bHeader ), + m_pPopupMenu(m_aBuilder.get_menu("menu")), + m_pLine( nullptr ), + m_bIsAppearing( false ), + m_nFadeRate( 100 ), + m_aFadeTimer( ) +{ + //FIXME RenderContext + + // Get the font and configure it + vcl::Font aFont = Application::GetSettings().GetStyleSettings().GetToolFont(); + SetZoomedPointFont(*this, aFont); + + // Create the line control + m_pLine = VclPtr<SwDashedLine>::Create(GetEditWin(), &SwViewOption::GetHeaderFooterMarkColor); + m_pLine->SetZOrder(this, ZOrderFlags::Before); + + // set the PopupMenu + // Rewrite the menu entries' text + if (m_bIsHeader) + { + m_pPopupMenu->SetItemText(m_pPopupMenu->GetItemId("edit"), SwResId(STR_FORMAT_HEADER)); + m_pPopupMenu->SetItemText(m_pPopupMenu->GetItemId("delete"), SwResId(STR_DELETE_HEADER)); + } + else + { + m_pPopupMenu->SetItemText(m_pPopupMenu->GetItemId("edit"), SwResId(STR_FORMAT_FOOTER)); + m_pPopupMenu->SetItemText(m_pPopupMenu->GetItemId("delete"), SwResId(STR_DELETE_FOOTER)); + } + + SetPopupMenu(m_pPopupMenu); + + m_aFadeTimer.SetTimeout(50); + m_aFadeTimer.SetInvokeHandler(LINK(this, SwHeaderFooterWin, FadeHandler)); +} + +SwHeaderFooterWin::~SwHeaderFooterWin( ) +{ + disposeOnce(); +} + +void SwHeaderFooterWin::dispose() +{ + m_pPopupMenu.clear(); + m_aBuilder.disposeBuilder(); + m_pLine.disposeAndClear(); + SwFrameMenuButtonBase::dispose(); +} + +void SwHeaderFooterWin::SetOffset(Point aOffset, long nXLineStart, long nXLineEnd) +{ + // Compute the text to show + const SwPageDesc* pDesc = GetPageFrame()->GetPageDesc(); + bool bIsFirst = !pDesc->IsFirstShared() && GetPageFrame()->OnFirstPage(); + bool bIsLeft = !pDesc->IsHeaderShared() && !GetPageFrame()->OnRightPage(); + bool bIsRight = !pDesc->IsHeaderShared() && GetPageFrame()->OnRightPage(); + m_sLabel = SwResId(STR_HEADER_TITLE); + if (!m_bIsHeader) + m_sLabel = bIsFirst ? SwResId(STR_FIRST_FOOTER_TITLE) + : bIsLeft ? SwResId(STR_LEFT_FOOTER_TITLE) + : bIsRight ? SwResId(STR_RIGHT_FOOTER_TITLE) + : SwResId(STR_FOOTER_TITLE ); + else + m_sLabel = bIsFirst ? SwResId(STR_FIRST_HEADER_TITLE) + : bIsLeft ? SwResId(STR_LEFT_HEADER_TITLE) + : bIsRight ? SwResId(STR_RIGHT_HEADER_TITLE) + : SwResId(STR_HEADER_TITLE); + + sal_Int32 nPos = m_sLabel.lastIndexOf("%1"); + m_sLabel = m_sLabel.replaceAt(nPos, 2, pDesc->GetName()); + + // Compute the text size and get the box position & size from it + ::tools::Rectangle aTextRect; + GetTextBoundRect(aTextRect, m_sLabel); + ::tools::Rectangle aTextPxRect = LogicToPixel(aTextRect); + FontMetric aFontMetric = GetFontMetric(GetFont()); + Size aBoxSize (aTextPxRect.GetWidth() + BUTTON_WIDTH + TEXT_PADDING * 2, + aFontMetric.GetLineHeight() + TEXT_PADDING * 2 ); + + long nYFooterOff = 0; + if (!m_bIsHeader) + nYFooterOff = aBoxSize.Height(); + + Point aBoxPos(aOffset.X() - aBoxSize.Width() - BOX_DISTANCE, + aOffset.Y() - nYFooterOff); + + if (AllSettings::GetLayoutRTL()) + { + aBoxPos.setX( aOffset.X() + BOX_DISTANCE ); + } + + // Set the position & Size of the window + SetPosSizePixel(aBoxPos, aBoxSize); + + double nYLinePos = aBoxPos.Y(); + if (!m_bIsHeader) + nYLinePos += aBoxSize.Height(); + Point aLinePos(nXLineStart, nYLinePos); + Size aLineSize(nXLineEnd - nXLineStart, 1); + m_pLine->SetPosSizePixel(aLinePos, aLineSize); +} + +void SwHeaderFooterWin::ShowAll(bool bShow) +{ + if (!PopupMenu::IsInExecute()) + { + m_bIsAppearing = bShow; + + if (m_aFadeTimer.IsActive()) + m_aFadeTimer.Stop(); + m_aFadeTimer.Start(); + } +} + +bool SwHeaderFooterWin::Contains( const Point &rDocPt ) const +{ + ::tools::Rectangle aRect(GetPosPixel(), GetSizePixel()); + if (aRect.IsInside(rDocPt)) + return true; + + ::tools::Rectangle aLineRect(m_pLine->GetPosPixel(), m_pLine->GetSizePixel()); + return aLineRect.IsInside(rDocPt); +} + +void SwHeaderFooterWin::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle&) +{ + // Use pixels for the rest of the drawing + SetMapMode(MapMode(MapUnit::MapPixel)); + drawinglayer::primitive2d::Primitive2DContainer aSeq; + const ::tools::Rectangle aRect(::tools::Rectangle(Point(0, 0), rRenderContext.PixelToLogic(GetSizePixel()))); + + SwFrameButtonPainter::PaintButton(aSeq, aRect, m_bIsHeader); + + // Create the text primitive + basegfx::BColor aLineColor = SwViewOption::GetHeaderFooterMarkColor().getBColor(); + B2DVector aFontSize; + FontAttribute aFontAttr = drawinglayer::primitive2d::getFontAttributeFromVclFont(aFontSize, rRenderContext.GetFont(), false, false); + + FontMetric aFontMetric = rRenderContext.GetFontMetric(rRenderContext.GetFont()); + double nTextOffsetY = aFontMetric.GetAscent() + TEXT_PADDING; + Point aTextPos(TEXT_PADDING, nTextOffsetY); + + basegfx::B2DHomMatrix aTextMatrix(createScaleTranslateB2DHomMatrix( + aFontSize.getX(), aFontSize.getY(), + double(aTextPos.X()), double(aTextPos.Y()))); + + aSeq.push_back(drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( + aTextMatrix, m_sLabel, 0, m_sLabel.getLength(), + std::vector<double>(), aFontAttr, css::lang::Locale(), aLineColor))); + + // Create the 'plus' or 'arrow' primitive + B2DRectangle aSignArea(B2DPoint(aRect.Right() - BUTTON_WIDTH, 0.0), + B2DSize(aRect.Right(), aRect.getHeight())); + + B2DPolygon aSign; + if (IsEmptyHeaderFooter()) + { + // Create the + polygon + double nLeft = aSignArea.getMinX() + TEXT_PADDING; + double nRight = aSignArea.getMaxX() - TEXT_PADDING; + double nHalfW = ( nRight - nLeft ) / 2.0; + + double nTop = aSignArea.getCenterY() - nHalfW; + double nBottom = aSignArea.getCenterY() + nHalfW; + + aSign.append(B2DPoint(nLeft, aSignArea.getCenterY() - 1.0)); + aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, aSignArea.getCenterY() - 1.0)); + aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, nTop)); + aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, nTop)); + aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, aSignArea.getCenterY() - 1.0)); + aSign.append(B2DPoint(nRight, aSignArea.getCenterY() - 1.0)); + aSign.append(B2DPoint(nRight, aSignArea.getCenterY() + 1.0)); + aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, aSignArea.getCenterY() + 1.0)); + aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, nBottom)); + aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, nBottom)); + aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, aSignArea.getCenterY() + 1.0)); + aSign.append(B2DPoint(nLeft, aSignArea.getCenterY() + 1.0)); + aSign.setClosed(true); + } + else + { + // Create the v polygon + B2DPoint aLeft(aSignArea.getMinX() + TEXT_PADDING, aSignArea.getCenterY()); + B2DPoint aRight(aSignArea.getMaxX() - TEXT_PADDING, aSignArea.getCenterY()); + B2DPoint aBottom((aLeft.getX() + aRight.getX()) / 2.0, aLeft.getY() + 4.0); + aSign.append(aLeft); + aSign.append(aRight); + aSign.append(aBottom); + aSign.setClosed(true); + } + + BColor aSignColor = COL_BLACK.getBColor(); + if (Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + aSignColor = COL_WHITE.getBColor(); + + aSeq.push_back( drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + B2DPolyPolygon(aSign), aSignColor)) ); + + // Create the processor and process the primitives + const drawinglayer::geometry::ViewInformation2D aNewViewInfos; + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor( + drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(rRenderContext, aNewViewInfos)); + + // TODO Ghost it all if needed + drawinglayer::primitive2d::Primitive2DContainer aGhostedSeq(1); + double nFadeRate = double(m_nFadeRate) / 100.0; + + const basegfx::BColorModifierSharedPtr aBColorModifier = + std::make_shared<basegfx::BColorModifier_interpolate>(COL_WHITE.getBColor(), + 1.0 - nFadeRate); + + aGhostedSeq[0] = drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::ModifiedColorPrimitive2D(aSeq, aBColorModifier)); + + pProcessor->process(aGhostedSeq); +} + +bool SwHeaderFooterWin::IsEmptyHeaderFooter( ) const +{ + bool bResult = true; + + // Actually check it + const SwPageDesc* pDesc = GetPageFrame()->GetPageDesc(); + + bool const bFirst(GetPageFrame()->OnFirstPage()); + const SwFrameFormat *const pFormat = (GetPageFrame()->OnRightPage()) + ? pDesc->GetRightFormat(bFirst) + : pDesc->GetLeftFormat(bFirst); + + if ( pFormat ) + { + if ( m_bIsHeader ) + bResult = !pFormat->GetHeader().IsActive(); + else + bResult = !pFormat->GetFooter().IsActive(); + } + + return bResult; +} + +void SwHeaderFooterWin::ExecuteCommand(const OString& rIdent) +{ + SwView& rView = GetEditWin()->GetView(); + SwWrtShell& rSh = rView.GetWrtShell(); + + const OUString& rStyleName = GetPageFrame()->GetPageDesc()->GetName(); + if (rIdent == "edit") + { + OString sPageId = m_bIsHeader ? OString("header") : OString("footer"); + rView.GetDocShell()->FormatPage(rStyleName, sPageId, rSh); + } + else if (rIdent == "borderback") + { + const SwPageDesc* pDesc = GetPageFrame()->GetPageDesc(); + const SwFrameFormat& rMaster = pDesc->GetMaster(); + SwFrameFormat* pHFFormat = const_cast< SwFrameFormat* >( rMaster.GetFooter().GetFooterFormat() ); + if ( m_bIsHeader ) + pHFFormat = const_cast< SwFrameFormat* >( rMaster.GetHeader().GetHeaderFormat() ); + SfxItemSet aSet( pHFFormat->GetAttrSet() ); + + // Items to hand over XPropertyList things like XColorList, + // XHatchList, XGradientList, and XBitmapList to the Area TabPage: + aSet.MergeRange( SID_COLOR_TABLE, SID_PATTERN_LIST ); + // create needed items for XPropertyList entries from the DrawModel so that + // the Area TabPage can access them + rSh.GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->PutAreaListItems( aSet ); + + aSet.MergeRange(SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER); + // Create a box info item... needed by the dialog + std::shared_ptr<SvxBoxInfoItem> aBoxInfo(std::make_shared<SvxBoxInfoItem>(SID_ATTR_BORDER_INNER)); + const SfxPoolItem *pBoxInfo; + if (SfxItemState::SET == pHFFormat->GetAttrSet().GetItemState(SID_ATTR_BORDER_INNER, true, &pBoxInfo)) + aBoxInfo.reset(static_cast<SvxBoxInfoItem*>(pBoxInfo->Clone())); + + aBoxInfo->SetTable(false); + aBoxInfo->SetDist(true); + aBoxInfo->SetMinDist(false); + aBoxInfo->SetDefDist(MIN_BORDER_DIST); + aBoxInfo->SetValid(SvxBoxInfoItemValidFlags::DISABLE); + aSet.Put(*aBoxInfo); + + if (svx::ShowBorderBackgroundDlg( GetFrameWeld(), &aSet ) ) + { + pHFFormat->SetFormatAttr( aSet ); + rView.GetDocShell()->SetModified(); + } + } + else if (rIdent == "delete") + { + rSh.ChangeHeaderOrFooter( rStyleName, m_bIsHeader, false, true ); + // warning: "this" may be disposed now + rSh.GetWin()->GrabFocusToDocument(); + } + else if (rIdent == "insert_pagenumber") + { + SfxViewFrame* pVFrame = rSh.GetView().GetViewFrame(); + pVFrame->GetBindings().Execute(FN_INSERT_FLD_PGNUMBER); + } + else if (rIdent == "insert_pagecount") + { + SfxViewFrame* pVFrame = rSh.GetView().GetViewFrame(); + pVFrame->GetBindings().Execute(FN_INSERT_FLD_PGCOUNT); + } +} + +void SwHeaderFooterWin::SetReadonly( bool bReadonly ) +{ + ShowAll( !bReadonly ); +} + +void SwHeaderFooterWin::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if (IsEmptyHeaderFooter()) + { + SwView& rView = GetEditWin()->GetView(); + SwWrtShell& rSh = rView.GetWrtShell(); + + const OUString& rStyleName = GetPageFrame()->GetPageDesc()->GetName(); + rSh.ChangeHeaderOrFooter( rStyleName, m_bIsHeader, true, false ); + } + else + MenuButton::MouseButtonDown( rMEvt ); +} + +void SwHeaderFooterWin::Select() +{ + ExecuteCommand(GetCurItemIdent()); +} + +IMPL_LINK_NOARG(SwHeaderFooterWin, FadeHandler, Timer *, void) +{ + if (m_bIsAppearing && m_nFadeRate > 0) + m_nFadeRate -= 25; + else if (!m_bIsAppearing && m_nFadeRate < 100) + m_nFadeRate += 25; + + if (m_nFadeRate != 100 && !IsVisible()) + { + Show(); + m_pLine->Show(); + } + else if (m_nFadeRate == 100 && IsVisible()) + { + Show(false); + m_pLine->Show(false); + } + else + Invalidate(); + + if (IsVisible() && m_nFadeRate > 0 && m_nFadeRate < 100) + m_aFadeTimer.Start(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |