/* -*- 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 #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 #include #include #include #include #include #include #include #include #include #include namespace { bool HasValidPropertyValue(const uno::Any& rAny) { if (bool bValue; rAny >>= bValue) { return true; } else if (OUString aValue; (rAny >>= aValue) && !(aValue.isEmpty())) { return true; } else if (awt::FontSlant eValue; rAny >>= eValue) { return true; } else if (tools::Long nValueLong; rAny >>= nValueLong) { return true; } else if (double fValue; rAny >>= fValue) { return true; } else if (short nValueShort; rAny >>= nValueShort) { return true; } else return false; } bool PSCSDFPropsQuickHelp(const HelpEvent &rEvt, SwWrtShell& rSh) { OUString sText; SwView& rView = rSh.GetView(); if (rView.IsHighlightCharDF() || rView.GetStylesHighlighterParaColorMap().size() || rView.GetStylesHighlighterCharColorMap().size()) { SwPosition aPos(rSh.GetDoc()->GetNodes()); Point aPt(rSh.GetWin()->PixelToLogic( rSh.GetWin()->ScreenToOutputPixel(rEvt.GetMousePosPixel()))); rSh.GetLayout()->GetModelPositionForViewPoint(&aPos, aPt); if (!aPos.GetContentNode()->IsTextNode()) return false; rtl::Reference xRange( SwXTextRange::CreateXTextRange(*(rView.GetDocShell()->GetDoc()), aPos, &aPos)); if (!xRange) throw uno::RuntimeException(); SwContentFrame* pContentFrame = aPos.GetContentNode()->GetTextNode()->getLayoutFrame( rSh.GetLayout()); SwRect aFrameAreaRect; bool bContainsPt = false; do { aFrameAreaRect = pContentFrame->getFrameArea(); if (aFrameAreaRect.Contains(aPt)) { bContainsPt = true; break; } } while((pContentFrame = pContentFrame->GetFollow())); if (bContainsPt) { if (rView.GetStylesHighlighterCharColorMap().size()) { // check if in CS formatting highlighted area OUString sCharStyle; xRange->getPropertyValue("CharStyleName") >>= sCharStyle; if (!sCharStyle.isEmpty()) sText = SwStyleNameMapper::GetUIName(sCharStyle, SwGetPoolIdFromName::ChrFmt); } if (sText.isEmpty() && rView.IsHighlightCharDF()) { // check if in direct formatting highlighted area const std::vector aHiddenProperties{ UNO_NAME_RSID, UNO_NAME_PARA_IS_NUMBERING_RESTART, UNO_NAME_PARA_STYLE_NAME, UNO_NAME_PARA_CONDITIONAL_STYLE_NAME, UNO_NAME_PAGE_STYLE_NAME, UNO_NAME_NUMBERING_START_VALUE, UNO_NAME_NUMBERING_IS_NUMBER, UNO_NAME_PARA_CONTINUEING_PREVIOUS_SUB_TREE, UNO_NAME_CHAR_STYLE_NAME, UNO_NAME_NUMBERING_LEVEL, UNO_NAME_SORTED_TEXT_ID, UNO_NAME_PARRSID, UNO_NAME_CHAR_COLOR_THEME, UNO_NAME_CHAR_COLOR_TINT_OR_SHADE }; SfxItemPropertySet const& rPropSet( *aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE)); SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap()); const uno::Sequence aProperties = xRange->getPropertySetInfo()->getProperties(); for (const beans::Property& rProperty : aProperties) { const OUString& rPropName = rProperty.Name; if (!rMap.hasPropertyByName(rPropName)) continue; if (std::find(aHiddenProperties.begin(), aHiddenProperties.end(), rPropName) != aHiddenProperties.end()) continue; if (xRange->getPropertyState(rPropName) == beans::PropertyState_DIRECT_VALUE) { const uno::Any aAny = xRange->getPropertyValue(rPropName); if (HasValidPropertyValue(aAny)) { sText = SwResId(STR_CHARACTER_DIRECT_FORMATTING); break; } } } } } else if (rView.GetStylesHighlighterParaColorMap().size()) { // check if in paragraph style formatting highlighted area pContentFrame = aPos.GetContentNode()->GetTextNode()->getLayoutFrame( rSh.GetLayout()); do { aFrameAreaRect = pContentFrame->getFrameArea(); if (pContentFrame->IsRightToLeft()) { aFrameAreaRect.AddRight(375); aFrameAreaRect.Left(aFrameAreaRect.Right() - 300); } else { aFrameAreaRect.AddLeft(-375); aFrameAreaRect.Right(aFrameAreaRect.Left() + 300); } if (aFrameAreaRect.Contains(aPt)) { OUString sParaStyle; xRange->getPropertyValue("ParaStyleName") >>= sParaStyle; sText = SwStyleNameMapper::GetUIName(sParaStyle, SwGetPoolIdFromName::TxtColl); // check for paragraph direct formatting if (SwDoc::HasParagraphDirectFormatting(aPos)) sText = sText + " + " + SwResId(STR_PARAGRAPH_DIRECT_FORMATTING); break; } } while((pContentFrame = pContentFrame->GetFollow())); } } if (!sText.isEmpty()) { tools::Rectangle aRect(rSh.GetWin()->PixelToLogic( rSh.GetWin()->ScreenToOutputPixel(rEvt.GetMousePosPixel())), Size(1, 1)); Point aPt(rSh.GetWin()->OutputToScreenPixel(rSh.GetWin()->LogicToPixel(aRect.TopLeft()))); aRect.SetLeft(aPt.X()); aRect.SetTop(aPt.Y()); aPt = rSh.GetWin()->OutputToScreenPixel(rSh.GetWin()->LogicToPixel(aRect.BottomRight())); aRect.SetRight(aPt.X()); aRect.SetBottom(aPt.Y()); // tdf#136336 ensure tooltip area surrounds the current mouse position with at least a pixel margin aRect.Union(tools::Rectangle(rEvt.GetMousePosPixel(), Size(1, 1))); aRect.AdjustLeft(-1); aRect.AdjustRight(1); aRect.AdjustTop(-1); aRect.AdjustBottom(1); QuickHelpFlags nStyle = QuickHelpFlags::NONE; //TipStyleBalloon; Help::ShowQuickHelp(rSh.GetWin(), aRect, sText, nStyle); } return !sText.isEmpty(); } } static OUString lcl_GetRedlineHelp( const SwRangeRedline& rRedl, bool bBalloon, bool bTableChange, bool bTableColChange ) { TranslateId pResId; switch( rRedl.GetType() ) { case RedlineType::Insert: pResId = bTableChange ? !bTableColChange ? STR_REDLINE_TABLE_ROW_INSERT : STR_REDLINE_TABLE_COLUMN_INSERT : rRedl.IsMoved() ? STR_REDLINE_INSERT_MOVED : STR_REDLINE_INSERT; break; case RedlineType::Delete: pResId = bTableChange ? !bTableColChange ? STR_REDLINE_TABLE_ROW_DELETE : STR_REDLINE_TABLE_COLUMN_DELETE : rRedl.IsMoved() ? STR_REDLINE_DELETE_MOVED : STR_REDLINE_DELETE; break; case RedlineType::Format: pResId = STR_REDLINE_FORMAT; break; case RedlineType::Table: pResId = STR_REDLINE_TABLE; break; case RedlineType::FmtColl: pResId = STR_REDLINE_FMTCOLL; break; case RedlineType::ParagraphFormat: pResId = STR_REDLINE_PARAGRAPH_FORMAT; break; case RedlineType::TableRowInsert: pResId = STR_REDLINE_TABLE_ROW_INSERT; break; case RedlineType::TableRowDelete: pResId = STR_REDLINE_TABLE_ROW_DELETE; break; case RedlineType::TableCellInsert: pResId = STR_REDLINE_TABLE_CELL_INSERT; break; case RedlineType::TableCellDelete: pResId = STR_REDLINE_TABLE_CELL_DELETE; break; default: break; } if (!pResId) return OUString(); OUStringBuffer sBuf(SwResId(pResId) + ": " + rRedl.GetAuthorString() + " - " + GetAppLangDateTimeString(rRedl.GetTimeStamp())); if( bBalloon && !rRedl.GetComment().isEmpty() ) sBuf.append("\n" + rRedl.GetComment()); return sBuf.makeStringAndClear(); } OUString SwEditWin::ClipLongToolTip(const OUString& rText) { OUString sDisplayText(rText); tools::Long nTextWidth = GetTextWidth(sDisplayText); tools::Long nMaxWidth = GetDesktopRectPixel().GetWidth() * 2 / 3; nMaxWidth = PixelToLogic(Size(nMaxWidth, 0)).Width(); if (nTextWidth > nMaxWidth) sDisplayText = GetOutDev()->GetEllipsisString(sDisplayText, nMaxWidth, DrawTextFlags::CenterEllipsis); return sDisplayText; } void SwEditWin::RequestHelp(const HelpEvent &rEvt) { SwWrtShell &rSh = m_rView.GetWrtShell(); if (PSCSDFPropsQuickHelp(rEvt, rSh)) return; bool bQuickBalloon = bool(rEvt.GetMode() & ( HelpEventMode::QUICK | HelpEventMode::BALLOON )); if(bQuickBalloon && !rSh.GetViewOptions()->IsShowContentTips()) return; bool bContinue = true; CurrShell aCurr(&rSh); OUString sText; Point aPos( PixelToLogic( ScreenToOutputPixel( rEvt.GetMousePosPixel() ) )); bool bBalloon = bool(rEvt.GetMode() & HelpEventMode::BALLOON); SdrView *pSdrView = rSh.GetDrawView(); if( bQuickBalloon && pSdrView ) { SdrPageView* pPV = pSdrView->GetSdrPageView(); SwDPage* pPage = pPV ? static_cast(pPV->GetPage()) : nullptr; bContinue = pPage && pPage->RequestHelp(this, pSdrView, rEvt); } if( bContinue && bQuickBalloon) { SwRect aFieldRect; SwContentAtPos aContentAtPos( IsAttrAtPos::Field | IsAttrAtPos::InetAttr | IsAttrAtPos::Ftn | IsAttrAtPos::Redline | IsAttrAtPos::ToxMark | IsAttrAtPos::RefMark | IsAttrAtPos::SmartTag | #ifdef DBG_UTIL IsAttrAtPos::TableBoxValue | ( bBalloon ? IsAttrAtPos::CurrAttrs : IsAttrAtPos::NONE) | #endif IsAttrAtPos::TableBoxFml | IsAttrAtPos::TableRedline | IsAttrAtPos::TableColRedline ); if( rSh.GetContentAtPos( aPos, aContentAtPos, false, &aFieldRect ) ) { QuickHelpFlags nStyle = QuickHelpFlags::NONE; // style of quick help switch( aContentAtPos.eContentAtPos ) { case IsAttrAtPos::TableBoxFml: sText = "= " + static_cast(aContentAtPos.aFnd.pAttr)->GetFormula(); break; #ifdef DBG_UTIL case IsAttrAtPos::TableBoxValue: { if(aContentAtPos.aFnd.pAttr) { sText = OUString::number( static_cast(aContentAtPos.aFnd.pAttr)->GetValue()); } break; } case IsAttrAtPos::CurrAttrs: sText = aContentAtPos.sStr; break; #endif case IsAttrAtPos::InetAttr: { sText = static_cast(aContentAtPos.aFnd.pAttr)->GetValue(); sText = URIHelper::removePassword( sText, INetURLObject::EncodeMechanism::WasEncoded, INetURLObject::DecodeMechanism::Unambiguous); //#i63832# remove the link target type sal_Int32 nFound = sText.indexOf(cMarkSeparator); if( nFound != -1 && (++nFound) < sText.getLength() ) { std::u16string_view sSuffix( sText.subView(nFound) ); if( sSuffix == u"table" || sSuffix == u"frame" || sSuffix == u"region" || sSuffix == u"outline" || sSuffix == u"text" || sSuffix == u"graphic" || sSuffix == u"ole" || sSuffix == u"drawingobject" ) sText = sText.copy( 0, nFound - 1); } // #i104300# // special handling if target is a cross-reference bookmark { OUString sTmpSearchStr = sText.copy( 1 ); IDocumentMarkAccess* pMarkAccess = rSh.getIDocumentMarkAccess(); IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findBookmark( sTmpSearchStr ); if ( ppBkmk != pMarkAccess->getBookmarksEnd() && IDocumentMarkAccess::GetType(**ppBkmk) == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK ) { SwTextNode* pTextNode = (*ppBkmk)->GetMarkStart().GetNode().GetTextNode(); if ( pTextNode ) { sText = sw::GetExpandTextMerged(rSh.GetLayout(), *pTextNode, true, false, ExpandMode(0)); if( !sText.isEmpty() ) { OUStringBuffer sTmp(sText.replaceAll(u"\u00ad", "")); for (sal_Int32 i = 0; i < sTmp.getLength(); ++i) { if (sTmp[i] < 0x20) sTmp[i] = 0x20; else if (sTmp[i] == 0x2011) sTmp[i] = '-'; } sText = sTmp.makeStringAndClear(); } } } } // #i80029# bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly(); if ( !bExecHyperlinks ) { sText = SfxHelp::GetURLHelpText(sText); } break; } case IsAttrAtPos::SmartTag: { vcl::KeyCode aCode( KEY_SPACE ); vcl::KeyCode aModifiedCode( KEY_SPACE, KEY_MOD1 ); OUString aModStr( aModifiedCode.GetName() ); aModStr = aModStr.replaceFirst(aCode.GetName(), ""); aModStr = aModStr.replaceAll("+", ""); sText = SwResId(STR_SMARTTAG_CLICK).replaceAll("%s", aModStr); break; } case IsAttrAtPos::Ftn: if( aContentAtPos.pFndTextAttr && aContentAtPos.aFnd.pAttr ) { const SwFormatFootnote* pFootnote = static_cast(aContentAtPos.aFnd.pAttr); OUString sTmp(pFootnote->GetFootnoteText(*rSh.GetLayout())); sText = SwResId( pFootnote->IsEndNote() ? STR_ENDNOTE : STR_FTNNOTE ) + sTmp; bBalloon = true; if( aContentAtPos.IsInRTLText() ) nStyle |= QuickHelpFlags::BiDiRtl; } break; case IsAttrAtPos::TableRedline: case IsAttrAtPos::TableColRedline: case IsAttrAtPos::Redline: { const bool bShowTrackChanges = IDocumentRedlineAccess::IsShowChanges( m_rView.GetDocShell()->GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags() ); const bool bShowInlineTooltips = rSh.GetViewOptions()->IsShowInlineTooltips(); if ( bShowTrackChanges && bShowInlineTooltips ) { sText = lcl_GetRedlineHelp(*aContentAtPos.aFnd.pRedl, bBalloon, IsAttrAtPos::TableRedline == aContentAtPos.eContentAtPos || IsAttrAtPos::TableColRedline == aContentAtPos.eContentAtPos, IsAttrAtPos::TableColRedline == aContentAtPos.eContentAtPos); } break; } case IsAttrAtPos::ToxMark: sText = aContentAtPos.sStr; if( !sText.isEmpty() && aContentAtPos.pFndTextAttr ) { const SwTOXType* pTType = aContentAtPos.pFndTextAttr-> GetTOXMark().GetTOXType(); if( pTType && !pTType->GetTypeName().isEmpty() ) { sText = ": " + sText; sText = pTType->GetTypeName() + sText; } } break; case IsAttrAtPos::RefMark: if(aContentAtPos.aFnd.pAttr) { sText = SwResId(STR_CONTENT_TYPE_SINGLE_REFERENCE) + ": " + static_cast(aContentAtPos.aFnd.pAttr)->GetRefName(); } break; default: { SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); if(!pModOpt->IsHideFieldTips()) { const SwField* pField = aContentAtPos.aFnd.pField; switch( pField->Which() ) { case SwFieldIds::SetExp: case SwFieldIds::Table: case SwFieldIds::GetExp: { sal_uInt16 nOldSubType = pField->GetSubType(); const_cast(pField)->SetSubType(nsSwExtendedSubType::SUB_CMD); sText = pField->ExpandField(true, rSh.GetLayout()); const_cast(pField)->SetSubType(nOldSubType); } break; case SwFieldIds::Postit: { break; } case SwFieldIds::Input: // BubbleHelp, because the suggestion could be quite long bBalloon = true; [[fallthrough]]; case SwFieldIds::Dropdown: case SwFieldIds::JumpEdit: sText = pField->GetPar2(); break; case SwFieldIds::Database: sText = pField->GetFieldName(); break; case SwFieldIds::User: { OUString aTitle = pField->GetTitle(); if (!aTitle.isEmpty()) { sText = aTitle; } else { sText = pField->GetPar1(); } break; } case SwFieldIds::HiddenText: sText = pField->GetPar1(); break; case SwFieldIds::DocStat: break; case SwFieldIds::Macro: sText = static_cast(pField)->GetMacro(); break; case SwFieldIds::GetRef: { // #i85090# const SwGetRefField* pRefField( dynamic_cast(pField) ); OSL_ENSURE( pRefField, " - unexpected type of " ); if ( pRefField ) { if ( pRefField->IsRefToHeadingCrossRefBookmark() || pRefField->IsRefToNumItemCrossRefBookmark() ) { sText = pRefField->GetExpandedTextOfReferencedTextNode(*rSh.GetLayout(), nullptr, nullptr); if ( sText.getLength() > 80 ) { sText = OUString::Concat(sText.subView(0, 80)) + "..."; } } else { sText = pRefField->GetSetRefName(); } } break; } case SwFieldIds::TableOfAuthorities: { const auto pAuthorityField = static_cast(pField); sText = pAuthorityField->GetAuthority(rSh.GetLayout()); if (auto t = pAuthorityField->GetTargetType(); t == SwAuthorityField::TargetType::UseDisplayURL || t == SwAuthorityField::TargetType::UseTargetURL) { const OUString& rURL = pAuthorityField->GetAbsoluteURL(); sText += "\n" + SfxHelp::GetURLHelpText(rURL); } break; } default: break; } } if( sText.isEmpty() ) { const bool bShowTrackChanges = IDocumentRedlineAccess::IsShowChanges( m_rView.GetDocShell()->GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags() ); const bool bShowInlineTooltips = rSh.GetViewOptions()->IsShowInlineTooltips(); if ( bShowTrackChanges && bShowInlineTooltips ) { aContentAtPos.eContentAtPos = IsAttrAtPos::Redline; if( rSh.GetContentAtPos( aPos, aContentAtPos, false, &aFieldRect ) ) sText = lcl_GetRedlineHelp(*aContentAtPos.aFnd.pRedl, bBalloon, /*bTableChange=*/false, /*bTableColChange=*/false); } } } } if (!sText.isEmpty()) { tools::Rectangle aRect( aFieldRect.SVRect() ); Point aPt( OutputToScreenPixel( LogicToPixel( aRect.TopLeft() ))); aRect.SetLeft( aPt.X() ); aRect.SetTop( aPt.Y() ); aPt = OutputToScreenPixel( LogicToPixel( aRect.BottomRight() )); aRect.SetRight( aPt.X() ); aRect.SetBottom( aPt.Y() ); // tdf#136336 ensure tooltip area surrounds the current mouse position with at least a pixel margin aRect.Union(tools::Rectangle(rEvt.GetMousePosPixel(), Size(1, 1))); aRect.AdjustLeft(-1); aRect.AdjustRight(1); aRect.AdjustTop(-1); aRect.AdjustBottom(1); if( bBalloon ) Help::ShowBalloon( this, rEvt.GetMousePosPixel(), aRect, sText ); else { // the show the help OUString sDisplayText(ClipLongToolTip(sText)); Help::ShowQuickHelp(this, aRect, sDisplayText, nStyle); } } bContinue = false; } } if( bContinue ) Window::RequestHelp( rEvt ); } void SwEditWin::PrePaint(vcl::RenderContext& /*rRenderContext*/) { if (SwWrtShell* pWrtShell = GetView().GetWrtShellPtr()) pWrtShell->PrePaint(); } void SwEditWin::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) { SwWrtShell* pWrtShell = GetView().GetWrtShellPtr(); if(!pWrtShell) return; if ( GetView().GetVisArea().GetWidth() <= 0 || GetView().GetVisArea().GetHeight() <= 0 ) Invalidate( rRect ); else { pWrtShell->setOutputToWindow(true); bool bTiledPainting = false; if (comphelper::LibreOfficeKit::isActive()) { bTiledPainting = comphelper::LibreOfficeKit::isTiledPainting(); comphelper::LibreOfficeKit::setTiledPainting(true); } pWrtShell->Paint(rRenderContext, rRect); if (comphelper::LibreOfficeKit::isActive()) { comphelper::LibreOfficeKit::setTiledPainting(bTiledPainting); } pWrtShell->setOutputToWindow(false); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */