/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TEXT_PADDING 3 #define BOX_DISTANCE 3 #define BUTTON_WIDTH 12 UnfloatTableButton::UnfloatTableButton(SwEditWin* pEditWin, const SwFrame* pFrame) : SwFrameMenuButtonBase(pEditWin, pFrame) , m_sLabel(SwResId(STR_UNFLOAT_TABLE)) { } UnfloatTableButton::~UnfloatTableButton() { disposeOnce(); } void UnfloatTableButton::SetOffset(Point aTopRightPixel) { // 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); Point aBoxPos(aTopRightPixel.X() - aBoxSize.Width() - BOX_DISTANCE, aTopRightPixel.Y()); if (AllSettings::GetLayoutRTL()) { aBoxPos.setX(aTopRightPixel.X() + BOX_DISTANCE); } // Set the position & Size of the window SetPosSizePixel(aBoxPos, aBoxSize); } void UnfloatTableButton::MouseButtonDown(const MouseEvent& /*rMEvt*/) { assert(GetFrame()->IsFlyFrame()); // const_cast is needed because of bad design of ISwFrameControl and derived classes SwFlyFrame* pFlyFrame = const_cast(static_cast(GetFrame())); // Find the table inside the text frame SwTabFrame* pTableFrame = nullptr; SwFrame* pLower = pFlyFrame->GetLower(); while (pLower) { if (pLower->IsTabFrame()) { pTableFrame = static_cast(pLower); break; } pLower = pLower->GetNext(); } if (pTableFrame == nullptr) return; // Insert the table at the position of the text node which has the frame anchored to SwFrame* pAnchoreFrame = pFlyFrame->AnchorFrame(); if (pAnchoreFrame == nullptr || !pAnchoreFrame->IsTextFrame()) return; SwTextFrame* pTextFrame = static_cast(pAnchoreFrame); if (pTextFrame->GetTextNodeFirst() == nullptr) return; SwNodeIndex aInsertPos((*pTextFrame->GetTextNodeFirst())); SwTableNode* pTableNode = pTableFrame->GetTable()->GetTableNode(); if (pTableNode == nullptr) return; SwDoc& rDoc = pTextFrame->GetDoc(); // tdf#129176: clear "TablePosition" grab bag, since we explicitly change the position here // See DomainMapperTableHandler::endTableGetTableStyle, where the grab bag is filled, and // DocxAttributeOutput::TableDefinition that uses it on export SwFrameFormat* pTableFormat = pTableFrame->GetTable()->GetFrameFormat(); assert(pTableFormat); if (const SfxGrabBagItem* pGrabBagItem = pTableFormat->GetAttrSet().GetItem(RES_FRMATR_GRABBAG)) { SfxGrabBagItem aGrabBagItem(*pGrabBagItem); // Editable copy if (aGrabBagItem.GetGrabBag().erase("TablePosition")) { css::uno::Any aVal; aGrabBagItem.QueryValue(aVal); const auto xTable = SwXTextTable::CreateXTextTable(pTableFormat); const css::uno::Reference xSet(xTable, css::uno::UNO_QUERY); assert(xSet); xSet->setPropertyValue(UNO_NAME_TABLE_INTEROP_GRAB_BAG, aVal); } } // When we move the table before the first text node, we need to clear RES_PAGEDESC attribute // of the text node otherwise LO will create a page break after the table if (pTextFrame->GetTextNodeFirst()) { const SwPageDesc* pPageDesc = pTextFrame->GetPageDescItem().GetPageDesc(); // First text node of the page has this if (pPageDesc) { // First set the existing page desc for the table node SfxItemSet aSet(GetEditWin()->GetView().GetWrtShell().GetAttrPool(), svl::Items{}); aSet.Put(SwFormatPageDesc(pPageDesc)); SwPaM aPaMTable(*pTableNode); rDoc.getIDocumentContentOperations().InsertItemSet( aPaMTable, aSet, SetAttrMode::DEFAULT, GetPageFrame()->getRootFrame()); // Then remove pagedesc from the attributes of the text node aSet.Put(SwFormatPageDesc(nullptr)); SwPaM aPaMTextNode(*pTextFrame->GetTextNodeFirst()); rDoc.getIDocumentContentOperations().InsertItemSet( aPaMTextNode, aSet, SetAttrMode::DEFAULT, GetPageFrame()->getRootFrame()); } } // Move the table outside of the text frame SwNodeRange aRange(*pTableNode, 0, *pTableNode->EndOfSectionNode(), 1); rDoc.getIDocumentContentOperations().MoveNodeRange(aRange, aInsertPos, SwMoveFlags::DEFAULT); // Remove the floating table's frame SwFlyFrameFormat* pFrameFormat = pFlyFrame->GetFormat(); if (pFrameFormat) { rDoc.getIDocumentLayoutAccess().DelLayoutFormat(pFrameFormat); } rDoc.getIDocumentState().SetModified(); // Undoing MoveNodeRange() is not working correctly in case of tables, it crashes sometimes // So don't allow to undo after unfloating (similar to MakeFlyAndMove() method) if (rDoc.GetIDocumentUndoRedo().DoesUndo()) { rDoc.GetIDocumentUndoRedo().DelAllUndoObj(); } } void UnfloatTableButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) { SetMapMode(MapMode(MapUnit::MapPixel)); drawinglayer::primitive2d::Primitive2DContainer aSeq; const ::tools::Rectangle aRect( ::tools::Rectangle(Point(0, 0), rRenderContext.PixelToLogic(GetSizePixel()))); // Create button SwFrameButtonPainter::PaintButton(aSeq, aRect, true); // Create the text primitive basegfx::BColor aLineColor = SwViewOption::GetHeaderFooterMarkColor().getBColor(); basegfx::B2DVector aFontSize; drawinglayer::attribute::FontAttribute aFontAttr = drawinglayer::primitive2d::getFontAttributeFromVclFont( aFontSize, rRenderContext.GetFont(), false, false); FontMetric aFontMetric = rRenderContext.GetFontMetric(rRenderContext.GetFont()); double nTextOffsetY = aFontMetric.GetAscent() + TEXT_PADDING; double nTextOffsetX = std::abs(aRect.GetWidth() - rRenderContext.GetTextWidth(m_sLabel)) / 2.0; Point aTextPos(nTextOffsetX, nTextOffsetY); basegfx::B2DHomMatrix aTextMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix( aFontSize.getX(), aFontSize.getY(), static_cast(aTextPos.X()), static_cast(aTextPos.Y()))); aSeq.push_back(drawinglayer::primitive2d::Primitive2DReference( new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( aTextMatrix, m_sLabel, 0, m_sLabel.getLength(), std::vector(), aFontAttr, css::lang::Locale(), aLineColor))); // Create the processor and process the primitives const drawinglayer::geometry::ViewInformation2D aNewViewInfos; std::unique_ptr pProcessor( drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(rRenderContext, aNewViewInfos)); pProcessor->process(aSeq); } void UnfloatTableButton::ShowAll(bool bShow) { Show(bShow); } bool UnfloatTableButton::Contains(const Point& rDocPt) const { ::tools::Rectangle aRect(GetPosPixel(), GetSizePixel()); if (aRect.IsInside(rDocPt)) return true; return false; } void UnfloatTableButton::SetReadonly(bool bReadonly) { ShowAll(!bReadonly); } /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */