diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/uibase/utlui | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/uibase/utlui')
21 files changed, 14184 insertions, 0 deletions
diff --git a/sw/source/uibase/utlui/AccessibilityStatusBarControl.cxx b/sw/source/uibase/utlui/AccessibilityStatusBarControl.cxx new file mode 100644 index 0000000000..9a99d3c578 --- /dev/null +++ b/sw/source/uibase/utlui/AccessibilityStatusBarControl.cxx @@ -0,0 +1,81 @@ +/* -*- 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 <swtypes.hxx> +#include <strings.hrc> +#include <AccessibilityStatusBarControl.hxx> +#include <svl/intitem.hxx> +#include <vcl/status.hxx> +#include <vcl/event.hxx> +#include <officecfg/Office/Common.hxx> +#include <bitmaps.hlst> + +SFX_IMPL_STATUSBAR_CONTROL(sw::AccessibilityStatusBarControl, SfxInt32Item); + +namespace sw +{ +AccessibilityStatusBarControl::AccessibilityStatusBarControl(sal_uInt16 _nSlotId, sal_uInt16 _nId, + StatusBar& rStb) + : SfxStatusBarControl(_nSlotId, _nId, rStb) + , mnIssues(0) + , maImageIssuesFound(Image(StockImage::Yes, RID_BMP_A11Y_CHECK_ISSUES_FOUND)) + , maImageIssuesNotFound(Image(StockImage::Yes, RID_BMP_A11Y_CHECK_ISSUES_NOT_FOUND)) +{ +} + +AccessibilityStatusBarControl::~AccessibilityStatusBarControl() = default; + +void AccessibilityStatusBarControl::StateChangedAtStatusBarControl(sal_uInt16 /*nSID*/, + SfxItemState eState, + const SfxPoolItem* pState) +{ + mnIssues = -1; + + bool bOnlineCheckStatus + = officecfg::Office::Common::Accessibility::OnlineAccessibilityCheck::get(); + + if (eState == SfxItemState::DEFAULT && bOnlineCheckStatus) + { + if (auto pItem = dynamic_cast<const SfxInt32Item*>(pState)) + mnIssues = pItem->GetValue(); + OUString aString = SwResId(STR_ACCESSIBILITY_CHECK_HINT) + .replaceFirst("%issues%", OUString::number(mnIssues)); + GetStatusBar().SetQuickHelpText(GetId(), aString); + } + else + { + GetStatusBar().SetQuickHelpText(GetId(), u""_ustr); + } + + GetStatusBar().Invalidate(); +} + +void AccessibilityStatusBarControl::Paint(const UserDrawEvent& rUserEvent) +{ + if (mnIssues < 0) + return; + + vcl::RenderContext* pRenderContext = rUserEvent.GetRenderContext(); + + tools::Rectangle aRect = rUserEvent.GetRect(); + const tools::Rectangle aControlRect = getControlRect(); + + Image aImage = mnIssues > 0 ? maImageIssuesFound : maImageIssuesNotFound; + + Size aSize(aImage.GetSizePixel()); + + auto aPosition = Point(aRect.Left() + (aControlRect.GetWidth() - aSize.Width()) / 2, + aRect.Top() + (aControlRect.GetHeight() - aSize.Height()) / 2); + + pRenderContext->DrawImage(aPosition, aImage); +} + +} // end sw + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/attrdesc.cxx b/sw/source/uibase/utlui/attrdesc.cxx new file mode 100644 index 0000000000..71fdd4ab54 --- /dev/null +++ b/sw/source/uibase/utlui/attrdesc.cxx @@ -0,0 +1,846 @@ +/* -*- 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 <i18nutil/unicode.hxx> +#include <svl/itemiter.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/GraphicAttributes.hxx> + +#include <editeng/itemtype.hxx> +#include <editeng/eerdll.hxx> +#include <unotools/intlwrapper.hxx> +#include <unotools/syslocale.hxx> +#include <rtl/ustrbuf.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <fmtinfmt.hxx> +#include <fchrfmt.hxx> +#include <fmtautofmt.hxx> +#include <fmtsrnd.hxx> +#include <fmtornt.hxx> +#include <fmtlsplt.hxx> +#include <fmtrowsplt.hxx> +#include <fmtpdsc.hxx> +#include <fmtclds.hxx> +#include <fmteiro.hxx> +#include <fmturl.hxx> +#include <fmthdft.hxx> +#include <fmtcnct.hxx> +#include <fmtline.hxx> +#include <tgrditem.hxx> +#include <hfspacingitem.hxx> +#include <fmtruby.hxx> +#include <paratr.hxx> +#include <grfatr.hxx> +#include <pagedesc.hxx> +#include <charfmt.hxx> +#include <strings.hrc> +#include <fmtftntx.hxx> +#include <fmtfollowtextflow.hxx> +#include <libxml/xmlwriter.h> + +using namespace com::sun::star; + + +// query the attribute descriptions +void SwAttrSet::GetPresentation( + SfxItemPresentation ePres, + MapUnit eCoreMetric, + MapUnit ePresMetric, + OUString &rText ) const +{ + rText.clear(); + OUString aStr; + if( !Count() ) + return; + + SfxItemIter aIter( *this ); + const SfxPoolItem* pItem = aIter.GetCurItem(); + const IntlWrapper aInt(SvtSysLocale().GetUILanguageTag()); + do + { + pItem->GetPresentation(ePres, eCoreMetric, ePresMetric, aStr, aInt); + if( rText.getLength() && aStr.getLength() ) + rText += ", "; + rText += aStr; + pItem = aIter.NextItem(); + } while (pItem); +} + +bool SwFormatCharFormat::GetPresentation +( + SfxItemPresentation ePres, + MapUnit eCoreUnit, + MapUnit ePresUnit, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + const SwCharFormat *pCharFormat = GetCharFormat(); + if ( pCharFormat ) + { + OUString aStr; + pCharFormat->GetPresentation( ePres, eCoreUnit, ePresUnit, aStr ); + rText = SwResId( STR_CHARFMT ) + "(" + aStr + ")"; + } + else + rText = SwResId( STR_NO_CHARFMT ); + return true; +} + +bool SwFormatAutoFormat::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + rText.clear(); //TODO + return true; +} + +bool SwFormatINetFormat::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + rText = GetValue(); + return true; +} + +bool SwFormatRuby::GetPresentation( SfxItemPresentation /*ePres*/, + MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, + OUString &rText, const IntlWrapper& /*rIntl*/ ) const +{ + rText.clear(); + return true; +} + +bool SwFormatDrop::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + rText.clear(); + if ( GetLines() > 1 ) + { + if ( GetChars() > 1 ) + { + rText = OUString::number( GetChars() ) + " "; + } + rText += SwResId( STR_DROP_OVER ) + + " " + + OUString::number( GetLines() ) + + " " + + SwResId( STR_DROP_LINES ); + } + else + rText = SwResId( STR_NO_DROP_LINES ); + return true; +} + +bool SwRegisterItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + TranslateId pId = GetValue() ? STR_REGISTER_ON : STR_REGISTER_OFF; + rText = SwResId(pId); + return true; +} + +bool SwNumRuleItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + if( !GetValue().isEmpty() ) + rText = SwResId( STR_NUMRULE_ON ).replaceFirst("%LISTSTYLENAME", GetValue()); + else + rText = SwResId( STR_NUMRULE_OFF ); + return true; +} + +bool SwParaConnectBorderItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + TranslateId pId = GetValue() ? STR_CONNECT_BORDER_ON : STR_CONNECT_BORDER_OFF; + rText = SwResId(pId); + return true; +} + +// Frame attribute + +bool SwFormatFrameSize::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit eCoreUnit, + MapUnit ePresUnit, + OUString& rText, + const IntlWrapper& rIntl +) const +{ + rText = SwResId( STR_FRM_WIDTH ) + " "; + if ( GetWidthPercent() ) + { + rText += unicode::formatPercent(GetWidthPercent(), + Application::GetSettings().GetUILanguageTag()); + } + else + { + rText += ::GetMetricText( GetWidth(), eCoreUnit, ePresUnit, &rIntl ) + + " " + ::EditResId( ::GetMetricId( ePresUnit ) ); + } + if ( SwFrameSize::Variable != GetHeightSizeType() ) + { + TranslateId pId = SwFrameSize::Fixed == m_eFrameHeightType ? + STR_FRM_FIXEDHEIGHT : STR_FRM_MINHEIGHT; + rText += ", " + SwResId(pId) + " "; + if ( GetHeightPercent() ) + { + rText += unicode::formatPercent(GetHeightPercent(), + Application::GetSettings().GetUILanguageTag()); + } + else + { + rText = ::GetMetricText( GetHeight(), eCoreUnit, ePresUnit, &rIntl ) + + " " + EditResId( ::GetMetricId( ePresUnit ) ); + } + } + return true; +} + +//Header for page formats. +//Client of FrameFormat which describes the header. + +bool SwFormatHeader::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + TranslateId pId = GetHeaderFormat() ? STR_HEADER : STR_NO_HEADER; + rText = SwResId(pId); + return true; +} + +//Footer for page formats. +//Client of FrameFormat which describes the footer. + +bool SwFormatFooter::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + TranslateId pId = GetFooterFormat() ? STR_FOOTER : STR_NO_FOOTER; + rText = SwResId(pId); + return true; +} + +bool SwFormatSurround::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + TranslateId pId; + switch ( GetValue() ) + { + case css::text::WrapTextMode_NONE: + pId = STR_SURROUND_NONE; + break; + case css::text::WrapTextMode_THROUGH: + pId = STR_SURROUND_THROUGH; + break; + case css::text::WrapTextMode_PARALLEL: + pId = STR_SURROUND_PARALLEL; + break; + case css::text::WrapTextMode_DYNAMIC: + pId = STR_SURROUND_IDEAL; + break; + case css::text::WrapTextMode_LEFT: + pId = STR_SURROUND_LEFT; + break; + case css::text::WrapTextMode_RIGHT: + pId = STR_SURROUND_RIGHT; + break; + default:;//prevent warning + } + if (pId) + rText = SwResId(pId); + + if ( IsAnchorOnly() ) + { + rText += " " + SwResId( STR_SURROUND_ANCHORONLY ); + } + return true; +} + +//VertOrientation, how and by what orientate the FlyFrame in the vertical? + +bool SwFormatVertOrient::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit eCoreUnit, + MapUnit ePresUnit, + OUString& rText, + const IntlWrapper& rIntl +) const +{ + TranslateId pId; + switch ( GetVertOrient() ) + { + case text::VertOrientation::NONE: + { + rText += SwResId( STR_POS_Y ) + " " + + ::GetMetricText( GetPos(), eCoreUnit, ePresUnit, &rIntl ) + + " " + EditResId( ::GetMetricId( ePresUnit ) ); + } + break; + case text::VertOrientation::TOP: + pId = STR_VERT_TOP; + break; + case text::VertOrientation::CENTER: + pId = STR_VERT_CENTER; + break; + case text::VertOrientation::BOTTOM: + pId = STR_VERT_BOTTOM; + break; + case text::VertOrientation::LINE_TOP: + pId = STR_LINE_TOP; + break; + case text::VertOrientation::LINE_CENTER: + pId = STR_LINE_CENTER; + break; + case text::VertOrientation::LINE_BOTTOM: + pId = STR_LINE_BOTTOM; + break; + default:;//prevent warning + } + if (pId) + rText += SwResId(pId); + return true; +} + +//HoriOrientation, how and by what orientate the FlyFrame in the horizontal? + +bool SwFormatHoriOrient::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit eCoreUnit, + MapUnit ePresUnit, + OUString& rText, + const IntlWrapper& rIntl +) const +{ + TranslateId pId; + switch ( GetHoriOrient() ) + { + case text::HoriOrientation::NONE: + { + rText += SwResId( STR_POS_X ) + " " + + ::GetMetricText( GetPos(), eCoreUnit, ePresUnit, &rIntl ) + + " " + EditResId( ::GetMetricId( ePresUnit ) ); + } + break; + case text::HoriOrientation::RIGHT: + pId = STR_HORI_RIGHT; + break; + case text::HoriOrientation::CENTER: + pId = STR_HORI_CENTER; + break; + case text::HoriOrientation::LEFT: + pId = STR_HORI_LEFT; + break; + case text::HoriOrientation::INSIDE: + pId = STR_HORI_INSIDE; + break; + case text::HoriOrientation::OUTSIDE: + pId = STR_HORI_OUTSIDE; + break; + case text::HoriOrientation::FULL: + pId = STR_HORI_FULL; + break; + default:;//prevent warning + } + if (pId) + rText += SwResId(pId); + return true; +} + +// FlyAnchor, Anchor of the free-flying frame + +bool SwFormatAnchor::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + TranslateId pId; + switch ( GetAnchorId() ) + { + case RndStdIds::FLY_AT_PARA: + pId = STR_FLY_AT_PARA; + break; + case RndStdIds::FLY_AS_CHAR: + pId = STR_FLY_AS_CHAR; + break; + case RndStdIds::FLY_AT_CHAR: + pId = STR_FLY_AT_CHAR; + break; + case RndStdIds::FLY_AT_PAGE: + pId = STR_FLY_AT_PAGE; + break; + default:;//prevent warning + } + if (pId) + rText += SwResId(pId); + return true; +} + +bool SwFormatPageDesc::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + const SwPageDesc *pPageDesc = GetPageDesc(); + if ( pPageDesc ) + rText = pPageDesc->GetName(); + else + rText = SwResId( STR_NO_PAGEDESC ); + return true; +} + +//The ColumnDescriptor + +bool SwFormatCol::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit eCoreUnit, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& rIntl +) const +{ + sal_uInt16 nCnt = GetNumCols(); + if ( nCnt > 1 ) + { + rText = OUString::number(nCnt) + " " + SwResId( STR_COLUMNS ); + if ( COLADJ_NONE != GetLineAdj() ) + { + const tools::Long nWdth = static_cast<tools::Long>(GetLineWidth()); + rText += " " + SwResId( STR_LINE_WIDTH ) + " " + + ::GetMetricText( nWdth, eCoreUnit, + MapUnit::MapPoint, &rIntl ); + } + } + else + rText.clear(); + return true; +} + +//URL's and maps + +bool SwFormatURL::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + rText.clear(); + if ( m_pMap ) + rText += "Client-Map"; + if ( !m_sURL.isEmpty() ) + { + if ( m_pMap ) + rText += " - "; + rText += "URL: " + m_sURL; + if ( m_bIsServerMap ) + rText += " (Server-Map)"; + } + if ( !m_sTargetFrameName.isEmpty() ) + { + rText += ", Target: " + m_sTargetFrameName; + } + return true; +} + +bool SwFormatEditInReadonly::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + rText.clear(); + if ( GetValue() ) + rText = SwResId(STR_EDIT_IN_READONLY); + return true; +} + +void SwFormatEditInReadonly::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatEditInReadonly")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(GetValue()).getStr())); + (void)xmlTextWriterEndElement(pWriter); +} + +bool SwFormatLayoutSplit::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + if ( GetValue() ) + rText = SwResId(STR_LAYOUT_SPLIT); + return true; +} + +bool SwFormatRowSplit::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& /*rText*/, + const IntlWrapper& /*rIntl*/ +) const +{ + return false; +} + +bool SwFormatFootnoteEndAtTextEnd::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& /*rText*/, + const IntlWrapper& /*rIntl*/ +) const +{ + return true; +} + +bool SwFormatChain::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + if ( GetPrev() || GetNext() ) + { + rText = SwResId(STR_CONNECT1); + if ( GetPrev() ) + { + rText += GetPrev()->GetName(); + if ( GetNext() ) + rText += SwResId(STR_CONNECT2); + } + if ( GetNext() ) + rText += GetNext()->GetName(); + } + return true; +} + +bool SwFormatLineNumber::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + if ( IsCount() ) + rText += SwResId(STR_LINECOUNT); + else + rText += SwResId(STR_DONTLINECOUNT); + if ( GetStartValue() ) + { + rText += " " + SwResId(STR_LINCOUNT_START) + + OUString::number( GetStartValue() ); + } + return true; +} + +bool SwTextGridItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + TranslateId pId; + + switch ( GetGridType() ) + { + case GRID_NONE : + pId = STR_GRID_NONE; + break; + case GRID_LINES_ONLY : + pId = STR_GRID_LINES_ONLY; + break; + case GRID_LINES_CHARS : + pId = STR_GRID_LINES_CHARS; + break; + } + if (pId) + rText += SwResId(pId); + return true; +} + +bool SwHeaderAndFooterEatSpacingItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& /*rText*/, + const IntlWrapper& /*rIntl*/ +) const +{ + return false; +} + +// Graphic attributes + +bool SwMirrorGrf::GetPresentation( + SfxItemPresentation /*ePres*/, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString& rText, const IntlWrapper& /*rIntl*/ ) const +{ + TranslateId pId; + switch( GetValue() ) + { + case MirrorGraph::Dont: pId = STR_NO_MIRROR; break; + case MirrorGraph::Vertical: pId = STR_VERT_MIRROR; break; + case MirrorGraph::Horizontal: pId = STR_HORI_MIRROR; break; + case MirrorGraph::Both: pId = STR_BOTH_MIRROR; break; + default: break; + } + if (pId) + { + rText = SwResId(pId); + if (m_bGrfToggle) + rText += SwResId( STR_MIRROR_TOGGLE ); + } + return true; +} + +bool SwRotationGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + if( SfxItemPresentation::Complete == ePres ) + rText = SwResId( STR_ROTATION ); + else if( rText.getLength() ) + rText.clear(); + rText += OUString::number( toDegrees(GetValue()) ) + "\xB0"; + return true; +} + +bool SwLuminanceGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + if( SfxItemPresentation::Complete == ePres ) + rText = SwResId( STR_LUMINANCE ); + else if( rText.getLength() ) + rText.clear(); + rText += unicode::formatPercent(GetValue(), + Application::GetSettings().GetUILanguageTag()); + return true; +} + +bool SwContrastGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + if( SfxItemPresentation::Complete == ePres ) + rText = SwResId( STR_CONTRAST ); + else if( rText.getLength() ) + rText.clear(); + rText += unicode::formatPercent(GetValue(), + Application::GetSettings().GetUILanguageTag()); + return true; +} + +bool SwChannelGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + if( SfxItemPresentation::Complete == ePres ) + { + TranslateId pId; + switch ( Which() ) + { + case RES_GRFATR_CHANNELR: pId = STR_CHANNELR; break; + case RES_GRFATR_CHANNELG: pId = STR_CHANNELG; break; + case RES_GRFATR_CHANNELB: pId = STR_CHANNELB; break; + default: break; + } + if (pId) + rText = SwResId(pId); + else if( rText.getLength() ) + rText.clear(); + } + else if( rText.getLength() ) + rText.clear(); + rText += unicode::formatPercent(GetValue(), + Application::GetSettings().GetUILanguageTag()); + return true; +} + +bool SwGammaGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + OUStringBuffer aText; + if( SfxItemPresentation::Complete == ePres ) + aText.append(SwResId(STR_GAMMA)); + aText.append(unicode::formatPercent(GetValue(), + Application::GetSettings().GetUILanguageTag())); + rText = aText.makeStringAndClear(); + return true; +} + +bool SwInvertGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + rText.clear(); + if( SfxItemPresentation::Complete == ePres ) + { + TranslateId pId = GetValue() ? STR_INVERT : STR_INVERT_NOT; + rText = SwResId(pId); + } + return true; +} + +bool SwTransparencyGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + if( SfxItemPresentation::Complete == ePres ) + rText = SwResId( STR_TRANSPARENCY ); + else if( rText.getLength() ) + rText.clear(); + rText += unicode::formatPercent(GetValue(), + Application::GetSettings().GetUILanguageTag()); + return true; +} + +bool SwDrawModeGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + rText.clear(); + if( SfxItemPresentation::Complete == ePres ) + { + TranslateId pId; + switch ( GetValue() ) + { + + case GraphicDrawMode::Greys: pId = STR_DRAWMODE_GREY; break; + case GraphicDrawMode::Mono: pId = STR_DRAWMODE_BLACKWHITE; break; + case GraphicDrawMode::Watermark: pId = STR_DRAWMODE_WATERMARK; break; + default: pId = STR_DRAWMODE_STD; break; + } + rText = SwResId( STR_DRAWMODE ) + SwResId(pId); + } + return true; +} + +bool SwFormatFollowTextFlow::GetPresentation( SfxItemPresentation ePres, + MapUnit /*eCoreMetric*/, + MapUnit /*ePresMetric*/, + OUString &rText, + const IntlWrapper& /*rIntl*/ ) const +{ + rText.clear(); + if( SfxItemPresentation::Complete == ePres ) + { + TranslateId pId = GetValue() ? STR_FOLLOW_TEXT_FLOW : STR_DONT_FOLLOW_TEXT_FLOW; + rText = SwResId(pId); + } + return true; +} + +void SwFormatFollowTextFlow::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatFollowTextFlow")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(GetValue()).getStr())); + (void)xmlTextWriterEndElement(pWriter); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/bookctrl.cxx b/sw/source/uibase/utlui/bookctrl.cxx new file mode 100644 index 0000000000..e79821022a --- /dev/null +++ b/sw/source/uibase/utlui/bookctrl.cxx @@ -0,0 +1,112 @@ +/* -*- 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 <svl/intitem.hxx> +#include <svl/slstitm.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/status.hxx> +#include <vcl/weldutils.hxx> +#include <cmdid.h> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <IMark.hxx> +#include <bookctrl.hxx> +#include <map> + +SFX_IMPL_STATUSBAR_CONTROL(SwBookmarkControl, SfxStringListItem); + +SwBookmarkControl::SwBookmarkControl( sal_uInt16 _nSlotId, + sal_uInt16 _nId, + StatusBar& rStb ) : + SfxStatusBarControl( _nSlotId, _nId, rStb ) +{ +} + +SwBookmarkControl::~SwBookmarkControl() +{ +} + +void SwBookmarkControl::StateChangedAtStatusBarControl( + sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState ) +{ + if (eState != SfxItemState::DEFAULT || SfxItemState::DISABLED == eState) + { + GetStatusBar().SetItemText(GetId(), OUString()); + GetStatusBar().SetQuickHelpText(GetId(), OUString()); + } + else if (auto pStringListItem = dynamic_cast<const SfxStringListItem*>(pState)) + { + const std::vector<OUString>& rStringList(pStringListItem->GetList()); + GetStatusBar().SetItemText(GetId(), rStringList[0]); + GetStatusBar().SetQuickHelpText(GetId(), rStringList[1]); + } +} + +void SwBookmarkControl::Paint( const UserDrawEvent& ) +{ +} + +void SwBookmarkControl::Command( const CommandEvent& rCEvt ) +{ + if ( rCEvt.GetCommand() != CommandEventId::ContextMenu || + GetStatusBar().GetItemText( GetId() ).isEmpty()) + return; + + SwWrtShell* pWrtShell = ::GetActiveWrtShell(); + if( !(pWrtShell && pWrtShell->getIDocumentMarkAccess()->getAllMarksCount() > 0) ) + return; + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (!pViewFrm) + return; + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/swriter/ui/bookmarkmenu.ui")); + std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu")); + + IDocumentMarkAccess* const pMarkAccess = pWrtShell->getIDocumentMarkAccess(); + IDocumentMarkAccess::const_iterator_t ppBookmarkStart = pMarkAccess->getBookmarksBegin(); + sal_uInt32 nPopupId = 1; + std::map<sal_Int32, sal_uInt16> aBookmarkIdx; + for(IDocumentMarkAccess::const_iterator_t ppBookmark = ppBookmarkStart; + ppBookmark != pMarkAccess->getBookmarksEnd(); + ++ppBookmark) + { + if(IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(**ppBookmark)) + { + xPopup->append(OUString::number(nPopupId), (*ppBookmark)->GetName()); + aBookmarkIdx[nPopupId] = o3tl::narrowing<sal_uInt16>(ppBookmark - ppBookmarkStart); + nPopupId++; + } + } + ::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1, 1)); + weld::Window* pParent = weld::GetPopupParent(GetStatusBar(), aRect); + OUString sResult = xPopup->popup_at_rect(pParent, aRect); + if (!sResult.isEmpty()) + { + SfxUInt16Item aBookmark( FN_STAT_BOOKMARK, aBookmarkIdx[sResult.toUInt32()] ); + pViewFrm->GetDispatcher()->ExecuteList(FN_STAT_BOOKMARK, + SfxCallMode::ASYNCHRON|SfxCallMode::RECORD, + { &aBookmark }); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/condedit.cxx b/sw/source/uibase/utlui/condedit.cxx new file mode 100644 index 0000000000..1303ddfc75 --- /dev/null +++ b/sw/source/uibase/utlui/condedit.cxx @@ -0,0 +1,84 @@ +/* -*- 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 <condedit.hxx> +#include <svx/dbaexchange.hxx> + +using namespace ::svx; +using namespace ::com::sun::star::uno; + +ConditionEdit::ConditionEdit(std::unique_ptr<weld::Entry> xControl) + : m_xControl(std::move(xControl)) + , m_aDropTargetHelper(*this) + , m_bBrackets(true) + , m_bEnableDrop(true) +{ +} + +sal_Int8 ConditionEditDropTarget::AcceptDrop( const AcceptDropEvent& /*rEvt*/ ) +{ + return OColumnTransferable::canExtractColumnDescriptor + ( GetDataFlavorExVector(), + ColumnTransferFormatFlags::COLUMN_DESCRIPTOR ) + ? DND_ACTION_COPY + : DND_ACTION_NONE; +} + +ConditionEditDropTarget::ConditionEditDropTarget(ConditionEdit& rEdit) + : DropTargetHelper(rEdit.get_widget().get_drop_target()) + , m_rEdit(rEdit) +{ +} + +sal_Int8 ConditionEditDropTarget::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + if (m_rEdit.GetDropEnable()) + { + TransferableDataHelper aData( rEvt.maDropEvent.Transferable ); + + const DataFlavorExVector& rVector = aData.GetDataFlavorExVector(); + if (OColumnTransferable::canExtractColumnDescriptor(rVector, ColumnTransferFormatFlags::COLUMN_DESCRIPTOR)) + { + ODataAccessDescriptor aColDesc = OColumnTransferable::extractColumnDescriptor( + aData); + OUString sDBName; + bool bBrackets = m_rEdit.GetBrackets(); + if (bBrackets) + sDBName += "["; + OUString sTmp = aColDesc.getDataSource(); + sDBName += sTmp + "."; + + aColDesc[DataAccessDescriptorProperty::Command] >>= sTmp; + sDBName += sTmp + "."; + + aColDesc[DataAccessDescriptorProperty::ColumnName] >>= sTmp; + sDBName += sTmp; + if (bBrackets) + sDBName += "]"; + + m_rEdit.get_widget().set_text( sDBName ); + nRet = DND_ACTION_COPY; + } + } + return nRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx new file mode 100644 index 0000000000..3bf29979dd --- /dev/null +++ b/sw/source/uibase/utlui/content.cxx @@ -0,0 +1,6647 @@ +/* -*- 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 <comphelper/lok.hxx> +#include <comphelper/string.hxx> +#include <editeng/frmdiritem.hxx> +#include <svl/urlbmk.hxx> +#include <osl/thread.h> +#include <sal/log.hxx> +#include <tools/urlobj.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/event.hxx> +#include <sfx2/viewfrm.hxx> +#include <o3tl/enumrange.hxx> +#include <o3tl/sorted_vector.hxx> +#include <utility> +#include <vcl/commandevent.hxx> +#include <vcl/weldutils.hxx> +#include <sot/formats.hxx> +#include <o3tl/string_view.hxx> +#include <uiitems.hxx> +#include <fmtanchr.hxx> +#include <fmtinfmt.hxx> +#include <txtinet.hxx> +#include <fmtfld.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <drawdoc.hxx> +#include <content.hxx> +#include <frmatr.hxx> +#include <frmfmt.hxx> +#include <fldbas.hxx> +#include <IMark.hxx> +#include <section.hxx> +#include <tox.hxx> +#include <navipi.hxx> +#include <navicont.hxx> +#include <navicfg.hxx> +#include <edtwin.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentOutlineNodes.hxx> +#include <unotxvw.hxx> +#include <cmdid.h> +#include <helpids.h> +#include <strings.hrc> +#include <com/sun/star/text/XTextSectionsSupplier.hpp> +#include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp> +#include <com/sun/star/text/XTextTablesSupplier.hpp> +#include <com/sun/star/text/XDocumentIndexesSupplier.hpp> +#include <com/sun/star/text/XDocumentIndex.hpp> +#include <com/sun/star/text/XBookmarksSupplier.hpp> +#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp> +#include <com/sun/star/text/XTextFramesSupplier.hpp> +#include <com/sun/star/ui/XSidebarProvider.hpp> +#include <com/sun/star/ui/XDecks.hpp> +#include <com/sun/star/ui/XDeck.hpp> +#include <com/sun/star/ui/XPanels.hpp> +#include <com/sun/star/ui/XPanel.hpp> +#include <svx/svdpage.hxx> +#include <svx/svdview.hxx> +#include <SwRewriter.hxx> +#include <hints.hxx> +#include <numrule.hxx> +#include <swundo.hxx> +#include <ndtxt.hxx> +#include <PostItMgr.hxx> +#include <postithelper.hxx> + +#include <swabstdlg.hxx> +#include <bitmaps.hlst> + +#include <AnnotationWin.hxx> +#include <memory> + +#include <fmtcntnt.hxx> +#include <docstat.hxx> + +#include <viewopt.hxx> + +#include <IDocumentFieldsAccess.hxx> +#include <txtfld.hxx> +#include <fldmgr.hxx> + +#include <frameformats.hxx> + +#include <ftnidx.hxx> +#include <txtftn.hxx> +#include <fmtftn.hxx> + +#include <txtannotationfld.hxx> +#include <txtfrm.hxx> +#include <txtrfmrk.hxx> +#include <svx/sdr/overlay/overlayselection.hxx> +#include <svx/sdr/overlay/overlayobject.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <node2lay.hxx> + +#include <sectfrm.hxx> + +#include <docufld.hxx> + +#define CTYPE_CNT 0 +#define CTYPE_CTT 1 + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; + +namespace { + +/* + Symbolic name representations of numeric values used for the Outline Content Visibility popup + menu item ids. The numbers are chosen arbitrarily to not over overlap other menu item ids. + see: SwContentTree::ExecuteContextMenuAction, navigatorcontextmenu.ui + + 1512 toggle outline content visibility of the selected outline entry + 1513 make the outline content of the selected outline entry and children not visible + 1514 make the outline content of the selected entry and children visible +*/ +const sal_uInt32 TOGGLE_OUTLINE_CONTENT_VISIBILITY = 1512; +const sal_uInt32 HIDE_OUTLINE_CONTENT_VISIBILITY = 1513; +const sal_uInt32 SHOW_OUTLINE_CONTENT_VISIBILITY = 1514; + +constexpr char NAVI_BOOKMARK_DELIM = '\x01'; + +} + +class SwContentArr + : public o3tl::sorted_vector<std::unique_ptr<SwContent>, o3tl::less_uniqueptr_to<SwContent>, + o3tl::find_partialorder_ptrequals> +{ +}; + +namespace +{ + std::map<OUString, std::map<void*, bool>> lcl_DocOutLineExpandStateMap; + + bool lcl_IsContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView) + { + return weld::fromId<const SwTypeNumber*>(rTreeView.get_id(rEntry))->GetTypeId() == CTYPE_CNT; + } + + bool lcl_IsContentType(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView) + { + return weld::fromId<const SwTypeNumber*>(rTreeView.get_id(rEntry))->GetTypeId() == CTYPE_CTT; + } + + bool lcl_IsLowerOutlineContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView, sal_uInt8 nLevel) + { + return weld::fromId<const SwOutlineContent*>(rTreeView.get_id(rEntry))->GetOutlineLevel() < nLevel; + } + + bool lcl_FindShell(SwWrtShell const * pShell) + { + bool bFound = false; + SwView *pView = SwModule::GetFirstView(); + while (pView) + { + if(pShell == &pView->GetWrtShell()) + { + bFound = true; + break; + } + pView = SwModule::GetNextView(pView); + } + return bFound; + } + + bool lcl_IsUiVisibleBookmark(const ::sw::mark::IMark* pMark) + { + return IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::BOOKMARK; + } + + OUString lcl_GetFootnoteText(const SwTextFootnote& rTextFootnote) + { + SwNodeIndex aIdx(*rTextFootnote.GetStartNode(), 1); + SwContentNode* pCNd = aIdx.GetNode().GetTextNode(); + if(!pCNd) + pCNd = aIdx.GetNodes().GoNext(&aIdx); + return pCNd->IsTextNode() ? static_cast<SwTextNode*>(pCNd)->GetText() : OUString(); + } + + void getAnchorPos(SwPosition& rPos) + { + // get the top most anchor position of the position + if (SwFrameFormat* pFlyFormat = rPos.GetNode().GetFlyFormat()) + { + SwNode* pAnchorNode; + SwFrameFormat* pTmp = pFlyFormat; + while (pTmp && (pAnchorNode = pTmp->GetAnchor().GetAnchorNode()) && + (pTmp = pAnchorNode->GetFlyFormat())) + { + pFlyFormat = pTmp; + } + if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor()) + rPos = *pPos; + } + } + + bool lcl_IsLowerRegionContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView, sal_uInt8 nLevel) + { + return weld::fromId<const SwRegionContent*>(rTreeView.get_id(rEntry))->GetRegionLevel() < nLevel; + } +} + +// Content, contains names and reference at the content type. + +SwContent::SwContent(const SwContentType* pCnt, OUString aName, double nYPos) : + SwTypeNumber(CTYPE_CNT), + m_pParent(pCnt), + m_sContentName(std::move(aName)), + m_nYPosition(nYPos), + m_bInvisible(false) +{ +} + + +SwTypeNumber::~SwTypeNumber() +{ +} + +bool SwContent::IsProtect() const +{ + return false; +} + +bool SwTextFieldContent::IsProtect() const +{ + return m_pFormatField->IsProtect(); +} + +bool SwPostItContent::IsProtect() const +{ + return m_pField->IsProtect(); +} + +bool SwURLFieldContent::IsProtect() const +{ + return m_pINetAttr->IsProtect(); +} + +bool SwRegionContent::IsProtect() const +{ + return m_pSectionFormat->GetSection()->IsProtect(); +} + +SwGraphicContent::~SwGraphicContent() +{ +} + +SwTOXBaseContent::~SwTOXBaseContent() +{ +} + +const TranslateId STR_CONTENT_TYPE_ARY[] = +{ + STR_CONTENT_TYPE_OUTLINE, + STR_CONTENT_TYPE_TABLE, + STR_CONTENT_TYPE_FRAME, + STR_CONTENT_TYPE_GRAPHIC, + STR_CONTENT_TYPE_OLE, + STR_CONTENT_TYPE_BOOKMARK, + STR_CONTENT_TYPE_REGION, + STR_CONTENT_TYPE_URLFIELD, + STR_CONTENT_TYPE_REFERENCE, + STR_CONTENT_TYPE_INDEX, + STR_CONTENT_TYPE_POSTIT, + STR_CONTENT_TYPE_DRAWOBJECT, + STR_CONTENT_TYPE_TEXTFIELD, + STR_CONTENT_TYPE_FOOTNOTE, + STR_CONTENT_TYPE_ENDNOTE +}; + +const TranslateId STR_CONTENT_TYPE_SINGLE_ARY[] = +{ + STR_CONTENT_TYPE_SINGLE_OUTLINE, + STR_CONTENT_TYPE_SINGLE_TABLE, + STR_CONTENT_TYPE_SINGLE_FRAME, + STR_CONTENT_TYPE_SINGLE_GRAPHIC, + STR_CONTENT_TYPE_SINGLE_OLE, + STR_CONTENT_TYPE_SINGLE_BOOKMARK, + STR_CONTENT_TYPE_SINGLE_REGION, + STR_CONTENT_TYPE_SINGLE_URLFIELD, + STR_CONTENT_TYPE_SINGLE_REFERENCE, + STR_CONTENT_TYPE_SINGLE_INDEX, + STR_CONTENT_TYPE_SINGLE_POSTIT, + STR_CONTENT_TYPE_SINGLE_DRAWOBJECT, + STR_CONTENT_TYPE_SINGLE_TEXTFIELD, + STR_CONTENT_TYPE_SINGLE_FOOTNOTE, + STR_CONTENT_TYPE_SINGLE_ENDNOTE +}; + +namespace +{ + bool checkVisibilityChanged( + const SwContentArr& rSwContentArrA, + const SwContentArr& rSwContentArrB) + { + if(rSwContentArrA.size() != rSwContentArrB.size()) + { + return true; + } + + for(size_t a(0); a < rSwContentArrA.size(); a++) + { + if(rSwContentArrA[a]->IsInvisible() != rSwContentArrB[a]->IsInvisible()) + { + return true; + } + } + + return false; + } +// Gets "YPos" for content, i.e. a number used to sort content members in Navigator's list +sal_Int32 getYPos(const SwNode& rNode) +{ + SwNodeOffset nIndex = rNode.GetIndex(); + if (rNode.GetNodes().GetEndOfExtras().GetIndex() >= nIndex) + { + // Not a node of BodyText + // Are we in a fly? + if (const auto pFlyFormat = rNode.GetFlyFormat()) + { + // Get node index of anchor + if (SwNode* pAnchorNode = pFlyFormat->GetAnchor().GetAnchorNode()) + { + return getYPos(*pAnchorNode); + } + } + } + return sal_Int32(nIndex); +} +} // end of anonymous namespace + +SwContentType::SwContentType(SwWrtShell* pShell, ContentTypeId nType, sal_uInt8 nLevel) : + SwTypeNumber(CTYPE_CTT), + m_pWrtShell(pShell), + m_sContentTypeName(SwResId(STR_CONTENT_TYPE_ARY[static_cast<int>(nType)])), + m_sSingleContentTypeName(SwResId(STR_CONTENT_TYPE_SINGLE_ARY[static_cast<int>(nType)])), + m_nMemberCount(0), + m_nContentType(nType), + m_nOutlineLevel(nLevel), + m_bDataValid(false), + m_bEdit(false), + m_bDelete(true) +{ + switch(m_nContentType) + { + case ContentTypeId::OUTLINE: + m_sTypeToken = "outline"; + break; + case ContentTypeId::TABLE: + m_sTypeToken = "table"; + m_bEdit = true; + break; + case ContentTypeId::FRAME: + m_sTypeToken = "frame"; + m_bEdit = true; + break; + case ContentTypeId::GRAPHIC: + m_sTypeToken = "graphic"; + m_bEdit = true; + break; + case ContentTypeId::OLE: + m_sTypeToken = "ole"; + m_bEdit = true; + break; + case ContentTypeId::TEXTFIELD: + m_bEdit = true; + m_bDelete = true; + break; + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + m_bEdit = true; + m_bDelete = false; + break; + case ContentTypeId::BOOKMARK: + { + const bool bProtectedBM = m_pWrtShell->getIDocumentSettingAccess().get( + DocumentSettingId::PROTECT_BOOKMARKS); + m_bEdit = true; + m_bDelete = !bProtectedBM; + } + break; + case ContentTypeId::REGION: + m_sTypeToken = "region"; + m_bEdit = true; + m_bDelete = true; + break; + case ContentTypeId::INDEX: + m_bEdit = true; + m_bDelete = true; + break; + case ContentTypeId::REFERENCE: + m_bEdit = false; + m_bDelete = true; + break; + case ContentTypeId::URLFIELD: + m_bEdit = true; + m_bDelete = true; + break; + case ContentTypeId::POSTIT: + m_bEdit = true; + break; + case ContentTypeId::DRAWOBJECT: + m_sTypeToken = "drawingobject"; + m_bEdit = true; + break; + default: break; + } + + const int nShift = static_cast<int>(m_nContentType); + assert(nShift > -1); + const sal_Int32 nMask = 1 << nShift; + const sal_Int32 nBlock = SW_MOD()->GetNavigationConfig()->GetSortAlphabeticallyBlock(); + m_bAlphabeticSort = nBlock & nMask; + + FillMemberList(); +} + +SwContentType::~SwContentType() +{ +} + +const SwContent* SwContentType::GetMember(size_t nIndex) +{ + if(!m_bDataValid || !m_pMember) + { + FillMemberList(); + } + if(nIndex < m_pMember->size()) + return (*m_pMember)[nIndex].get(); + + return nullptr; +} + +void SwContentType::Invalidate() +{ + m_bDataValid = false; +} + +void SwContentType::FillMemberList(bool* pbContentChanged) +{ + std::unique_ptr<SwContentArr> pOldMember; + size_t nOldMemberCount = 0; + if(m_pMember && pbContentChanged) + { + pOldMember = std::move(m_pMember); + nOldMemberCount = pOldMember->size(); + m_pMember.reset( new SwContentArr ); + *pbContentChanged = false; + } + else if(!m_pMember) + m_pMember.reset( new SwContentArr ); + else + m_pMember->clear(); + switch(m_nContentType) + { + case ContentTypeId::OUTLINE : + { + const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex(); + // provide for up to 99999 outline nodes in frames to be sorted in document layout order + double nOutlinesInFramesIndexAdjustment = 0.00001; + const SwOutlineNodes& rOutlineNodes(m_pWrtShell->GetNodes().GetOutLineNds()); + const size_t nOutlineCount = rOutlineNodes.size(); + + for (size_t i = 0; i < nOutlineCount; ++i) + { + SwTextNode* pNode = rOutlineNodes[i]->GetTextNode(); + const sal_uInt8 nLevel = pNode->GetAttrOutlineLevel() - 1; + if (nLevel >= m_nOutlineLevel) + continue; + double nYPos = m_bAlphabeticSort ? 0 : static_cast<double>(getYPos(*pNode)); + if (nEndOfExtrasIndex >= pNode->GetIndex() && pNode->GetFlyFormat()) + { + nYPos += nOutlinesInFramesIndexAdjustment; + nOutlinesInFramesIndexAdjustment += 0.00001; + } + OUString aEntry(comphelper::string::stripStart( + m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText( + i, m_pWrtShell->GetLayout(), true, false, false), ' ')); + aEntry = SwNavigationPI::CleanEntry(aEntry); + auto pCnt(std::make_unique<SwOutlineContent>(this, aEntry, i, nLevel, + m_pWrtShell->IsOutlineMovable(i), nYPos)); + if (!pNode->getLayoutFrame(m_pWrtShell->GetLayout())) + pCnt->SetInvisible(); + m_pMember->insert(std::move(pCnt)); + } + + // need to check level and equal entry number after creation due to possible outline + // nodes in frames, headers, footers + if (pOldMember) + { + assert(pbContentChanged && "pbContentChanged is always set if pOldMember is"); + if (pOldMember->size() != m_pMember->size()) + { + *pbContentChanged = true; + break; + } + for (size_t i = 0; i < pOldMember->size(); i++) + { + if (static_cast<SwOutlineContent*>((*pOldMember)[i].get())->GetOutlineLevel() != + static_cast<SwOutlineContent*>((*m_pMember)[i].get())->GetOutlineLevel()) + { + *pbContentChanged = true; + break; + } + } + } + } + break; + case ContentTypeId::TABLE : + { + const size_t nCount = m_pWrtShell->GetTableFrameFormatCount(true); + const sw::TableFrameFormats* pFrameFormats = m_pWrtShell->GetDoc()->GetTableFrameFormats(); + for(size_t n = 0, i = 0; i < nCount + n; ++i) + { + const SwTableFormat& rTableFormat = *(*pFrameFormats)[i]; + if(!rTableFormat.IsUsed()) // skip deleted tables + { + n++; + continue; + } + tools::Long nYPos = 0; + if (!m_bAlphabeticSort) + { + if (SwTable* pTable = SwTable::FindTable(&rTableFormat)) + nYPos = getYPos(*pTable->GetTableNode()); + } + auto pCnt = std::make_unique<SwContent>(this, rTableFormat.GetName(), nYPos); + if(!rTableFormat.IsVisible()) + pCnt->SetInvisible(); + m_pMember->insert(std::move(pCnt)); + } + + if (pOldMember) + { + // need to check visibility (and equal entry number) after + // creation due to a sorted list being used here (before, + // entries with same index were compared already at creation + // time what worked before a sorted list was used) + *pbContentChanged = checkVisibilityChanged( + *pOldMember, + *m_pMember); + } + } + break; + case ContentTypeId::OLE : + case ContentTypeId::FRAME : + case ContentTypeId::GRAPHIC : + { + FlyCntType eType = FLYCNTTYPE_FRM; + if(m_nContentType == ContentTypeId::OLE) + eType = FLYCNTTYPE_OLE; + else if(m_nContentType == ContentTypeId::GRAPHIC) + eType = FLYCNTTYPE_GRF; + Point aNullPt; + size_t nCount = m_pWrtShell->GetFlyCount(eType, /*bIgnoreTextBoxes=*/true); + std::vector<SwFrameFormat const*> formats(m_pWrtShell->GetFlyFrameFormats(eType, /*bIgnoreTextBoxes=*/true)); + SAL_WARN_IF(nCount != formats.size(), "sw.ui", "Count differs"); + nCount = formats.size(); + for (size_t i = 0; i < nCount; ++i) + { + SwFrameFormat const*const pFrameFormat = formats[i]; + const OUString sFrameName = pFrameFormat->GetName(); + + SwContent* pCnt; + tools::Long nYPos = + m_bAlphabeticSort ? 0 : pFrameFormat->FindLayoutRect(false, &aNullPt).Top(); + if(ContentTypeId::GRAPHIC == m_nContentType) + { + OUString sLink; + m_pWrtShell->GetGrfNms( &sLink, nullptr, static_cast<const SwFlyFrameFormat*>( pFrameFormat)); + pCnt = new SwGraphicContent(this, sFrameName, INetURLObject::decode(sLink, + INetURLObject::DecodeMechanism::Unambiguous), nYPos); + } + else + { + pCnt = new SwContent(this, sFrameName, nYPos); + } + if(!pFrameFormat->IsVisible()) + pCnt->SetInvisible(); + m_pMember->insert(std::unique_ptr<SwContent>(pCnt)); + } + + if (pOldMember) + { + // need to check visibility (and equal entry number) after + // creation due to a sorted list being used here (before, + // entries with same index were compared already at creation + // time what worked before a sorted list was used) + assert(pbContentChanged && "pbContentChanged is always set if pOldMember is"); + *pbContentChanged = checkVisibilityChanged( + *pOldMember, + *m_pMember); + } + } + break; + case ContentTypeId::BOOKMARK: + { + tools::Long nYPos = 0; + IDocumentMarkAccess* const pMarkAccess = m_pWrtShell->getIDocumentMarkAccess(); + for(IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin(); + ppBookmark != pMarkAccess->getBookmarksEnd(); + ++ppBookmark) + { + if(lcl_IsUiVisibleBookmark(*ppBookmark)) + { + const OUString& rBkmName = (*ppBookmark)->GetName(); + //nYPos from 0 -> text::Bookmarks will be sorted alphabetically + auto pCnt(std::make_unique<SwContent>(this, rBkmName, + m_bAlphabeticSort ? 0 : nYPos++)); + m_pMember->insert(std::move(pCnt)); + } + } + } + break; + case ContentTypeId::TEXTFIELD: + { + std::vector<SwTextField*> aArr; + const SwFieldTypes& rFieldTypes = + *m_pWrtShell->GetDoc()->getIDocumentFieldsAccess().GetFieldTypes(); + const size_t nSize = rFieldTypes.size(); + for (size_t i = 0; i < nSize; ++i) + { + const SwFieldType* pFieldType = rFieldTypes[i].get(); + if (pFieldType->Which() == SwFieldIds::Postit) + continue; + std::vector<SwFormatField*> vFields; + pFieldType->GatherFields(vFields); + for (SwFormatField* pFormatField: vFields) + { + if (SwTextField* pTextField = pFormatField->GetTextField()) + { + // fields in header footer don't behave well, skip them + if (m_pWrtShell->GetDoc()->IsInHeaderFooter(pTextField->GetTextNode())) + continue; + aArr.emplace_back(pTextField); + } + } + } + if (!m_bAlphabeticSort) + { + const SwNodeOffset nEndOfExtrasIndex = + m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex(); + // use stable sort array to list fields in document model order + std::stable_sort(aArr.begin(), aArr.end(), + [&nEndOfExtrasIndex, this]( + const SwTextField* a, const SwTextField* b){ + SwPosition aPos(a->GetTextNode(), a->GetStart()); + SwPosition bPos(b->GetTextNode(), b->GetStart()); + // use anchor position for entries that are located in flys + if (nEndOfExtrasIndex >= aPos.GetNodeIndex()) + getAnchorPos(aPos); + if (nEndOfExtrasIndex >= bPos.GetNodeIndex()) + getAnchorPos(bPos); + if (aPos == bPos) + { + // probably in same or nested fly frame + // sort using layout position + SwRect aCharRect, bCharRect; + std::shared_ptr<SwPaM> pPamForTextField; + if (SwTextFrame* pFrame = static_cast<SwTextFrame*>( + a->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout()))) + { + SwTextField::GetPamForTextField(*a, pPamForTextField); + if (pPamForTextField) + pFrame->GetCharRect(aCharRect, *pPamForTextField->GetPoint()); + } + if (SwTextFrame* pFrame = static_cast<SwTextFrame*>( + b->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout()))) + { + SwTextField::GetPamForTextField(*b, pPamForTextField); + if (pPamForTextField) + pFrame->GetCharRect(bCharRect, *pPamForTextField->GetPoint()); + } + return aCharRect.Top() < bCharRect.Top(); + } + return aPos < bPos;}); + } + std::vector<OUString> aDocumentStatisticsSubTypesList; + tools::Long nYPos = 0; + for (SwTextField* pTextField : aArr) + { + const SwField* pField = pTextField->GetFormatField().GetField(); + OUString sExpandField = pField->ExpandField(true, m_pWrtShell->GetLayout()); + if (!sExpandField.isEmpty()) + sExpandField = u" - " + sExpandField; + OUString sText; + if (pField->GetTypeId() == SwFieldTypesEnum::DocumentStatistics) + { + if (aDocumentStatisticsSubTypesList.empty()) + SwFieldMgr(m_pWrtShell).GetSubTypes(SwFieldTypesEnum::DocumentStatistics, + aDocumentStatisticsSubTypesList); + OUString sSubType; + if (pField->GetSubType() < aDocumentStatisticsSubTypesList.size()) + sSubType = u" - " + aDocumentStatisticsSubTypesList[pField->GetSubType()]; + sText = pField->GetDescription() + u" - " + pField->GetFieldName() + sSubType + + sExpandField; + } + else if (pField->GetTypeId() == SwFieldTypesEnum::GetRef) + { + assert(dynamic_cast<const SwGetRefField*>(pField)); + const SwGetRefField* pRefField(static_cast<const SwGetRefField*>(pField)); + if (pRefField->IsRefToHeadingCrossRefBookmark() || + pRefField->IsRefToNumItemCrossRefBookmark()) + { + OUString sExpandedTextOfReferencedTextNode = + pRefField->GetExpandedTextOfReferencedTextNode( + *m_pWrtShell->GetLayout(), nullptr, nullptr); + if (sExpandedTextOfReferencedTextNode.getLength() > 80) + { + sExpandedTextOfReferencedTextNode = OUString::Concat( + sExpandedTextOfReferencedTextNode.subView(0, 80)) + u"..."; + } + sText = pField->GetDescription() + u" - " + + sExpandedTextOfReferencedTextNode + sExpandField; + } + else + { + OUString sFieldSubTypeOrName; + auto nSubType = pField->GetSubType(); + if (nSubType == REF_FOOTNOTE) + sFieldSubTypeOrName = SwResId(STR_FLDREF_FOOTNOTE); + else if (nSubType == REF_ENDNOTE) + sFieldSubTypeOrName = SwResId(STR_FLDREF_ENDNOTE); + else + sFieldSubTypeOrName = pField->GetFieldName(); + sText = pField->GetDescription() + u" - " + sFieldSubTypeOrName + + sExpandField; + } + } + else + sText = pField->GetDescription() + u" - " + pField->GetFieldName() + + sExpandField; + auto pCnt(std::make_unique<SwTextFieldContent>(this, sText, + &pTextField->GetFormatField(), + m_bAlphabeticSort ? 0 : nYPos++)); + if (!pTextField->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout())) + pCnt->SetInvisible(); + m_pMember->insert(std::move(pCnt)); + } + } + break; + // We will separate footnotes and endnotes here. + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + { + const SwFootnoteIdxs& rFootnoteIdxs = m_pWrtShell->GetDoc()->GetFootnoteIdxs(); + if (rFootnoteIdxs.size() == 0) + break; + // insert footnotes and endnotes + tools::Long nPos = 0; + for (const SwTextFootnote* pTextFootnote : rFootnoteIdxs) + { + if ((!pTextFootnote->GetFootnote().IsEndNote() + && m_nContentType == ContentTypeId::FOOTNOTE) + || (pTextFootnote->GetFootnote().IsEndNote() + && m_nContentType == ContentTypeId::ENDNOTE)) + { + const SwFormatFootnote& rFormatFootnote = pTextFootnote->GetFootnote(); + const OUString& sText + = rFormatFootnote.GetViewNumStr(*m_pWrtShell->GetDoc(), + m_pWrtShell->GetLayout(), true) + + " " + lcl_GetFootnoteText(*pTextFootnote); + auto pCnt(std::make_unique<SwTextFootnoteContent>( + this, sText, pTextFootnote, ++nPos)); + if (!pTextFootnote->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout())) + pCnt->SetInvisible(); + m_pMember->insert(std::move(pCnt)); + } + } + } + break; + case ContentTypeId::REGION : + { + size_t nCount = m_pWrtShell->GetSectionFormatCount(); + for (size_t i = 0; i < nCount; ++i) + { + const SwSectionFormat* pFormat = &m_pWrtShell->GetSectionFormat(i); + if (!pFormat->IsInNodesArr()) + continue; + const SwSection* pSection = pFormat->GetSection(); + if (SectionType eTmpType = pSection->GetType(); + eTmpType == SectionType::ToxContent || eTmpType == SectionType::ToxHeader) + continue; + const SwNodeIndex* pNodeIndex = pFormat->GetContent().GetContentIdx(); + if (pNodeIndex) + { + const OUString& sSectionName = pSection->GetSectionName(); + + sal_uInt8 nLevel = 0; + SwSectionFormat* pParentFormat = pFormat->GetParent(); + while(pParentFormat) + { + nLevel++; + pParentFormat = pParentFormat->GetParent(); + } + + auto pCnt(std::make_unique<SwRegionContent>(this, sSectionName, nLevel, + m_bAlphabeticSort ? 0 : getYPos(pNodeIndex->GetNode()), + pFormat)); + + if (!pFormat->IsVisible() || pSection->IsHidden()) + pCnt->SetInvisible(); + m_pMember->insert(std::move(pCnt)); + } + + if (pOldMember) + { + // need to check visibility (and equal entry number) after + // creation due to a sorted list being used here (before, + // entries with same index were compared already at creation + // time what worked before a sorted list was used) + assert(pbContentChanged && "pbContentChanged is always set if pOldMember is"); + *pbContentChanged = checkVisibilityChanged( + *pOldMember, + *m_pMember); + } + } + } + break; + case ContentTypeId::REFERENCE: + { + std::vector<OUString> aRefMarks; + m_pWrtShell->GetRefMarks( &aRefMarks ); + + tools::Long nYPos = 0; + for (const auto& rRefMark : aRefMarks) + { + m_pMember->insert(std::make_unique<SwContent>(this, rRefMark, + m_bAlphabeticSort ? 0 : nYPos++)); + } + } + break; + case ContentTypeId::URLFIELD: + { + SwGetINetAttrs aArr; + m_pWrtShell->GetINetAttrs(aArr, false); + + if (m_bAlphabeticSort) + { + for (auto& r : aArr) + { + auto pCnt(std::make_unique<SwURLFieldContent>(this, r.sText, INetURLObject::decode( + r.rINetAttr.GetINetFormat().GetValue(), + INetURLObject::DecodeMechanism::Unambiguous), + &r.rINetAttr, 0)); + m_pMember->insert(std::move(pCnt)); + } + break; + } + + // use stable sort array to list hyperlinks in document order + const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex(); + bool bHasEntryInFly = false; + std::vector<SwGetINetAttr*> aStableSortINetAttrsArray; + + for (SwGetINetAttr& r : aArr) + { + aStableSortINetAttrsArray.emplace_back(&r); + if (!bHasEntryInFly) + { + if (nEndOfExtrasIndex >= r.rINetAttr.GetTextNode().GetIndex()) + { + // Not a node of BodyText + // Are we in a fly? + if (r.rINetAttr.GetTextNode().GetFlyFormat()) + bHasEntryInFly = true; + } + } + } + + std::stable_sort(aStableSortINetAttrsArray.begin(), aStableSortINetAttrsArray.end(), + [](const SwGetINetAttr* a, const SwGetINetAttr* b){ + SwPosition aSwPos(a->rINetAttr.GetTextNode(), + a->rINetAttr.GetStart()); + SwPosition bSwPos(b->rINetAttr.GetTextNode(), + b->rINetAttr.GetStart()); + return aSwPos < bSwPos;}); + + // When there are hyperlinks in text frames do an additional sort using the text frame + // anchor position to place entries in the order of document layout appearance. + if (bHasEntryInFly) + { + std::stable_sort(aStableSortINetAttrsArray.begin(), aStableSortINetAttrsArray.end(), + [nEndOfExtrasIndex](const SwGetINetAttr* a, const SwGetINetAttr* b){ + const SwTextNode& aTextNode = a->rINetAttr.GetTextNode(); + const SwTextNode& bTextNode = b->rINetAttr.GetTextNode(); + SwPosition aPos(aTextNode, a->rINetAttr.GetStart()); + SwPosition bPos(bTextNode, b->rINetAttr.GetStart()); + // use anchor position for entries that are located in flys + if (nEndOfExtrasIndex >= aTextNode.GetIndex()) + if (auto pFlyFormat = aTextNode.GetFlyFormat()) + if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor()) + aPos = *pPos; + if (nEndOfExtrasIndex >= bTextNode.GetIndex()) + if (auto pFlyFormat = bTextNode.GetFlyFormat()) + if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor()) + bPos = *pPos; + return aPos < bPos;}); + } + + SwGetINetAttrs::size_type n = 0; + for (auto p : aStableSortINetAttrsArray) + { + auto pCnt = std::make_unique<SwURLFieldContent>(this, p->sText, + INetURLObject::decode(p->rINetAttr.GetINetFormat().GetValue(), + INetURLObject::DecodeMechanism::Unambiguous), + &p->rINetAttr, ++n); + m_pMember->insert(std::move(pCnt)); + } + } + break; + case ContentTypeId::INDEX: + { + const sal_uInt16 nCount = m_pWrtShell->GetTOXCount(); + + for ( sal_uInt16 nTox = 0; nTox < nCount; nTox++ ) + { + const SwTOXBase* pBase = m_pWrtShell->GetTOX( nTox ); + OUString sTOXNm( pBase->GetTOXName() ); + + SwContent* pCnt = new SwTOXBaseContent( + this, sTOXNm, m_bAlphabeticSort ? 0 : nTox, *pBase); + + if(pBase && !pBase->IsVisible()) + pCnt->SetInvisible(); + + m_pMember->insert( std::unique_ptr<SwContent>(pCnt) ); + const size_t nPos = m_pMember->size() - 1; + if (pOldMember) + { + assert(pbContentChanged && "pbContentChanged is always set if pOldMember is"); + if (!*pbContentChanged && nOldMemberCount > nPos && + (*pOldMember)[nPos]->IsInvisible() != pCnt->IsInvisible()) + *pbContentChanged = true; + } + } + } + break; + case ContentTypeId::POSTIT: + { + SwPostItMgr* aMgr = m_pWrtShell->GetView().GetPostItMgr(); + if (aMgr) + { + tools::Long nYPos = 0; + for(SwPostItMgr::const_iterator i = aMgr->begin(); i != aMgr->end(); ++i) + { + if (const SwFormatField* pFormatField = dynamic_cast<const SwFormatField *>((*i)->GetBroadcaster())) // SwPostit + { + if (pFormatField->GetTextField() && pFormatField->IsFieldInDoc()) + { + OUString sEntry = pFormatField->GetField()->GetPar2(); + sEntry = RemoveNewline(sEntry); + std::unique_ptr<SwPostItContent> pCnt(new SwPostItContent( + this, + sEntry, + pFormatField, + nYPos)); + if (!pFormatField->GetTextField()->GetTextNode().getLayoutFrame( + m_pWrtShell->GetLayout())) + pCnt->SetInvisible(); + if (pOldMember) + { + assert(pbContentChanged && "pbContentChanged is always set if pOldMember is"); + if (!*pbContentChanged && + nOldMemberCount > o3tl::make_unsigned(nYPos) && + (*pOldMember)[nYPos]->IsInvisible() != pCnt->IsInvisible()) + *pbContentChanged = true; + } + m_pMember->insert(std::move(pCnt)); + nYPos++; + } + } + } + } + } + break; + case ContentTypeId::DRAWOBJECT: + { + IDocumentDrawModelAccess& rIDDMA = m_pWrtShell->getIDocumentDrawModelAccess(); + SwDrawModel* pModel = rIDDMA.GetDrawModel(); + if(pModel) + { + SdrPage* pPage = pModel->GetPage(0); + for (const rtl::Reference<SdrObject>& pTemp : *pPage) + { + // #i51726# - all drawing objects can be named now + if (!pTemp->GetName().isEmpty()) + { + tools::Long nYPos = LONG_MIN; + const bool bIsVisible = rIDDMA.IsVisibleLayerId(pTemp->GetLayer()); + if (bIsVisible) + nYPos = m_bAlphabeticSort ? 0 : pTemp->GetLogicRect().Top(); + auto pCnt(std::make_unique<SwContent>(this, pTemp->GetName(), nYPos)); + if (!bIsVisible) + pCnt->SetInvisible(); + m_pMember->insert(std::move(pCnt)); + } + } + + if (pOldMember) + { + // need to check visibility (and equal entry number) after + // creation due to a sorted list being used here (before, + // entries with same index were compared already at creation + // time what worked before a sorted list was used) + assert(pbContentChanged && "pbContentChanged is always set if pOldMember is"); + *pbContentChanged = checkVisibilityChanged( + *pOldMember, + *m_pMember); + } + } + } + break; + default: break; + } + m_nMemberCount = m_pMember->size(); + if (pOldMember) + { + assert(pbContentChanged && "pbContentChanged is always set if pOldMember is"); + if (!*pbContentChanged && pOldMember->size() != m_nMemberCount) + *pbContentChanged = true; + } + + m_bDataValid = true; +} + +namespace { + +enum STR_CONTEXT_IDX +{ + IDX_STR_OUTLINE_LEVEL = 0, + IDX_STR_DRAGMODE = 1, + IDX_STR_HYPERLINK = 2, + IDX_STR_LINK_REGION = 3, + IDX_STR_COPY_REGION = 4, + IDX_STR_DISPLAY = 5, + IDX_STR_ACTIVE_VIEW = 6, + IDX_STR_HIDDEN = 7, + IDX_STR_ACTIVE = 8, + IDX_STR_INACTIVE = 9, + IDX_STR_EDIT_ENTRY = 10, + IDX_STR_DELETE_ENTRY = 11, + IDX_STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY = 12, + IDX_STR_OUTLINE_TRACKING = 13, + IDX_STR_OUTLINE_TRACKING_DEFAULT = 14, + IDX_STR_OUTLINE_TRACKING_FOCUS = 15, + IDX_STR_OUTLINE_TRACKING_OFF = 16 +}; + +} + +const TranslateId STR_CONTEXT_ARY[] = +{ + STR_OUTLINE_LEVEL, + STR_DRAGMODE, + STR_HYPERLINK, + STR_LINK_REGION, + STR_COPY_REGION, + STR_DISPLAY, + STR_ACTIVE_VIEW, + STR_HIDDEN, + STR_ACTIVE, + STR_INACTIVE, + STR_EDIT_ENTRY, + STR_DELETE_ENTRY, + STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY, + STR_OUTLINE_TRACKING, + STR_OUTLINE_TRACKING_DEFAULT, + STR_OUTLINE_TRACKING_FOCUS, + STR_OUTLINE_TRACKING_OFF +}; + +SwContentTree::SwContentTree(std::unique_ptr<weld::TreeView> xTreeView, SwNavigationPI* pDialog) + : m_xTreeView(std::move(xTreeView)) + , m_aDropTargetHelper(*this) + , m_pDialog(pDialog) + , m_sSpace(OUString(" ")) + , m_aUpdTimer("SwContentTree m_aUpdTimer") + , m_aOverlayObjectDelayTimer("SwContentTree m_aOverlayObjectDelayTimer") + , m_sInvisible(SwResId(STR_INVISIBLE)) + , m_pHiddenShell(nullptr) + , m_pActiveShell(nullptr) + , m_pConfig(SW_MOD()->GetNavigationConfig()) + , m_nActiveBlock(0) + , m_nHiddenBlock(0) + , m_nEntryCount(0) + , m_nRootType(ContentTypeId::UNKNOWN) + , m_nLastSelType(ContentTypeId::UNKNOWN) + , m_nOutlineLevel(MAXLEVEL) + , m_eState(State::ACTIVE) + , m_bIsRoot(false) + , m_bIsIdleClear(false) + , m_bIsLastReadOnly(false) + , m_bIsOutlineMoveable(true) + , m_bViewHasChanged(false) +{ + m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 30, + m_xTreeView->get_text_height() * 14); + + m_xTreeView->set_help_id(HID_NAVIGATOR_TREELIST); + + m_xTreeView->connect_expanding(LINK(this, SwContentTree, ExpandHdl)); + m_xTreeView->connect_collapsing(LINK(this, SwContentTree, CollapseHdl)); + m_xTreeView->connect_row_activated(LINK(this, SwContentTree, ContentDoubleClickHdl)); + m_xTreeView->connect_changed(LINK(this, SwContentTree, SelectHdl)); + m_xTreeView->connect_focus_in(LINK(this, SwContentTree, FocusInHdl)); + m_xTreeView->connect_key_press(LINK(this, SwContentTree, KeyInputHdl)); + m_xTreeView->connect_popup_menu(LINK(this, SwContentTree, CommandHdl)); + m_xTreeView->connect_query_tooltip(LINK(this, SwContentTree, QueryTooltipHdl)); + m_xTreeView->connect_drag_begin(LINK(this, SwContentTree, DragBeginHdl)); + m_xTreeView->connect_mouse_move(LINK(this, SwContentTree, MouseMoveHdl)); + m_xTreeView->connect_mouse_press(LINK(this, SwContentTree, MousePressHdl)); + + for (ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + if (i != ContentTypeId::OUTLINE) + mTrackContentType[i] = true; + m_aActiveContentArr[i] = nullptr; + m_aHiddenContentArr[i] = nullptr; + } + for (int i = 0; i < CONTEXT_COUNT; ++i) + { + m_aContextStrings[i] = SwResId(STR_CONTEXT_ARY[i]); + } + m_nActiveBlock = m_pConfig->GetActiveBlock(); + + // Restore outline headings expand state (same session persistence only) + if (SwView* pView = GetActiveView(); pView && pView->GetDocShell()) + { + OUString sDocTitle = pView->GetDocShell()->GetTitle(); + auto it = lcl_DocOutLineExpandStateMap.find(sDocTitle); + if (it != lcl_DocOutLineExpandStateMap.end()) + mOutLineNodeMap = it->second; + if (comphelper::LibreOfficeKit::isActive()) { + if (pView->m_nNaviExpandedStatus < 0) + m_nActiveBlock = 1; + else + m_nActiveBlock = pView->m_nNaviExpandedStatus; + } + } + + m_aUpdTimer.SetInvokeHandler(LINK(this, SwContentTree, TimerUpdate)); + m_aUpdTimer.SetTimeout(1000); + m_aOverlayObjectDelayTimer.SetInvokeHandler(LINK(this, SwContentTree, OverlayObjectDelayTimerHdl)); + m_aOverlayObjectDelayTimer.SetTimeout(500); +} + +SwContentTree::~SwContentTree() +{ + if (SwView* pView = GetActiveView(); pView && pView->GetDocShell()) + { + OUString sDocTitle = pView->GetDocShell()->GetTitle(); + lcl_DocOutLineExpandStateMap[sDocTitle] = mOutLineNodeMap; + if (comphelper::LibreOfficeKit::isActive()) + pView->m_nNaviExpandedStatus = m_nActiveBlock; + } + clear(); // If applicable erase content types previously. + m_aUpdTimer.Stop(); + SetActiveShell(nullptr); +} + +IMPL_LINK(SwContentTree, MousePressHdl, const MouseEvent&, rMEvt, bool) +{ + m_bSelectTo = rMEvt.IsShift() && (m_pConfig->IsNavigateOnSelect() || rMEvt.GetClicks() == 2); + return false; +} + +IMPL_LINK(SwContentTree, MouseMoveHdl, const MouseEvent&, rMEvt, bool) +{ + if (m_eState == State::HIDDEN) + return false; + if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + m_xTreeView->get_dest_row_at_pos(rMEvt.GetPosPixel(), xEntry.get(), false, false) && + !rMEvt.IsLeaveWindow()) + { + if (!m_xOverlayCompareEntry) + m_xOverlayCompareEntry.reset(m_xTreeView->make_iterator().release()); + else if (m_xTreeView->iter_compare(*xEntry, *m_xOverlayCompareEntry) == 0) + return false; // The entry under the mouse has not changed. + m_xTreeView->copy_iterator(*xEntry, *m_xOverlayCompareEntry); + BringEntryToAttention(*xEntry); + } + else + { + if (m_xOverlayCompareEntry) + m_xOverlayCompareEntry.reset(); + m_aOverlayObjectDelayTimer.Stop(); + if (m_xOverlayObject && m_xOverlayObject->getOverlayManager()) + { + m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject); + m_xOverlayObject.reset(); + } + } + return false; +} + +// Drag&Drop methods +IMPL_LINK(SwContentTree, DragBeginHdl, bool&, rUnsetDragIcon, bool) +{ + rUnsetDragIcon = true; + + bool bDisallow = true; + + // don't allow if tree root is selected + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_selected(xEntry.get()); + if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView)) + { + return true; // disallow + } + + rtl::Reference<TransferDataContainer> xContainer = new TransferDataContainer; + sal_Int8 nDragMode = DND_ACTION_COPYMOVE | DND_ACTION_LINK; + + if (FillTransferData(*xContainer, nDragMode)) + bDisallow = false; + + if (m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE) + { + // Only move drag entry and continuous selected siblings: + m_aDndOutlinesSelected.clear(); + + std::unique_ptr<weld::TreeIter> xScratch(m_xTreeView->make_iterator()); + + // Find first selected of continuous siblings + while (true) + { + m_xTreeView->copy_iterator(*xEntry, *xScratch); + if (!m_xTreeView->iter_previous_sibling(*xScratch)) + break; + if (!m_xTreeView->is_selected(*xScratch)) + break; + m_xTreeView->copy_iterator(*xScratch, *xEntry); + } + // Record continuous selected siblings + do + { + m_aDndOutlinesSelected.push_back(m_xTreeView->make_iterator(xEntry.get())); + } + while (m_xTreeView->iter_next_sibling(*xEntry) && m_xTreeView->is_selected(*xEntry)); + bDisallow = false; + } + + if (!bDisallow) + m_xTreeView->enable_drag_source(xContainer, nDragMode); + return bDisallow; +} + +SwContentTreeDropTarget::SwContentTreeDropTarget(SwContentTree& rTreeView) + : DropTargetHelper(rTreeView.get_widget().get_drop_target()) + , m_rTreeView(rTreeView) +{ +} + +sal_Int8 SwContentTreeDropTarget::AcceptDrop(const AcceptDropEvent& rEvt) +{ + sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt); + + if (nAccept != DND_ACTION_NONE) + { + // to enable the autoscroll when we're close to the edges + weld::TreeView& rWidget = m_rTreeView.get_widget(); + rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true); + } + + return nAccept; +} + +bool SwContentTree::IsInDrag() const +{ + return m_xTreeView->get_drag_source() == m_xTreeView.get(); +} + +bool SwContentTree::HasHeadings() const +{ + const std::unique_ptr<SwContentType>& rpContentT = m_aActiveContentArr[ContentTypeId::OUTLINE]; + if (rpContentT && rpContentT->GetMemberCount() > 0) + return true; + return false; +} + +// QueryDrop will be executed in the navigator +sal_Int8 SwContentTree::AcceptDrop(const AcceptDropEvent& rEvt) +{ + sal_Int8 nRet = DND_ACTION_NONE; + if( m_bIsRoot ) + { + if( m_bIsOutlineMoveable ) + nRet = rEvt.mnAction; + } + else if (!IsInDrag()) + nRet = GetParentWindow()->AcceptDrop(); + return nRet; +} + +// Drop will be executed in the navigator +static void* lcl_GetOutlineKey(SwContentTree& rTree, SwOutlineContent const * pContent) +{ + void* key = nullptr; + if (pContent) + { + SwWrtShell* pShell = rTree.GetWrtShell(); + auto const nPos = pContent->GetOutlinePos(); + + key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos )); + } + return key; +} + +sal_Int8 SwContentTreeDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt) +{ + return m_rTreeView.ExecuteDrop(rEvt); +} + +sal_Int8 SwContentTree::ExecuteDrop(const ExecuteDropEvent& rEvt) +{ + std::unique_ptr<weld::TreeIter> xDropEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_dest_row_at_pos(rEvt.maPosPixel, xDropEntry.get(), true)) + xDropEntry.reset(); + + if (m_nRootType == ContentTypeId::OUTLINE) + { + if (xDropEntry && lcl_IsContent(*xDropEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry)))); + SwOutlineContent* pOutlineContent = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry)); + assert(pOutlineContent); + + void* key = lcl_GetOutlineKey(*this, pOutlineContent); + assert(key); + if (!mOutLineNodeMap[key]) + { + while (m_xTreeView->iter_has_child(*xDropEntry)) + { + std::unique_ptr<weld::TreeIter> xChildEntry(m_xTreeView->make_iterator(xDropEntry.get())); + bool bChildEntry = m_xTreeView->iter_children(*xChildEntry); + while (bChildEntry) + { + m_xTreeView->copy_iterator(*xChildEntry, *xDropEntry); + bChildEntry = m_xTreeView->iter_next_sibling(*xChildEntry); + } + } + } + } + + SwOutlineNodes::size_type nTargetPos = 0; + if (!xDropEntry) + { + // dropped in blank space -> move to bottom + nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1; + } + else if (!lcl_IsContent(*xDropEntry, *m_xTreeView)) + { + // dropped on "heading" parent -> move to start + nTargetPos = SwOutlineNodes::npos; + } + else + { + assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry)))); + nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry))->GetOutlinePos(); + } + + if( MAXLEVEL > m_nOutlineLevel && // Not all layers are displayed. + nTargetPos != SwOutlineNodes::npos) + { + std::unique_ptr<weld::TreeIter> xNext(m_xTreeView->make_iterator(xDropEntry.get())); + bool bNext = m_xTreeView->iter_next(*xNext); + if (bNext) + { + assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xNext)))); + nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xNext))->GetOutlinePos() - 1; + } + else + nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1; + } + + // remove the drop highlight before we change the contents of the tree so we don't + // try and dereference a removed entry in post-processing drop + m_xTreeView->unset_drag_dest_row(); + MoveOutline(nTargetPos); + + } + return IsInDrag() ? DND_ACTION_NONE : GetParentWindow()->ExecuteDrop(rEvt); +} + +namespace +{ + bool IsAllExpanded(const weld::TreeView& rContentTree, const weld::TreeIter& rEntry) + { + if (!rContentTree.get_row_expanded(rEntry)) + return false; + + if (!rContentTree.iter_has_child(rEntry)) + return false; + + std::unique_ptr<weld::TreeIter> xChild(rContentTree.make_iterator(&rEntry)); + (void)rContentTree.iter_children(*xChild); + + do + { + if (rContentTree.iter_has_child(*xChild) || rContentTree.get_children_on_demand(*xChild)) + { + if (!IsAllExpanded(rContentTree, *xChild)) + return false; + } + } + while (rContentTree.iter_next_sibling(*xChild)); + return true; + } + + void ExpandOrCollapseAll(weld::TreeView& rContentTree, weld::TreeIter& rEntry) + { + bool bExpand = !IsAllExpanded(rContentTree, rEntry); + bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry); + int nRefDepth = rContentTree.get_iter_depth(rEntry); + while (rContentTree.iter_next(rEntry) && rContentTree.get_iter_depth(rEntry) > nRefDepth) + { + if (rContentTree.iter_has_child(rEntry)) + bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry); + } + } +} + +// Handler for Dragging and ContextMenu +static bool lcl_InsertExpandCollapseAllItem(const weld::TreeView& rContentTree, const weld::TreeIter& rEntry, weld::Menu& rPop) +{ + if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry)) + { + rPop.set_label(OUString::number(800), IsAllExpanded(rContentTree, rEntry) ? SwResId(STR_COLLAPSEALL) : SwResId(STR_EXPANDALL)); + return false; + } + return true; +} + +static void lcl_SetOutlineContentEntriesSensitivities(SwContentTree* pThis, const weld::TreeView& rContentTree, const weld::TreeIter& rEntry, weld::Menu& rPop) +{ + rPop.set_sensitive(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), false); + rPop.set_sensitive(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), false); + rPop.set_sensitive(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), false); + + // todo: multi selection + if (rContentTree.count_selected_rows() > 1) + return; + + bool bIsRoot = lcl_IsContentType(rEntry, rContentTree); + + const SwNodes& rNodes = pThis->GetWrtShell()->GetNodes(); + const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds(); + size_t nOutlinePos = weld::GetAbsPos(rContentTree, rEntry); + + if (!bIsRoot) + --nOutlinePos; + + if (nOutlinePos >= rOutlineNodes.size()) + return; + + int nFirstLevel = pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nOutlinePos); + { + // determine if any concerned outline node has content + bool bHasContent(false); + size_t nPos = nOutlinePos; + SwNode* pSttNd = rOutlineNodes[nPos]; + SwNode* pEndNd = &rNodes.GetEndOfContent(); + if (rOutlineNodes.size() > nPos + 1) + pEndNd = rOutlineNodes[nPos + 1]; + + // selected + SwNodeIndex aIdx(*pSttNd); + if (rNodes.GoNext(&aIdx) != pEndNd) + bHasContent = true; + + // descendants + if (!bHasContent && (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry))) + { + while (++nPos < rOutlineNodes.size() && + (bIsRoot || pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nFirstLevel)) + { + pSttNd = rOutlineNodes[nPos]; + pEndNd = &rNodes.GetEndOfContent(); + if (rOutlineNodes.size() > nPos + 1) + pEndNd = rOutlineNodes[nPos + 1]; + + // test for content in outline node + aIdx.Assign(*pSttNd); + if (rNodes.GoNext(&aIdx) != pEndNd) + { + bHasContent = true; + break; + } + } + } + + if (!bHasContent) + return; // no content in any of the concerned outline nodes + } + + // determine for subs if all are folded or unfolded or if they are mixed + if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry)) + { + // skip no content nodes + // we know there is content from results above so this is presumably safe + size_t nPos = nOutlinePos; + while (true) + { + SwNode* pSttNd = rOutlineNodes[nPos]; + SwNode* pEndNd = rOutlineNodes.back(); + if (!bIsRoot && rOutlineNodes.size() > nPos + 1) + pEndNd = rOutlineNodes[nPos + 1]; + + SwNodeIndex aIdx(*pSttNd); + if (rNodes.GoNext(&aIdx) != pEndNd) + break; + nPos++; + } + + bool bHasFolded(!pThis->GetWrtShell()->IsOutlineContentVisible(nPos)); + bool bHasUnfolded(!bHasFolded); + + while ((++nPos < pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount()) && + (bIsRoot || pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nFirstLevel)) + { + + SwNode* pSttNd = rOutlineNodes[nPos]; + SwNode* pEndNd = &rNodes.GetEndOfContent(); + if (rOutlineNodes.size() > nPos + 1) + pEndNd = rOutlineNodes[nPos + 1]; + + SwNodeIndex aIdx(*pSttNd); + if (rNodes.GoNext(&aIdx) == pEndNd) + continue; // skip if no content + + if (!pThis->GetWrtShell()->IsOutlineContentVisible(nPos)) + bHasFolded = true; + else + bHasUnfolded = true; + + if (bHasFolded && bHasUnfolded) + break; // mixed so no need to continue + } + + rPop.set_sensitive(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), bHasUnfolded); + rPop.set_sensitive(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), bHasFolded); + } + + rPop.set_sensitive(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), !bIsRoot); +} + +IMPL_LINK(SwContentTree, CommandHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + grab_focus(); + + // select clicked entry or limit selection to root entry if needed + if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + rCEvt.IsMouseEvent() && m_xTreeView->get_dest_row_at_pos( + rCEvt.GetMousePosPixel(), xEntry.get(), false)) + { + // if clicked entry is not currently selected then clear selections and select it + if (!m_xTreeView->is_selected(*xEntry)) + m_xTreeView->set_cursor(*xEntry); + // if root entry is selected then clear selections and select it + else if (m_xTreeView->is_selected(0)) + m_xTreeView->set_cursor(0); + } + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "modules/swriter/ui/navigatorcontextmenu.ui")); + std::unique_ptr<weld::Menu> xPop = xBuilder->weld_menu("navmenu"); + + bool bOutline(false); + std::unique_ptr<weld::Menu> xSubPop1 = xBuilder->weld_menu("outlinelevel"); + std::unique_ptr<weld::Menu> xSubPop2 = xBuilder->weld_menu("dragmodemenu"); + std::unique_ptr<weld::Menu> xSubPop3 = xBuilder->weld_menu("displaymenu"); + std::unique_ptr<weld::Menu> xSubPopOutlineTracking = xBuilder->weld_menu("outlinetracking"); + + std::unique_ptr<weld::Menu> xSubPopOutlineContent = xBuilder->weld_menu("outlinecontent"); + + xSubPopOutlineContent->append(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), + SwResId(STR_OUTLINE_CONTENT_VISIBILITY_TOGGLE)); + xSubPopOutlineContent->append(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), + SwResId(STR_OUTLINE_CONTENT_VISIBILITY_HIDE_ALL)); + xSubPopOutlineContent->append(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), + SwResId(STR_OUTLINE_CONTENT_VISIBILITY_SHOW_ALL)); + + xSubPopOutlineContent->set_item_help_id(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), + HID_NAVIGATOR_TREELIST); + xSubPopOutlineContent->set_item_help_id(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), + HID_NAVIGATOR_TREELIST); + xSubPopOutlineContent->set_item_help_id(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), + HID_NAVIGATOR_TREELIST); + + // Add entries to the Outline Tracking submenu + OUString sId; + for(int i = 1; i <= 3; ++i) + { + sId = OUString::number(i + 10); + xSubPopOutlineTracking->append_radio(sId, m_aContextStrings[IDX_STR_OUTLINE_TRACKING + i]); + xSubPopOutlineTracking->set_item_help_id(sId, HID_NAV_OUTLINE_TRACKING); + } + xSubPopOutlineTracking->set_active(OUString::number(10 + m_nOutlineTracking), true); + + // Add entries to the Outline Level submenu + for (int i = 1; i <= MAXLEVEL; ++i) + { + sId = OUString::number(i + 100); + xSubPop1->append_radio(sId, OUString::number(i)); + xSubPop1->set_item_help_id(sId, HID_NAV_OUTLINE_LEVEL); + } + xSubPop1->set_active(OUString::number(100 + m_nOutlineLevel), true); + + // Add entries to the Drag Mode submenu + for (int i=0; i < 3; ++i) + { + sId = OUString::number(i + 201); + xSubPop2->append_radio(sId, m_aContextStrings[IDX_STR_HYPERLINK + i]); + xSubPop2->set_item_help_id(sId, HID_NAV_DRAG_MODE); + } + xSubPop2->set_active(OUString::number(201 + static_cast<int>(GetParentWindow()->GetRegionDropMode())), true); + + // Insert the list of the open files in the Display submenu + { + sal_uInt16 nId = 301; + SwView *pView = SwModule::GetFirstView(); + while (pView) + { + OUString sInsert = pView->GetDocShell()->GetTitle() + " (" + + m_aContextStrings[pView == GetActiveView() ? IDX_STR_ACTIVE : + IDX_STR_INACTIVE] + ")"; + sId = OUString::number(nId); + xSubPop3->append_radio(sId, sInsert); + xSubPop3->set_item_help_id(sId, HID_NAV_DISPLAY); + if (State::CONSTANT == m_eState && m_pActiveShell == &pView->GetWrtShell()) + xSubPop3->set_active(sId, true); + pView = SwModule::GetNextView(pView); + nId++; + } + // Active Window + sId = OUString::number(nId++); + xSubPop3->append_radio(sId, m_aContextStrings[IDX_STR_ACTIVE_VIEW]); + xSubPop3->set_item_help_id(sId, HID_NAV_DISPLAY); + // There can be only one hidden shell + if (m_pHiddenShell) + { + OUString sHiddenEntry = m_pHiddenShell->GetView().GetDocShell()->GetTitle() + + " (" + + m_aContextStrings[IDX_STR_HIDDEN] + + ")"; + sId = OUString::number(nId); + xSubPop3->append_radio(sId, sHiddenEntry); + xSubPop3->set_item_help_id(sId, HID_NAV_DISPLAY); + } + if (State::ACTIVE == m_eState) + xSubPop3->set_active(OUString::number(--nId), true); + else if (State::HIDDEN == m_eState) + xSubPop3->set_active(OUString::number(nId), true); + } + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xEntry.get())) + xEntry.reset(); + + bool bRemoveGotoEntry = false; + if (State::HIDDEN == m_eState || !xEntry || !lcl_IsContent(*xEntry, *m_xTreeView) || + weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsInvisible()) + bRemoveGotoEntry = true; + + bool bRemovePostItEntries = true; + bool bRemoveIndexEntries = true; + bool bRemoveCopyEntry = true; + bool bRemoveEditEntry = true; + bool bRemoveUnprotectEntry = true; + bool bRemoveDeleteChapterEntry = true, + bRemoveDeleteTableEntry = true, + bRemoveDeleteFrameEntry = true, + bRemoveDeleteImageEntry = true, + bRemoveDeleteOLEObjectEntry = true, + bRemoveDeleteBookmarkEntry = true, + bRemoveDeleteRegionEntry = true, + bRemoveDeleteHyperlinkEntry = true, + bRemoveDeleteReferenceEntry = true, + bRemoveDeleteIndexEntry= true, + bRemoveDeleteCommentEntry = true, + bRemoveDeleteDrawingObjectEntry = true, + bRemoveDeleteFieldEntry = true; + bool bRemoveRenameEntry = true; + bool bRemoveSelectEntry = true; + bool bRemoveToggleExpandEntry = true; + bool bRemoveChapterEntries = true; + bool bRemoveSendOutlineEntry = true; + + bool bRemoveTableTracking = true; + bool bRemoveSectionTracking = true; + bool bRemoveFrameTracking = true; + bool bRemoveImageTracking = true; + bool bRemoveOLEobjectTracking = true; + bool bRemoveBookmarkTracking = true; + bool bRemoveHyperlinkTracking = true; + bool bRemoveReferenceTracking = true; + bool bRemoveIndexTracking = true; + bool bRemoveCommentTracking = true; + bool bRemoveDrawingObjectTracking = true; + bool bRemoveFieldTracking = true; + bool bRemoveFootnoteTracking = true; + bool bRemoveEndnoteTracking = true; + + bool bRemoveSortEntry = true; + + bool bRemoveProtectSection = true; + bool bRemoveHideSection = true; + + if (xEntry) + { + const SwContentType* pType; + if (lcl_IsContentType(*xEntry, *m_xTreeView)) + pType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry)); + else + pType = weld::fromId<SwContent*>( + m_xTreeView->get_id(*xEntry))->GetParent(); + const ContentTypeId nContentType = pType->GetType(); + + if (nContentType != ContentTypeId::FOOTNOTE && nContentType != ContentTypeId::ENDNOTE + && nContentType != ContentTypeId::POSTIT && nContentType != ContentTypeId::UNKNOWN) + { + bRemoveSortEntry = false; + const sal_Int32 nMask = 1 << static_cast<int>(nContentType); + sal_uInt64 nSortAlphabeticallyBlock = m_pConfig->GetSortAlphabeticallyBlock(); + xPop->set_active("sort", nSortAlphabeticallyBlock & nMask); + } + + OUString aIdent; + switch (nContentType) + { + case ContentTypeId::TABLE: + aIdent = "tabletracking"; + bRemoveTableTracking = false; + break; + case ContentTypeId::REGION: + aIdent = "sectiontracking"; + bRemoveSectionTracking = false; + break; + case ContentTypeId::FRAME: + aIdent = "frametracking"; + bRemoveFrameTracking = false; + break; + case ContentTypeId::GRAPHIC: + aIdent = "imagetracking"; + bRemoveImageTracking = false; + break; + case ContentTypeId::OLE: + aIdent = "oleobjecttracking"; + bRemoveOLEobjectTracking = false; + break; + case ContentTypeId::BOOKMARK: + aIdent = "bookmarktracking"; + bRemoveBookmarkTracking = false; + break; + case ContentTypeId::URLFIELD: + aIdent = "hyperlinktracking"; + bRemoveHyperlinkTracking = false; + break; + case ContentTypeId::REFERENCE: + aIdent = "referencetracking"; + bRemoveReferenceTracking = false; + break; + case ContentTypeId::INDEX: + aIdent = "indextracking"; + bRemoveIndexTracking = false; + break; + case ContentTypeId::POSTIT: + aIdent = "commenttracking"; + bRemoveCommentTracking = false; + break; + case ContentTypeId::DRAWOBJECT: + aIdent = "drawingobjecttracking"; + bRemoveDrawingObjectTracking = false; + break; + case ContentTypeId::TEXTFIELD: + aIdent = "fieldtracking"; + bRemoveFieldTracking = false; + break; + case ContentTypeId::FOOTNOTE: + aIdent = "footnotetracking"; + bRemoveFootnoteTracking = false; + break; + case ContentTypeId::ENDNOTE: + aIdent = "endnotetracking"; + bRemoveEndnoteTracking = false; + break; + default: break; + } + if (!aIdent.isEmpty()) + xPop->set_active(aIdent, mTrackContentType[nContentType]); + + // Edit only if the shown content is coming from the current view. + if (State::HIDDEN != m_eState && + (State::ACTIVE == m_eState || (GetActiveView() && m_pActiveShell == GetActiveView()->GetWrtShellPtr())) + && lcl_IsContent(*xEntry, *m_xTreeView)) + { + const bool bReadonly = m_pActiveShell->GetView().GetDocShell()->IsReadOnly(); + const bool bVisible = !weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsInvisible(); + const bool bProtected = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsProtect(); + const bool bProtectBM = (ContentTypeId::BOOKMARK == nContentType) + && m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS); + const bool bEditable = pType->IsEditable() && + ((bVisible && !bProtected) || ContentTypeId::REGION == nContentType); + const bool bDeletable = pType->IsDeletable() + && ((bVisible && !bProtected && !bProtectBM) || ContentTypeId::REGION == nContentType); + const bool bRenamable = bEditable && !bReadonly && + (ContentTypeId::TABLE == nContentType || + ContentTypeId::FRAME == nContentType || + ContentTypeId::GRAPHIC == nContentType || + ContentTypeId::OLE == nContentType || + (ContentTypeId::BOOKMARK == nContentType && !bProtectBM) || + ContentTypeId::REGION == nContentType || + ContentTypeId::INDEX == nContentType || + ContentTypeId::DRAWOBJECT == nContentType); + // Choose which Delete entry to show. + if (bDeletable) + { + switch (nContentType) + { + case ContentTypeId::OUTLINE: + bRemoveDeleteChapterEntry = false; + break; + case ContentTypeId::TABLE: + bRemoveDeleteTableEntry = false; + break; + case ContentTypeId::FRAME: + bRemoveDeleteFrameEntry = false; + break; + case ContentTypeId::GRAPHIC: + bRemoveDeleteImageEntry = false; + break; + case ContentTypeId::OLE: + bRemoveDeleteOLEObjectEntry = false; + break; + case ContentTypeId::BOOKMARK: + bRemoveDeleteBookmarkEntry = false; + break; + case ContentTypeId::REGION: + bRemoveDeleteRegionEntry = false; + break; + case ContentTypeId::URLFIELD: + bRemoveDeleteHyperlinkEntry = false; + break; + case ContentTypeId::REFERENCE: + bRemoveDeleteReferenceEntry = false; + break; + case ContentTypeId::INDEX: + bRemoveDeleteIndexEntry = false; + break; + case ContentTypeId::POSTIT: + bRemoveDeleteCommentEntry = false; + break; + case ContentTypeId::DRAWOBJECT: + bRemoveDeleteDrawingObjectEntry = false; + break; + case ContentTypeId::TEXTFIELD: + bRemoveDeleteFieldEntry = false; + break; + default: break; + } + } + if (ContentTypeId::FOOTNOTE == nContentType || ContentTypeId::ENDNOTE == nContentType) + { + void* pUserData = weld::fromId<void*>(m_xTreeView->get_id(*xEntry)); + const SwTextFootnote* pFootnote = + static_cast<const SwTextFootnoteContent*>(pUserData)->GetTextFootnote(); + if (!pFootnote) + bRemoveGotoEntry = true; + } + else if(ContentTypeId::OUTLINE == nContentType) + { + bOutline = true; + lcl_SetOutlineContentEntriesSensitivities(this, *m_xTreeView, *xEntry, *xSubPopOutlineContent); + bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, *xPop); + if (!bReadonly) + { + bRemoveSelectEntry = false; + bRemoveChapterEntries = false; + } + bRemoveCopyEntry = false; + } + else if (!bReadonly && bEditable) + { + if(ContentTypeId::INDEX == nContentType) + { + bRemoveIndexEntries = false; + + const SwTOXBase* pBase = weld::fromId<SwTOXBaseContent*>(m_xTreeView->get_id(*xEntry))->GetTOXBase(); + if (!pBase->IsTOXBaseInReadonly()) + bRemoveEditEntry = false; + + xPop->set_active(OUString::number(405), SwEditShell::IsTOXBaseReadonly(*pBase)); + } + else if(ContentTypeId::TABLE == nContentType) + { + bRemoveSelectEntry = false; + bRemoveEditEntry = false; + bRemoveUnprotectEntry = false; + bool bFull = false; + OUString sTableName = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetName(); + bool bProt = m_pActiveShell->HasTableAnyProtection( &sTableName, &bFull ); + xPop->set_sensitive(OUString::number(403), !bFull); + xPop->set_sensitive(OUString::number(404), bProt); + } + else if(ContentTypeId::REGION == nContentType) + { + bRemoveEditEntry = false; + bRemoveProtectSection = false; + bRemoveHideSection = false; + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry)); + assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pCnt))); + const SwSectionFormat* pSectionFormat + = static_cast<SwRegionContent*>(pCnt)->GetSectionFormat(); + bool bHidden = pSectionFormat->GetSection()->IsHidden(); + bRemoveSelectEntry = bHidden || !bVisible; + xPop->set_active("protectsection", bProtected); + xPop->set_active("hidesection", bHidden); + } + else if (bEditable) + bRemoveEditEntry = false; + //Rename object + if (bRenamable) + bRemoveRenameEntry = false; + } + } + else + { + if (lcl_IsContentType(*xEntry, *m_xTreeView)) + pType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry)); + else + pType = weld::fromId<SwContent*>( + m_xTreeView->get_id(*xEntry))->GetParent(); + if (pType) + { + if (ContentTypeId::OUTLINE == nContentType) + { + bOutline = true; + if (State::HIDDEN != m_eState) + { + lcl_SetOutlineContentEntriesSensitivities(this, *m_xTreeView, *xEntry, + *xSubPopOutlineContent); + bRemoveSendOutlineEntry = false; + } + bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, + *xPop); + } + else if (State::HIDDEN != m_eState && + nContentType == ContentTypeId::POSTIT && + !m_pActiveShell->GetView().GetDocShell()->IsReadOnly() && + pType->GetMemberCount() > 0) + bRemovePostItEntries = false; + } + } + } + + if (bRemoveToggleExpandEntry) + xPop->remove(OUString::number(800)); + + if (bRemoveGotoEntry) + xPop->remove(OUString::number(900)); + + if (bRemoveSelectEntry) + xPop->remove(OUString::number(805)); + + if (bRemoveChapterEntries) + { + xPop->remove(OUString::number(801)); + xPop->remove(OUString::number(802)); + xPop->remove(OUString::number(803)); + xPop->remove(OUString::number(804)); + } + + if (bRemoveSendOutlineEntry) + xPop->remove(OUString::number(700)); + + if (bRemovePostItEntries) + { + xPop->remove(OUString::number(600)); + xPop->remove(OUString::number(601)); + xPop->remove(OUString::number(602)); + } + + if (bRemoveDeleteChapterEntry) + xPop->remove("deletechapter"); + if (bRemoveDeleteTableEntry) + xPop->remove("deletetable"); + if (bRemoveDeleteFrameEntry) + xPop->remove("deleteframe"); + if (bRemoveDeleteImageEntry) + xPop->remove("deleteimage"); + if (bRemoveDeleteOLEObjectEntry) + xPop->remove("deleteoleobject"); + if (bRemoveDeleteBookmarkEntry) + xPop->remove("deletebookmark"); + if (bRemoveDeleteRegionEntry) + xPop->remove("deleteregion"); + if (bRemoveDeleteHyperlinkEntry) + xPop->remove("deletehyperlink"); + if (bRemoveDeleteReferenceEntry) + xPop->remove("deletereference"); + if (bRemoveDeleteIndexEntry) + xPop->remove("deleteindex"); + if (bRemoveDeleteCommentEntry) + xPop->remove("deletecomment"); + if (bRemoveDeleteDrawingObjectEntry) + xPop->remove("deletedrawingobject"); + if (bRemoveDeleteFieldEntry) + xPop->remove("deletefield"); + + bool bRemoveDeleteEntry = + bRemoveDeleteChapterEntry && + bRemoveDeleteTableEntry && + bRemoveDeleteFrameEntry && + bRemoveDeleteImageEntry && + bRemoveDeleteOLEObjectEntry && + bRemoveDeleteBookmarkEntry && + bRemoveDeleteRegionEntry && + bRemoveDeleteHyperlinkEntry && + bRemoveDeleteReferenceEntry && + bRemoveDeleteIndexEntry && + bRemoveDeleteCommentEntry && + bRemoveDeleteDrawingObjectEntry && + bRemoveDeleteFieldEntry; + + if (bRemoveRenameEntry) + xPop->remove(OUString::number(502)); + + if (bRemoveIndexEntries) + { + xPop->remove(OUString::number(401)); + xPop->remove(OUString::number(402)); + xPop->remove(OUString::number(405)); + } + + if (bRemoveUnprotectEntry) + xPop->remove(OUString::number(404)); + + if (bRemoveEditEntry) + xPop->remove(OUString::number(403)); + + if (bRemoveToggleExpandEntry && + bRemoveSendOutlineEntry) + xPop->remove("separator1"); + + if (bRemoveCopyEntry) + xPop->remove("copy"); + + if (bRemoveGotoEntry && + bRemoveCopyEntry && + bRemoveSelectEntry && + bRemoveDeleteEntry && + bRemoveChapterEntries && + bRemovePostItEntries && + bRemoveRenameEntry && + bRemoveIndexEntries && + bRemoveUnprotectEntry && + bRemoveEditEntry) + xPop->remove("separator2"); + + if (!bOutline) + { + xSubPop1.reset(); + xPop->remove(OUString::number(1)); // outline level menu + } + if (!bOutline || State::HIDDEN == m_eState) + { + xSubPopOutlineTracking.reset(); + xPop->remove(OUString::number(4)); // outline tracking menu + } + if (!bOutline || State::HIDDEN == m_eState || + !m_pActiveShell->GetViewOptions()->IsShowOutlineContentVisibilityButton() || + m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() == 0) + { + xSubPopOutlineContent.reset(); + xPop->remove(OUString::number(5)); // outline folding menu + xPop->remove("separator3"); + } + + if (bRemoveTableTracking) + xPop->remove("tabletracking"); + if (bRemoveSectionTracking) + xPop->remove("sectiontracking"); + if (bRemoveFrameTracking) + xPop->remove("frametracking"); + if (bRemoveImageTracking) + xPop->remove("imagetracking"); + if (bRemoveOLEobjectTracking) + xPop->remove("oleobjecttracking"); + if (bRemoveBookmarkTracking) + xPop->remove("bookmarktracking"); + if (bRemoveHyperlinkTracking) + xPop->remove("hyperlinktracking"); + if (bRemoveReferenceTracking) + xPop->remove("referencetracking"); + if (bRemoveIndexTracking) + xPop->remove("indextracking"); + if (bRemoveCommentTracking) + xPop->remove("commenttracking"); + if (bRemoveDrawingObjectTracking) + xPop->remove("drawingobjecttracking"); + if (bRemoveFieldTracking) + xPop->remove("fieldtracking"); + if (bRemoveFootnoteTracking) + xPop->remove("footnotetracking"); + if (bRemoveEndnoteTracking) + xPop->remove("endnotetracking"); + if (bRemoveSortEntry) + xPop->remove("sort"); + if (bRemoveProtectSection) + xPop->remove("protectsection"); + if (bRemoveHideSection) + xPop->remove("hidesection"); + + bool bSetSensitiveCollapseAllCategories = false; + if (!m_bIsRoot && xEntry) + { + bool bEntry = m_xTreeView->get_iter_first(*xEntry); + while (bEntry) + { + if (m_xTreeView->get_row_expanded(*xEntry)) + { + bSetSensitiveCollapseAllCategories = true; + break; + } + bEntry = m_xTreeView->iter_next_sibling(*xEntry); + } + } + xPop->set_sensitive("collapseallcategories", bSetSensitiveCollapseAllCategories); + + OUString sCommand = xPop->popup_at_rect(m_xTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + if (!sCommand.isEmpty()) + ExecuteContextMenuAction(sCommand); + + return true; +} + +void SwContentTree::InsertContent(const weld::TreeIter& rParent) +{ + assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent)))); + SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent)); + bool bGraphic = pCntType->GetType() == ContentTypeId::GRAPHIC; + std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(); + const size_t nCount = pCntType->GetMemberCount(); + for(size_t i = 0; i < nCount; ++i) + { + const SwContent* pCnt = pCntType->GetMember(i); + OUString sEntry = pCnt->GetName(); + if (sEntry.isEmpty()) + sEntry = m_sSpace; + OUString sId(weld::toId(pCnt)); + insert(&rParent, sEntry, sId, false, xChild.get()); + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + if (bGraphic && !static_cast<const SwGraphicContent*>(pCnt)->GetLink().isEmpty()) + m_xTreeView->set_image(*xChild, RID_BMP_NAVI_GRAPHIC_LINK); + } +} + +void SwContentTree::insert(const weld::TreeIter* pParent, const OUString& rStr, const OUString& rId, + bool bChildrenOnDemand, weld::TreeIter* pRet) +{ + m_xTreeView->insert(pParent, -1, &rStr, &rId, nullptr, nullptr, bChildrenOnDemand, pRet); + ++m_nEntryCount; +} + +void SwContentTree::remove(const weld::TreeIter& rIter) +{ + if (m_xTreeView->iter_has_child(rIter)) + { + std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(&rIter); + (void)m_xTreeView->iter_children(*xChild); + remove(*xChild); + } + m_xTreeView->remove(rIter); + --m_nEntryCount; +} + +// Content will be integrated into the Box only on demand. +bool SwContentTree::RequestingChildren(const weld::TreeIter& rParent) +{ + // Does the parent already have children or is it not a 'children on demand' node? + if (m_xTreeView->iter_has_child(rParent) || !m_xTreeView->get_children_on_demand(rParent)) + return false; + + // Is this a content type? + if (lcl_IsContentType(rParent, *m_xTreeView)) + { + std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(); + + assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent)))); + SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent)); + + const size_t nCount = pCntType->GetMemberCount(); + // Add for outline plus/minus + if (pCntType->GetType() == ContentTypeId::OUTLINE) + { + std::vector<std::unique_ptr<weld::TreeIter>> aParentCandidates; + for(size_t i = 0; i < nCount; ++i) + { + const SwContent* pCnt = pCntType->GetMember(i); + if(pCnt) + { + const auto nLevel = static_cast<const SwOutlineContent*>(pCnt)->GetOutlineLevel(); + OUString sEntry = pCnt->GetName(); + if(sEntry.isEmpty()) + sEntry = m_sSpace; + OUString sId(weld::toId(pCnt)); + + auto lambda = [nLevel, this](const std::unique_ptr<weld::TreeIter>& entry) + { + return lcl_IsLowerOutlineContent(*entry, *m_xTreeView, nLevel); + }; + + // if there is a preceding outline node candidate with a lower outline level use + // that as a parent, otherwise use the root node + auto aFind = std::find_if(aParentCandidates.rbegin(), aParentCandidates.rend(), lambda); + if (aFind != aParentCandidates.rend()) + insert(aFind->get(), sEntry, sId, false, xChild.get()); + else + insert(&rParent, sEntry, sId, false, xChild.get()); + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + m_xTreeView->set_extra_row_indent(*xChild, nLevel + 1 - m_xTreeView->get_iter_depth(*xChild)); + + // remove any parent candidates equal to or higher than this node + std::erase_if(aParentCandidates, std::not_fn(lambda)); + + // add this node as a parent candidate for any following nodes at a higher outline level + aParentCandidates.emplace_back(m_xTreeView->make_iterator(xChild.get())); + } + } + } + else if (pCntType->GetType() == ContentTypeId::REGION) + { + if (pCntType->IsAlphabeticSort()) + { + for(size_t i = 0; i < nCount; ++i) + { + const SwRegionContent* pCnt = + static_cast<const SwRegionContent*>(pCntType->GetMember(i)); + + OUString sEntry = pCnt->GetName(); + OUString sId(weld::toId(pCnt)); + + const auto nLevel = pCnt->GetRegionLevel(); + insert(&rParent, sEntry, sId, false, xChild.get()); + + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + m_xTreeView->set_extra_row_indent(*xChild, nLevel); + + bool bHidden = pCnt->GetSectionFormat()->GetSection()->IsHidden(); + if (pCnt->IsProtect()) + m_xTreeView->set_image(*xChild, bHidden ? RID_BMP_PROT_HIDE : RID_BMP_PROT_NO_HIDE); + else + m_xTreeView->set_image(*xChild, bHidden ? RID_BMP_HIDE : RID_BMP_NO_HIDE); + } + } + else + { + std::vector<std::unique_ptr<weld::TreeIter>> aParentCandidates; + for(size_t i = 0; i < nCount; ++i) + { + const SwRegionContent* pCnt = + static_cast<const SwRegionContent*>(pCntType->GetMember(i)); + + OUString sEntry = pCnt->GetName(); + OUString sId(weld::toId(pCnt)); + + const auto nLevel = pCnt->GetRegionLevel(); + auto lambda = [nLevel, this](const std::unique_ptr<weld::TreeIter>& xEntry) + { + return lcl_IsLowerRegionContent(*xEntry, *m_xTreeView, nLevel); + }; + + // if there is a preceding region node candidate with a lower region level use + // that as a parent, otherwise use the root node + auto aFind = std::find_if(aParentCandidates.rbegin(), aParentCandidates.rend(), lambda); + if (aFind != aParentCandidates.rend()) + insert(aFind->get(), sEntry, sId, false, xChild.get()); + else + insert(&rParent, sEntry, sId, false, xChild.get()); + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + + bool bHidden = pCnt->GetSectionFormat()->GetSection()->IsHidden(); + if (pCnt->IsProtect()) + m_xTreeView->set_image(*xChild, bHidden ? RID_BMP_PROT_HIDE : RID_BMP_PROT_NO_HIDE); + else + m_xTreeView->set_image(*xChild, bHidden ? RID_BMP_HIDE : RID_BMP_NO_HIDE); + + // remove any parent candidates equal to or higher than this node + std::erase_if(aParentCandidates, std::not_fn(lambda)); + + // add this node as a parent candidate for any following nodes at a higher region level + aParentCandidates.emplace_back(m_xTreeView->make_iterator(xChild.get())); + } + } + } + else if (pCntType->GetType() == ContentTypeId::POSTIT) + { + std::vector<std::unique_ptr<weld::TreeIter>> aParentCandidates; + for(size_t i = 0; i < nCount; ++i) + { + const SwPostItContent* pCnt = + static_cast<const SwPostItContent*>(pCntType->GetMember(i)); + + OUString sEntry = pCnt->GetName(); + OUString sId(weld::toId(pCnt)); + + const SwPostItField* pPostItField = + static_cast<const SwPostItField*>(pCnt->GetPostIt()->GetField()); + auto lambda = [&pPostItField, this](const std::unique_ptr<weld::TreeIter>& xEntry) + { + SwPostItContent* pParentCandidateCnt = + weld::fromId<SwPostItContent*>(m_xTreeView->get_id(*xEntry)); + return pPostItField->GetParentPostItId() == + static_cast<const SwPostItField*>(pParentCandidateCnt->GetPostIt() + ->GetField())->GetPostItId(); + }; + + // if a parent candidate is not found use the passed root node + auto aFind = std::find_if(aParentCandidates.rbegin(), aParentCandidates.rend(), lambda); + if (aFind != aParentCandidates.rend()) + insert(aFind->get(), sEntry, sId, false, xChild.get()); + else + insert(&rParent, sEntry, sId, false, xChild.get()); + + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + + // clear parent candidates when encountering a postit that doesn't have a parent + // following postits can't have a parent that is in these candidates + if (pPostItField->GetParentPostItId() == 0) + aParentCandidates.clear(); + + aParentCandidates.emplace_back(m_xTreeView->make_iterator(xChild.get())); + } + } + else + InsertContent(rParent); + + return nCount != 0; + } + + return false; +} + +void SwContentTree::ExpandAllHeadings() +{ + if (HasHeadings()) + { + std::unique_ptr<weld::TreeIter> xEntry = GetEntryAtAbsPos(0); + if (xEntry) + { + if (!IsAllExpanded(*m_xTreeView, *xEntry)) + ExpandOrCollapseAll(*m_xTreeView, *xEntry); + } + } +} + +SdrObject* SwContentTree::GetDrawingObjectsByContent(const SwContent *pCnt) +{ + SdrObject *pRetObj = nullptr; + switch(pCnt->GetParent()->GetType()) + { + case ContentTypeId::DRAWOBJECT: + { + SdrView* pDrawView = m_pActiveShell->GetDrawView(); + if (pDrawView) + { + SwDrawModel* pDrawModel = m_pActiveShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + SdrPage* pPage = pDrawModel->GetPage(0); + + for (const rtl::Reference<SdrObject>& pTemp : *pPage) + { + if( pTemp->GetName() == pCnt->GetName()) + { + pRetObj = pTemp.get(); + break; + } + } + } + break; + } + default: + pRetObj = nullptr; + } + return pRetObj; +} + +void SwContentTree::Expand(const weld::TreeIter& rParent, + std::vector<std::unique_ptr<weld::TreeIter>>* pNodesToExpand) +{ + if (!m_xTreeView->iter_has_child(rParent) && !m_xTreeView->get_children_on_demand(rParent)) + return; + + // pNodesToExpand is used by the Display function to restore the trees expand structure for + // hierarchical content types, e.g., OUTLINE and REGION. + if (pNodesToExpand) + pNodesToExpand->emplace_back(m_xTreeView->make_iterator(&rParent)); + + // rParentId is a string representation of a pointer to SwContentType or SwContent + const OUString& rParentId = m_xTreeView->get_id(rParent); + // bParentIsContentType tells if the passed rParent tree entry is a content type or content + const bool bParentIsContentType = lcl_IsContentType(rParent, *m_xTreeView); + // eParentContentTypeId is the content type of the passed rParent tree entry + const ContentTypeId eParentContentTypeId = + bParentIsContentType ? weld::fromId<SwContentType*>(rParentId)->GetType() : + weld::fromId<SwContent*>(rParentId)->GetParent()->GetType(); + + if (m_nRootType == ContentTypeId::UNKNOWN && bParentIsContentType) + { + // m_nActiveBlock and m_nHiddenBlock are used to persist the content type expand state for + // the all content view mode + const int nShift = static_cast<int>(eParentContentTypeId); + SAL_WARN_IF(nShift < 0, "sw.ui", "ContentTypeId::UNKNOWN negative shift"); + if (nShift >= 0) + { + const sal_Int32 nOr = 1 << nShift; //linear -> Bitposition + if (State::HIDDEN != m_eState) + { + m_nActiveBlock |= nOr; + m_pConfig->SetActiveBlock(m_nActiveBlock); + } + else + m_nHiddenBlock |= nOr; + } + } + + if (m_nRootType == ContentTypeId::OUTLINE || (m_nRootType == ContentTypeId::UNKNOWN && + eParentContentTypeId == ContentTypeId::OUTLINE)) + { + if (bParentIsContentType) + { + std::map< void*, bool > aCurrOutLineNodeMap; + + SwWrtShell* pShell = GetWrtShell(); + bool bParentHasChild = RequestingChildren(rParent); + if (bParentHasChild) + { + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rParent)); + bool bChild = m_xTreeView->iter_next(*xChild); + while (bChild && lcl_IsContent(*xChild, *m_xTreeView)) + { + if (m_xTreeView->iter_has_child(*xChild)) + { + assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xChild)))); + auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xChild))->GetOutlinePos(); + void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos )); + aCurrOutLineNodeMap.emplace( key, false ); + std::map<void*, bool>::iterator iter = mOutLineNodeMap.find( key ); + if( iter != mOutLineNodeMap.end() && mOutLineNodeMap[key]) + { + aCurrOutLineNodeMap[key] = true; + RequestingChildren(*xChild); + if (pNodesToExpand) + pNodesToExpand->emplace_back(m_xTreeView->make_iterator(xChild.get())); + m_xTreeView->set_children_on_demand(*xChild, false); + } + } + bChild = m_xTreeView->iter_next(*xChild); + } + } + mOutLineNodeMap = aCurrOutLineNodeMap; + return; + } + else // content entry + { + SwWrtShell* pShell = GetWrtShell(); + assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(rParentId))); + auto const nPos = weld::fromId<SwOutlineContent*>(rParentId)->GetOutlinePos(); + void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos )); + mOutLineNodeMap[key] = true; + } + } + else if (m_nRootType == ContentTypeId::REGION || (m_nRootType == ContentTypeId::UNKNOWN && + eParentContentTypeId == ContentTypeId::REGION)) + { + if (bParentIsContentType) + { + std::map<const void*, bool> aCurrentRegionNodeExpandMap; + if (RequestingChildren(rParent)) + { + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rParent)); + while (m_xTreeView->iter_next(*xChild) && lcl_IsContent(*xChild, *m_xTreeView)) + { + if (m_xTreeView->iter_has_child(*xChild)) + { + assert(dynamic_cast<SwRegionContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xChild)))); + const void* key = + static_cast<const void*>(weld::fromId<SwRegionContent*>( + m_xTreeView->get_id(*xChild))->GetSectionFormat()); + bool bExpandNode = + m_aRegionNodeExpandMap.contains(key) && m_aRegionNodeExpandMap[key]; + aCurrentRegionNodeExpandMap.emplace(key, bExpandNode); + if (bExpandNode) + { + if (pNodesToExpand) + pNodesToExpand->emplace_back(m_xTreeView->make_iterator(xChild.get())); + RequestingChildren(*xChild); + m_xTreeView->set_children_on_demand(*xChild, false); + } + } + } + } + m_aRegionNodeExpandMap = aCurrentRegionNodeExpandMap; + return; + } + else // content entry + { + assert(dynamic_cast<SwRegionContent*>(weld::fromId<SwTypeNumber*>(rParentId))); + const void* key = static_cast<const void*>( + weld::fromId<SwRegionContent*>(rParentId)->GetSectionFormat()); + m_aRegionNodeExpandMap[key] = true; + } + } + else if (m_nRootType == ContentTypeId::POSTIT || (m_nRootType == ContentTypeId::UNKNOWN && + eParentContentTypeId == ContentTypeId::POSTIT)) + { + if (bParentIsContentType) + { + std::map<const void*, bool> aCurrentPostItNodeExpandMap; + if (RequestingChildren(rParent)) + { + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rParent)); + while (m_xTreeView->iter_next(*xChild) && lcl_IsContent(*xChild, *m_xTreeView)) + { + if (m_xTreeView->iter_has_child(*xChild)) + { + assert(dynamic_cast<SwPostItContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xChild)))); + const void* key = + static_cast<const void*>(weld::fromId<SwPostItContent*>( + m_xTreeView->get_id(*xChild))->GetPostIt()); + bool bExpandNode = + m_aPostItNodeExpandMap.contains(key) && m_aPostItNodeExpandMap[key]; + aCurrentPostItNodeExpandMap.emplace(key, bExpandNode); + if (bExpandNode) + { + if (pNodesToExpand) + pNodesToExpand->emplace_back(m_xTreeView->make_iterator(xChild.get())); + RequestingChildren(*xChild); + m_xTreeView->set_children_on_demand(*xChild, false); + } + } + } + } + m_aPostItNodeExpandMap = aCurrentPostItNodeExpandMap; + return; + } + else // content entry + { + assert(dynamic_cast<SwPostItContent*>(weld::fromId<SwTypeNumber*>(rParentId))); + const void* key = static_cast<const void*>( + weld::fromId<SwPostItContent*>(rParentId)->GetPostIt()); + m_aPostItNodeExpandMap[key] = true; + } + } + + RequestingChildren(rParent); +} + +IMPL_LINK(SwContentTree, ExpandHdl, const weld::TreeIter&, rParent, bool) +{ + Expand(rParent, nullptr); + return true; +} + +IMPL_LINK(SwContentTree, CollapseHdl, const weld::TreeIter&, rParent, bool) +{ + if (!m_xTreeView->iter_has_child(rParent) || m_xTreeView->get_children_on_demand(rParent)) + return true; + + if (lcl_IsContentType(rParent, *m_xTreeView)) + { + if (m_bIsRoot) + { + // collapse to children of root node + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(&rParent)); + if (m_xTreeView->iter_children(*xEntry)) + { + do + { + m_xTreeView->collapse_row(*xEntry); + } + while (m_xTreeView->iter_next(*xEntry)); + } + return false; // return false to notify caller not to do collapse + } + ContentTypeId eContentTypeId = + weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent))->GetType(); + const int nShift = static_cast<int>(eContentTypeId); + SAL_WARN_IF(nShift < 0, "sw.ui", "ContentTypeId::UNKNOWN negative shift"); + if (nShift >= 0) + { + const sal_Int32 nAnd = ~(1 << nShift); + if (State::HIDDEN != m_eState) + { + m_nActiveBlock &= nAnd; + m_pConfig->SetActiveBlock(m_nActiveBlock); + } + else + m_nHiddenBlock &= nAnd; + } + } + else // content entry + { + SwWrtShell* pShell = GetWrtShell(); + ContentTypeId eContentTypeId = + weld::fromId<SwContent*>(m_xTreeView->get_id(rParent))->GetParent()->GetType(); + if (eContentTypeId == ContentTypeId::OUTLINE) + { + assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent)))); + auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rParent))->GetOutlinePos(); + void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos )); + mOutLineNodeMap[key] = false; + } + else if(eContentTypeId == ContentTypeId::REGION) + { + assert(dynamic_cast<SwRegionContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent)))); + const void* key = static_cast<const void*>(weld::fromId<SwRegionContent*>(m_xTreeView->get_id(rParent))->GetSectionFormat()); + m_aRegionNodeExpandMap[key] = false; + } + else if(eContentTypeId == ContentTypeId::POSTIT) + { + assert(dynamic_cast<SwPostItContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent)))); + const void* key = static_cast<const void*>(weld::fromId<SwPostItContent*>(m_xTreeView->get_id(rParent))->GetPostIt()); + m_aPostItNodeExpandMap[key] = false; + } + } + + return true; +} + +// Also on double click will be initially opened only. +IMPL_LINK_NOARG(SwContentTree, ContentDoubleClickHdl, weld::TreeView&, bool) +{ + if (m_nRowActivateEventId) + Application::RemoveUserEvent(m_nRowActivateEventId); + // post the event to process row activate after mouse press event to be able to set key + // modifier for selection feature (tdf#154211) + m_nRowActivateEventId + = Application::PostUserEvent(LINK(this, SwContentTree, AsyncContentDoubleClickHdl)); + + bool bConsumed = false; + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xEntry.get()) && lcl_IsContent(*xEntry, *m_xTreeView) && + (State::HIDDEN != m_eState)) + { + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry)); + assert(pCnt && "no UserData"); + if (pCnt && !pCnt->IsInvisible()) + { + // fdo#36308 don't expand outlines on double-click + bConsumed = pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE; + } + } + + return bConsumed; // false/true == allow/disallow more to be done, i.e. expand/collapse children +} + +IMPL_LINK_NOARG(SwContentTree, AsyncContentDoubleClickHdl, void*, void) +{ + m_nRowActivateEventId = nullptr; + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_cursor(xEntry.get()); + // Is it a content type? + OSL_ENSURE(bEntry, "no current entry!"); + if (bEntry) + { + if (lcl_IsContentType(*xEntry, *m_xTreeView) && !m_xTreeView->iter_has_child(*xEntry)) + { + RequestingChildren(*xEntry); + m_xTreeView->set_children_on_demand(*xEntry, false); + } + else if (!lcl_IsContentType(*xEntry, *m_xTreeView) && (State::HIDDEN != m_eState)) + { + assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry)); + assert(pCnt && "no UserData"); + if (pCnt && !pCnt->IsInvisible()) + { + if (State::CONSTANT == m_eState) + { + m_pActiveShell->GetView().GetViewFrame().GetWindow().ToTop(); + } + //Jump to content type: + GotoContent(pCnt); + } + } + } +} + +namespace +{ + OUString GetImageIdForContentTypeId(ContentTypeId eType) + { + OUString sResId; + + switch (eType) + { + case ContentTypeId::OUTLINE: + sResId = RID_BMP_NAVI_OUTLINE; + break; + case ContentTypeId::TABLE: + sResId = RID_BMP_NAVI_TABLE; + break; + case ContentTypeId::FRAME: + sResId = RID_BMP_NAVI_FRAME; + break; + case ContentTypeId::GRAPHIC: + sResId = RID_BMP_NAVI_GRAPHIC; + break; + case ContentTypeId::OLE: + sResId = RID_BMP_NAVI_OLE; + break; + case ContentTypeId::BOOKMARK: + sResId = RID_BMP_NAVI_BOOKMARK; + break; + case ContentTypeId::REGION: + sResId = RID_BMP_NAVI_REGION; + break; + case ContentTypeId::URLFIELD: + sResId = RID_BMP_NAVI_URLFIELD; + break; + case ContentTypeId::REFERENCE: + sResId = RID_BMP_NAVI_REFERENCE; + break; + case ContentTypeId::INDEX: + sResId = RID_BMP_NAVI_INDEX; + break; + case ContentTypeId::POSTIT: + sResId = RID_BMP_NAVI_POSTIT; + break; + case ContentTypeId::DRAWOBJECT: + sResId = RID_BMP_NAVI_DRAWOBJECT; + break; + case ContentTypeId::TEXTFIELD: + sResId = RID_BMP_NAVI_TEXTFIELD; + break; + case ContentTypeId::FOOTNOTE: + sResId = RID_BMP_NAVI_FOOTNOTE; + break; + case ContentTypeId::ENDNOTE: + sResId = RID_BMP_NAVI_ENDNOTE; + break; + case ContentTypeId::UNKNOWN: + SAL_WARN("sw.ui", "ContentTypeId::UNKNOWN has no bitmap preview"); + break; + } + + return sResId; + }; +} + +size_t SwContentTree::GetAbsPos(const weld::TreeIter& rIter) +{ + return weld::GetAbsPos(*m_xTreeView, rIter); +} + +size_t SwContentTree::GetEntryCount() const +{ + return m_nEntryCount; +} + +size_t SwContentTree::GetChildCount(const weld::TreeIter& rParent) const +{ + if (!m_xTreeView->iter_has_child(rParent)) + return 0; + + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rParent)); + + size_t nCount = 0; + auto nRefDepth = m_xTreeView->get_iter_depth(*xParent); + auto nActDepth = nRefDepth; + do + { + if (!m_xTreeView->iter_next(*xParent)) + xParent.reset(); + else + nActDepth = m_xTreeView->get_iter_depth(*xParent); + nCount++; + } while(xParent && nRefDepth < nActDepth); + + nCount--; + return nCount; +} + +std::unique_ptr<weld::TreeIter> SwContentTree::GetEntryAtAbsPos(size_t nAbsPos) const +{ + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xEntry)) + xEntry.reset(); + + while (nAbsPos && xEntry) + { + if (!m_xTreeView->iter_next(*xEntry)) + xEntry.reset(); + nAbsPos--; + } + return xEntry; +} + +void SwContentTree::Display( bool bActive ) +{ + // First read the selected entry to select it later again if necessary + // -> the user data here are no longer valid! + std::unique_ptr<weld::TreeIter> xOldSelEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xOldSelEntry.get())) + xOldSelEntry.reset(); + OUString sOldSelEntryId; + size_t nEntryRelPos = 0; // relative position to their parent + size_t nOldEntryCount = GetEntryCount(); + sal_Int32 nOldScrollPos = 0; + if (xOldSelEntry) + { + UpdateLastSelType(); + sOldSelEntryId = m_xTreeView->get_id(*xOldSelEntry); + nOldScrollPos = m_xTreeView->vadjustment_get_value(); + std::unique_ptr<weld::TreeIter> xParentEntry = m_xTreeView->make_iterator(xOldSelEntry.get()); + while (m_xTreeView->get_iter_depth(*xParentEntry)) + m_xTreeView->iter_parent(*xParentEntry); + if (m_xTreeView->get_iter_depth(*xOldSelEntry)) + nEntryRelPos = GetAbsPos(*xOldSelEntry) - GetAbsPos(*xParentEntry); + } + + clear(); + + if (!bActive) + { + m_aOverlayObjectDelayTimer.Stop(); + if (m_xOverlayObject && m_xOverlayObject->getOverlayManager()) + { + m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject); + m_xOverlayObject.reset(); + } + m_eState = State::HIDDEN; + } + else if (State::HIDDEN == m_eState) + m_eState = State::ACTIVE; + SwWrtShell* pShell = GetWrtShell(); + const bool bReadOnly = !pShell || pShell->GetView().GetDocShell()->IsReadOnly(); + if(bReadOnly != m_bIsLastReadOnly) + { + m_bIsLastReadOnly = bReadOnly; + bool bDisable = pShell == nullptr || bReadOnly; + SwNavigationPI* pNavi = GetParentWindow(); + pNavi->m_xContent6ToolBox->set_item_sensitive("chapterup", !bDisable); + pNavi->m_xContent6ToolBox->set_item_sensitive("chapterdown", !bDisable); + pNavi->m_xContent6ToolBox->set_item_sensitive("promote", !bDisable); + pNavi->m_xContent6ToolBox->set_item_sensitive("demote", !bDisable); + pNavi->m_xContent5ToolBox->set_item_sensitive("reminder", !bDisable); + } + + if (pShell) + { + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + std::unique_ptr<weld::TreeIter> xCntTypeEntry; + std::vector<std::unique_ptr<weld::TreeIter>> aNodesToExpand; + // all content navigation view + if(m_nRootType == ContentTypeId::UNKNOWN) + { + m_xTreeView->freeze(); + + for( ContentTypeId nCntType : o3tl::enumrange<ContentTypeId>() ) + { + std::unique_ptr<SwContentType>& rpContentT = bActive ? + m_aActiveContentArr[nCntType] : + m_aHiddenContentArr[nCntType]; + if(!rpContentT) + rpContentT.reset(new SwContentType(pShell, nCntType, m_nOutlineLevel )); + + OUString aImage(GetImageIdForContentTypeId(nCntType)); + bool bChOnDemand = 0 != rpContentT->GetMemberCount(); + + // In case of LOK, empty content types must be hidden in the contenttree + if (comphelper::LibreOfficeKit::isActive() && !bChOnDemand) + { + continue; + } + + OUString sId(weld::toId(rpContentT.get())); + insert(nullptr, rpContentT->GetName(), sId, bChOnDemand, xEntry.get()); + m_xTreeView->set_image(*xEntry, aImage); + + m_xTreeView->set_sensitive(*xEntry, bChOnDemand); + + if (nCntType == m_nLastSelType) + xCntTypeEntry = m_xTreeView->make_iterator(xEntry.get()); + + sal_Int32 nExpandOptions = (State::HIDDEN == m_eState) + ? m_nHiddenBlock + : m_nActiveBlock; + if (nExpandOptions & (1 << static_cast<int>(nCntType))) + { + // fill contents of to-be expanded entries while frozen + Expand(*xEntry, &aNodesToExpand); + m_xTreeView->set_children_on_demand(*xEntry, false); + } + } + + m_xTreeView->thaw(); + + // restore visual expanded tree state + for (const auto& rNode : aNodesToExpand) + m_xTreeView->expand_row(*rNode); + } + // root content navigation view + else + { + m_xTreeView->freeze(); + + std::unique_ptr<SwContentType>& rpRootContentT = bActive ? + m_aActiveContentArr[m_nRootType] : + m_aHiddenContentArr[m_nRootType]; + if(!rpRootContentT) + rpRootContentT.reset(new SwContentType(pShell, m_nRootType, m_nOutlineLevel )); + OUString aImage(GetImageIdForContentTypeId(m_nRootType)); + bool bChOnDemand(m_nRootType == ContentTypeId::OUTLINE || + m_nRootType == ContentTypeId::REGION || + m_nRootType == ContentTypeId::POSTIT); + OUString sId(weld::toId(rpRootContentT.get())); + insert(nullptr, rpRootContentT->GetName(), sId, bChOnDemand, xEntry.get()); + m_xTreeView->set_image(*xEntry, aImage); + + xCntTypeEntry = m_xTreeView->make_iterator(xEntry.get()); + + if (!bChOnDemand) + InsertContent(*xEntry); + else + { + // fill contents of to-be expanded entries while frozen + Expand(*xEntry, &aNodesToExpand); + m_xTreeView->set_children_on_demand(*xEntry, false); + } + + m_xTreeView->set_sensitive(*xEntry, m_xTreeView->iter_has_child(*xEntry)); + + m_xTreeView->thaw(); + + if (bChOnDemand) + { + // restore visual expanded tree state + for (const auto& rNode : aNodesToExpand) + m_xTreeView->expand_row(*rNode); + } + else + m_xTreeView->expand_row(*xEntry); + } + + // Reselect the old selected entry. If it is not available, select the entry at the old + // selected entry position unless that entry position is now a content type or is past the + // end of the member list then select the entry at the previous entry position. + if (xOldSelEntry) + { + std::unique_ptr<weld::TreeIter> xSelEntry = m_xTreeView->make_iterator(xCntTypeEntry.get()); + if (nEntryRelPos) + { + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(xCntTypeEntry.get())); + std::unique_ptr<weld::TreeIter> xTemp(m_xTreeView->make_iterator(xIter.get())); + sal_uLong nPos = 1; + bool bNext; + while ((bNext = m_xTreeView->iter_next(*xIter) && lcl_IsContent(*xIter, *m_xTreeView))) + { + if (m_xTreeView->get_id(*xIter) == sOldSelEntryId || nPos == nEntryRelPos) + { + m_xTreeView->copy_iterator(*xIter, *xSelEntry); + break; + } + m_xTreeView->copy_iterator(*xIter, *xTemp); // note previous entry + nPos++; + } + if (!bNext) + xSelEntry = std::move(xTemp); + } + // set_cursor unselects all entries, makes passed entry visible, and selects it + m_xTreeView->set_cursor(*xSelEntry); + Select(); + } + } + + if (!m_bIgnoreDocChange && GetEntryCount() == nOldEntryCount) + { + m_xTreeView->vadjustment_set_value(nOldScrollPos); + } +} + +void SwContentTree::clear() +{ + m_xTreeView->freeze(); + m_xTreeView->clear(); + m_nEntryCount = 0; + m_xTreeView->thaw(); +} + +bool SwContentTree::FillTransferData( TransferDataContainer& rTransfer, + sal_Int8& rDragMode ) +{ + bool bRet = false; + SwWrtShell* pWrtShell = GetWrtShell(); + OSL_ENSURE(pWrtShell, "no Shell!"); + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_cursor(xEntry.get()); + if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView) || !pWrtShell) + return false; + OUString sEntry; + assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry)); + + const ContentTypeId nActType = pCnt->GetParent()->GetType(); + OUString sUrl; + bool bOutline = false; + OUString sOutlineText; + switch( nActType ) + { + case ContentTypeId::OUTLINE: + { + const SwOutlineNodes::size_type nPos = static_cast<SwOutlineContent*>(pCnt)->GetOutlinePos(); + OSL_ENSURE(nPos < pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(), + "outlinecnt changed"); + + // make sure outline may actually be copied + if( pWrtShell->IsOutlineCopyable( nPos ) ) + { + const SwNumRule* pOutlRule = pWrtShell->GetOutlineNumRule(); + const SwTextNode* pTextNd = + pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNode(nPos); + if (pTextNd && pOutlRule && pTextNd->IsNumbered(pWrtShell->GetLayout())) + { + SwNumberTree::tNumberVector aNumVector = + pTextNd->GetNumberVector(pWrtShell->GetLayout()); + for( int nLevel = 0; + nLevel <= pTextNd->GetActualListLevel(); + nLevel++ ) + { + const SwNumberTree::tSwNumTreeNumber nVal = aNumVector[nLevel] + 1; + sEntry += OUString::number( nVal - pOutlRule->Get(nLevel).GetStart() ) + "."; + } + } + sEntry += pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout(), false); + sOutlineText = pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout()); + m_bIsOutlineMoveable = static_cast<SwOutlineContent*>(pCnt)->IsMoveable(); + bOutline = true; + } + } + break; + case ContentTypeId::POSTIT: + case ContentTypeId::INDEX: + case ContentTypeId::REFERENCE : + case ContentTypeId::TEXTFIELD: + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + // cannot be inserted, neither as URL nor as section + break; + case ContentTypeId::URLFIELD: + sUrl = static_cast<SwURLFieldContent*>(pCnt)->GetURL(); + [[fallthrough]]; + case ContentTypeId::OLE: + case ContentTypeId::GRAPHIC: + if(GetParentWindow()->GetRegionDropMode() != RegionMode::NONE) + break; + else + rDragMode &= ~( DND_ACTION_MOVE | DND_ACTION_LINK ); + [[fallthrough]]; + default: + sEntry = m_xTreeView->get_text(*xEntry); + } + + if(!sEntry.isEmpty()) + { + const SwDocShell* pDocShell = pWrtShell->GetView().GetDocShell(); + if(sUrl.isEmpty()) + { + if(pDocShell->HasName()) + { + SfxMedium* pMedium = pDocShell->GetMedium(); + sUrl = pMedium->GetURLObject().GetURLNoMark(); + // only if a primarily link shall be integrated. + bRet = true; + } + else if ( nActType == ContentTypeId::REGION || nActType == ContentTypeId::BOOKMARK ) + { + // For field and bookmarks a link is also allowed + // without a filename into its own document. + bRet = true; + } + else if (State::CONSTANT == m_eState && + ( !::GetActiveView() || + m_pActiveShell != ::GetActiveView()->GetWrtShellPtr())) + { + // Urls of inactive views cannot dragged without + // file names, also. + bRet = false; + } + else + { + bRet = GetParentWindow()->GetRegionDropMode() == RegionMode::NONE; + rDragMode = DND_ACTION_MOVE; + } + + const OUString& rToken = pCnt->GetParent()->GetTypeToken(); + sUrl += "#" + sEntry; + if(!rToken.isEmpty()) + { + sUrl += OUStringChar(cMarkSeparator) + rToken; + } + } + else + bRet = true; + + if( bRet ) + { + // In Outlines of heading text must match + // the real number into the description. + if(bOutline) + sEntry = sOutlineText; + + { + NaviContentBookmark aBmk( sUrl, sEntry, + GetParentWindow()->GetRegionDropMode(), + pDocShell); + aBmk.Copy( rTransfer ); + } + + // An INetBookmark must a be delivered to foreign DocShells + if( pDocShell->HasName() ) + { + INetBookmark aBkmk( sUrl, sEntry ); + rTransfer.CopyINetBookmark( aBkmk ); + } + } + } + return bRet; +} + +void SwContentTree::ToggleToRoot() +{ + if(!m_bIsRoot) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_cursor(xEntry.get()); + if (bEntry) + { + const SwContentType* pCntType; + if (lcl_IsContentType(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry)); + } + else + { + assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + pCntType = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetParent(); + } + m_nRootType = pCntType->GetType(); + m_bIsRoot = true; + if (m_nRootType == ContentTypeId::OUTLINE || m_nRootType == ContentTypeId::DRAWOBJECT) + { + m_xTreeView->set_selection_mode(SelectionMode::Multiple); + } + Display(State::HIDDEN != m_eState); + } + } + else + { + m_xTreeView->set_selection_mode(SelectionMode::Single); + m_nLastSelType = m_nRootType; + m_nRootType = ContentTypeId::UNKNOWN; + m_bIsRoot = false; + // Other content type member data could have changed while in root view. Fill the content + // member lists excluding the toggled from root content which should already have the most + // recent data. + if (State::HIDDEN != m_eState) + { + for (ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + if (i != m_nLastSelType && m_aActiveContentArr[i]) + m_aActiveContentArr[i]->FillMemberList(); + } + } + Display(State::HIDDEN != m_eState); + } + m_pConfig->SetRootType( m_nRootType ); + weld::Toolbar* pBox = GetParentWindow()->m_xContent5ToolBox.get(); + pBox->set_item_active("root", m_bIsRoot); +} + +bool SwContentTree::HasContentChanged() +{ + bool bContentChanged = false; + +// - Run through the local array and the Treelistbox in parallel. +// - Are the records not expanded, they are discarded only in the array +// and the content type will be set as the new UserData. +// - Is the root mode is active only this will be updated. + +// Valid for the displayed content types is: +// the Memberlist will be erased and the membercount will be updated +// If content will be checked, the memberlists will be replenished +// at the same time. Once a difference occurs it will be only replenished +// no longer checked. Finally, the box is filled again. + + if (State::HIDDEN == m_eState) + { + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + if(m_aActiveContentArr[i]) + m_aActiveContentArr[i]->Invalidate(); + } + return false; + } + + // single content type navigation view + if(m_bIsRoot) + { + std::unique_ptr<weld::TreeIter> xRootEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xRootEntry)) + return true; + + assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xRootEntry)))); + const ContentTypeId nType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xRootEntry))->GetType(); + SwContentType* pArrType = m_aActiveContentArr[nType].get(); + assert(weld::toId(pArrType) == m_xTreeView->get_id(*xRootEntry)); + if (!pArrType) + return true; + + pArrType->FillMemberList(&bContentChanged); + if (bContentChanged) + return true; + + // FillMemberList tests if member count in old member array equals member count in new + // member array. Test here for member count difference between array and tree. + const size_t nChildCount = GetChildCount(*xRootEntry); + if (nChildCount != pArrType->GetMemberCount()) + return true; + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(xRootEntry.get())); + for (size_t j = 0; j < nChildCount; ++j) + { + if (!m_xTreeView->iter_next(*xEntry)) + { + SAL_WARN("sw.ui", "unexpected missing entry"); + return true; + } + + // FillMemberList clears the content type member list and refills with new data. + // Treeview entry user data is set here to the string representation of the pointer to + // the member data in the array. The Display function will clear and recreate the + // treeview from the content type member arrays if content change is detected. + const SwContent* pCnt = pArrType->GetMember(j); + + if (pCnt->IsInvisible() != m_xTreeView->get_sensitive(*xEntry, 0)) + return true; + + OUString sEntryText = m_xTreeView->get_text(*xEntry); + if (sEntryText != pCnt->GetName() && + !(sEntryText == m_sSpace && pCnt->GetName().isEmpty())) + { + return true; + } + + // Set_id needs to be done here because FillMemberList clears the content type member + // list and refills with new data making the previously set id invalid. If there is no + // content change detected the Display function will not be called and the tree entry + // user data will not be set to the new content member pointer address. + OUString sSubId(weld::toId(pCnt)); + m_xTreeView->set_id(*xEntry, sSubId); + } + } + // all content types navigation view + else + { + // Fill member list for each content type and check for content change. If content change + // is detected only fill member lists for remaining content types. The Display function + // will clear and recreate the treeview from the content type member arrays if content has + // changed. + + if (comphelper::LibreOfficeKit::isActive()) + { + // In case of LOK, empty contentTypes are hidden, even in all content view + // so it is not enough to check only the m_xTreeView. + bool bCountChanged = false; + bool bHasContentChanged = false; + for (ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + if (m_aActiveContentArr[i]) + { + auto nLastTMCount = m_aActiveContentArr[i]->GetMemberCount(); + if (i == ContentTypeId::OUTLINE) // this is required for checking if header level is changed + m_aActiveContentArr[i]->FillMemberList(&bHasContentChanged); + else + m_aActiveContentArr[i]->FillMemberList(); + // If the member count of a type is changed, then the content is surely changed + if (m_aActiveContentArr[i]->GetMemberCount() != nLastTMCount) + bCountChanged = true; + if (bHasContentChanged) + bContentChanged = true; + } + } + if (bCountChanged || bContentChanged) + return true; + } + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + + // lambda function to find the next content type entry + auto lcl_nextContentTypeEntry = [this, &xEntry](){ + while (m_xTreeView->get_iter_depth(*xEntry)) + m_xTreeView->iter_parent(*xEntry); + return m_xTreeView->iter_next_sibling(*xEntry); + }; + + for (bool bEntry = m_xTreeView->get_iter_first(*xEntry); bEntry; + bEntry = lcl_nextContentTypeEntry()) + { + assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry)); + const size_t nCntCount = pCntType->GetMemberCount(); + const ContentTypeId nType = pCntType->GetType(); + SwContentType* pArrType = m_aActiveContentArr[nType].get(); + assert(weld::toId(pArrType) == m_xTreeView->get_id(*xEntry)); + + if (!pArrType) + { + bContentChanged = true; + continue; + } + + // all content type member lists must be filled! + if (bContentChanged) + { + // If content change has already been detected there is no need to detect + // other content change so no argument is supplied here to FillMemberList. + pArrType->FillMemberList(); + continue; + } + + pArrType->FillMemberList(&bContentChanged); + if (bContentChanged) + continue; + + // does entry have children? + if (m_xTreeView->get_row_expanded(*xEntry)) + { + const size_t nChildCount = GetChildCount(*xEntry); + if(nChildCount != pArrType->GetMemberCount()) + { + bContentChanged = true; + continue; + } + + for(size_t j = 0; j < nChildCount; ++j) + { + if (!m_xTreeView->iter_next(*xEntry)) + { + SAL_WARN("sw.ui", "unexpected missing entry"); + bContentChanged = true; + break; + } + + const SwContent* pCnt = pArrType->GetMember(j); + + if (pCnt->IsInvisible() != m_xTreeView->get_sensitive(*xEntry, 0)) + { + bContentChanged = true; + break; + } + + OUString sEntryText = m_xTreeView->get_text(*xEntry); + if( sEntryText != pCnt->GetName() && + !(sEntryText == m_sSpace && pCnt->GetName().isEmpty())) + { + bContentChanged = true; + break; + } + + // See comment above in single content type navigation view block for why the + // following is done here. + OUString sSubId(weld::toId(pCnt)); + m_xTreeView->set_id(*xEntry, sSubId); + } + } + // not expanded and has children + else if (m_xTreeView->iter_has_child(*xEntry)) + { + bool bRemoveChildren = false; + const size_t nOldChildCount = GetChildCount(*xEntry); + const size_t nNewChildCount = pArrType->GetMemberCount(); + if (nOldChildCount != nNewChildCount) + { + bRemoveChildren = true; + } + else + { + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xEntry.get())); + (void)m_xTreeView->iter_children(*xChild); + for (size_t j = 0; j < nOldChildCount; ++j) + { + const SwContent* pCnt = pArrType->GetMember(j); + OUString sSubId(weld::toId(pCnt)); + m_xTreeView->set_id(*xChild, sSubId); + OUString sEntryText = m_xTreeView->get_text(*xChild); + if( sEntryText != pCnt->GetName() && + !(sEntryText == m_sSpace && pCnt->GetName().isEmpty())) + { + bRemoveChildren = true; + } + (void)m_xTreeView->iter_next(*xChild); + } + } + if (bRemoveChildren) + { + std::unique_ptr<weld::TreeIter> xRemove(m_xTreeView->make_iterator(xEntry.get())); + while (m_xTreeView->iter_children(*xRemove)) + { + remove(*xRemove); + m_xTreeView->copy_iterator(*xEntry, *xRemove); + } + m_xTreeView->set_children_on_demand(*xEntry, nNewChildCount != 0); + } + } + else if((nCntCount != 0) + != (pArrType->GetMemberCount()!=0)) + { + bContentChanged = true; + continue; + } + } + } + + return bContentChanged; +} + +void SwContentTree::UpdateLastSelType() +{ + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get())) + { + while (m_xTreeView->get_iter_depth(*xEntry)) + m_xTreeView->iter_parent(*xEntry); + void* pId = weld::fromId<void*>(m_xTreeView->get_id(*xEntry)); + if (pId && lcl_IsContentType(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pId))); + m_nLastSelType = static_cast<SwContentType*>(pId)->GetType(); + } + } +} + +void SwContentTree::FindActiveTypeAndRemoveUserData() +{ + UpdateLastSelType(); + + // If clear is called by TimerUpdate: + // Only for root can the validity of the UserData be guaranteed. + m_xTreeView->all_foreach([this](weld::TreeIter& rEntry){ + m_xTreeView->set_id(rEntry, ""); + return false; + }); +} + +void SwContentTree::SetHiddenShell(SwWrtShell* pSh) +{ + m_pHiddenShell = pSh; + m_eState = State::HIDDEN; + FindActiveTypeAndRemoveUserData(); + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + m_aHiddenContentArr[i].reset(); + } + Display(false); + + GetParentWindow()->UpdateListBox(); +} + +void SwContentTree::SetActiveShell(SwWrtShell* pSh) +{ + bool bClear = m_pActiveShell != pSh; + if (State::ACTIVE == m_eState && bClear) + { + EndListeningAll(); + m_pActiveShell = pSh; + FindActiveTypeAndRemoveUserData(); + clear(); + } + else if (State::CONSTANT == m_eState) + { + EndListeningAll(); + m_pActiveShell = pSh; + m_eState = State::ACTIVE; + bClear = true; + } + + // tdf#148432 in LTR UI override the navigator treeview direction based on + // the first page directionality + if (m_pActiveShell && !AllSettings::GetLayoutRTL()) + { + const SwPageDesc& rDesc = m_pActiveShell->GetPageDesc(0); + const SvxFrameDirectionItem& rFrameDir = rDesc.GetMaster().GetFrameDir(); + m_xTreeView->set_direction(rFrameDir.GetValue() == SvxFrameDirection::Horizontal_RL_TB); + } + + // Only if it is the active view, the array will be deleted and + // the screen filled new. + if (State::ACTIVE == m_eState && bClear) + { + if (m_pActiveShell) + StartListening(*m_pActiveShell->GetView().GetDocShell()); + FindActiveTypeAndRemoveUserData(); + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + m_aActiveContentArr[i].reset(); + } + Display(true); + } +} + +void SwContentTree::SetConstantShell(SwWrtShell* pSh) +{ + EndListeningAll(); + m_pActiveShell = pSh; + m_eState = State::CONSTANT; + StartListening(*m_pActiveShell->GetView().GetDocShell()); + FindActiveTypeAndRemoveUserData(); + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + m_aActiveContentArr[i].reset(); + } + Display(true); +} + +void SwContentTree::Notify(SfxBroadcaster & rBC, SfxHint const& rHint) +{ + SfxViewEventHint const*const pVEHint(dynamic_cast<SfxViewEventHint const*>(&rHint)); + SwXTextView* pDyingShell = nullptr; + if (m_pActiveShell && pVEHint && pVEHint->GetEventName() == "OnViewClosed") + pDyingShell = dynamic_cast<SwXTextView*>(pVEHint->GetController().get()); + if (pDyingShell && pDyingShell->GetView() == &m_pActiveShell->GetView()) + { + SetActiveShell(nullptr); // our view is dying, clear our pointers to it + } + else + { + SfxListener::Notify(rBC, rHint); + } + switch (rHint.GetId()) + { + case SfxHintId::SwNavigatorUpdateTracking: + UpdateTracking(); + break; + case SfxHintId::SwNavigatorSelectOutlinesWithSelections: + { + if (m_nRootType == ContentTypeId::OUTLINE) + { + SelectOutlinesWithSelection(); + // make first selected entry visible + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (xEntry && m_xTreeView->get_selected(xEntry.get())) + m_xTreeView->scroll_to_row(*xEntry); + } + else if (m_nRootType == ContentTypeId::UNKNOWN) + m_xTreeView->unselect_all(); + break; + } + case SfxHintId::DocChanged: + OverlayObject(); + if (!m_bIgnoreDocChange) + { + m_bDocHasChanged = true; + TimerUpdate(&m_aUpdTimer); + } + break; + case SfxHintId::ModeChanged: + if (SwWrtShell* pShell = GetWrtShell()) + { + const bool bReadOnly = pShell->GetView().GetDocShell()->IsReadOnly(); + if (bReadOnly != m_bIsLastReadOnly) + { + m_bIsLastReadOnly = bReadOnly; + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xEntry.get())) + { + m_xTreeView->select(*xEntry); + Select(); + } + else + m_xTreeView->unselect_all(); + } + } + break; + default: + break; + } +} + +// Handler for outline entry up/down left/right movement +void SwContentTree::ExecCommand(std::u16string_view rCmd, bool bOutlineWithChildren) +{ + if (m_xTreeView->count_selected_rows() == 0) + return; + + const bool bUp = rCmd == u"chapterup"; + const bool bUpDown = bUp || rCmd == u"chapterdown"; + const bool bLeft = rCmd == u"promote"; + const bool bLeftRight = bLeft || rCmd == u"demote"; + if (!bUpDown && !bLeftRight) + return; + if (GetWrtShell()->GetView().GetDocShell()->IsReadOnly() || + (State::ACTIVE != m_eState && + (State::CONSTANT != m_eState || m_pActiveShell != GetParentWindow()->GetCreateView()->GetWrtShellPtr()))) + { + return; + } + + SwWrtShell *const pShell = GetWrtShell(); + + const SwNodes& rNodes = pShell->GetNodes(); + const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds(); + const SwOutlineNodes::size_type nOutlineNdsSize = rOutlineNodes.size(); + + std::vector<SwTextNode*> selectedOutlineNodes; + std::vector<std::unique_ptr<weld::TreeIter>> selected; + + m_xTreeView->selected_foreach([&](weld::TreeIter& rEntry){ + // it's possible to select the root node too which is a really bad idea + if (lcl_IsContentType(rEntry, *m_xTreeView)) + return false; + // filter out children of selected parents so they don't get promoted + // or moved twice (except if there is Ctrl modifier, since in that + // case children are re-parented) + if ((bLeftRight || bOutlineWithChildren) && !selected.empty()) + { + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry)); + for (bool bParent = m_xTreeView->iter_parent(*xParent); bParent; bParent = m_xTreeView->iter_parent(*xParent)) + { + if (m_xTreeView->iter_compare(*selected.back(), *xParent) == 0) + { + return false; + } + } + } + selected.emplace_back(m_xTreeView->make_iterator(&rEntry)); + + // Use the outline node position in the SwOutlineNodes array. Bad things + // happen if the tree entry position is used and it doesn't match the node position + // in SwOutlineNodes, which is usually the case for outline nodes in frames. + const SwOutlineNodes::size_type nPos + = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rEntry))->GetOutlinePos(); + if (nPos < nOutlineNdsSize) + { + SwNode* pNode = rNodes.GetOutLineNds()[ nPos ]; + if (pNode) + { + selectedOutlineNodes.push_back(pNode->GetTextNode()); + } + } + return false; + }); + + if (!selected.size()) + return; + + if (bUpDown && !bUp) + { // to move down, start at the end! + std::reverse(selected.begin(), selected.end()); + } + + m_bIgnoreDocChange = true; + + SwOutlineNodes::size_type nActPos; + bool bStartedAction = false; + + MakeAllOutlineContentTemporarilyVisible a(GetWrtShell()->GetDoc()); + + // get first regular document content node outline node position in outline nodes array + SwOutlineNodes::size_type nFirstRegularDocContentOutlineNodePos = SwOutlineNodes::npos; + SwNodeOffset nEndOfExtrasIndex = rNodes.GetEndOfExtras().GetIndex(); + for (SwOutlineNodes::size_type nPos = 0; nPos < nOutlineNdsSize; nPos++) + { + if (rOutlineNodes[nPos]->GetIndex() > nEndOfExtrasIndex) + { + nFirstRegularDocContentOutlineNodePos = nPos; + break; + } + } + + for (auto const& pCurrentEntry : selected) + { + nActPos = weld::fromId<SwOutlineContent*>( + m_xTreeView->get_id(*pCurrentEntry))->GetOutlinePos(); + + // outline nodes in frames and tables are not up/down moveable + if (nActPos == SwOutlineNodes::npos || + (bUpDown && (!pShell->IsOutlineMovable(nActPos) || + nFirstRegularDocContentOutlineNodePos == SwOutlineNodes::npos))) + { + continue; + } + + if (!bStartedAction) + { + pShell->StartAllAction(); + pShell->StartUndo(bLeftRight ? SwUndoId::OUTLINE_LR : SwUndoId::OUTLINE_UD); + bStartedAction = true; + } + + pShell->GotoOutline( nActPos); // If text selection != box selection + pShell->Push(); + + if (bUpDown) + { + // move outline position up/down (outline position promote/demote) + SwOutlineNodes::difference_type nDir = bUp ? -1 : 1; + if ((nDir == -1 && nActPos > 0) || (nDir == 1 && nActPos < nOutlineNdsSize - 1)) + { + // make outline selection for use by MoveOutlinePara + pShell->MakeOutlineSel(nActPos, nActPos, bOutlineWithChildren); + + int nActPosOutlineLevel = + rOutlineNodes[nActPos]->GetTextNode()->GetAttrOutlineLevel(); + SwOutlineNodes::size_type nPos = nActPos; + if (!bUp) + { + // move down + int nPosOutlineLevel = -1; + while (++nPos < nOutlineNdsSize) + { + nPosOutlineLevel = + rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel(); + // discontinue if moving out of parent or equal level is found + if (nPosOutlineLevel <= nActPosOutlineLevel) + break; + // count the children of the node when they are not included in the move + if (!bOutlineWithChildren) + nDir++; + } + if (nPosOutlineLevel >= nActPosOutlineLevel) + { + // move past children + while (++nPos < nOutlineNdsSize) + { + nPosOutlineLevel = + rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel(); + // discontinue if moving out of parent or equal level is found + if (nPosOutlineLevel <= nActPosOutlineLevel) + break; + nDir++; + } + } + } + else + { + // move up + while (nPos && --nPos >= nFirstRegularDocContentOutlineNodePos) + { + int nPosOutlineLevel = + rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel(); + // discontinue if equal level is found + if (nPosOutlineLevel == nActPosOutlineLevel) + break; + // discontinue if moving out of parent + if (nPosOutlineLevel < nActPosOutlineLevel) + { + // Required for expected chapter placement when the chapter being moved + // up has an outline level less than the outline level of chapters it + // is being moved above and then encounters a chapter with an outline + // level that is greater before reaching a chapter with the same + // outline level as itself. + if (nDir < -1) + nDir++; + break; + } + nDir--; + } + } + pShell->MoveOutlinePara(nDir); + } + pShell->ClearMark(); + } + else + { + // move outline left/right (outline level promote/demote) + if (!pShell->IsProtectedOutlinePara()) + { + bool bAllow = true; + const SwOutlineNodes& rOutlNds = pShell->GetDoc()->GetNodes().GetOutLineNds(); + const int nActLevel = rOutlNds[nActPos]->GetTextNode()->GetAttrOutlineLevel(); + if (!bLeft) + { + // disallow if any outline node to demote will exceed MAXLEVEL + SwOutlineNodes::size_type nPos = nActPos; + do + { + int nLevel = rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel(); + if (nLevel == MAXLEVEL) + { + bAllow = false; + break; + } + } while (bOutlineWithChildren && ++nPos < rOutlNds.size() && + rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel() > nActLevel); + } + else + { + // disallow if trying to promote outline of level 1 + if (nActLevel == 1) + bAllow = false; + } + if (bAllow) + { + SwOutlineNodes::size_type nPos = nActPos; + do + { + pShell->SwCursorShell::GotoOutline(nPos); + pShell->OutlineUpDown(bLeft ? -1 : 1); + } while (bOutlineWithChildren && ++nPos < rOutlNds.size() && + rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel() > nActLevel); + } + } + } + + pShell->Pop(SwCursorShell::PopMode::DeleteCurrent); // Cursor is now back at the current heading. + } + + if (bStartedAction) + { + pShell->EndUndo(); + pShell->EndAllAction(); + if (m_aActiveContentArr[ContentTypeId::OUTLINE]) + m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate(); + + // tdf#143547 LO Writer: navigator should stand still on promoting and demoting + // In addition to m_bIgnoreDocChange being true, selections are cleared before the Display + // call. Either of these conditions disable restore of scroll position happening in the + // Display function so it needs to be done here. + auto nOldScrollPos = m_xTreeView->vadjustment_get_value(); + + // clear all selections to prevent the Display function from trying to reselect selected entries + m_xTreeView->unselect_all(); + Display(true); + m_xTreeView->vadjustment_set_value(nOldScrollPos); + + if (m_bIsRoot) + { + // reselect entries, do this only when in outline content navigation mode + const SwOutlineNodes& rOutlineNds = pShell->GetNodes().GetOutLineNds(); + for (SwTextNode* pNode : selectedOutlineNodes) + { + m_xTreeView->all_foreach([this, &rOutlineNds, pNode](weld::TreeIter& rEntry){ + if (lcl_IsContentType(rEntry, *m_xTreeView)) + return false; + SwOutlineNodes::size_type nPos = weld::fromId<SwOutlineContent*>( + m_xTreeView->get_id(rEntry))->GetOutlinePos(); + if (pNode == rOutlineNds[nPos]->GetTextNode()) + { + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry)); + if (m_xTreeView->iter_parent(*xParent) + && !m_xTreeView->get_row_expanded(*xParent)) + { + m_xTreeView->expand_row(*xParent); + } + m_xTreeView->select(rEntry); + return true; + } + return false; + }); + } + } + else + { + m_pActiveShell->GetView().GetEditWin().GrabFocus(); + m_bIgnoreDocChange = false; + UpdateTracking(); + grab_focus(); + } + } + m_bIgnoreDocChange = false; +} + +void SwContentTree::ShowTree() +{ + m_xTreeView->show(); + m_aUpdTimer.Start(); +} + +void SwContentTree::HideTree() +{ + // folded together will not be idled + m_aUpdTimer.Stop(); + m_xTreeView->hide(); +} + +static void lcl_SelectByContentTypeAndAddress(SwContentTree* pThis, weld::TreeView& rContentTree, + ContentTypeId nType, const void* ptr) +{ + if (!ptr) + { + rContentTree.set_cursor(-1); + pThis->Select(); + return; + } + + // find content type entry + std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator()); + + bool bFoundEntry = rContentTree.get_iter_first(*xIter); + while (bFoundEntry) + { + void* pUserData = weld::fromId<void*>(rContentTree.get_id(*xIter)); + assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData))); + if (nType == static_cast<SwContentType*>(pUserData)->GetType()) + break; + bFoundEntry = rContentTree.iter_next_sibling(*xIter); + } + + if (!bFoundEntry) + { + rContentTree.set_cursor(-1); + pThis->Select(); + return; + } + + // assure content type entry is expanded + rContentTree.expand_row(*xIter); + + // find content type content entry and select it + const void* p = nullptr; + while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree)) + { + void* pUserData = weld::fromId<void*>(rContentTree.get_id(*xIter)); + switch( nType ) + { + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + { + assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData))); + SwTextFootnoteContent* pCnt = static_cast<SwTextFootnoteContent*>(pUserData); + p = pCnt->GetTextFootnote(); + break; + } + case ContentTypeId::URLFIELD: + { + assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pUserData))); + SwURLFieldContent* pCnt = static_cast<SwURLFieldContent*>(pUserData); + p = static_cast<const SwTextAttr*>(pCnt->GetINetAttr()); + break; + } + case ContentTypeId::TEXTFIELD: + { + assert(dynamic_cast<SwTextFieldContent*>(static_cast<SwTypeNumber*>(pUserData))); + SwTextFieldContent* pCnt = static_cast<SwTextFieldContent*>(pUserData); + p = pCnt->GetFormatField()->GetField(); + break; + } + case ContentTypeId::POSTIT: + { + assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData))); + SwPostItContent* pCnt = static_cast<SwPostItContent*>(pUserData); + p = pCnt->GetPostIt()->GetField(); + break; + } + default: + break; + } + if (ptr == p) + { + // get first selected for comparison + std::unique_ptr<weld::TreeIter> xFirstSelected(rContentTree.make_iterator()); + if (!rContentTree.get_selected(xFirstSelected.get())) + xFirstSelected.reset(); + if (rContentTree.count_selected_rows() != 1 || !xFirstSelected || + rContentTree.iter_compare(*xIter, *xFirstSelected) != 0) + { + // unselect all entries and make passed entry visible and selected + rContentTree.set_cursor(*xIter); + pThis->Select(); + } + return; + } + } + + rContentTree.set_cursor(-1); + pThis->Select(); + return; +} + +static void lcl_SelectByContentTypeAndName(SwContentTree* pThis, weld::TreeView& rContentTree, + std::u16string_view rContentTypeName, std::u16string_view rName) +{ + if (rName.empty()) + return; + + // find content type entry + std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator()); + bool bFoundEntry = rContentTree.get_iter_first(*xIter); + while (bFoundEntry && rContentTypeName != rContentTree.get_text(*xIter)) + bFoundEntry = rContentTree.iter_next_sibling(*xIter); + // find content type content entry and select it + if (!bFoundEntry) + return; + + rContentTree.expand_row(*xIter); // assure content type entry is expanded + while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree)) + { + if (rName == rContentTree.get_text(*xIter)) + { + // get first selected for comparison + std::unique_ptr<weld::TreeIter> xFirstSelected(rContentTree.make_iterator()); + if (!rContentTree.get_selected(xFirstSelected.get())) + xFirstSelected.reset(); + if (rContentTree.count_selected_rows() != 1 || !xFirstSelected || + rContentTree.iter_compare(*xIter, *xFirstSelected) != 0) + { + // unselect all entries and make passed entry visible and selected + rContentTree.set_cursor(*xIter); + pThis->Select(); + } + break; + } + } +} + +static void lcl_SelectDrawObjectByName(weld::TreeView& rContentTree, std::u16string_view rName) +{ + if (rName.empty()) + return; + + // find content type entry + std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator()); + bool bFoundEntry = rContentTree.get_iter_first(*xIter); + while (bFoundEntry && SwResId(STR_CONTENT_TYPE_DRAWOBJECT) != rContentTree.get_text(*xIter)) + bFoundEntry = rContentTree.iter_next_sibling(*xIter); + // find content type content entry and select it + if (bFoundEntry) + { + rContentTree.expand_row(*xIter); // assure content type entry is expanded + while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree)) + { + if (rName == rContentTree.get_text(*xIter)) + { + if (!rContentTree.is_selected(*xIter)) + { + rContentTree.select(*xIter); + rContentTree.scroll_to_row(*xIter); + } + break; + } + } + } +} + +/** No idle with focus or while dragging */ +IMPL_LINK_NOARG(SwContentTree, TimerUpdate, Timer *, void) +{ + // No need to update if content tree is not visible + if (!m_xTreeView->is_visible()) + return; + + // No update while focus is not in document. + // No update while drag and drop. + // Query view because the Navigator is cleared too late. + SwView* pView = GetParentWindow()->GetCreateView(); + + SwWrtShell* pActShell = pView ? pView->GetWrtShellPtr() : nullptr; + if(pActShell && pActShell->GetWin() && + (pActShell->GetWin()->HasFocus() || m_bDocHasChanged || m_bViewHasChanged) && + !IsInDrag() && !pActShell->ActionPend()) + { + if (m_bDocHasChanged || m_bViewHasChanged) + { + if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell)) + { + SetActiveShell(pActShell); + GetParentWindow()->UpdateListBox(); + } + if (State::ACTIVE == m_eState && pActShell != GetWrtShell()) + { + SetActiveShell(pActShell); + } + else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) && + HasContentChanged()) + { + FindActiveTypeAndRemoveUserData(); + Display(true); + } + } + UpdateTracking(); + m_bIsIdleClear = false; + m_bDocHasChanged = false; + m_bViewHasChanged = false; + } + else if (!pView && State::ACTIVE == m_eState && !m_bIsIdleClear) // this block seems never to be entered + { + if(m_pActiveShell) + { + SetActiveShell(nullptr); + } + clear(); + m_bIsIdleClear = true; + } +} + +void SwContentTree::UpdateTracking() +{ + if (State::HIDDEN == m_eState || !m_pActiveShell) + return; + + // only when treeview or treeview context menu does not have focus + if (m_xTreeView->has_focus() || m_xTreeView->has_child_focus()) + return; + + // m_bIgnoreDocChange is set on delete and outline visibility toggle + if (m_bIgnoreDocChange) + { + m_bIgnoreDocChange = false; + return; + } + + // bTrack is used to disallow tracking after jumping to an outline until the outline position + // that was jumped to is no longer the current outline position. + bool bTrack = true; + if (m_nLastGotoContentWasOutlinePos != SwOutlineNodes::npos) + { + if (m_pActiveShell->GetOutlinePos() == m_nLastGotoContentWasOutlinePos) + bTrack = false; + else + m_nLastGotoContentWasOutlinePos = SwOutlineNodes::npos; + } + + if (bTrack) + { + // graphic, frame, and ole + if (m_pActiveShell->GetSelectionType() & + (SelectionType::Graphic | SelectionType::Frame | SelectionType::Ole)) + { + OUString aContentTypeName; + if (m_pActiveShell->GetSelectionType() == SelectionType::Graphic && + !(m_bIsRoot && m_nRootType != ContentTypeId::GRAPHIC)) + { + if (!mTrackContentType[ContentTypeId::GRAPHIC]) return; + aContentTypeName = SwResId(STR_CONTENT_TYPE_GRAPHIC); + } + else if (m_pActiveShell->GetSelectionType() == SelectionType::Frame && + !(m_bIsRoot && m_nRootType != ContentTypeId::FRAME)) + { + if (!mTrackContentType[ContentTypeId::FRAME]) return; + aContentTypeName = SwResId(STR_CONTENT_TYPE_FRAME); + } + else if (m_pActiveShell->GetSelectionType() == SelectionType::Ole && + !(m_bIsRoot && m_nRootType != ContentTypeId::OLE)) + { + if (!mTrackContentType[ContentTypeId::OLE]) return; + aContentTypeName = SwResId(STR_CONTENT_TYPE_OLE); + } + if (!aContentTypeName.isEmpty()) + { + OUString aName(m_pActiveShell->GetFlyName()); + lcl_SelectByContentTypeAndName(this, *m_xTreeView, aContentTypeName, aName); + return; + } + } + // drawing + if ((m_pActiveShell->GetSelectionType() & (SelectionType::DrawObject | + SelectionType::DrawObjectEditMode | + SelectionType::DbForm)) && + !(m_bIsRoot && m_nRootType != ContentTypeId::DRAWOBJECT)) + { + if (mTrackContentType[ContentTypeId::DRAWOBJECT]) + { + // Multiple selection is possible when in root content navigation view so unselect all + // selected entries before reselecting. This causes a bit of an annoyance when the treeview + // scroll bar is used and focus is in the document by causing the last selected entry to + // scroll back into view. + if (m_bIsRoot) + m_xTreeView->unselect_all(); + SdrView* pSdrView = m_pActiveShell->GetDrawView(); + if (pSdrView) + { + for (size_t nIdx(0); nIdx < pSdrView->GetMarkedObjectCount(); nIdx++) + { + SdrObject* pSelected = pSdrView->GetMarkedObjectByIndex(nIdx); + OUString aName(pSelected->GetName()); + if (!aName.isEmpty()) + lcl_SelectDrawObjectByName(*m_xTreeView, aName); + } + } + else + { + // clear treeview selections + m_xTreeView->unselect_all(); + } + Select(); + } + return; + } + // footnotes and endnotes + if (SwContentAtPos aContentAtPos(IsAttrAtPos::Ftn); + m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos) + && aContentAtPos.pFndTextAttr && + !(m_bIsRoot && (m_nRootType != ContentTypeId::FOOTNOTE && + m_nRootType != ContentTypeId::ENDNOTE))) + { + if (!aContentAtPos.pFndTextAttr->GetFootnote().IsEndNote()) + { + if (mTrackContentType[ContentTypeId::FOOTNOTE]) + lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::FOOTNOTE, + aContentAtPos.pFndTextAttr); + } + else if (mTrackContentType[ContentTypeId::ENDNOTE]) + lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::ENDNOTE, + aContentAtPos.pFndTextAttr); + return; + } + // bookmarks - track first bookmark at cursor + if (mTrackContentType[ContentTypeId::BOOKMARK] && + (m_pActiveShell->GetSelectionType() & SelectionType::Text)) + { + SwPaM* pCursor = m_pActiveShell->GetCursor(); + IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess(); + IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin(); + if (pCursor && ppBookmark != pMarkAccess->getBookmarksEnd() && + !(m_bIsRoot && m_nRootType != ContentTypeId::BOOKMARK)) + { + OUString sBookmarkName; + SwPosition* pCursorPoint = pCursor->GetPoint(); + while (ppBookmark != pMarkAccess->getBookmarksEnd()) + { + if (lcl_IsUiVisibleBookmark(*ppBookmark) && + *pCursorPoint >= (*ppBookmark)->GetMarkStart() && + *pCursorPoint <= (*ppBookmark)->GetMarkEnd()) + { + sBookmarkName = (*ppBookmark)->GetName(); + // keep previously selected bookmark instead + // of selecting a different bookmark inside of it + if (sBookmarkName == m_sSelectedItem) + break; + } + else if (!sBookmarkName.isEmpty() && + *pCursorPoint < (*ppBookmark)->GetMarkStart()) + { + // don't search a different bookmark inside the + // previous one, if the starting position of the next bookmarks + // is after the cursor position (assuming that the + // bookmark iterator jumps inside the same text by positions) + break; + } + ++ppBookmark; + } + + if (!sBookmarkName.isEmpty()) + { + // select the bookmark + lcl_SelectByContentTypeAndName(this, *m_xTreeView, + SwResId(STR_CONTENT_TYPE_BOOKMARK), + sBookmarkName); + return; + } + } + } + // references + if (SwContentAtPos aContentAtPos(IsAttrAtPos::RefMark); + m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos) && + aContentAtPos.pFndTextAttr && + !(m_bIsRoot && m_nRootType != ContentTypeId::REFERENCE)) + { + if (mTrackContentType[ContentTypeId::REFERENCE]) + { + const SwFormatRefMark& rRefMark = aContentAtPos.pFndTextAttr->GetRefMark(); + lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REFERENCE), + rRefMark.GetRefName()); + } + return; + } + // hyperlinks + // not in ToxContent tdf#148312 + if (const SwSection* pSection = m_pActiveShell->GetCurrSection(); !pSection + || (pSection && pSection->GetType() != SectionType::ToxContent)) + { + if (SwContentAtPos aContentAtPos(IsAttrAtPos::InetAttr); + m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos) + && (!m_bIsRoot || m_nRootType == ContentTypeId::URLFIELD)) + { + // Because hyperlink item names do not need to be unique, finding the corresponding + // item in the tree by name may result in incorrect selection. Find the item in the + // tree by comparing the SwTextINetFormat pointer at the document cursor position to + // that stored in the item SwURLFieldContent. + if (mTrackContentType[ContentTypeId::URLFIELD]) + lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::URLFIELD, + aContentAtPos.pFndTextAttr); + return; + } + } + // fields, comments + if (SwField* pField = m_pActiveShell->GetCurField(); pField && + !(m_bIsRoot && + m_nRootType != ContentTypeId::TEXTFIELD && + m_nRootType != ContentTypeId::POSTIT)) + { + ContentTypeId eCntTypeId = + pField->GetTypeId() == SwFieldTypesEnum::Postit ? ContentTypeId::POSTIT : + ContentTypeId::TEXTFIELD; + if (mTrackContentType[eCntTypeId]) + lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, eCntTypeId, pField); + return; + } + // table + if (m_pActiveShell->IsCursorInTable() && + !(m_bIsRoot && m_nRootType != ContentTypeId::TABLE)) + { + if (mTrackContentType[ContentTypeId::TABLE] && m_pActiveShell->GetTableFormat()) + { + OUString aName = m_pActiveShell->GetTableFormat()->GetName(); + lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_TABLE), + aName); + return; + } + } + // indexes + if (const SwTOXBase* pTOX = m_pActiveShell->GetCurTOX(); pTOX && + !(m_bIsRoot && m_nRootType != ContentTypeId::INDEX)) + { + if (mTrackContentType[ContentTypeId::INDEX]) + lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_INDEX), + pTOX->GetTOXName()); + return; + } + // section + if (const SwSection* pSection = m_pActiveShell->GetCurrSection(); pSection && + !(m_bIsRoot && m_nRootType != ContentTypeId::REGION)) + { + if (mTrackContentType[ContentTypeId::REGION]) + { + lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REGION), + pSection->GetSectionName()); + return; + } + else + { + // prevent fall through to outline tracking when section tracking is off and the last + // GotoContent is the current section + if (m_nLastSelType == ContentTypeId::REGION && + m_xTreeView->get_selected_text() == pSection->GetSectionName()) + return; + } + // fall through to outline tracking when section tracking is off and the last GotoContent + // is not the current section + } + } + // outline + if (m_nOutlineTracking == 3) + return; + // find out where the cursor is + const SwOutlineNodes::size_type nActPos = GetWrtShell()->GetOutlinePos(MAXLEVEL); + if (!((m_bIsRoot && m_nRootType != ContentTypeId::OUTLINE) || nActPos == SwOutlineNodes::npos)) + { + // assure outline content type is expanded + // this assumes outline content type is first in treeview + std::unique_ptr<weld::TreeIter> xFirstEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_iter_first(*xFirstEntry)) + m_xTreeView->expand_row(*xFirstEntry); + + m_xTreeView->all_foreach([this, nActPos](weld::TreeIter& rEntry){ + bool bRet = false; + if (lcl_IsContent(rEntry, *m_xTreeView) && weld::fromId<SwContent*>( + m_xTreeView->get_id(rEntry))->GetParent()->GetType() == + ContentTypeId::OUTLINE) + { + if (weld::fromId<SwOutlineContent*>( + m_xTreeView->get_id(rEntry))->GetOutlinePos() == nActPos) + { + std::unique_ptr<weld::TreeIter> xFirstSelected( + m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xFirstSelected.get())) + xFirstSelected.reset(); + // only select if not already selected or tree has multiple entries selected + if (m_xTreeView->count_selected_rows() != 1 || !xFirstSelected || + m_xTreeView->iter_compare(rEntry, *xFirstSelected) != 0) + { + if (m_nOutlineTracking == 2) // focused outline tracking + { + // collapse to children of root node + std::unique_ptr<weld::TreeIter> xChildEntry( + m_xTreeView->make_iterator()); + if (m_xTreeView->get_iter_first(*xChildEntry) && + m_xTreeView->iter_children(*xChildEntry)) + { + do + { + if (weld::fromId<SwContent*>( + m_xTreeView->get_id(*xChildEntry))-> + GetParent()->GetType() == ContentTypeId::OUTLINE) + m_xTreeView->collapse_row(*xChildEntry); + else + break; + } + while (m_xTreeView->iter_next(*xChildEntry)); + } + } + // unselect all entries, make pEntry visible, and select + m_xTreeView->set_cursor(rEntry); + Select(); + + // tdf#149279 show at least two outline entries before the set cursor entry + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(&rEntry)); + for (int i = 0; i < 2; i++) + { + if (m_xTreeView->get_iter_depth(*xIter) == 0) + break; + if (!m_xTreeView->iter_previous(*xIter)) + break; + while (!weld::IsEntryVisible(*m_xTreeView, *xIter)) + m_xTreeView->iter_parent(*xIter); + } + // Assure the scroll to row is collapsed after scrolling if it was collapsed + // before. This is required here to make gtkinst scroll_to_row behave like + // salinst. + const bool bRowExpanded = m_xTreeView->get_row_expanded(*xIter); + m_xTreeView->scroll_to_row(*xIter); + if (!bRowExpanded) + m_xTreeView->collapse_row(*xIter); + } + bRet = true; + } + } + else + { + // use of this break assumes outline content type is first in tree + if (lcl_IsContentType(rEntry, *m_xTreeView) && + weld::fromId<SwContentType*>( + m_xTreeView->get_id(rEntry))->GetType() != + ContentTypeId::OUTLINE) + bRet = true; + } + return bRet; + }); + } + else + { + // clear treeview selections + if (m_xTreeView->count_selected_rows() > 0) + { + m_xTreeView->unselect_all(); + m_xTreeView->set_cursor(-1); + Select(); + } + } +} + +void SwContentTree::SelectOutlinesWithSelection() +{ + SwCursor* pFirstCursor = m_pActiveShell->GetCursor(); + SwCursor* pCursor = pFirstCursor; + std::vector<SwOutlineNodes::size_type> aOutlinePositions; + do + { + if (pCursor) + { + if (pCursor->HasMark()) + { + aOutlinePositions.push_back(m_pActiveShell->GetOutlinePos(UCHAR_MAX, pCursor)); + } + pCursor = pCursor->GetNext(); + } + } while (pCursor && pCursor != pFirstCursor); + + if (aOutlinePositions.empty()) + return; + + // remove duplicates before selecting + aOutlinePositions.erase(std::unique(aOutlinePositions.begin(), aOutlinePositions.end()), + aOutlinePositions.end()); + + m_xTreeView->unselect_all(); + + for (auto nOutlinePosition : aOutlinePositions) + { + m_xTreeView->all_foreach([this, nOutlinePosition](const weld::TreeIter& rEntry){ + if (lcl_IsContent(rEntry, *m_xTreeView) && + weld::fromId<SwContent*>( + m_xTreeView->get_id(rEntry))->GetParent()->GetType() == + ContentTypeId::OUTLINE) + { + if (weld::fromId<SwOutlineContent*>( + m_xTreeView->get_id(rEntry))->GetOutlinePos() == + nOutlinePosition) + { + std::unique_ptr<weld::TreeIter> xParent = + m_xTreeView->make_iterator(&rEntry); + if (m_xTreeView->iter_parent(*xParent) && + !m_xTreeView->get_row_expanded(*xParent)) + m_xTreeView->expand_row(*xParent); + m_xTreeView->select(rEntry); + return true; + } + } + return false; + }); + } + + Select(); +} + +void SwContentTree::MoveOutline(SwOutlineNodes::size_type nTargetPos) +{ + MakeAllOutlineContentTemporarilyVisible a(GetWrtShell()->GetDoc()); + + SwWrtShell *const pShell = GetWrtShell(); + pShell->StartAllAction(); + pShell->StartUndo(SwUndoId::OUTLINE_UD); + + SwOutlineNodes::size_type nPrevSourcePos = SwOutlineNodes::npos; + SwOutlineNodes::size_type nPrevTargetPosOrOffset = SwOutlineNodes::npos; + + bool bFirstMove = true; + + for (const auto& source : m_aDndOutlinesSelected) + { + SwOutlineNodes::size_type nSourcePos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*source))->GetOutlinePos(); + + // Done on the first selection move + if (bFirstMove) // only do once + { + if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos) + { + // Up moves + // The first up move sets the up move amount for the remaining selected outlines to be moved + if (nTargetPos != SwOutlineNodes::npos) + nPrevTargetPosOrOffset = nSourcePos - nTargetPos; + else + nPrevTargetPosOrOffset = nSourcePos + 1; + } + else if (nSourcePos < nTargetPos) + { + // Down moves + // The first down move sets the source and target positions for the remaining selected outlines to be moved + nPrevSourcePos = nSourcePos; + nPrevTargetPosOrOffset = nTargetPos; + } + bFirstMove = false; + } + else + { + if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos) + { + // Move up + nTargetPos = nSourcePos - nPrevTargetPosOrOffset; + } + else if (nSourcePos < nTargetPos) + { + // Move down + nSourcePos = nPrevSourcePos; + nTargetPos = nPrevTargetPosOrOffset; + } + } + GetParentWindow()->MoveOutline(nSourcePos, nTargetPos); + } + + pShell->EndUndo(); + pShell->EndAllAction(); + m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate(); + Display(true); + m_aDndOutlinesSelected.clear(); +} + +// Update immediately +IMPL_LINK_NOARG(SwContentTree, FocusInHdl, weld::Widget&, void) +{ + SwView* pActView = GetParentWindow()->GetCreateView(); + if(pActView) + { + SwWrtShell* pActShell = pActView->GetWrtShellPtr(); + if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell)) + { + SetActiveShell(pActShell); + } + + if (State::ACTIVE == m_eState && pActShell != GetWrtShell()) + SetActiveShell(pActShell); + // Only call HasContentChanged() if the document has changed since last called + else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) && + m_bDocHasChanged) + { + if (HasContentChanged()) + Display(true); + m_bDocHasChanged = false; + } + } + else if (State::ACTIVE == m_eState) + clear(); +} + +IMPL_LINK(SwContentTree, KeyInputHdl, const KeyEvent&, rEvent, bool) +{ + bool bConsumed = true; + + const vcl::KeyCode aCode = rEvent.GetKeyCode(); + if (aCode.GetCode() == KEY_MULTIPLY && aCode.IsMod1()) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get())) + ExpandOrCollapseAll(*m_xTreeView, *xEntry); + } + else if (aCode.GetCode() == KEY_RETURN) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get())) + { + switch(aCode.GetModifier()) + { + case KEY_MOD2: + // Switch boxes + GetParentWindow()->ToggleTree(); + break; + case KEY_MOD1: + // Switch RootMode + ToggleToRoot(); + break; + case 0: + if (lcl_IsContentType(*xEntry, *m_xTreeView)) + { + m_xTreeView->get_row_expanded(*xEntry) ? m_xTreeView->collapse_row(*xEntry) + : m_xTreeView->expand_row(*xEntry); + } + else + ContentDoubleClickHdl(*m_xTreeView); + break; + case KEY_SHIFT: + m_bSelectTo = true; + ContentDoubleClickHdl(*m_xTreeView); + break; + } + } + } + else if(aCode.GetCode() == KEY_DELETE && 0 == aCode.GetModifier()) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get()) && lcl_IsContent(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + if (weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetParent()->IsDeletable() && + !m_pActiveShell->GetView().GetDocShell()->IsReadOnly()) + { + EditEntry(*xEntry, EditEntryMode::DELETE); + } + } + } + //Make KEY_SPACE has same function as DoubleClick, and realize + //multi-selection. + else if (aCode.GetCode() == KEY_SPACE && 0 == aCode.GetModifier()) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xEntry.get())) + { + if (State::HIDDEN != m_eState) + { + if (State::CONSTANT == m_eState) + { + m_pActiveShell->GetView().GetViewFrame().GetWindow().ToTop(); + } + + SwContent* pCnt = dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))); + + if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::DRAWOBJECT) + { + SdrView* pDrawView = m_pActiveShell->GetDrawView(); + if (pDrawView) + { + pDrawView->SdrEndTextEdit(); + + SwDrawModel* pDrawModel = m_pActiveShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + SdrPage* pPage = pDrawModel->GetPage(0); + bool hasObjectMarked = false; + + if (SdrObject* pObject = GetDrawingObjectsByContent(pCnt)) + { + SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/); + if( pPV ) + { + bool bUnMark = pDrawView->IsObjMarked(pObject); + pDrawView->MarkObj( pObject, pPV, bUnMark); + + } + } + for (const rtl::Reference<SdrObject>& pTemp : *pPage) + { + bool bMark = pDrawView->IsObjMarked(pTemp.get()); + switch( pTemp->GetObjIdentifier() ) + { + case SdrObjKind::Group: + case SdrObjKind::Text: + case SdrObjKind::Line: + case SdrObjKind::Rectangle: + case SdrObjKind::CircleOrEllipse: + case SdrObjKind::CircleSection: + case SdrObjKind::CircleArc: + case SdrObjKind::CircleCut: + case SdrObjKind::Polygon: + case SdrObjKind::PolyLine: + case SdrObjKind::PathLine: + case SdrObjKind::PathFill: + case SdrObjKind::FreehandLine: + case SdrObjKind::FreehandFill: + case SdrObjKind::PathPoly: + case SdrObjKind::PathPolyLine: + case SdrObjKind::Caption: + case SdrObjKind::CustomShape: + if( bMark ) + hasObjectMarked = true; + break; + default: + if ( bMark ) + { + SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/); + if (pPV) + { + pDrawView->MarkObj(pTemp.get(), pPV, true); + } + } + } + //mod end + } + if ( !hasObjectMarked ) + { + SwEditWin& rEditWindow = m_pActiveShell->GetView().GetEditWin(); + vcl::KeyCode tempKeycode( KEY_ESCAPE ); + KeyEvent rKEvt( 0 , tempKeycode ); + static_cast<vcl::Window*>(&rEditWindow)->KeyInput( rKEvt ); + } + } + } + + m_bViewHasChanged = true; + } + } + } + else + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xEntry.get())) + { + SwContent* pCnt = dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))); + if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE) + { + if (m_bIsRoot && aCode.GetCode() == KEY_LEFT && aCode.GetModifier() == 0) + { + m_xTreeView->unselect_all(); + bConsumed = false; + } + else if (aCode.IsMod1()) + { + if (aCode.GetCode() == KEY_LEFT) + ExecCommand(u"promote", !aCode.IsShift()); + else if (aCode.GetCode() == KEY_RIGHT) + ExecCommand(u"demote", !aCode.IsShift()); + else if (aCode.GetCode() == KEY_UP) + ExecCommand(u"chapterup", !aCode.IsShift()); + else if (aCode.GetCode() == KEY_DOWN) + ExecCommand(u"chapterdown", !aCode.IsShift()); + else if (aCode.GetCode() == KEY_C) + CopyOutlineSelections(); + else + bConsumed = false; + } + else + bConsumed = false; + } + else + bConsumed = false; + } + else + bConsumed = false; + } + return bConsumed; +} + +IMPL_LINK(SwContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString) +{ + ContentTypeId nType; + bool bContent = false; + void* pUserData = weld::fromId<void*>(m_xTreeView->get_id(rEntry)); + if (lcl_IsContentType(rEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData))); + nType = static_cast<SwContentType*>(pUserData)->GetType(); + } + else + { + assert(dynamic_cast<SwContent*>(static_cast<SwTypeNumber*>(pUserData))); + nType = static_cast<SwContent*>(pUserData)->GetParent()->GetType(); + bContent = true; + } + OUString sEntry; + if(bContent) + { + switch( nType ) + { + case ContentTypeId::URLFIELD: + assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwURLFieldContent*>(pUserData)->GetURL(); + break; + + case ContentTypeId::POSTIT: + assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwPostItContent*>(pUserData)->GetName(); + break; + case ContentTypeId::OUTLINE: + assert(dynamic_cast<SwOutlineContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwOutlineContent*>(pUserData)->GetName(); + break; + case ContentTypeId::GRAPHIC: + assert(dynamic_cast<SwGraphicContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwGraphicContent*>(pUserData)->GetLink(); + break; + case ContentTypeId::REGION: + { + assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwRegionContent*>(pUserData)->GetName(); + const SwSectionFormats& rFormats = GetWrtShell()->GetDoc()->GetSections(); + for (SwSectionFormats::size_type n = rFormats.size(); n;) + { + const SwNodeIndex* pIdx = nullptr; + const SwSectionFormat* pFormat = rFormats[--n]; + const SwSection* pSect; + if (nullptr != (pSect = pFormat->GetSection()) && + pSect->GetSectionName() == sEntry && + nullptr != (pIdx = pFormat->GetContent().GetContentIdx()) && + pIdx->GetNode().GetNodes().IsDocNodes()) + { + SwDocStat aDocStat; + SwPaM aPaM(pIdx->GetNode(), *pIdx->GetNode().EndOfSectionNode()); + SwDoc::CountWords(aPaM, aDocStat); + sEntry = SwResId(STR_REGION_DEFNAME) + ": " + sEntry + "\n" + + SwResId(FLD_STAT_WORD) + ": " + OUString::number(aDocStat.nWord) + "\n" + + SwResId(FLD_STAT_CHAR) + ": " + OUString::number(aDocStat.nChar); + break; + } + } + } + break; + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + { + assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData))); + const SwTextFootnote* pFootnote = + static_cast<const SwTextFootnoteContent*>(pUserData)->GetTextFootnote(); + + sEntry = pFootnote->GetFootnote().IsEndNote() ? SwResId(STR_CONTENT_ENDNOTE) : + SwResId(STR_CONTENT_FOOTNOTE); + } + break; + default: break; + } + if(static_cast<SwContent*>(pUserData)->IsInvisible()) + { + if(!sEntry.isEmpty()) + sEntry += ", "; + sEntry += m_sInvisible; + } + } + else + { + size_t nMemberCount = static_cast<SwContentType*>(pUserData)->GetMemberCount(); + sEntry = OUString::number(nMemberCount) + " " + + (nMemberCount == 1 + ? static_cast<SwContentType*>(pUserData)->GetSingleName() + : static_cast<SwContentType*>(pUserData)->GetName()); + } + + return sEntry; +} + +void SwContentTree::ExecuteContextMenuAction(const OUString& rSelectedPopupEntry) +{ + if (rSelectedPopupEntry == "copy") + { + CopyOutlineSelections(); + return; + } + if (rSelectedPopupEntry == "collapseallcategories") + { + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + bool bEntry = m_xTreeView->get_iter_first(*xEntry); + while (bEntry) + { + m_xTreeView->collapse_row(*xEntry); + bEntry = m_xTreeView->iter_next_sibling(*xEntry); + } + return; + } + + { + std::map<OUString, ContentTypeId> mPopupEntryToContentTypeId + { + {"tabletracking", ContentTypeId::TABLE}, + {"frametracking", ContentTypeId::FRAME}, + {"imagetracking", ContentTypeId::GRAPHIC}, + {"oleobjecttracking", ContentTypeId::OLE}, + {"bookmarktracking", ContentTypeId::BOOKMARK}, + {"sectiontracking", ContentTypeId::REGION}, + {"hyperlinktracking", ContentTypeId::URLFIELD}, + {"referencetracking", ContentTypeId::REFERENCE}, + {"indextracking", ContentTypeId::INDEX}, + {"commenttracking", ContentTypeId::POSTIT}, + {"drawingobjecttracking", ContentTypeId::DRAWOBJECT}, + {"fieldtracking", ContentTypeId::TEXTFIELD}, + {"footnotetracking", ContentTypeId::FOOTNOTE}, + {"endnotetracking", ContentTypeId::ENDNOTE} + }; + + if (mPopupEntryToContentTypeId.count(rSelectedPopupEntry)) + { + ContentTypeId eCntTypeId = mPopupEntryToContentTypeId[rSelectedPopupEntry]; + SetContentTypeTracking(eCntTypeId, !mTrackContentType[eCntTypeId]); + return; + } + } + + std::unique_ptr<weld::TreeIter> xFirst(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xFirst.get())) + return; // this shouldn't happen, but better to be safe than ... + + if (rSelectedPopupEntry == "protectsection" || rSelectedPopupEntry == "hidesection") + { + SwRegionContent* pCnt = weld::fromId<SwRegionContent*>(m_xTreeView->get_id(*xFirst)); + assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pCnt))); + const SwSectionFormat* pSectionFormat = pCnt->GetSectionFormat(); + SwSection* pSection = pSectionFormat->GetSection(); + SwSectionData aSectionData(*pSection); + if (rSelectedPopupEntry == "protectsection") + aSectionData.SetProtectFlag(!pSection->IsProtect()); + else + aSectionData.SetHidden(!pSection->IsHidden()); + m_pActiveShell->UpdateSection(m_pActiveShell->GetSectionFormatPos(*pSectionFormat), + aSectionData); + } + else if (rSelectedPopupEntry == "sort") + { + SwContentType* pCntType; + const OUString& rId(m_xTreeView->get_id(*xFirst)); + if (lcl_IsContentType(*xFirst, *m_xTreeView)) + pCntType = weld::fromId<SwContentType*>(rId); + else + pCntType = const_cast<SwContentType*>(weld::fromId<SwContent*>(rId)->GetParent()); + + // toggle and persist alphabetical sort setting + const int nShift = static_cast<int>(pCntType->GetType()); + assert(nShift > -1); + const sal_Int32 nMask = 1 << nShift; + const sal_Int32 nBlock = m_pConfig->GetSortAlphabeticallyBlock(); + pCntType->SetAlphabeticSort(~nBlock & nMask); + m_pConfig->SetSortAlphabeticallyBlock(nBlock ^ nMask); + + pCntType->FillMemberList(); + Display(true); + return; + } + else if (rSelectedPopupEntry == "deletechapter" || + rSelectedPopupEntry == "deletetable" || + rSelectedPopupEntry == "deleteframe" || + rSelectedPopupEntry == "deleteimage" || + rSelectedPopupEntry == "deleteoleobject" || + rSelectedPopupEntry == "deletebookmark" || + rSelectedPopupEntry == "deleteregion" || + rSelectedPopupEntry == "deletehyperlink" || + rSelectedPopupEntry == "deletereference" || + rSelectedPopupEntry == "deleteindex" || + rSelectedPopupEntry == "deletecomment" || + rSelectedPopupEntry == "deletedrawingobject" || + rSelectedPopupEntry == "deletefield") + { + EditEntry(*xFirst, EditEntryMode::DELETE); + return; + } + + auto nSelectedPopupEntry = rSelectedPopupEntry.toUInt32(); + switch (nSelectedPopupEntry) + { + case TOGGLE_OUTLINE_CONTENT_VISIBILITY: + case HIDE_OUTLINE_CONTENT_VISIBILITY: + case SHOW_OUTLINE_CONTENT_VISIBILITY: + { + m_pActiveShell->EnterStdMode(); + m_bIgnoreDocChange = true; + SwOutlineContent* pCntFirst = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xFirst)); + + // toggle the outline node outline content visible attribute + if (nSelectedPopupEntry == TOGGLE_OUTLINE_CONTENT_VISIBILITY) + { + SwNode* pNode = m_pActiveShell->GetDoc()->GetNodes().GetOutLineNds()[pCntFirst->GetOutlinePos()]; + pNode->GetTextNode()->SetAttrOutlineContentVisible( + !m_pActiveShell->GetAttrOutlineContentVisible(pCntFirst->GetOutlinePos())); + } + else + { + // with subs + SwOutlineNodes::size_type nPos = pCntFirst->GetOutlinePos(); + if (lcl_IsContentType(*xFirst, *m_xTreeView)) // Headings root entry + nPos = SwOutlineNodes::npos; + SwOutlineNodes::size_type nOutlineNodesCount = m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(); + int nLevel = -1; + if (nPos != SwOutlineNodes::npos) // not root + nLevel = m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos); + else + nPos = 0; + bool bShow(nSelectedPopupEntry == SHOW_OUTLINE_CONTENT_VISIBILITY); + do + { + if (m_pActiveShell->IsOutlineContentVisible(nPos) != bShow) + m_pActiveShell->GetDoc()->GetNodes().GetOutLineNds()[nPos]->GetTextNode()->SetAttrOutlineContentVisible(bShow); + } while (++nPos < nOutlineNodesCount + && (nLevel == -1 || m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nLevel)); + } + m_pActiveShell->InvalidateOutlineContentVisibility(); + // show in the document what was toggled + if (lcl_IsContentType(*xFirst, *m_xTreeView)) // Headings root entry + m_pActiveShell->GotoPage(1, true); + else + m_pActiveShell->GotoOutline(pCntFirst->GetOutlinePos()); + grab_focus(); + m_bIgnoreDocChange = false; + m_pActiveShell->SetModified(); + m_pActiveShell->GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged)); + } + break; + case 11: + case 12: + case 13: + nSelectedPopupEntry -= 10; + if(m_nOutlineTracking != nSelectedPopupEntry) + SetOutlineTracking(static_cast<sal_uInt8>(nSelectedPopupEntry)); + break; + //Outlinelevel + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + case 108: + case 109: + case 110: + nSelectedPopupEntry -= 100; + if(m_nOutlineLevel != nSelectedPopupEntry ) + SetOutlineLevel(static_cast<sal_Int8>(nSelectedPopupEntry)); + break; + case 201: + case 202: + case 203: + GetParentWindow()->SetRegionDropMode(static_cast<RegionMode>(nSelectedPopupEntry - 201)); + break; + case 401: + case 402: + EditEntry(*xFirst, nSelectedPopupEntry == 401 ? EditEntryMode::RMV_IDX : EditEntryMode::UPD_IDX); + break; + // Edit entry + case 403: + EditEntry(*xFirst, EditEntryMode::EDIT); + break; + case 404: + EditEntry(*xFirst, EditEntryMode::UNPROTECT_TABLE); + break; + case 405 : + { + const SwTOXBase* pBase = weld::fromId<SwTOXBaseContent*>(m_xTreeView->get_id(*xFirst)) + ->GetTOXBase(); + m_pActiveShell->SetTOXBaseReadonly(*pBase, !SwEditShell::IsTOXBaseReadonly(*pBase)); + } + break; + case 502 : + EditEntry(*xFirst, EditEntryMode::RENAME); + break; + case 600: + m_pActiveShell->GetView().GetPostItMgr()->Show(); + break; + case 601: + m_pActiveShell->GetView().GetPostItMgr()->Hide(); + break; + case 602: + { + m_pActiveShell->GetView().GetPostItMgr()->SetActiveSidebarWin(nullptr); + m_pActiveShell->GetView().GetPostItMgr()->Delete(); + break; + } + case 700: + { + m_pActiveShell->GetView().GetViewFrame().GetDispatcher()->Execute(FN_OUTLINE_TO_CLIPBOARD); + break; + } + case 800: + ExpandOrCollapseAll(*m_xTreeView, *xFirst); + break; + case 801: + ExecCommand(u"chapterup", true); + break; + case 802: + ExecCommand(u"chapterdown", true); + break; + case 803: + ExecCommand(u"promote", true); + break; + case 804: + ExecCommand(u"demote", true); + break; + case 805: // select document content + { + m_pActiveShell->KillPams(); + m_pActiveShell->ClearMark(); + m_pActiveShell->EnterAddMode(); + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xFirst)); + const ContentTypeId eTypeId = pCnt->GetParent()->GetType(); + if (eTypeId == ContentTypeId::OUTLINE) + { + SwOutlineNodes::size_type nActPos = weld::fromId<SwOutlineContent*>( + m_xTreeView->get_id(*xFirst))->GetOutlinePos(); + m_pActiveShell->GotoOutline(nActPos); + m_xTreeView->selected_foreach([this](weld::TreeIter& rEntry){ + SwOutlineNodes::size_type nPos = weld::fromId<SwOutlineContent*>( + m_xTreeView->get_id(rEntry))->GetOutlinePos(); + m_pActiveShell->SttSelect(); + // select children if not expanded and don't kill PaMs + m_pActiveShell->MakeOutlineSel(nPos, nPos, + !m_xTreeView->get_row_expanded(rEntry), false); + m_pActiveShell->EndSelect(); + return false; + }); + } + else if (eTypeId == ContentTypeId::TABLE) + { + m_pActiveShell->GotoTable(pCnt->GetName()); + m_pActiveShell->GetView().GetViewFrame().GetDispatcher()->Execute(FN_TABLE_SELECT_ALL); + } + else if (eTypeId == ContentTypeId::REGION) + { + m_pActiveShell->EnterStdMode(); + m_pActiveShell->GotoRegion(pCnt->GetName()); + GotoCurrRegionAndSkip(m_pActiveShell->GetCurrentShellCursor(), fnRegionEnd, m_pActiveShell->IsReadOnlyAvailable()); + m_pActiveShell->SttSelect(); + GotoCurrRegionAndSkip(m_pActiveShell->GetCurrentShellCursor(), fnRegionStart, m_pActiveShell->IsReadOnlyAvailable()); + m_pActiveShell->EndSelect(); + m_pActiveShell->UpdateCursor(); + } + m_pActiveShell->LeaveAddMode(); + } + break; + case 900: + { + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xFirst)); + GotoContent(pCnt); + } + break; + //Display + default: + if(nSelectedPopupEntry > 300 && nSelectedPopupEntry < 400) + { + nSelectedPopupEntry -= 300; + SwView *pView = SwModule::GetFirstView(); + while (pView) + { + nSelectedPopupEntry --; + if(nSelectedPopupEntry == 0) + { + SetConstantShell(&pView->GetWrtShell()); + break; + } + pView = SwModule::GetNextView(pView); + } + if(nSelectedPopupEntry) + { + m_bViewHasChanged = nSelectedPopupEntry == 1; + m_eState = (nSelectedPopupEntry == 1) ? State::ACTIVE : State::HIDDEN; + Display(nSelectedPopupEntry == 1); + } + GetParentWindow()->UpdateListBox(); + } + } +} + +void SwContentTree::DeleteOutlineSelections() +{ + const SwOutlineNodes& rOutlineNodes = m_pActiveShell->GetNodes().GetOutLineNds(); + auto nChapters(0); + + m_pActiveShell->StartAction(); + + m_pActiveShell->EnterAddMode(); + m_xTreeView->selected_foreach([this, &rOutlineNodes, &nChapters](weld::TreeIter& rEntry){ + ++nChapters; + if (m_xTreeView->iter_has_child(rEntry) && + !m_xTreeView->get_row_expanded(rEntry)) // only count children if not expanded + { + nChapters += m_xTreeView->iter_n_children(rEntry); + } + SwOutlineNodes::size_type nActPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rEntry))->GetOutlinePos(); + if (m_pActiveShell->GetViewOptions()->IsShowOutlineContentVisibilityButton()) + { + // make folded content visible so it can be selected + if (!m_pActiveShell->IsOutlineContentVisible(nActPos)) + m_pActiveShell->MakeOutlineContentVisible(nActPos); + if (!m_xTreeView->get_row_expanded(rEntry)) + { + // include children + SwNode* pNode = rOutlineNodes[nActPos]; + const int nLevel = pNode->GetTextNode()->GetAttrOutlineLevel() - 1; + for (auto nPos = nActPos + 1; nPos < rOutlineNodes.size(); ++nPos) + { + pNode = rOutlineNodes[nPos]; + const int nNextLevel = pNode->GetTextNode()->GetAttrOutlineLevel() - 1; + if (nNextLevel <= nLevel) + break; + if (!m_pActiveShell->IsOutlineContentVisible(nNextLevel)) + m_pActiveShell->MakeOutlineContentVisible(nNextLevel); + } + } + } + m_pActiveShell->SttSelect(); + m_pActiveShell->MakeOutlineSel(nActPos, nActPos, !m_xTreeView->get_row_expanded(rEntry), false); // select children if not expanded + // The outline selection may already be to the start of the following outline paragraph + // as happens when a table is the last content of the to be deleted outline. In this case + // do not extend the to be deleted selection right or the first character of the following + // outline paragraph will be removed. Also check if no selection was made which indicates + // an empty paragraph and selection right is needed. + if (!m_pActiveShell->IsSttPara() || !m_pActiveShell->HasSelection()) + m_pActiveShell->Right(SwCursorSkipMode::Chars, true, 1, false); + m_pActiveShell->EndSelect(); + return false; + }); + m_pActiveShell->LeaveAddMode(); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_CHAPTERS, nChapters)); + m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter); + m_pActiveShell->Delete(false); + m_pActiveShell->EndUndo(); + + m_pActiveShell->EndAction(); +} + +void SwContentTree::SetOutlineLevel(sal_uInt8 nSet) +{ + if (nSet == m_nOutlineLevel) + return; + m_nOutlineLevel = nSet; + m_pConfig->SetOutlineLevel( m_nOutlineLevel ); + std::unique_ptr<SwContentType>& rpContentT = (State::ACTIVE == m_eState) + ? m_aActiveContentArr[ContentTypeId::OUTLINE] + : m_aHiddenContentArr[ContentTypeId::OUTLINE]; + if(rpContentT) + { + rpContentT->SetOutlineLevel(m_nOutlineLevel); + rpContentT->FillMemberList(); + } + Display(State::ACTIVE == m_eState); +} + +void SwContentTree::SetOutlineTracking(sal_uInt8 nSet) +{ + m_nOutlineTracking = nSet; + m_pConfig->SetOutlineTracking(m_nOutlineTracking); +} + +void SwContentTree::SetContentTypeTracking(ContentTypeId eCntTypeId, bool bSet) +{ + mTrackContentType[eCntTypeId] = bSet; + m_pConfig->SetContentTypeTrack(eCntTypeId, bSet); +} + +// Mode Change: Show dropped Doc +void SwContentTree::ShowHiddenShell() +{ + if(m_pHiddenShell) + { + m_eState = State::HIDDEN; + Display(false); + } +} + +// Mode Change: Show active view +void SwContentTree::ShowActualView() +{ + m_eState = State::ACTIVE; + Display(true); + GetParentWindow()->UpdateListBox(); +} + +IMPL_LINK_NOARG(SwContentTree, SelectHdl, weld::TreeView&, void) +{ + if (m_pConfig->IsNavigateOnSelect()) + { + ContentDoubleClickHdl(*m_xTreeView); + grab_focus(); + } + Select(); + if (m_bIsRoot) + return; + // Select the content type in the Navigate By control + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xEntry.get())) + return; + while (m_xTreeView->get_iter_depth(*xEntry)) + m_xTreeView->iter_parent(*xEntry); + m_pDialog->SelectNavigateByContentType(m_xTreeView->get_text(*xEntry)); +} + +// Here the buttons for moving outlines are en-/disabled. +void SwContentTree::Select() +{ + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xEntry.get())) + return; + + bool bEnable = false; + std::unique_ptr<weld::TreeIter> xParentEntry(m_xTreeView->make_iterator(xEntry.get())); + bool bParentEntry = m_xTreeView->iter_parent(*xParentEntry); + while (bParentEntry && (!lcl_IsContentType(*xParentEntry, *m_xTreeView))) + bParentEntry = m_xTreeView->iter_parent(*xParentEntry); + if (!m_bIsLastReadOnly) + { + if (!m_xTreeView->get_visible()) + bEnable = true; + else if (bParentEntry) + { + if ((m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE) || + (lcl_IsContent(*xEntry, *m_xTreeView) && + weld::fromId<SwContentType*>(m_xTreeView->get_id(*xParentEntry))->GetType() == ContentTypeId::OUTLINE)) + { + bEnable = true; + } + } + } + + SwNavigationPI* pNavi = GetParentWindow(); + pNavi->m_xContent6ToolBox->set_item_sensitive("chapterup", bEnable); + pNavi->m_xContent6ToolBox->set_item_sensitive("chapterdown", bEnable); + pNavi->m_xContent6ToolBox->set_item_sensitive("promote", bEnable); + pNavi->m_xContent6ToolBox->set_item_sensitive("demote", bEnable); +} + +void SwContentTree::SetRootType(ContentTypeId nType) +{ + m_nRootType = nType; + m_bIsRoot = true; + m_pConfig->SetRootType( m_nRootType ); +} + +OUString SwContentType::RemoveNewline(const OUString& rEntry) +{ + if (rEntry.isEmpty()) + return rEntry; + + OUStringBuffer aEntry(rEntry); + for (sal_Int32 i = 0; i < rEntry.getLength(); ++i) + if(aEntry[i] == 10 || aEntry[i] == 13) + aEntry[i] = 0x20; + + return aEntry.makeStringAndClear(); +} + +void SwContentTree::EditEntry(const weld::TreeIter& rEntry, EditEntryMode nMode) +{ + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(rEntry)); + GotoContent(pCnt); + const ContentTypeId nType = pCnt->GetParent()->GetType(); + sal_uInt16 nSlot = 0; + + if(EditEntryMode::DELETE == nMode) + m_bIgnoreDocChange = true; + + uno::Reference< container::XNameAccess > xNameAccess, xSecond, xThird; + switch(nType) + { + case ContentTypeId::OUTLINE : + if(nMode == EditEntryMode::DELETE) + { + DeleteOutlineSelections(); + } + break; + + case ContentTypeId::TABLE : + if(nMode == EditEntryMode::UNPROTECT_TABLE) + { + m_pActiveShell->GetView().GetDocShell()-> + GetDoc()->UnProtectCells( pCnt->GetName()); + } + else if(nMode == EditEntryMode::DELETE) + { + m_pActiveShell->StartAction(); + OUString sTable = SwResId(STR_TABLE_NAME); + SwRewriter aRewriterTableName; + aRewriterTableName.AddRule(UndoArg1, SwResId(STR_START_QUOTE)); + aRewriterTableName.AddRule(UndoArg2, pCnt->GetName()); + aRewriterTableName.AddRule(UndoArg3, SwResId(STR_END_QUOTE)); + sTable = aRewriterTableName.Apply(sTable); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, sTable); + m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter); + m_pActiveShell->GetView().GetViewFrame().GetDispatcher()->Execute(FN_TABLE_SELECT_ALL); + m_pActiveShell->DeleteRow(); + m_pActiveShell->EndUndo(); + m_pActiveShell->EndAction(); + } + else if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextTablesSupplier > xTables(xModel, uno::UNO_QUERY); + xNameAccess = xTables->getTextTables(); + } + else + nSlot = FN_FORMAT_TABLE_DLG; + break; + + case ContentTypeId::GRAPHIC : + if(nMode == EditEntryMode::DELETE) + { + m_pActiveShell->DelRight(); + } + else if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextGraphicObjectsSupplier > xGraphics(xModel, uno::UNO_QUERY); + xNameAccess = xGraphics->getGraphicObjects(); + uno::Reference< text::XTextFramesSupplier > xFrames(xModel, uno::UNO_QUERY); + xSecond = xFrames->getTextFrames(); + uno::Reference< text::XTextEmbeddedObjectsSupplier > xObjs(xModel, uno::UNO_QUERY); + xThird = xObjs->getEmbeddedObjects(); + } + else + nSlot = FN_FORMAT_GRAFIC_DLG; + break; + + case ContentTypeId::FRAME : + case ContentTypeId::OLE : + if(nMode == EditEntryMode::DELETE) + { + m_pActiveShell->DelRight(); + } + else if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextFramesSupplier > xFrames(xModel, uno::UNO_QUERY); + uno::Reference< text::XTextEmbeddedObjectsSupplier > xObjs(xModel, uno::UNO_QUERY); + if(ContentTypeId::FRAME == nType) + { + xNameAccess = xFrames->getTextFrames(); + xSecond = xObjs->getEmbeddedObjects(); + } + else + { + xNameAccess = xObjs->getEmbeddedObjects(); + xSecond = xFrames->getTextFrames(); + } + uno::Reference< text::XTextGraphicObjectsSupplier > xGraphics(xModel, uno::UNO_QUERY); + xThird = xGraphics->getGraphicObjects(); + } + else + nSlot = FN_FORMAT_FRAME_DLG; + break; + case ContentTypeId::BOOKMARK : + if(nMode == EditEntryMode::DELETE) + { + assert(!m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS)); + IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess(); + pMarkAccess->deleteMark(pMarkAccess->findMark(pCnt->GetName()), false); + } + else if(nMode == EditEntryMode::RENAME) + { + assert(!m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS)); + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XBookmarksSupplier > xBkms(xModel, uno::UNO_QUERY); + xNameAccess = xBkms->getBookmarks(); + } + else + { + // allowed despite PROTECT_BOOKMARKS: the dialog itself enforces it + SfxStringItem const name(FN_EDIT_BOOKMARK, pCnt->GetName()); + SfxPoolItem const* args[2] = { &name, nullptr }; + m_pActiveShell->GetView().GetViewFrame(). + GetDispatcher()->Execute(FN_EDIT_BOOKMARK, SfxCallMode::SYNCHRON, args); + } + break; + + case ContentTypeId::REGION : + if (nMode == EditEntryMode::DELETE) + { + assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pCnt))); + const SwSectionFormat* pSectionFormat + = static_cast<SwRegionContent*>(pCnt)->GetSectionFormat(); + m_pActiveShell->GetDoc()->DelSectionFormat( + const_cast<SwSectionFormat*>(pSectionFormat), false); + } + else if (nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextSectionsSupplier > xSects(xModel, uno::UNO_QUERY); + xNameAccess = xSects->getTextSections(); + } + else + nSlot = FN_EDIT_REGION; + break; + + case ContentTypeId::URLFIELD: + if (nMode == EditEntryMode::DELETE) + nSlot = SID_REMOVE_HYPERLINK; + else + nSlot = SID_EDIT_HYPERLINK; + break; + case ContentTypeId::REFERENCE: + { + if(nMode == EditEntryMode::DELETE) + { + const OUString& rName = pCnt->GetName(); + for (const SfxPoolItem* pItem : + m_pActiveShell->GetDoc()->GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK)) + { + assert(dynamic_cast<const SwFormatRefMark*>(pItem)); + const auto pFormatRefMark = static_cast<const SwFormatRefMark*>(pItem); + if (!pFormatRefMark) + continue; + const SwTextRefMark* pTextRef = pFormatRefMark->GetTextRefMark(); + if (pTextRef && &pTextRef->GetTextNode().GetNodes() == + &m_pActiveShell->GetNodes() && rName == pFormatRefMark->GetRefName()) + { + m_pActiveShell->GetDoc()->DeleteFormatRefMark(pFormatRefMark); + m_pActiveShell->SwViewShell::UpdateFields(); + break; + } + } + } + } + break; + case ContentTypeId::TEXTFIELD: + { + if (nMode == EditEntryMode::DELETE) + { + const SwTextFieldContent* pTextFieldCnt = + static_cast<const SwTextFieldContent*>(pCnt); + const SwTextField* pTextField = pTextFieldCnt->GetFormatField()->GetTextField(); + SwTextField::DeleteTextField(*pTextField); + } + else + nSlot = FN_EDIT_FIELD; + } + break; + case ContentTypeId::POSTIT: + { + auto& rView = m_pActiveShell->GetView(); + auto pPostItMgr = rView.GetPostItMgr(); + pPostItMgr->AssureStdModeAtShell(); + pPostItMgr->SetActiveSidebarWin(nullptr); + rView.GetEditWin().GrabFocus(); + if(nMode == EditEntryMode::DELETE) + m_pActiveShell->DelRight(); + else + nSlot = FN_POSTIT; + } + break; + case ContentTypeId::INDEX: + { + const SwTOXBase* pBase = static_cast<SwTOXBaseContent*>(pCnt)->GetTOXBase(); + switch(nMode) + { + case EditEntryMode::EDIT: + if(pBase) + { + SwPtrItem aPtrItem( FN_INSERT_MULTI_TOX, const_cast<SwTOXBase *>(pBase)); + m_pActiveShell->GetView().GetViewFrame(). + GetDispatcher()->ExecuteList(FN_INSERT_MULTI_TOX, + SfxCallMode::ASYNCHRON, { &aPtrItem }); + + } + break; + case EditEntryMode::RMV_IDX: + case EditEntryMode::DELETE: + { + if( pBase ) + m_pActiveShell->DeleteTOX(*pBase, EditEntryMode::DELETE == nMode); + } + break; + case EditEntryMode::UPD_IDX: + case EditEntryMode::RENAME: + { + Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + Reference< XDocumentIndexesSupplier > xIndexes(xModel, UNO_QUERY); + Reference< XIndexAccess> xIdxAcc(xIndexes->getDocumentIndexes()); + Reference< XNameAccess >xLocalNameAccess(xIdxAcc, UNO_QUERY); + if(EditEntryMode::RENAME == nMode) + xNameAccess = xLocalNameAccess; + else if(xLocalNameAccess.is() && xLocalNameAccess->hasByName(pBase->GetTOXName())) + { + Any aIdx = xLocalNameAccess->getByName(pBase->GetTOXName()); + Reference< XDocumentIndex> xIdx; + if(aIdx >>= xIdx) + xIdx->update(); + } + } + break; + default: break; + } + } + break; + case ContentTypeId::DRAWOBJECT : + if(EditEntryMode::DELETE == nMode) + nSlot = SID_DELETE; + else if(nMode == EditEntryMode::RENAME) + nSlot = FN_NAME_SHAPE; + else if (nMode == EditEntryMode::EDIT) + { + vcl::KeyCode aKeyCode(KEY_RETURN, false, false, false, false); + KeyEvent aKeyEvent(0, aKeyCode); + m_pActiveShell->GetWin()->KeyInput(aKeyEvent); + } + break; + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + if (EditEntryMode::EDIT == nMode) + nSlot = FN_FORMAT_FOOTNOTE_DLG; + break; + default: break; + } + if(nSlot) + m_pActiveShell->GetView().GetViewFrame(). + GetDispatcher()->Execute(nSlot, SfxCallMode::SYNCHRON); + else if(xNameAccess.is()) + { + uno::Any aObj = xNameAccess->getByName(pCnt->GetName()); + uno::Reference< uno::XInterface > xTmp; + aObj >>= xTmp; + uno::Reference< container::XNamed > xNamed(xTmp, uno::UNO_QUERY); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwRenameXNamedDlg> pDlg(pFact->CreateSwRenameXNamedDlg(m_xTreeView.get(), xNamed, xNameAccess)); + if(xSecond.is()) + pDlg->SetAlternativeAccess( xSecond, xThird); + + OUString sForbiddenChars; + if(ContentTypeId::BOOKMARK == nType) + { + sForbiddenChars = "/\\@:*?\";,.#"; + } + else if(ContentTypeId::TABLE == nType) + { + sForbiddenChars = " .<>"; + } + pDlg->SetForbiddenChars(sForbiddenChars); + pDlg->Execute(); + } + if(EditEntryMode::DELETE == nMode) + { + auto nPos = m_xTreeView->vadjustment_get_value(); + m_bViewHasChanged = true; + TimerUpdate(&m_aUpdTimer); + grab_focus(); + m_xTreeView->vadjustment_set_value(nPos); + } +} + +void SwContentTree::CopyOutlineSelections() +{ + m_pActiveShell->LockView(true); + { + MakeAllOutlineContentTemporarilyVisible a(m_pActiveShell->GetDoc()); + m_pActiveShell->AssureStdMode(); + m_pActiveShell->EnterAddMode(); + size_t nCount = m_xTreeView->get_selected_rows().size(); + m_xTreeView->selected_foreach([this, &nCount](weld::TreeIter& rEntry){ + SwOutlineNodes::size_type nOutlinePos = reinterpret_cast<SwOutlineContent*>( + m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos(); + m_pActiveShell->SttSelect(); + m_pActiveShell->MakeOutlineSel(nOutlinePos, nOutlinePos, + !m_xTreeView->get_row_expanded(rEntry), false); + // don't move if this is the last selected outline or the cursor is at start of para + if (--nCount && !m_pActiveShell->IsSttPara()) + m_pActiveShell->Right(SwCursorSkipMode::Chars, true, 1, false); + m_pActiveShell->EndSelect(); + return false; + }); + m_pActiveShell->LeaveAddMode(); + m_pActiveShell->GetView().GetViewFrame().GetBindings().Execute(SID_COPY); + } + m_pActiveShell->LockView(false); +} + +void SwContentTree::GotoContent(const SwContent* pCnt) +{ + if (pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE) + { + // Maybe the outline node doesn't have a layout frame to go to. + const SwOutlineNodes::size_type nPos = + static_cast<const SwOutlineContent*>(pCnt)->GetOutlinePos(); + const SwNodes& rNds = m_pActiveShell->GetDoc()->GetNodes(); + SwTextNode* pTextNd = rNds.GetOutLineNds()[nPos]->GetTextNode(); + if (!pTextNd->getLayoutFrame(m_pActiveShell->GetLayout())) + return; + } + + if (m_bSelectTo) + { + if (m_pActiveShell->IsCursorInTable() || + (m_pActiveShell->GetCursor()->GetPoint()->nNode.GetIndex() <= + m_pActiveShell->GetDoc()->GetNodes().GetEndOfExtras().GetIndex())) + { + m_bSelectTo = false; + m_pActiveShell->GetView().GetEditWin().GrabFocus(); + return; + } + } + + m_nLastGotoContentWasOutlinePos = SwOutlineNodes::npos; + m_sSelectedItem = ""; + + m_pActiveShell->AssureStdMode(); + + std::optional<std::unique_ptr<SwPosition>> oPosition; + if (m_bSelectTo) + oPosition.emplace(new SwPosition(m_pActiveShell->GetCursor()->GetPoint()->nNode, + m_pActiveShell->GetCursor()->GetPoint()->nContent)); + + switch(m_nLastSelType = pCnt->GetParent()->GetType()) + { + case ContentTypeId::TEXTFIELD: + { + m_pActiveShell->GotoFormatField( + *static_cast<const SwTextFieldContent*>(pCnt)->GetFormatField()); + } + break; + case ContentTypeId::OUTLINE : + { + const SwOutlineNodes::size_type nPos = + static_cast<const SwOutlineContent*>(pCnt)->GetOutlinePos(); + m_pActiveShell->GotoOutline(nPos); + m_nLastGotoContentWasOutlinePos = nPos; + } + break; + case ContentTypeId::TABLE : + { + m_pActiveShell->GotoTable(pCnt->GetName()); + } + break; + case ContentTypeId::FRAME : + case ContentTypeId::GRAPHIC : + case ContentTypeId::OLE : + { + m_pActiveShell->GotoFly(pCnt->GetName()); + } + break; + case ContentTypeId::BOOKMARK: + { + m_pActiveShell->StartAction(); + m_pActiveShell->GotoMark(pCnt->GetName()); + m_pActiveShell->EndAction(); + m_sSelectedItem = pCnt->GetName(); + + // If the hidden title of SwNavigatorPanel was emptied via UNO XPanel interface, + // store the name of the selected bookmark there. This allows to query the + // selected bookmark using UNO e.g. in add-ons, i.e. to disambiguate when + // multiple bookmarks are there on the selected text range. + // Note: this is a workaround because getDialog() of XPanel is not implemented + // for SwNavigatorPanel. + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + + Reference<frame::XController2> xController( xModel->getCurrentController(), uno::UNO_QUERY); + if ( !xController.is() ) + break; + + Reference<ui::XSidebarProvider> xSidebarProvider = xController->getSidebar(); + if ( !xSidebarProvider.is() ) + break; + + Reference<ui::XDecks> xDecks = xSidebarProvider->getDecks(); + if ( !xDecks.is() ) + break; + + if (!xDecks->hasByName("NavigatorDeck")) + break; + + Reference<ui::XDeck> xDeck ( xDecks->getByName("NavigatorDeck"), uno::UNO_QUERY); + if ( !xDeck.is() ) + break; + + Reference<ui::XPanels> xPanels = xDeck->getPanels(); + if ( !xPanels.is() ) + break; + + if (xPanels->hasByName("SwNavigatorPanel")) + { + Reference<ui::XPanel> xPanel ( xPanels->getByName("SwNavigatorPanel"), uno::UNO_QUERY); + if ( !xPanel.is() || !xPanel->getTitle().isEmpty() ) + break; + + xPanel->setTitle( pCnt->GetName() ); + } + } + break; + case ContentTypeId::REGION : + { + m_pActiveShell->GotoRegion(pCnt->GetName()); + } + break; + case ContentTypeId::URLFIELD: + { + if(m_pActiveShell->GotoINetAttr( + *static_cast<const SwURLFieldContent*>(pCnt)->GetINetAttr() )) + { + m_pActiveShell->Right( SwCursorSkipMode::Chars, true, 1, false); + m_pActiveShell->SwCursorShell::SelectTextAttr( RES_TXTATR_INETFMT, true ); + } + } + break; + case ContentTypeId::REFERENCE: + { + m_pActiveShell->GotoRefMark(pCnt->GetName()); + } + break; + case ContentTypeId::INDEX: + { + const OUString& sName(pCnt->GetName()); + if (!m_pActiveShell->GotoNextTOXBase(&sName)) + m_pActiveShell->GotoPrevTOXBase(&sName); + } + break; + case ContentTypeId::POSTIT: + m_pActiveShell->GotoFormatField(*static_cast<const SwPostItContent*>(pCnt)->GetPostIt()); + break; + case ContentTypeId::DRAWOBJECT: + { + m_pActiveShell->GotoDrawingObject(pCnt->GetName()); + } + break; + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + { + const SwTextFootnote* pFootnote = + static_cast<const SwTextFootnoteContent*>(pCnt)->GetTextFootnote(); + if (!pFootnote) + return; + m_pActiveShell->GotoFootnoteAnchor(*pFootnote); + } + break; + default: break; + } + + if (m_bSelectTo) + { + m_pActiveShell->SttCursorMove(); + while (m_pActiveShell->IsCursorInTable()) + { + m_pActiveShell->MoveTable(GotoCurrTable, fnTableStart); + if (!m_pActiveShell->Left(SwCursorSkipMode::Chars, false, 1, false)) + break; // Table is at the beginning of the document. It can't be selected this way. + } + m_pActiveShell->EndCursorMove(); + + m_pActiveShell->AssureStdMode(); + + m_pActiveShell->SetMark(); + m_pActiveShell->GetCursor()->GetMark()->nNode = oPosition.value()->nNode; + m_pActiveShell->GetCursor()->GetMark()->nContent = oPosition.value()->nContent; + m_pActiveShell->UpdateCursor(); + + m_pActiveShell->GetView().GetEditWin().GrabFocus(); + + m_bSelectTo = false; + } + else + { + if (m_pActiveShell->IsFrameSelected() || m_pActiveShell->IsObjSelected()) + { + m_pActiveShell->HideCursor(); + m_pActiveShell->EnterSelFrameMode(); + } + + SwView& rView = m_pActiveShell->GetView(); + rView.StopShellTimer(); + rView.GetPostItMgr()->SetActiveSidebarWin(nullptr); + rView.GetEditWin().GrabFocus(); + + // Assure cursor is in visible view area. + // (tdf#147041) Always show the navigated outline at the top of the visible view area. + if (pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE || + (!m_pActiveShell->IsCursorVisible() && !m_pActiveShell->IsFrameSelected() && + !m_pActiveShell->IsObjSelected())) + { + Point aPoint(rView.GetVisArea().getX(), m_pActiveShell->GetCursorDocPos().getY()); + rView.SetVisArea(aPoint); + } + } +} + +// Now even the matching text::Bookmark +NaviContentBookmark::NaviContentBookmark() + : + m_nDocSh(0), + m_nDefaultDrag( RegionMode::NONE ) +{ +} + +NaviContentBookmark::NaviContentBookmark( OUString aUrl, + OUString aDesc, + RegionMode nDragType, + const SwDocShell* pDocSh ) : + m_aUrl(std::move( aUrl )), + m_aDescription(std::move(aDesc)), + m_nDocSh(reinterpret_cast<sal_IntPtr>(pDocSh)), + m_nDefaultDrag( nDragType ) +{ +} + +void NaviContentBookmark::Copy( TransferDataContainer& rData ) const +{ + rtl_TextEncoding eSysCSet = osl_getThreadTextEncoding(); + + OString sStrBuf(OUStringToOString(m_aUrl, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) + + OUStringToOString(m_aDescription, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) + + OString::number(static_cast<int>(m_nDefaultDrag)) + OStringChar(NAVI_BOOKMARK_DELIM) + + OString::number(m_nDocSh)); + rData.CopyByteString(SotClipboardFormatId::SONLK, sStrBuf); +} + +bool NaviContentBookmark::Paste( const TransferableDataHelper& rData, const OUString& rsDesc ) +{ + OUString sStr; + bool bRet = rData.GetString( SotClipboardFormatId::SONLK, sStr ); + if( bRet ) + { + sal_Int32 nPos = 0; + m_aUrl = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ); + m_aDescription = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ); + m_nDefaultDrag= static_cast<RegionMode>( o3tl::toInt32(o3tl::getToken(sStr, 0, NAVI_BOOKMARK_DELIM, nPos )) ); + m_nDocSh = o3tl::toInt32(o3tl::getToken(sStr, 0, NAVI_BOOKMARK_DELIM, nPos )); + if (!rsDesc.isEmpty()) + m_aDescription = rsDesc; + } + return bRet; +} + +SwNavigationPI* SwContentTree::GetParentWindow() +{ + return m_pDialog; +} + +void SwContentTree::SelectContentType(std::u16string_view rContentTypeName) +{ + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xIter)) + return; + do + { + if (m_xTreeView->get_text(*xIter) == rContentTypeName) + { + m_xTreeView->set_cursor(*xIter); + Select(); + break; + } + } while (m_xTreeView->iter_next_sibling(*xIter)); +} + +IMPL_LINK_NOARG(SwContentTree, OverlayObjectDelayTimerHdl, Timer *, void) +{ + m_aOverlayObjectDelayTimer.Stop(); + if (m_xOverlayObject) + { + if (SdrView* pView = m_pActiveShell->GetDrawView()) + { + if (SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(0)) + { + const rtl::Reference<sdr::overlay::OverlayManager>& xOverlayManager = + pPaintWindow->GetOverlayManager(); + xOverlayManager->add(*m_xOverlayObject); + } + } + } +} + +void SwContentTree::OverlayObject(std::vector<basegfx::B2DRange>&& aRanges) +{ + m_aOverlayObjectDelayTimer.Stop(); + if (m_xOverlayObject && m_xOverlayObject->getOverlayManager()) + m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject); + if (aRanges.empty()) + m_xOverlayObject.reset(); + else + { + m_xOverlayObject.reset(new sdr::overlay::OverlaySelection( + sdr::overlay::OverlayType::Invert, + Color(), std::move(aRanges), true/*unused for Invert type*/)); + m_aOverlayObjectDelayTimer.Start(); + } +} + +void SwContentTree::BringEntryToAttention(const weld::TreeIter& rEntry) +{ + if (lcl_IsContent(rEntry, *m_xTreeView)) // content entry + { + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(rEntry)); + if (pCnt->IsInvisible()) + OverlayObject(); + else + { + const ContentTypeId nType = pCnt->GetParent()->GetType(); + if (nType == ContentTypeId::OUTLINE) + { + BringTypesWithFlowFramesToAttention({m_pActiveShell->GetNodes(). + GetOutLineNds()[static_cast<SwOutlineContent*>(pCnt)->GetOutlinePos()]}, + /*bIncludeTopMargin*/ false); + } + else if (nType == ContentTypeId::TABLE) + { + if (const sw::TableFrameFormats* pFrameFormats = m_pActiveShell->GetDoc()->GetTableFrameFormats()) + if (const SwTableFormat* pFrameFormat = pFrameFormats->FindFrameFormatByName(pCnt->GetName())) + { + SwTable* pTable = SwTable::FindTable(pFrameFormat); + if (pTable) + BringTypesWithFlowFramesToAttention({pTable->GetTableNode()}, false); + } + } + else if (nType == ContentTypeId::FRAME || nType == ContentTypeId::GRAPHIC || + nType == ContentTypeId::OLE) + { + SwNodeType eNodeType = SwNodeType::Text; + if(nType == ContentTypeId::GRAPHIC) + eNodeType = SwNodeType::Grf; + else if(nType == ContentTypeId::OLE) + eNodeType = SwNodeType::Ole; + if (const SwFrameFormat* pFrameFormat = + m_pActiveShell->GetDoc()->FindFlyByName(pCnt->GetName(), eNodeType)) + BringFramesToAttention(std::vector<const SwFrameFormat*> {pFrameFormat}); + } + else if (nType == ContentTypeId::BOOKMARK) + { + BringBookmarksToAttention(std::vector<OUString> {pCnt->GetName()}); + } + else if (nType == ContentTypeId::REGION || nType == ContentTypeId::INDEX) + { + size_t nSectionFormatCount = m_pActiveShell->GetSectionFormatCount(); + for (size_t i = 0; i < nSectionFormatCount; ++i) + { + const SwSectionFormat& rSectionFormat = m_pActiveShell->GetSectionFormat(i); + if (!rSectionFormat.IsInNodesArr()) + continue; + const SwSection* pSection = rSectionFormat.GetSection(); + if (!pSection) + continue; + if (pCnt->GetName() == pSection->GetSectionName()) + { + BringTypesWithFlowFramesToAttention({rSectionFormat.GetSectionNode()}); + break; + } + } + } + else if (nType == ContentTypeId::URLFIELD) + { + // tdf#159147 - Assure the SwURLFieldContent::SwTextINetFormat pointer is valid + // before bringing to attention. + const SwTextINetFormat* pTextINetFormat + = static_cast<SwURLFieldContent*>(pCnt)->GetINetAttr(); + const SwCharFormats* pFormats = m_pActiveShell->GetDoc()->GetCharFormats(); + for (auto n = pFormats->size(); 1 < n;) + { + SwIterator<SwTextINetFormat, SwCharFormat> aIter(*(*pFormats)[--n]); + for (SwTextINetFormat* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() ) + { + if (pTextINetFormat == pFnd) + { + BringURLFieldsToAttention(SwGetINetAttrs {SwGetINetAttr(pCnt->GetName(), + *pTextINetFormat)}); + break; + } + } + } + } + else if (nType == ContentTypeId::REFERENCE) + { + if (const SwTextAttr* pTextAttr = + m_pActiveShell->GetDoc()->GetRefMark(pCnt->GetName())->GetTextRefMark()) + { + std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr}; + BringReferencesToAttention(aTextAttrArr); + } + } + else if (nType == ContentTypeId::POSTIT) + { + if (const SwTextAttr* pTextAttr = + static_cast<SwPostItContent*>(pCnt)->GetPostIt()->GetTextField()) + { + std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr}; + BringPostItFieldsToAttention(aTextAttrArr); + } + } + else if (nType == ContentTypeId::DRAWOBJECT) + { + std::vector<const SdrObject*> aSdrObjectArr {GetDrawingObjectsByContent(pCnt)}; + BringDrawingObjectsToAttention(aSdrObjectArr); + } + else if (nType == ContentTypeId::TEXTFIELD) + { + if (const SwTextAttr* pTextAttr = + static_cast<SwTextFieldContent*>(pCnt)->GetFormatField()->GetTextField()) + { + std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr}; + BringTextFieldsToAttention(aTextAttrArr); + } + } + else if (nType == ContentTypeId::FOOTNOTE || nType == ContentTypeId::ENDNOTE) + { + if (const SwTextAttr* pTextAttr = + static_cast<SwTextFootnoteContent*> (pCnt)->GetTextFootnote()) + { + std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr}; + BringFootnotesToAttention(aTextAttrArr); + } + } + } + } + else // content type entry + { + SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rEntry)); + if (pCntType->GetMemberCount() == 0) + OverlayObject(); + else + { + const ContentTypeId nType = pCntType->GetType(); + if (nType == ContentTypeId::OUTLINE) + { + std::vector<const SwNode*> aNodesArr( + m_pActiveShell->GetNodes().GetOutLineNds().begin(), + m_pActiveShell->GetNodes().GetOutLineNds().end()); + BringTypesWithFlowFramesToAttention(aNodesArr, /*bIncludeTopMargin*/ false); + } + else if (nType == ContentTypeId::TABLE) + { + std::vector<const SwNode*> aNodesArr; + const size_t nCount = m_pActiveShell->GetTableFrameFormatCount(false); + const sw::TableFrameFormats& rTableFormats = *m_pActiveShell->GetDoc()->GetTableFrameFormats(); + for(size_t i = 0; i < nCount; ++i) + { + if (const SwTableFormat* pTableFormat = rTableFormats[i]) + if(pTableFormat->IsUsed()) // skip deleted tables + { + SwTable* pTable = SwTable::FindTable(pTableFormat); + if (pTable) + aNodesArr.push_back(pTable->GetTableNode()); + } + } + BringTypesWithFlowFramesToAttention(aNodesArr, false); + } + else if (nType == ContentTypeId::FRAME || nType == ContentTypeId::GRAPHIC || + nType == ContentTypeId::OLE) + { + FlyCntType eType = FLYCNTTYPE_FRM; + if(nType == ContentTypeId::GRAPHIC) + eType = FLYCNTTYPE_GRF; + else if(nType == ContentTypeId::OLE) + eType = FLYCNTTYPE_OLE; + BringFramesToAttention(m_pActiveShell->GetFlyFrameFormats(eType, true)); + } + else if (nType == ContentTypeId::BOOKMARK) + { + std::vector<OUString> aNames; + const auto nCount = pCntType->GetMemberCount(); + for (size_t i = 0; i < nCount; i++) + { + const SwContent* pMember = pCntType->GetMember(i); + if (pMember && !pMember->IsInvisible()) + aNames.push_back(pMember->GetName()); + } + BringBookmarksToAttention(aNames); + } + else if (nType == ContentTypeId::REGION || nType == ContentTypeId::INDEX) + { + std::vector<const SwNode*> aNodesArr; + const SwSectionFormats& rFormats = m_pActiveShell->GetDoc()->GetSections(); + const size_t nSize = rFormats.size(); + for (SwSectionFormats::size_type n = nSize; n;) + { + const SwSectionFormat* pSectionFormat = rFormats[--n]; + if (pSectionFormat && pSectionFormat->IsInNodesArr()) + { + const SwSection* pSection = pSectionFormat->GetSection(); + if (pSection && !pSection->IsHiddenFlag()) + { + const SectionType eSectionType = pSection->GetType(); + if (nType == ContentTypeId::REGION && + (eSectionType == SectionType::ToxContent || + eSectionType == SectionType::ToxHeader)) + continue; + if (nType == ContentTypeId::INDEX && + eSectionType != SectionType::ToxContent) + continue; + if (const SwNode* pNode = pSectionFormat->GetSectionNode()) + aNodesArr.push_back(pNode); + } + } + } + BringTypesWithFlowFramesToAttention(aNodesArr); + } + else if (nType == ContentTypeId::URLFIELD) + { + SwGetINetAttrs aINetAttrsArr; + m_pActiveShell->GetINetAttrs(aINetAttrsArr, false); + BringURLFieldsToAttention(aINetAttrsArr); + } + else if (nType == ContentTypeId::REFERENCE) + { + std::vector<const SwTextAttr*> aTextAttrArr; + for (const SfxPoolItem* pItem : + m_pActiveShell->GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK)) + { + if (const auto pRefMark = dynamic_cast<const SwFormatRefMark*>(pItem)) + { + const SwTextRefMark* pTextRef = pRefMark->GetTextRefMark(); + if (pTextRef && &pTextRef->GetTextNode().GetNodes() == + &m_pActiveShell->GetNodes()) + aTextAttrArr.push_back(pTextRef); + } + } + BringReferencesToAttention(aTextAttrArr); + } + else if (nType == ContentTypeId::POSTIT) + { + std::vector<const SwTextAttr*> aTextAttrArr; + const auto nCount = pCntType->GetMemberCount(); + for (size_t i = 0; i < nCount; i++) + { + const SwPostItContent* pPostItContent = static_cast<const SwPostItContent*>( + pCntType->GetMember(i)); + if (pPostItContent && !pPostItContent->IsInvisible()) + if (const SwFormatField* pFormatField = pPostItContent->GetPostIt()) + if (const SwTextAttr* pTextAttr = pFormatField->GetTextField()) + aTextAttrArr.push_back(pTextAttr); + } + BringPostItFieldsToAttention(aTextAttrArr); + } + else if (nType == ContentTypeId::DRAWOBJECT) + { + IDocumentDrawModelAccess& rIDDMA = m_pActiveShell->getIDocumentDrawModelAccess(); + if (const SwDrawModel* pModel = rIDDMA.GetDrawModel()) + { + if (const SdrPage* pPage = pModel->GetPage(0)) + { + if (pPage->GetObjCount()) + { + std::vector<const SdrObject*> aSdrObjectArr; + for (const rtl::Reference<SdrObject>& pObject : *pPage) + { + if (pObject && !pObject->GetName().isEmpty() && + rIDDMA.IsVisibleLayerId(pObject->GetLayer())) + aSdrObjectArr.push_back(pObject.get()); + } + BringDrawingObjectsToAttention(aSdrObjectArr); + } + } + } + } + else if (nType == ContentTypeId::TEXTFIELD) + { + std::vector<const SwTextAttr*> aTextAttrArr; + const auto nCount = pCntType->GetMemberCount(); + for (size_t i = 0; i < nCount; i++) + { + const SwTextFieldContent* pTextFieldCnt = + static_cast<const SwTextFieldContent*>(pCntType->GetMember(i)); + if (pTextFieldCnt && !pTextFieldCnt->IsInvisible()) + if (const SwFormatField* pFormatField = pTextFieldCnt->GetFormatField()) + if (const SwTextAttr* pTextAttr = pFormatField->GetTextField()) + aTextAttrArr.push_back(pTextAttr); + } + BringTextFieldsToAttention(aTextAttrArr); + } + else if (nType == ContentTypeId::FOOTNOTE || nType == ContentTypeId::ENDNOTE) + { + std::vector<const SwTextAttr*> aTextAttrArr; + const auto nCount = pCntType->GetMemberCount(); + for (size_t i = 0; i < nCount; i++) + { + const SwTextFootnoteContent* pTextFootnoteCnt = + static_cast<const SwTextFootnoteContent*>(pCntType->GetMember(i)); + if (pTextFootnoteCnt && !pTextFootnoteCnt->IsInvisible()) + if (const SwTextAttr* pTextAttr = pTextFootnoteCnt->GetTextFootnote()) + aTextAttrArr.push_back(pTextAttr); + } + BringFootnotesToAttention(aTextAttrArr); + } + } + } +} + +static void lcl_CalcOverlayRanges(const SwTextFrame* pStartFrame, const SwTextFrame* pEndFrame, + const SwPosition& aStartPos, const SwPosition& aEndPos, + std::vector<basegfx::B2DRange>& aRanges) +{ + if (pStartFrame && pEndFrame) + { + SwRect aStartCharRect; + pStartFrame->GetCharRect(aStartCharRect, aStartPos); + SwRect aEndCharRect; + pEndFrame->GetCharRect(aEndCharRect, aEndPos); + if (aStartCharRect.Top() == aEndCharRect.Top()) + { + // single line range + aRanges.emplace_back(aStartCharRect.Left(), aStartCharRect.Top(), + aEndCharRect.Right() + 1, aEndCharRect.Bottom() + 1); + } + else + { + // multi line range + SwRect aFrameRect = pStartFrame->getFrameArea(); + aRanges.emplace_back(aStartCharRect.Left(), aStartCharRect.Top(), + aFrameRect.Right(), aStartCharRect.Bottom() + 1); + if (aStartCharRect.Bottom() + 1 != aEndCharRect.Top()) + aRanges.emplace_back(aFrameRect.Left(), aStartCharRect.Bottom() + 1, + aFrameRect.Right(), aEndCharRect.Top() + 1); + aRanges.emplace_back(aFrameRect.Left(), aEndCharRect.Top() + 1, + aEndCharRect.Right() + 1, aEndCharRect.Bottom() + 1); + } + } +} + +void SwContentTree::BringFramesToAttention(const std::vector<const SwFrameFormat*>& rFrameFormats) +{ + std::vector<basegfx::B2DRange> aRanges; + for (const SwFrameFormat* pFrameFormat : rFrameFormats) + { + if (!pFrameFormat) + continue; + SwRect aFrameRect = pFrameFormat->FindLayoutRect(); + if (!aFrameRect.IsEmpty()) + aRanges.emplace_back(aFrameRect.Left(), aFrameRect.Top(), aFrameRect.Right(), + aFrameRect.Bottom()); + } + OverlayObject(std::move(aRanges)); +} + +void SwContentTree::BringBookmarksToAttention(const std::vector<OUString>& rNames) +{ + std::vector<basegfx::B2DRange> aRanges; + IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess(); + for (const auto& rName : rNames) + { + IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findBookmark(rName); + if (ppBkmk == pMarkAccess->getBookmarksEnd()) + continue; + SwPosition aMarkStart = (*ppBkmk)->GetMarkStart(); + const SwTextNode* pMarkStartTextNode = aMarkStart.GetNode().GetTextNode(); + if (!pMarkStartTextNode) + continue; + const SwTextFrame* pMarkStartFrame = static_cast<const SwTextFrame*>( + pMarkStartTextNode->getLayoutFrame(m_pActiveShell->GetLayout())); + if (!pMarkStartFrame) + continue; + SwPosition aMarkEnd = (*ppBkmk)->GetMarkEnd(); + const SwTextNode* pMarkEndTextNode = aMarkEnd.GetNode().GetTextNode(); + if (!pMarkEndTextNode) + continue; + const SwTextFrame* pMarkEndFrame = static_cast<const SwTextFrame*>( + pMarkEndTextNode->getLayoutFrame(m_pActiveShell->GetLayout())); + if (!pMarkEndFrame) + continue; + // adjust span when mark start equals mark end + if (aMarkStart == aMarkEnd) + { + if (aMarkEnd.GetContentIndex() < pMarkEndTextNode->Len() - 1) + aMarkEnd.AdjustContent(+1); + else if (aMarkStart.GetContentIndex() > 0) + aMarkStart.AdjustContent(-1); + } + lcl_CalcOverlayRanges(pMarkStartFrame, pMarkEndFrame, aMarkStart, aMarkEnd, aRanges); + } + OverlayObject(std::move(aRanges)); +} + +void SwContentTree::BringTypesWithFlowFramesToAttention(const std::vector<const SwNode*>& rNodes, + const bool bIncludeTopMargin) +{ + std::vector<basegfx::B2DRange> aRanges; + for (const auto* pNode : rNodes) + { + if (!pNode) + continue; + const SwFrame* pFrame; + if (pNode->IsContentNode() || pNode->IsTableNode()) + { + if (pNode->IsContentNode()) + pFrame = pNode->GetContentNode()->getLayoutFrame(m_pActiveShell->GetLayout()); + else // table node + { + SwNode2Layout aTmp(*pNode, pNode->GetIndex() - 1); + pFrame = aTmp.NextFrame(); + } + while (pFrame) + { + const SwRect& rFrameRect = pFrame->getFrameArea(); + if (!rFrameRect.IsEmpty()) + aRanges.emplace_back(rFrameRect.Left(), bIncludeTopMargin ? rFrameRect.Top() : + rFrameRect.Top() + pFrame->GetTopMargin(), + rFrameRect.Right(), rFrameRect.Bottom()); + if (!pFrame->IsFlowFrame()) + break; + const SwFlowFrame *pFollow = SwFlowFrame::CastFlowFrame(pFrame)->GetFollow(); + if (!pFollow) + break; + pFrame = &pFollow->GetFrame(); + } + } + else if (pNode->IsSectionNode()) + { + const SwNode* pEndOfSectionNode = pNode->EndOfSectionNode(); + SwNodeIndex aIdx(*pNode); + while (&aIdx.GetNode() != pEndOfSectionNode) + { + if (aIdx.GetNode().IsContentNode()) + { + if ((pFrame = aIdx.GetNode().GetContentNode()-> + getLayoutFrame(m_pActiveShell->GetLayout()))) + { + if (pFrame->IsInSct()) + pFrame = pFrame->FindSctFrame(); + if (pFrame) + { + const SwRect& rFrameRect = pFrame->getFrameArea(); + if (!rFrameRect.IsEmpty()) + aRanges.emplace_back(rFrameRect.Left(), rFrameRect.Top(), + rFrameRect.Right(), rFrameRect.Bottom()); + } + } + ++aIdx; + while (!aIdx.GetNode().IsEndNode() && !aIdx.GetNode().IsSectionNode()) + ++aIdx; + continue; + } + if (!aIdx.GetNode().IsSectionNode()) + { + ++aIdx; + continue; + } + SwNode2Layout aTmp(aIdx.GetNode(), aIdx.GetNode().GetIndex() - 1); + pFrame = aTmp.NextFrame(); + if (pFrame) + { + if (!pFrame->getFrameArea().IsEmpty()) + { + const SwRect& rFrameRect = pFrame->getFrameArea(); + aRanges.emplace_back(rFrameRect.Left(), rFrameRect.Top(), + rFrameRect.Right(), rFrameRect.Bottom()); + } + if (pFrame->IsSctFrame()) + { + const SwSectionFrame* pSectionFrame + = static_cast<const SwSectionFrame*>(pFrame); + if (pSectionFrame->HasFollow()) + { + const SwFlowFrame *pFollow + = SwFlowFrame::CastFlowFrame(pSectionFrame)->GetFollow(); + while (pFollow) + { + pFrame = &pFollow->GetFrame(); + if (!pFrame->getFrameArea().IsEmpty()) + { + const SwRect& rFrameRect = pFrame->getFrameArea(); + aRanges.emplace_back(rFrameRect.Left(), rFrameRect.Top(), + rFrameRect.Right(), rFrameRect.Bottom()); + } + pFollow = SwFlowFrame::CastFlowFrame(pFrame)->GetFollow(); + } + } + } + } + ++aIdx; + while (!aIdx.GetNode().IsEndNode() && !aIdx.GetNode().IsSectionNode()) + ++aIdx; + } + // Remove nested sections. This wouldn't be needed if the overlay wasn't invert type. + auto end = aRanges.end(); + for (auto it = aRanges.begin(); it != end; ++it) + end = std::remove_if(it + 1, end, [&it](auto itt){ return it->isInside(itt); }); + aRanges.erase(end, aRanges.end()); + } + } + OverlayObject(std::move(aRanges)); +} + +void SwContentTree::BringURLFieldsToAttention(const SwGetINetAttrs& rINetAttrsArr) +{ + std::vector<basegfx::B2DRange> aRanges; + for (const auto& r : rINetAttrsArr) + { + const SwTextNode& rTextNode = r.rINetAttr.GetTextNode(); + if (SwTextFrame* pFrame = static_cast<SwTextFrame*>( + rTextNode.getLayoutFrame(m_pActiveShell->GetLayout()))) + { + auto nStart = r.rINetAttr.GetStart(); + auto nEnd = r.rINetAttr.GetAnyEnd(); + SwPosition aStartPos(rTextNode, nStart), aEndPos(rTextNode, nEnd); + lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges); + } + } + OverlayObject(std::move(aRanges)); +} + +void SwContentTree::BringReferencesToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr) +{ + std::vector<basegfx::B2DRange> aRanges; + for (const SwTextAttr* p : rTextAttrsArr) + { + if (!p) + continue; + const SwTextRefMark* pTextRefMark = p->GetRefMark().GetTextRefMark(); + if (!pTextRefMark) + continue; + const SwTextNode& rTextNode = pTextRefMark->GetTextNode(); + if (SwTextFrame* pFrame = static_cast<SwTextFrame*>( + rTextNode.getLayoutFrame(m_pActiveShell->GetLayout()))) + { + auto nStart = p->GetStart(); + auto nEnd = p->GetAnyEnd(); + SwPosition aStartPos(rTextNode, nStart), aEndPos(rTextNode, nEnd); + lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges); + } + } + OverlayObject(std::move(aRanges)); +} + +void SwContentTree::BringPostItFieldsToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr) +{ + std::vector<basegfx::B2DRange> aRanges; + for (const SwTextAttr* p : rTextAttrsArr) + { + if (!p) + continue; + const SwTextField* pTextField = p->GetFormatField().GetTextField(); + if (!pTextField) + continue; + // use as a fallback when there is no mark + SwTextNode& rTextNode = pTextField->GetTextNode(); + if (!rTextNode.getLayoutFrame(m_pActiveShell->GetLayout())) + continue; + assert(dynamic_cast<const SwTextAnnotationField*>(pTextField)); + const SwTextAnnotationField* pTextAnnotationField = + static_cast<const SwTextAnnotationField*>(pTextField); + const ::sw::mark::IMark* pAnnotationMark = pTextAnnotationField->GetAnnotationMark(); + const SwPosition aMarkStart = pAnnotationMark ? pAnnotationMark->GetMarkStart() + : SwPosition(rTextNode, p->GetStart()); + const SwPosition aMarkEnd = pAnnotationMark ? pAnnotationMark->GetMarkEnd() + : SwPosition(rTextNode, p->GetAnyEnd()); + const SwTextFrame* pMarkStartFrame = static_cast<SwTextFrame*>( + aMarkStart.GetNode().GetTextNode()->getLayoutFrame(m_pActiveShell->GetLayout())); + const SwTextFrame* pMarkEndFrame = static_cast<SwTextFrame*>( + aMarkEnd.GetNode().GetTextNode()->getLayoutFrame(m_pActiveShell->GetLayout())); + if (!pMarkStartFrame || !pMarkEndFrame) + continue; + lcl_CalcOverlayRanges(pMarkStartFrame, pMarkEndFrame, aMarkStart, + aMarkEnd, aRanges); + } + OverlayObject(std::move(aRanges)); +} + +void SwContentTree::BringFootnotesToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr) +{ + std::vector<basegfx::B2DRange> aRanges; + for (const SwTextAttr* p : rTextAttrsArr) + { + if (!p) + continue; + const SwTextFootnote* pTextFootnote = p->GetFootnote().GetTextFootnote(); + if (!pTextFootnote) + continue; + const SwTextNode& rTextNode = pTextFootnote->GetTextNode(); + if (SwTextFrame* pFrame = static_cast<SwTextFrame*>( + rTextNode.getLayoutFrame(m_pActiveShell->GetLayout()))) + { + auto nStart = p->GetStart(); + auto nEnd = nStart + 1; + SwPosition aStartPos(rTextNode, nStart), aEndPos(rTextNode, nEnd); + lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges); + } + } + OverlayObject(std::move(aRanges)); +} + +void SwContentTree::BringDrawingObjectsToAttention(std::vector<const SdrObject*>& rDrawingObjectsArr) +{ + std::vector<basegfx::B2DRange> aRanges; + for (const SdrObject* pObject : rDrawingObjectsArr) + { + if (pObject) + { + tools::Rectangle aRect(pObject->GetLogicRect()); + if (!aRect.IsEmpty()) + aRanges.emplace_back(aRect.Left(), aRect.Top(), aRect.Right(), aRect.Bottom()); + } + } + OverlayObject(std::move(aRanges)); +} + +void SwContentTree::BringTextFieldsToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr) +{ + std::vector<basegfx::B2DRange> aRanges; + std::shared_ptr<SwPaM> pPamForTextField; + for (const SwTextAttr* p : rTextAttrsArr) + { + if (!p) + continue; + const SwTextField* pTextField = p->GetFormatField().GetTextField(); + if (!pTextField) + continue; + if (SwTextFrame* pFrame = static_cast<SwTextFrame*>( + pTextField->GetTextNode().getLayoutFrame(m_pActiveShell->GetLayout()))) + { + SwTextField::GetPamForTextField(*pTextField, pPamForTextField); + if (!pPamForTextField) + continue; + SwPosition aStartPos(*pPamForTextField->GetMark()); + SwPosition aEndPos(*pPamForTextField->GetPoint()); + lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges); + } + } + OverlayObject(std::move(aRanges)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/glbltree.cxx b/sw/source/uibase/utlui/glbltree.cxx new file mode 100644 index 0000000000..4ddf32dd02 --- /dev/null +++ b/sw/source/uibase/utlui/glbltree.cxx @@ -0,0 +1,1185 @@ +/* -*- 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 <o3tl/safeint.hxx> +#include <svl/stritem.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <sot/filelist.hxx> +#include <svl/eitem.hxx> +#include <vcl/graphicfilter.hxx> +#include <osl/diagnose.h> + +#include <sfx2/docinsert.hxx> +#include <sfx2/filedlghelper.hxx> + +#include <wrtsh.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <edglbldc.hxx> +#include <section.hxx> +#include <tox.hxx> +#include <navipi.hxx> +#include <edtwin.hxx> +#include <toxmgr.hxx> + +#include <cmdid.h> +#include <helpids.h> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <swabstdlg.hxx> +#include <memory> + +#include <sfx2/event.hxx> +#include <unotxvw.hxx> + +using namespace ::com::sun::star::uno; + +#define GLOBAL_UPDATE_TIMEOUT 2000 + +const SfxObjectShell* SwGlobalTree::s_pShowShell = nullptr; + +namespace { + +class SwGlobalFrameListener_Impl : public SfxListener +{ + bool m_bValid; +public: + explicit SwGlobalFrameListener_Impl(SfxViewFrame& rFrame) + : m_bValid(true) + { + StartListening(rFrame); + } + + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + bool IsValid() const {return m_bValid;} +}; + +} + +void SwGlobalFrameListener_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) +{ + if( rHint.GetId() == SfxHintId::Dying) + m_bValid = false; +} + +namespace { + +enum GLOBAL_CONTEXT_IDX +{ + IDX_STR_UPDATE = 0, + IDX_STR_EDIT_CONTENT = 1, + IDX_STR_EDIT_INSERT = 2, + IDX_STR_INDEX = 3, + IDX_STR_FILE = 4, + IDX_STR_NEW_FILE = 5, + IDX_STR_INSERT_TEXT = 6, + IDX_STR_DELETE = 7, + IDX_STR_UPDATE_SEL = 8, + IDX_STR_UPDATE_INDEX = 9, + IDX_STR_UPDATE_LINK = 10, + IDX_STR_UPDATE_ALL = 11, + IDX_STR_BROKEN_LINK = 12, + IDX_STR_EDIT_LINK = 13 +}; + +} + +const TranslateId GLOBAL_CONTEXT_ARY[] = +{ + STR_UPDATE, + STR_EDIT_CONTENT, + STR_EDIT_INSERT, + STR_INDEX, + STR_FILE, + STR_NEW_FILE, + STR_INSERT_TEXT, + STR_DELETE, + STR_UPDATE_SEL, + STR_UPDATE_INDEX, + STR_UPDATE_LINK, + STR_UPDATE_ALL, + STR_BROKEN_LINK, + STR_EDIT_LINK +}; + +SwGlobalTree::SwGlobalTree(std::unique_ptr<weld::TreeView> xTreeView, SwNavigationPI* pDialog) + : m_xTreeView(std::move(xTreeView)) + , m_aDropTargetHelper(*this) + , m_pDialog(pDialog) + , m_aUpdateTimer("SwGlobalTree m_aUpdateTimer") + , m_pActiveShell(nullptr) +{ + m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 30, + m_xTreeView->get_text_height() * 14); + + m_aUpdateTimer.SetTimeout(GLOBAL_UPDATE_TIMEOUT); + m_aUpdateTimer.SetInvokeHandler(LINK(this, SwGlobalTree, Timeout)); + m_aUpdateTimer.Start(); + for (sal_uInt16 i = 0; i < GLOBAL_CONTEXT_COUNT; i++) + { + m_aContextStrings[i] = SwResId(GLOBAL_CONTEXT_ARY[i]); + } + m_xTreeView->set_help_id(HID_NAVIGATOR_GLOB_TREELIST); + Select(); + m_xTreeView->connect_row_activated(LINK(this, SwGlobalTree, DoubleClickHdl)); + m_xTreeView->connect_changed(LINK(this, SwGlobalTree, SelectHdl)); + m_xTreeView->connect_focus_in(LINK(this, SwGlobalTree, FocusInHdl)); + m_xTreeView->connect_key_press(LINK(this, SwGlobalTree, KeyInputHdl)); + m_xTreeView->connect_popup_menu(LINK(this, SwGlobalTree, CommandHdl)); + m_xTreeView->connect_query_tooltip(LINK(this, SwGlobalTree, QueryTooltipHdl)); +} + +SwGlobalTree::~SwGlobalTree() +{ + m_pSwGlblDocContents.reset(); + m_pDocInserter.reset(); + m_aUpdateTimer.Stop(); +} + +SwGlobalTreeDropTarget::SwGlobalTreeDropTarget(SwGlobalTree& rTreeView) + : DropTargetHelper(rTreeView.get_widget().get_drop_target()) + , m_rTreeView(rTreeView) +{ +} + +sal_Int8 SwGlobalTreeDropTarget::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + + weld::TreeView& rWidget = m_rTreeView.get_widget(); + std::unique_ptr<weld::TreeIter> xDropEntry(rWidget.make_iterator()); + if (!rWidget.get_dest_row_at_pos(rEvt.maPosPixel, xDropEntry.get(), true)) + xDropEntry.reset(); + + if (rWidget.get_drag_source() == &rWidget) // internal drag + m_rTreeView.MoveSelectionTo(xDropEntry.get()); + else + { + TransferableDataHelper aData( rEvt.maDropEvent.Transferable ); + + OUString sFileName; + const SwGlblDocContent* pCnt = xDropEntry ? + weld::fromId<const SwGlblDocContent*>(rWidget.get_id(*xDropEntry)) : + nullptr; + if( aData.HasFormat( SotClipboardFormatId::FILE_LIST )) + { + nRet = rEvt.mnAction; + SwGlblDocContents aTempContents; + int nAbsContPos = xDropEntry ? + rWidget.get_iter_index_in_parent(*xDropEntry): + - 1; + size_t nEntryCount = rWidget.n_children(); + + // Get data + FileList aFileList; + aData.GetFileList( SotClipboardFormatId::FILE_LIST, aFileList ); + for ( size_t n = aFileList.Count(); n--; ) + { + sFileName = aFileList.GetFile(n); + m_rTreeView.InsertRegion(pCnt, &sFileName); + // The list of contents must be newly fetched after inserting, + // to not work on an old content. + if(n) + { + if (const SwWrtShell* pSh = m_rTreeView.GetActiveWrtShell()) + { + pSh->GetGlobalDocContent(aTempContents); + // If the file was successfully inserted, + // then the next content must also be fetched. + if(nEntryCount < aTempContents.size()) + { + nEntryCount++; + nAbsContPos++; + pCnt = aTempContents[ nAbsContPos ].get(); + } + } + } + } + } + else if( !(sFileName = + SwNavigationPI::CreateDropFileName( aData )).isEmpty()) + { + INetURLObject aTemp(sFileName); + GraphicDescriptor aDesc(aTemp); + if( !aDesc.Detect() ) // accept no graphics + { + nRet = rEvt.mnAction; + m_rTreeView.InsertRegion(pCnt, &sFileName); + } + } + } + return nRet; +} + +sal_Int8 SwGlobalTreeDropTarget::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + // to enable the autoscroll when we're close to the edges + weld::TreeView& rWidget = m_rTreeView.get_widget(); + rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true); + + sal_Int8 nRet = rEvt.mnAction; + + if (rWidget.get_drag_source() == &rWidget) // internal drag + return nRet; + + if (IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE) || + IsDropFormatSupported( SotClipboardFormatId::STRING) || + IsDropFormatSupported( SotClipboardFormatId::FILE_LIST) || + IsDropFormatSupported( SotClipboardFormatId::SOLK) || + IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK )|| + IsDropFormatSupported( SotClipboardFormatId::FILECONTENT) || + IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR) || + IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR) || + IsDropFormatSupported( SotClipboardFormatId::FILENAME)) + { + nRet = DND_ACTION_LINK; + } + + return nRet; +} + +IMPL_LINK(SwGlobalTree, CommandHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + bool bPop = false; + if (m_pActiveShell && !m_pActiveShell->GetView().GetDocShell()->IsReadOnly()) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "modules/swriter/ui/mastercontextmenu.ui")); + std::unique_ptr<weld::Menu> xPopup = xBuilder->weld_menu("navmenu"); + std::unique_ptr<weld::Menu> xSubPopup = xBuilder->weld_menu("insertmenu"); + + const MenuEnableFlags nEnableFlags = GetEnableFlags(); + + xPopup->set_sensitive("updatesel", bool(nEnableFlags & MenuEnableFlags::UpdateSel)); + + xPopup->set_sensitive("editlink", bool(nEnableFlags & MenuEnableFlags::EditLink)); + + //disabling if applicable + xSubPopup->set_sensitive("insertindex", bool(nEnableFlags & MenuEnableFlags::InsertIdx )); + xSubPopup->set_sensitive("insertfile", bool(nEnableFlags & MenuEnableFlags::InsertFile)); + xSubPopup->set_sensitive("insertnewfile", bool(nEnableFlags & MenuEnableFlags::InsertFile)); + xSubPopup->set_sensitive("inserttext", bool(nEnableFlags & MenuEnableFlags::InsertText)); + + xPopup->set_sensitive("update", bool(nEnableFlags & MenuEnableFlags::Update)); + xPopup->set_sensitive("insert", bool(nEnableFlags & MenuEnableFlags::InsertIdx)); + xPopup->set_sensitive("editcontent", bool(nEnableFlags & MenuEnableFlags::Edit)); + xPopup->set_sensitive("deleteentry", bool(nEnableFlags & MenuEnableFlags::Delete)); + + OUString sCommand = xPopup->popup_at_rect(m_xTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + if (!sCommand.isEmpty()) + ExecuteContextMenuAction(sCommand); + + bPop = true; + } + return bPop; +} + +void SwGlobalTree::TbxMenuHdl(std::u16string_view rCommand, weld::Menu& rMenu) +{ + const MenuEnableFlags nEnableFlags = GetEnableFlags(); + if (rCommand == u"insert") + { + rMenu.set_sensitive("insertindex", bool(nEnableFlags & MenuEnableFlags::InsertIdx)); + rMenu.set_sensitive("insertfile", bool(nEnableFlags & MenuEnableFlags::InsertFile)); + rMenu.set_sensitive("insertnewfile", bool(nEnableFlags & MenuEnableFlags::InsertFile)); + rMenu.set_sensitive("inserttext", bool(nEnableFlags & MenuEnableFlags::InsertText)); + } + else if (rCommand == u"update") + { + rMenu.set_sensitive("updatesel", bool(nEnableFlags & MenuEnableFlags::UpdateSel)); + } +} + +MenuEnableFlags SwGlobalTree::GetEnableFlags() const +{ + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_selected(xEntry.get()); + + int nSelCount = m_xTreeView->count_selected_rows(); + size_t nEntryCount = m_xTreeView->n_children(); + std::unique_ptr<weld::TreeIter> xPrevEntry; + bool bPrevEntry = false; + if (bEntry) + { + xPrevEntry = m_xTreeView->make_iterator(xEntry.get()); + bPrevEntry = m_xTreeView->iter_previous(*xPrevEntry); + } + + MenuEnableFlags nRet = MenuEnableFlags::NONE; + if(nSelCount == 1 || !nEntryCount) + nRet |= MenuEnableFlags::InsertIdx|MenuEnableFlags::InsertFile; + if(nSelCount == 1) + { + nRet |= MenuEnableFlags::Edit; + if (bEntry && weld::fromId<SwGlblDocContent*>(m_xTreeView->get_id(*xEntry))->GetType() != GLBLDOC_UNKNOWN && + (!bPrevEntry || weld::fromId<SwGlblDocContent*>(m_xTreeView->get_id(*xPrevEntry))->GetType() != GLBLDOC_UNKNOWN)) + nRet |= MenuEnableFlags::InsertText; + if (bEntry && GLBLDOC_SECTION == weld::fromId<SwGlblDocContent*>(m_xTreeView->get_id(*xEntry))->GetType()) + nRet |= MenuEnableFlags::EditLink; + } + else if(!nEntryCount) + { + nRet |= MenuEnableFlags::InsertText; + } + if(nEntryCount) + nRet |= MenuEnableFlags::Update|MenuEnableFlags::Delete; + if(nSelCount) + nRet |= MenuEnableFlags::UpdateSel; + return nRet; +} + +IMPL_LINK(SwGlobalTree, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString) +{ + OUString sEntry; + + const SwGlblDocContent* pCont = weld::fromId<const SwGlblDocContent*>(m_xTreeView->get_id(rIter)); + if (pCont && GLBLDOC_SECTION == pCont->GetType()) + { + const SwSection* pSect = pCont->GetSection(); + sEntry = pSect->GetLinkFileName().getToken(0, sfx2::cTokenSeparator); + if (!pSect->IsConnectFlag()) + sEntry = m_aContextStrings[IDX_STR_BROKEN_LINK] + sEntry; + } + + return sEntry; +} + +IMPL_LINK_NOARG(SwGlobalTree, SelectHdl, weld::TreeView&, void) +{ + Select(); +} + +void SwGlobalTree::Select() +{ + int nSelCount = m_xTreeView->count_selected_rows(); + int nSel = m_xTreeView->get_selected_index(); + int nAbsPos = nSel != -1 ? nSel : 0; + SwNavigationPI* pNavi = GetParentWindow(); + bool bReadonly = !m_pActiveShell || + m_pActiveShell->GetView().GetDocShell()->IsReadOnly(); + pNavi->m_xGlobalToolBox->set_item_sensitive("edit", nSelCount == 1 && !bReadonly); + pNavi->m_xGlobalToolBox->set_item_sensitive("insert", nSelCount <= 1 && !bReadonly); + pNavi->m_xGlobalToolBox->set_item_sensitive("update", m_xTreeView->n_children() > 0 && !bReadonly); + pNavi->m_xGlobalToolBox->set_item_sensitive("moveup", + nSelCount == 1 && nAbsPos && !bReadonly); + pNavi->m_xGlobalToolBox->set_item_sensitive("movedown", + nSelCount == 1 && nAbsPos < m_xTreeView->n_children() - 1 && !bReadonly); + +} + +void SwGlobalTree::MoveSelectionTo(const weld::TreeIter* pDropEntry) +{ + int nSource = m_xTreeView->get_selected_index(); + + int nDest = pDropEntry ? m_xTreeView->get_iter_index_in_parent(*pDropEntry) + : m_pSwGlblDocContents->size(); + + if (m_pActiveShell->MoveGlobalDocContent( + *m_pSwGlblDocContents, nSource, nSource + 1, nDest ) && + Update( false )) + Display(); +} + +IMPL_LINK_NOARG(SwGlobalTree, FocusInHdl, weld::Widget&, void) +{ + if (Update(false)) + Display(); +} + +IMPL_LINK(SwGlobalTree, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = false; + const vcl::KeyCode aCode = rKEvt.GetKeyCode(); + if (aCode.GetCode() == KEY_RETURN) + { + switch (aCode.GetModifier()) + { + case KEY_MOD2: + // Switch boxes + GetParentWindow()->ToggleTree(); + bHandled = true; + break; + } + } + return bHandled; +} + +void SwGlobalTree::Display(bool bOnlyUpdateUserData) +{ + size_t nCount = m_pSwGlblDocContents->size(); + size_t nChildren = m_xTreeView->n_children(); + if (bOnlyUpdateUserData && nChildren == m_pSwGlblDocContents->size()) + { + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + bool bEntry = m_xTreeView->get_iter_first(*xEntry); + for (size_t i = 0; i < nCount && bEntry; i++) + { + const SwGlblDocContent* pCont = (*m_pSwGlblDocContents)[i].get(); + OUString sId(weld::toId(pCont)); + m_xTreeView->set_id(*xEntry, sId); + if (pCont->GetType() == GLBLDOC_SECTION && !pCont->GetSection()->IsConnectFlag()) + m_xTreeView->set_font_color(*xEntry, COL_LIGHTRED); + else + m_xTreeView->set_font_color(*xEntry, COL_AUTO); + bEntry = m_xTreeView->iter_next(*xEntry); + assert(bEntry || i == nCount - 1); + } + } + else + { + int nOldSelEntry = m_xTreeView->get_selected_index(); + OUString sEntryName; // Name of the entry + int nSelPos = -1; + if (nOldSelEntry != -1) + { + sEntryName = m_xTreeView->get_text(nOldSelEntry); + nSelPos = nOldSelEntry; + } + m_xTreeView->freeze(); + m_xTreeView->clear(); + + int nSelEntry = -1; + for (size_t i = 0; i < nCount; ++i) + { + const SwGlblDocContent* pCont = (*m_pSwGlblDocContents)[i].get(); + + OUString sId(weld::toId(pCont)); + OUString sEntry; + OUString aImage; + switch (pCont->GetType()) + { + case GLBLDOC_UNKNOWN: + sEntry = m_aContextStrings[IDX_STR_INSERT_TEXT]; + break; + case GLBLDOC_TOXBASE: + { + const SwTOXBase* pBase = pCont->GetTOX(); + sEntry = pBase->GetTitle(); + aImage = RID_BMP_NAVI_INDEX; + } + break; + case GLBLDOC_SECTION: + { + const SwSection* pSect = pCont->GetSection(); + sEntry = pSect->GetSectionName(); + aImage = RID_BMP_DROP_REGION; + } + break; + } + + m_xTreeView->append(sId, sEntry); + if (!aImage.isEmpty()) + m_xTreeView->set_image(i, aImage); + + if (pCont->GetType() == GLBLDOC_SECTION && !pCont->GetSection()->IsConnectFlag()) + m_xTreeView->set_font_color(i, COL_LIGHTRED); + + if (sEntry == sEntryName) + nSelEntry = i; + } + m_xTreeView->thaw(); + if (nSelEntry != -1) + m_xTreeView->select(nSelEntry); + else if (nSelPos != -1 && o3tl::make_unsigned(nSelPos) < nCount) + m_xTreeView->select(nSelPos); + else if (nCount) + m_xTreeView->select(0); + Select(); + } +} + +void SwGlobalTree::InsertRegion( const SwGlblDocContent* pCont, const OUString* pFileName ) +{ + Sequence< OUString > aFileNames; + if ( !pFileName ) + { + SwNavigationPI* pNavi = GetParentWindow(); + m_pDocInserter.reset(new ::sfx2::DocumentInserter(pNavi->GetFrameWeld(), "swriter", sfx2::DocumentInserter::Mode::InsertMulti)); + m_pDocInserter->StartExecuteModal( LINK( this, SwGlobalTree, DialogClosedHdl ) ); + } + else if ( !pFileName->isEmpty() ) + { + aFileNames.realloc(1); + INetURLObject aFileName; + aFileName.SetSmartURL( *pFileName ); + // tdf#127978 - don't URL encode filename for navigator's tooltip + aFileNames.getArray()[0] + = aFileName.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous); + InsertRegion( pCont, aFileNames ); + } +} + +void SwGlobalTree::EditContent(const SwGlblDocContent* pCont ) +{ + sal_uInt16 nSlot = 0; + switch( pCont->GetType() ) + { + case GLBLDOC_UNKNOWN: + m_pActiveShell->GetView().GetEditWin().GrabFocus(); + break; + case GLBLDOC_TOXBASE: + { + const SwTOXBase* pBase = pCont->GetTOX(); + if(pBase) + nSlot = FN_INSERT_MULTI_TOX; + } + break; + case GLBLDOC_SECTION: + { + OpenDoc(pCont); + + nSlot = 0; + pCont = nullptr; + } + break; + } + if(pCont) + GotoContent(pCont); + if(nSlot) + { + m_pActiveShell->GetView().GetViewFrame().GetDispatcher()->Execute(nSlot); + if(Update( false )) + Display(); + } +} + +void SwGlobalTree::ExecuteContextMenuAction(std::u16string_view rSelectedPopupEntry) +{ + bool bUpdateHard = false; + + int nEntry = m_xTreeView->get_selected_index(); + SwGlblDocContent* pCont = nEntry != -1 ? weld::fromId<SwGlblDocContent*>(m_xTreeView->get_id(nEntry)) : nullptr; + // If a RequestHelp is called during the dialogue, + // then the content gets lost. Because of that a copy + // is created in which only the DocPos is set correctly. + std::optional<SwGlblDocContent> oContCopy; + if(pCont) + oContCopy.emplace(pCont->GetDocPos()); + SfxDispatcher& rDispatch = *m_pActiveShell->GetView().GetViewFrame().GetDispatcher(); + sal_uInt16 nSlot = 0; + if (rSelectedPopupEntry == u"updatesel") + { + // Two passes: first update the areas, then the directories. + m_xTreeView->selected_foreach([this](weld::TreeIter& rSelEntry){ + SwGlblDocContent* pContent = weld::fromId<SwGlblDocContent*>(m_xTreeView->get_id(rSelEntry)); + if (GLBLDOC_SECTION == pContent->GetType() && + pContent->GetSection()->IsConnected()) + { + const_cast<SwSection*>(pContent->GetSection())->UpdateNow(); + } + return false; + }); + m_xTreeView->selected_foreach([this](weld::TreeIter& rSelEntry){ + SwGlblDocContent* pContent = weld::fromId<SwGlblDocContent*>(m_xTreeView->get_id(rSelEntry)); + if (GLBLDOC_TOXBASE == pContent->GetType()) + m_pActiveShell->UpdateTableOf(*pContent->GetTOX()); + return false; + }); + + bUpdateHard = true; + } + else if (rSelectedPopupEntry == u"updateindex") + { + nSlot = FN_UPDATE_TOX; + bUpdateHard = true; + } + else if (rSelectedPopupEntry == u"updatelinks" || rSelectedPopupEntry == u"updateall") + { + m_pActiveShell->GetLinkManager().UpdateAllLinks(true, false, nullptr); + if (rSelectedPopupEntry == u"updateall") + nSlot = FN_UPDATE_TOX; + pCont = nullptr; + bUpdateHard = true; + } + else if (rSelectedPopupEntry == u"editcontent") + { + OSL_ENSURE(pCont, "edit without entry ? " ); + if (pCont) + { + EditContent(pCont); + } + } + else if (rSelectedPopupEntry == u"editlink") + { + OSL_ENSURE(pCont, "edit without entry ? " ); + if (pCont) + { + SfxStringItem aName(FN_EDIT_REGION, + pCont->GetSection()->GetSectionName()); + rDispatch.ExecuteList(FN_EDIT_REGION, SfxCallMode::ASYNCHRON, + { &aName }); + } + } + else if (rSelectedPopupEntry == u"deleteentry") + { + // If several entries selected, then after each delete the array + // must be refilled. So you do not have to remember anything, + // deleting begins at the end. + std::vector<int> aRows = m_xTreeView->get_selected_rows(); + std::sort(aRows.begin(), aRows.end()); + + std::unique_ptr<SwGlblDocContents> pTempContents; + m_pActiveShell->StartAction(); + for (auto iter = aRows.rbegin(); iter != aRows.rend(); ++iter) + { + m_pActiveShell->DeleteGlobalDocContent( + pTempContents ? *pTempContents : *m_pSwGlblDocContents, + *iter); + pTempContents.reset(new SwGlblDocContents); + m_pActiveShell->GetGlobalDocContent(*pTempContents); + } + pTempContents.reset(); + m_pActiveShell->EndAction(); + pCont = nullptr; + } + else if (rSelectedPopupEntry == u"insertindex") + { + if(oContCopy) + { + SfxItemSetFixed< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_LR_SPACE, RES_LR_SPACE, + RES_BACKGROUND, RES_BACKGROUND, + RES_COL, RES_COL, + SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE, + FN_PARAM_TOX_TYPE, FN_PARAM_TOX_TYPE> + aSet( m_pActiveShell->GetView().GetPool() ); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractMultiTOXTabDialog> pDlg(pFact->CreateMultiTOXTabDialog( + m_xTreeView.get(), aSet, + *m_pActiveShell, + nullptr, + true)); + if(RET_OK == pDlg->Execute()) + { + SwTOXDescription& rDesc = pDlg->GetTOXDescription( + pDlg->GetCurrentTOXType()); + SwTOXMgr aMgr(m_pActiveShell); + SwTOXBase* pToInsert = nullptr; + if(aMgr.UpdateOrInsertTOX(rDesc, &pToInsert, pDlg->GetOutputItemSet())) + m_pActiveShell->InsertGlobalDocContent( *oContCopy, *pToInsert ); + } + pCont = nullptr; + } + } + else if (rSelectedPopupEntry == u"insertfile") + { + m_oDocContent = std::move(oContCopy); + InsertRegion( &*m_oDocContent ); + pCont = nullptr; + } + else if (rSelectedPopupEntry == u"insertnewfile") + { + SfxViewFrame& rGlobFrame = m_pActiveShell->GetView().GetViewFrame(); + SwGlobalFrameListener_Impl aFrameListener(rGlobFrame); + + // Creating a new doc + SfxStringItem aFactory(SID_NEWDOCDIRECT, + SwDocShell::Factory().GetFilterContainer()->GetName()); + + SfxPoolItemHolder aResult( + rDispatch.ExecuteList(SID_NEWDOCDIRECT, + SfxCallMode::SYNCHRON, { &aFactory })); + const SfxFrameItem* pItem(static_cast<const SfxFrameItem*>(aResult.getItem())); + + // save at + SfxFrame* pFrame = pItem ? pItem->GetFrame() : nullptr; + SfxViewFrame* pViewFrame = pFrame ? pFrame->GetCurrentViewFrame() : nullptr; + if (pViewFrame) + { + aResult = pViewFrame->GetDispatcher()->Execute( + SID_SAVEASDOC, SfxCallMode::SYNCHRON ); + const SfxBoolItem* pBool(static_cast<const SfxBoolItem*>(aResult.getItem())); + SfxObjectShell& rObj = *pViewFrame->GetObjectShell(); + const SfxMedium* pMedium = rObj.GetMedium(); + OUString sNewFile(pMedium->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri)); + // Insert the area with the Doc-Name + // Bring the own Doc in the foreground + if(aFrameListener.IsValid() && !sNewFile.isEmpty()) + { + rGlobFrame.ToTop(); + // Due to the update the entries are invalid + if (nEntry != -1) + { + Update( false ); + Display(); + m_xTreeView->select(nEntry); + Select(); + nEntry = m_xTreeView->get_selected_index(); + pCont = nEntry != -1 ? weld::fromId<SwGlblDocContent*>(m_xTreeView->get_id(nEntry)) : nullptr; + } + else + { + nEntry = -1; + pCont = nullptr; + } + if(pBool->GetValue()) + { + InsertRegion(pCont, &sNewFile); + pViewFrame->ToTop(); + } + else + pViewFrame->GetDispatcher()->Execute(SID_CLOSEWIN, SfxCallMode::SYNCHRON); + } + else + { + pViewFrame->ToTop(); + return; + } + } + } + else if (rSelectedPopupEntry == u"inserttext") + { + if (pCont) + m_pActiveShell->InsertGlobalDocContent(*pCont); + else + { + m_pActiveShell->SplitNode(); // Empty document + m_pActiveShell->Up( false ); + } + m_pActiveShell->GetView().GetEditWin().GrabFocus(); + } + else if (rSelectedPopupEntry == u"update") + pCont = nullptr; + + if (pCont) + GotoContent(pCont); + if (nSlot) + rDispatch.Execute(nSlot); + if (Update(bUpdateHard)) + Display(); +} + +IMPL_LINK_NOARG(SwGlobalTree, Timeout, Timer *, void) +{ + SwView* pView = GetParentWindow()->GetCreateView(); + if (pView && pView->GetEditWin().HasFocus()) + { + if (Update(false)) + Display(); + UpdateTracking(); + } +} + +// track GlobalDocContentType at the cursor position in the document +void SwGlobalTree::UpdateTracking() +{ + if (!m_pActiveShell) + return; + + m_xTreeView->unselect_all(); + + const SwSection* pActiveShellCurrSection = m_pActiveShell->GetCurrSection(); + if (pActiveShellCurrSection) + { + const SwSection* pSection = pActiveShellCurrSection; + while (SwSection* pParent = pSection->GetParent()) + pSection = pParent; + for (const std::unique_ptr<SwGlblDocContent>& rGlblDocContent : *m_pSwGlblDocContents) + { + if (rGlblDocContent->GetType() == GlobalDocContentType::GLBLDOC_UNKNOWN) + continue; + if ((pSection->GetType() == SectionType::ToxContent + && rGlblDocContent->GetTOX() == pSection->GetTOXBase()) + || (pSection->GetType() != SectionType::ToxContent + && rGlblDocContent->GetSection() == pSection)) + { + const OUString& rId(weld::toId(rGlblDocContent.get())); + m_xTreeView->select(m_xTreeView->find_id(rId)); + break; + } + } + } + else + { + const SwCursor* pCursor = m_pActiveShell->GetCursor(); + const SwNode& rNode = pCursor->GetPoint()->GetNode(); + if (rNode.IsTextNode()) + { + // only the first text node in each series of text nodes is stored in the + // SwGlblDocContents array + SwNodeIndex aIdx(rNode); + do + { + --aIdx; + } while (aIdx.GetNode().IsTextNode()); + ++aIdx; + SwNodeOffset aTextNodeIndex(aIdx.GetNode().GetIndex()); + for (const std::unique_ptr<SwGlblDocContent>& rGlblDocContent : *m_pSwGlblDocContents) + { + if (rGlblDocContent->GetType() == GlobalDocContentType::GLBLDOC_UNKNOWN + && rGlblDocContent->GetDocPos() == aTextNodeIndex) + { + const OUString& rId(weld::toId(rGlblDocContent.get())); + m_xTreeView->select(m_xTreeView->find_id(rId)); + } + } + } + } + + Select(); +} + +void SwGlobalTree::GotoContent(const SwGlblDocContent* pCont) +{ + m_pActiveShell->EnterStdMode(); + + switch( pCont->GetType() ) + { + case GLBLDOC_UNKNOWN: + m_pActiveShell->GotoGlobalDocContent(*pCont); + break; + case GLBLDOC_TOXBASE: + { + const OUString sName = pCont->GetTOX()->GetTOXName(); + if (!m_pActiveShell->GotoNextTOXBase(&sName)) + m_pActiveShell->GotoPrevTOXBase(&sName); + } + break; + case GLBLDOC_SECTION: + break; + } + +} + +void SwGlobalTree::ShowTree() +{ + m_aUpdateTimer.Start(); + m_xTreeView->show(); + UpdateTracking(); +} + +void SwGlobalTree::HideTree() +{ + m_aUpdateTimer.Stop(); + m_xTreeView->hide(); +} + +void SwGlobalTree::ExecCommand(std::u16string_view rCmd) +{ + int nEntry = m_xTreeView->get_selected_index(); + if (nEntry == -1) + return; + if (rCmd == u"edit") + { + const SwGlblDocContent* pCont = weld::fromId<const SwGlblDocContent*>( + m_xTreeView->get_id(nEntry)); + EditContent(pCont); + } + else + { + if (m_xTreeView->count_selected_rows() == 1) + { + bool bMove = false; + int nSource = nEntry; + int nDest = nSource; + if (rCmd == u"movedown") + { + int nEntryCount = m_xTreeView->n_children(); + bMove = nEntryCount > nSource + 1; + nDest+= 2; + } + else if (rCmd == u"moveup") + { + bMove = 0 != nSource; + nDest--; + } + if( bMove && m_pActiveShell->MoveGlobalDocContent( + *m_pSwGlblDocContents, nSource, nSource + 1, nDest ) && + Update( false )) + Display(); + } + } +} + +bool SwGlobalTree::Update(bool bHard) +{ + SwView* pActView = GetParentWindow()->GetCreateView(); + bool bRet = false; + if (pActView && pActView->GetWrtShellPtr()) + { + const SwWrtShell* pOldShell = m_pActiveShell; + m_pActiveShell = pActView->GetWrtShellPtr(); + if(m_pActiveShell != pOldShell) + { + m_pSwGlblDocContents.reset(); + if (!IsListening(*m_pActiveShell->GetView().GetDocShell())) + StartListening(*m_pActiveShell->GetView().GetDocShell()); + } + if(!m_pSwGlblDocContents) + { + m_pSwGlblDocContents.reset(new SwGlblDocContents); + bRet = true; + m_pActiveShell->GetGlobalDocContent(*m_pSwGlblDocContents); + } + else + { + bool bCopy = false; + SwGlblDocContents aTempContents; + m_pActiveShell->GetGlobalDocContent(aTempContents); + size_t nChildren = m_xTreeView->n_children(); + if (aTempContents.size() != m_pSwGlblDocContents->size() || + aTempContents.size() != nChildren) + { + bRet = true; + bCopy = true; + } + else + { + for(size_t i = 0; i < aTempContents.size() && !bCopy; i++) + { + SwGlblDocContent* pLeft = aTempContents[i].get(); + SwGlblDocContent* pRight = (*m_pSwGlblDocContents)[i].get(); + GlobalDocContentType eType = pLeft->GetType(); + OUString sTemp = m_xTreeView->get_text(i); + if ( + eType != pRight->GetType() || + ( + eType == GLBLDOC_SECTION && + pLeft->GetSection()->GetSectionName() != sTemp + ) || + ( + eType == GLBLDOC_TOXBASE && + pLeft->GetTOX()->GetTitle() != sTemp + ) + ) + { + bCopy = true; + } + } + } + if (bCopy || bHard) + { + *m_pSwGlblDocContents = std::move( aTempContents ); + bRet = true; + } + } + } + else + { + m_xTreeView->clear(); + if(m_pSwGlblDocContents) + m_pSwGlblDocContents->clear(); + } + // FIXME: Implement a test for changes! + return bRet; +} + +void SwGlobalTree::OpenDoc(const SwGlblDocContent* pCont) +{ + const OUString sFileName(pCont->GetSection()->GetLinkFileName().getToken(0, + sfx2::cTokenSeparator)); + bool bFound = false; + const SfxObjectShell* pCurr = SfxObjectShell::GetFirst(); + while( !bFound && pCurr ) + { + if(pCurr->GetMedium() && + pCurr->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri) == sFileName) + { + bFound = true; + SwGlobalTree::SetShowShell(pCurr); + Application::PostUserEvent(LINK(this, SwGlobalTree, ShowFrameHdl)); + pCurr = nullptr; + } + else + pCurr = SfxObjectShell::GetNext(*pCurr); + } + if(!bFound) + { + SfxStringItem aURL(SID_FILE_NAME, sFileName); + SfxBoolItem aReadOnly(SID_DOC_READONLY, false); + SfxStringItem aTargetFrameName( SID_TARGETNAME, "_blank" ); + SfxStringItem aReferer(SID_REFERER, m_pActiveShell->GetView().GetDocShell()->GetTitle()); + m_pActiveShell->GetView().GetViewFrame().GetDispatcher()-> + ExecuteList(SID_OPENDOC, SfxCallMode::ASYNCHRON, + { &aURL, &aReadOnly, &aReferer, &aTargetFrameName }); + } +} + +IMPL_LINK_NOARG( SwGlobalTree, DoubleClickHdl, weld::TreeView&, bool) +{ + int nEntry = m_xTreeView->get_cursor_index(); + SwGlblDocContent* pCont = weld::fromId<SwGlblDocContent*>(m_xTreeView->get_id(nEntry)); + if (pCont->GetType() == GLBLDOC_SECTION) + OpenDoc(pCont); + else + { + GotoContent(pCont); + m_pActiveShell->GetView().GetEditWin().GrabFocus(); + } + return false; +} + +SwNavigationPI* SwGlobalTree::GetParentWindow() +{ + return m_pDialog; +} + +IMPL_STATIC_LINK_NOARG(SwGlobalTree, ShowFrameHdl, void*, void) +{ + SfxViewFrame* pFirst = s_pShowShell ? SfxViewFrame::GetFirst(s_pShowShell) : nullptr; + if (pFirst) + pFirst->ToTop(); + SwGlobalTree::SetShowShell(nullptr); +} + +void SwGlobalTree::InsertRegion( const SwGlblDocContent* _pContent, const Sequence< OUString >& _rFiles ) +{ + sal_Int32 nFiles = _rFiles.getLength(); + if (!nFiles) + return; + + size_t nEntryCount = m_xTreeView->n_children(); + + bool bMove = _pContent == nullptr; + const OUString* pFileNames = _rFiles.getConstArray(); + SwWrtShell& rSh = GetParentWindow()->GetCreateView()->GetWrtShell(); + rSh.StartAction(); + // after insertion of the first new content the 'pCont' parameter becomes invalid + // find the index of the 'anchor' content to always use a current anchor content + size_t nAnchorContent = m_pSwGlblDocContents->size() - 1; + if (!bMove) + { + for (size_t nContent = 0; nContent < m_pSwGlblDocContents->size(); + ++nContent) + { + if( *_pContent == *(*m_pSwGlblDocContents)[ nContent ] ) + { + nAnchorContent = nContent; + break; + } + } + } + SwGlblDocContents aTempContents; + for ( sal_Int32 nFile = 0; nFile < nFiles; ++nFile ) + { + //update the global document content after each inserted document + rSh.GetGlobalDocContent(aTempContents); + SwGlblDocContent* pAnchorContent = nullptr; + OSL_ENSURE(aTempContents.size() > (nAnchorContent + nFile), "invalid anchor content -> last insertion failed"); + if ( aTempContents.size() > (nAnchorContent + nFile) ) + pAnchorContent = aTempContents[nAnchorContent + nFile].get(); + else + pAnchorContent = aTempContents.back().get(); + OUString sFileName(pFileNames[nFile]); + INetURLObject aFileUrl; + aFileUrl.SetSmartURL( sFileName ); + OUString sSectionName(aFileUrl.GetLastName( + INetURLObject::DecodeMechanism::Unambiguous).getToken(0, sfx2::cTokenSeparator)); + sal_uInt16 nSectCount = rSh.GetSectionFormatCount(); + OUString sTempSectionName(sSectionName); + sal_uInt16 nAddNumber = 0; + sal_uInt16 nCount = 0; + // if applicable: add index if the range name is already in use. + while ( nCount < nSectCount ) + { + const SwSectionFormat& rFormat = rSh.GetSectionFormat(nCount); + if ((rFormat.GetSection()->GetSectionName() == sTempSectionName) + && rFormat.IsInNodesArr()) + { + nCount = 0; + nAddNumber++; + sTempSectionName = sSectionName + ":" + OUString::number( nAddNumber ); + } + else + nCount++; + } + + if ( nAddNumber ) + sSectionName = sTempSectionName; + + SwSectionData aSectionData(SectionType::Content, sSectionName); + aSectionData.SetProtectFlag(true); + aSectionData.SetHidden(false); + + aSectionData.SetLinkFileName(sFileName); + aSectionData.SetType(SectionType::FileLink); + aSectionData.SetLinkFilePassword( OUString() ); + + rSh.InsertGlobalDocContent( *pAnchorContent, aSectionData ); + } + if (bMove) + { + Update( false ); + rSh.MoveGlobalDocContent( + *m_pSwGlblDocContents, nEntryCount, nEntryCount + nFiles, nEntryCount - nFiles ); + } + rSh.EndAction(); + Update( false ); + Display(); + +} + +IMPL_LINK( SwGlobalTree, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void ) +{ + if ( ERRCODE_NONE != _pFileDlg->GetError() ) + return; + + SfxMediumList aMedList(m_pDocInserter->CreateMediumList()); + if ( aMedList.empty() ) + return; + + Sequence< OUString >aFileNames( aMedList.size() ); + OUString* pFileNames = aFileNames.getArray(); + sal_Int32 nPos = 0; + for (const std::unique_ptr<SfxMedium>& pMed : aMedList) + { + // tdf#127978 - don't URL encode filename for navigator's tooltip + OUString sFileName + = pMed->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::Unambiguous) + + OUStringChar(sfx2::cTokenSeparator) + + pMed->GetFilter()->GetFilterName() + + OUStringChar(sfx2::cTokenSeparator); + pFileNames[nPos++] = sFileName; + } + InsertRegion( &*m_oDocContent, aFileNames ); + m_oDocContent.reset(); +} + +void SwGlobalTree::Notify(SfxBroadcaster& rBC, SfxHint const& rHint) +{ + SfxViewEventHint const*const pVEHint(dynamic_cast<SfxViewEventHint const*>(&rHint)); + SwXTextView* pDyingShell = nullptr; + if (m_pActiveShell && pVEHint && pVEHint->GetEventName() == "OnViewClosed") + pDyingShell = dynamic_cast<SwXTextView*>(pVEHint->GetController().get()); + if (pDyingShell && pDyingShell->GetView() == &m_pActiveShell->GetView()) + { + EndListening(*m_pActiveShell->GetView().GetDocShell()); + m_pActiveShell = nullptr; + } + else + { + SfxListener::Notify(rBC, rHint); + if (rHint.GetId() == SfxHintId::SwNavigatorUpdateTracking) + UpdateTracking(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/gloslst.cxx b/sw/source/uibase/utlui/gloslst.cxx new file mode 100644 index 0000000000..9878d0aa67 --- /dev/null +++ b/sw/source/uibase/utlui/gloslst.cxx @@ -0,0 +1,445 @@ +/* -*- 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 <vcl/weld.hxx> +#include <svl/fstathelper.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <osl/diagnose.h> +#include <o3tl/string_view.hxx> +#include <swtypes.hxx> +#include <swmodule.hxx> +#include <shellio.hxx> +#include <initui.hxx> +#include <glosdoc.hxx> +#include <gloslst.hxx> +#include <swunohelper.hxx> +#include <view.hxx> + +#include <vector> + +#define STRING_DELIM char(0x0A) +#define GLOS_TIMEOUT 30000 // update every 30 seconds +#define FIND_MAX_GLOS 20 + +namespace { + +struct TripleString +{ + OUString sGroup; + OUString sBlock; + OUString sShort; +}; + +class SwGlossDecideDlg : public weld::GenericDialogController +{ + std::unique_ptr<weld::Button> m_xOk; + std::unique_ptr<weld::TreeView> m_xListLB; + + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(SelectHdl, weld::TreeView&, void); + +public: + explicit SwGlossDecideDlg(weld::Window* pParent); + + weld::TreeView& GetTreeView() {return *m_xListLB;} +}; + +} + +SwGlossDecideDlg::SwGlossDecideDlg(weld::Window* pParent) + : GenericDialogController(pParent, "modules/swriter/ui/selectautotextdialog.ui", "SelectAutoTextDialog") + , m_xOk(m_xBuilder->weld_button("ok")) + , m_xListLB(m_xBuilder->weld_tree_view("treeview")) +{ + m_xListLB->set_size_request(m_xListLB->get_approximate_digit_width() * 32, + m_xListLB->get_height_rows(8)); + m_xListLB->connect_row_activated(LINK(this, SwGlossDecideDlg, DoubleClickHdl)); + m_xListLB->connect_changed(LINK(this, SwGlossDecideDlg, SelectHdl)); +} + +IMPL_LINK_NOARG(SwGlossDecideDlg, DoubleClickHdl, weld::TreeView&, bool) +{ + m_xDialog->response(RET_OK); + return true; +} + +IMPL_LINK_NOARG(SwGlossDecideDlg, SelectHdl, weld::TreeView&, void) +{ + m_xOk->set_sensitive(m_xListLB->get_selected_index() != -1); +} + +SwGlossaryList::SwGlossaryList() : + AutoTimer("SwGlossaryList"), m_bFilled(false) +{ + SvtPathOptions aPathOpt; + m_sPath = aPathOpt.GetAutoTextPath(); + SetTimeout(GLOS_TIMEOUT); +} + +SwGlossaryList::~SwGlossaryList() +{ + ClearGroups(); +} + +// If the GroupName is already known, then only rShortName +// will be filled. Otherwise also rGroupName will be set and +// on demand asked for the right group. + +bool SwGlossaryList::GetShortName(std::u16string_view rLongName, + OUString& rShortName, OUString& rGroupName ) +{ + if(!m_bFilled) + Update(); + + std::vector<TripleString> aTripleStrings; + + size_t nCount = m_aGroupArr.size(); + for(size_t i = 0; i < nCount; i++ ) + { + AutoTextGroup* pGroup = m_aGroupArr[i].get(); + if(!rGroupName.isEmpty() && rGroupName != pGroup->sName) + continue; + + sal_Int32 nPosLong = 0; + for(sal_uInt16 j = 0; j < pGroup->nCount; j++) + { + const OUString sLong = pGroup->sLongNames.getToken(0, STRING_DELIM, nPosLong); + if(rLongName != sLong) + continue; + + TripleString aTriple; + aTriple.sGroup = pGroup->sName; + aTriple.sBlock = sLong; + aTriple.sShort = pGroup->sShortNames.getToken(j, STRING_DELIM); + aTripleStrings.push_back(aTriple); + } + } + + bool bRet = false; + nCount = aTripleStrings.size(); + if(1 == nCount) + { + const TripleString& rTriple(aTripleStrings.front()); + rShortName = rTriple.sShort; + rGroupName = rTriple.sGroup; + bRet = true; + } + else if(1 < nCount) + { + SwView *pView = ::GetActiveView(); + if (!pView) + return bRet; + SwGlossDecideDlg aDlg(pView->GetFrameWeld()); + OUString sTitle = aDlg.get_title() + " " + aTripleStrings.front().sBlock; + aDlg.set_title(sTitle); + + weld::TreeView& rLB = aDlg.GetTreeView(); + for (const auto& rTriple : aTripleStrings) + rLB.append_text(rTriple.sGroup.getToken(0, GLOS_DELIM)); + + rLB.select(0); + if (aDlg.run() == RET_OK && rLB.get_selected_index() != -1) + { + const TripleString& rTriple(aTripleStrings[rLB.get_selected_index()]); + rShortName = rTriple.sShort; + rGroupName = rTriple.sGroup; + bRet = true; + } + else + bRet = false; + } + return bRet; +} + +size_t SwGlossaryList::GetGroupCount() +{ + if(!m_bFilled) + Update(); + return m_aGroupArr.size(); +} + +OUString SwGlossaryList::GetGroupName(size_t nPos) +{ + OSL_ENSURE(m_aGroupArr.size() > nPos, "group not available"); + if(nPos < m_aGroupArr.size()) + { + AutoTextGroup* pGroup = m_aGroupArr[nPos].get(); + OUString sRet = pGroup->sName; + return sRet; + } + return OUString(); +} + +OUString SwGlossaryList::GetGroupTitle(size_t nPos) +{ + OSL_ENSURE(m_aGroupArr.size() > nPos, "group not available"); + if(nPos < m_aGroupArr.size()) + { + AutoTextGroup* pGroup = m_aGroupArr[nPos].get(); + return pGroup->sTitle; + } + return OUString(); +} + +sal_uInt16 SwGlossaryList::GetBlockCount(size_t nGroup) +{ + OSL_ENSURE(m_aGroupArr.size() > nGroup, "group not available"); + if(nGroup < m_aGroupArr.size()) + { + AutoTextGroup* pGroup = m_aGroupArr[nGroup].get(); + return pGroup->nCount; + } + return 0; +} + +OUString SwGlossaryList::GetBlockLongName(size_t nGroup, sal_uInt16 nBlock) +{ + OSL_ENSURE(m_aGroupArr.size() > nGroup, "group not available"); + if(nGroup < m_aGroupArr.size()) + { + AutoTextGroup* pGroup = m_aGroupArr[nGroup].get(); + return pGroup->sLongNames.getToken(nBlock, STRING_DELIM); + } + return OUString(); +} + +OUString SwGlossaryList::GetBlockShortName(size_t nGroup, sal_uInt16 nBlock) +{ + OSL_ENSURE(m_aGroupArr.size() > nGroup, "group not available"); + if(nGroup < m_aGroupArr.size()) + { + AutoTextGroup* pGroup = m_aGroupArr[nGroup].get(); + return pGroup->sShortNames.getToken(nBlock, STRING_DELIM); + } + return OUString(); +} + +void SwGlossaryList::Update() +{ + if(!IsActive()) + Start(); + + SvtPathOptions aPathOpt; + const OUString& sTemp( aPathOpt.GetAutoTextPath() ); + if(sTemp != m_sPath) + { + m_sPath = sTemp; + m_bFilled = false; + ClearGroups(); + } + SwGlossaries* pGlossaries = ::GetGlossaries(); + const std::vector<OUString> & rPathArr = pGlossaries->GetPathArray(); + const OUString sExt( SwGlossaries::GetExtension() ); + if(!m_bFilled) + { + const size_t nGroupCount = pGlossaries->GetGroupCnt(); + for(size_t i = 0; i < nGroupCount; ++i) + { + OUString sGrpName = pGlossaries->GetGroupName(i); + const size_t nPath = static_cast<size_t>( + o3tl::toInt32(o3tl::getToken(sGrpName, 1, GLOS_DELIM))); + if( nPath < rPathArr.size() ) + { + std::unique_ptr<AutoTextGroup> pGroup(new AutoTextGroup); + pGroup->sName = sGrpName; + + FillGroup(pGroup.get(), pGlossaries); + OUString sName = rPathArr[nPath] + "/" + + o3tl::getToken(pGroup->sName, 0, GLOS_DELIM) + sExt; + FStatHelper::GetModifiedDateTimeOfFile( sName, + &pGroup->aDateModified, + &pGroup->aDateModified ); + + m_aGroupArr.insert( m_aGroupArr.begin(), std::move(pGroup) ); + } + } + m_bFilled = true; + } + else + { + for( size_t nPath = 0; nPath < rPathArr.size(); nPath++ ) + { + std::vector<OUString> aFoundGroupNames; + std::vector<OUString> aFiles; + std::vector<DateTime> aDateTimeArr; + + SWUnoHelper::UCB_GetFileListOfFolder( rPathArr[nPath], aFiles, + &sExt, &aDateTimeArr ); + for( size_t nFiles = 0; nFiles < aFiles.size(); ++nFiles ) + { + const OUString aTitle = aFiles[ nFiles ]; + ::DateTime& rDT = aDateTimeArr[ nFiles ]; + + OUString sName( aTitle.copy( 0, aTitle.getLength() - sExt.getLength() )); + + aFoundGroupNames.push_back(sName); + sName += OUStringChar(GLOS_DELIM) + OUString::number( o3tl::narrowing<sal_uInt16>(nPath) ); + AutoTextGroup* pFound = FindGroup( sName ); + if( !pFound ) + { + pFound = new AutoTextGroup; + pFound->sName = sName; + FillGroup( pFound, pGlossaries ); + pFound->aDateModified = rDT; + + m_aGroupArr.push_back(std::unique_ptr<AutoTextGroup>(pFound)); + } + else if( pFound->aDateModified != rDT ) + { + FillGroup(pFound, pGlossaries); + pFound->aDateModified = rDT; + } + } + + for( size_t i = m_aGroupArr.size(); i>0; ) + { + --i; + // maybe remove deleted groups + AutoTextGroup* pGroup = m_aGroupArr[i].get(); + const size_t nGroupPath = static_cast<size_t>( + o3tl::toInt32(o3tl::getToken(pGroup->sName, 1, GLOS_DELIM))); + // Only the groups will be checked which are registered + // for the current subpath. + if( nGroupPath == nPath ) + { + std::u16string_view sCompareGroup = o3tl::getToken(pGroup->sName, 0, GLOS_DELIM); + bool bFound = std::any_of(aFoundGroupNames.begin(), aFoundGroupNames.end(), + [&sCompareGroup](const OUString& rGroupName) { return sCompareGroup == rGroupName; }); + + if(!bFound) + { + m_aGroupArr.erase(m_aGroupArr.begin() + i); + } + } + } + } + } +} + +void SwGlossaryList::Invoke() +{ + // Only update automatically if a SwView has the focus. + if(::GetActiveView()) + Update(); +} + +AutoTextGroup* SwGlossaryList::FindGroup(std::u16string_view rGroupName) +{ + for(const auto & pRet : m_aGroupArr) + { + if(pRet->sName == rGroupName) + return pRet.get(); + } + return nullptr; +} + +void SwGlossaryList::FillGroup(AutoTextGroup* pGroup, SwGlossaries* pGlossaries) +{ + std::unique_ptr<SwTextBlocks> pBlock = pGlossaries->GetGroupDoc(pGroup->sName); + pGroup->nCount = pBlock ? pBlock->GetCount() : 0; + pGroup->sLongNames.clear(); + pGroup->sShortNames.clear(); + if(pBlock) + pGroup->sTitle = pBlock->GetName(); + + for(sal_uInt16 j = 0; j < pGroup->nCount; j++) + { + pGroup->sLongNames += pBlock->GetLongName(j) + + OUStringChar(STRING_DELIM); + pGroup->sShortNames += pBlock->GetShortName(j) + + OUStringChar(STRING_DELIM); + } +} + +// Give back all (not exceeding FIND_MAX_GLOS) found modules +// with matching beginning. + +void SwGlossaryList::HasLongName(const std::vector<OUString>& rBeginCandidates, + std::vector<std::pair<OUString, sal_uInt16>>& rLongNames) +{ + if(!m_bFilled) + Update(); + const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); + // We store results for all candidate words in separate lists, so that later + // we can sort them according to the candidate position + std::vector<std::vector<OUString>> aResults(rBeginCandidates.size()); + + // We can't break after FIND_MAX_GLOS items found, since first items may have ended up in + // lower-priority lists, and those from higher-priority lists are yet to come. So process all. + for (const auto& pGroup : m_aGroupArr) + { + sal_Int32 nIdx{ 0 }; + for(sal_uInt16 j = 0; j < pGroup->nCount; j++) + { + OUString sBlock = pGroup->sLongNames.getToken(0, STRING_DELIM, nIdx); + for (size_t i = 0; i < rBeginCandidates.size(); ++i) + { + const OUString& s = rBeginCandidates[i]; + if (s.getLength() + 1 < sBlock.getLength() + && rSCmp.isEqual(sBlock.copy(0, s.getLength()), s)) + { + aResults[i].push_back(sBlock); + } + } + } + } + + std::vector<std::pair<OUString, sal_uInt16>> aAllResults; + // Sort and concatenate all result lists. See QuickHelpData::SortAndFilter + for (size_t i = 0; i < rBeginCandidates.size(); ++i) + { + std::sort(aResults[i].begin(), aResults[i].end(), + [origWord = rBeginCandidates[i]](const OUString& s1, const OUString& s2) { + int nRet = s1.compareToIgnoreAsciiCase(s2); + if (nRet == 0) + { + // fdo#61251 sort stuff that starts with the exact rOrigWord before + // another ignore-case candidate + int n1StartsWithOrig = s1.startsWith(origWord) ? 0 : 1; + int n2StartsWithOrig = s2.startsWith(origWord) ? 0 : 1; + return n1StartsWithOrig < n2StartsWithOrig; + } + return nRet < 0; + }); + // All suggestions must be accompanied with length of the text they would replace + std::transform(aResults[i].begin(), aResults[i].end(), std::back_inserter(aAllResults), + [nLen = sal_uInt16(rBeginCandidates[i].getLength())](const OUString& s) { + return std::make_pair(s, nLen); + }); + } + + const auto& it = std::unique( + aAllResults.begin(), aAllResults.end(), + [](const std::pair<OUString, sal_uInt16>& s1, const std::pair<OUString, sal_uInt16>& s2) { + return s1.first.equalsIgnoreAsciiCase(s2.first); + }); + if (const auto nCount = std::min<size_t>(std::distance(aAllResults.begin(), it), FIND_MAX_GLOS)) + { + rLongNames.insert(rLongNames.end(), aAllResults.begin(), aAllResults.begin() + nCount); + } +} + +void SwGlossaryList::ClearGroups() +{ + m_aGroupArr.clear(); + m_bFilled = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/gotodlg.cxx b/sw/source/uibase/utlui/gotodlg.cxx new file mode 100644 index 0000000000..711717ee44 --- /dev/null +++ b/sw/source/uibase/utlui/gotodlg.cxx @@ -0,0 +1,105 @@ +/* -*- 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 <swmodule.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <gotodlg.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> + +using namespace com::sun::star; + +SwGotoPageDlg::SwGotoPageDlg(weld::Window* pParent, SfxBindings& rBindings) + : GenericDialogController(pParent, "modules/swriter/ui/gotopagedialog.ui", "GotoPageDialog") + , m_pCreateView(nullptr) + , m_rBindings(rBindings) + , mnMaxPageCnt(1) + , mxMtrPageCtrl(m_xBuilder->weld_spin_button("page")) + , mxPageNumberLbl(m_xBuilder->weld_label("page_count")) +{ + sal_uInt16 nTotalPage = GetPageInfo(); + + if (nTotalPage) + { + OUString sStr = mxPageNumberLbl->get_label(); + mxPageNumberLbl->set_label(sStr.replaceFirst("$1", OUString::number(nTotalPage))); + mnMaxPageCnt = nTotalPage; + } + mxMtrPageCtrl->connect_changed(LINK(this, SwGotoPageDlg, PageModifiedHdl)); + mxMtrPageCtrl->set_position(-1); + mxMtrPageCtrl->select_region(0, -1); +} + +IMPL_LINK_NOARG(SwGotoPageDlg, PageModifiedHdl, weld::Entry&, void) +{ + if (mxMtrPageCtrl->get_text().isEmpty()) + return; + + int page_value = mxMtrPageCtrl->get_text().toInt32(); + + if (page_value <= 0) + mxMtrPageCtrl->set_value(1); + else if (page_value > mnMaxPageCnt) + mxMtrPageCtrl->set_value(mnMaxPageCnt); + else + mxMtrPageCtrl->set_value(page_value); + + mxMtrPageCtrl->set_position(-1); +} + +SwView* SwGotoPageDlg::GetCreateView() const +{ + if (!m_pCreateView) + { + SwView* pView = SwModule::GetFirstView(); + while (pView) + { + if (&pView->GetViewFrame().GetBindings() == &m_rBindings) + { + const_cast<SwGotoPageDlg*>(this)->m_pCreateView = pView; + break; + } + pView = SwModule::GetNextView(pView); + } + } + + return m_pCreateView; +} + +// If the page can be set here, the maximum is set. + +sal_uInt16 SwGotoPageDlg::GetPageInfo() +{ + SwView* pView = GetCreateView(); + SwWrtShell* pSh = pView ? &pView->GetWrtShell() : nullptr; + mxMtrPageCtrl->set_value(1); + if (pSh) + { + const sal_uInt16 nPageCnt = pSh->GetPageCnt(); + sal_uInt16 nPhyPage, nVirPage; + pSh->GetPageNum(nPhyPage, nVirPage); + mxMtrPageCtrl->set_max(nPageCnt); + mxMtrPageCtrl->set_value(nPhyPage); + return nPageCnt; + } + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/initui.cxx b/sw/source/uibase/utlui/initui.cxx new file mode 100644 index 0000000000..f24cded867 --- /dev/null +++ b/sw/source/uibase/utlui/initui.cxx @@ -0,0 +1,311 @@ +/* -*- 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 <libxml/xmlwriter.h> + +#include <unotools/localedatawrapper.hxx> +#include <viewsh.hxx> +#include <initui.hxx> +#include <edtwin.hxx> +#include <shellres.hxx> +#include <fldbas.hxx> +#include <glosdoc.hxx> +#include <gloslst.hxx> +#include <comcore.hxx> +#include <strings.hrc> +#include <utlui.hrc> +#include <authfld.hxx> +#include <unotools/syslocale.hxx> + +// Global Pointer + +static std::unique_ptr<SwGlossaries> pGlossaries; + +// Provides all needed paths. Is initialized by UI. +static SwGlossaryList* pGlossaryList = nullptr; + +namespace +{ +OUString CurrGlosGroup; +} + +const OUString& GetCurrGlosGroup() +{ + return CurrGlosGroup; +} + +void SetCurrGlosGroup(const OUString& sStr) +{ + CurrGlosGroup = sStr; +} + +namespace +{ + +std::vector<OUString>* pAuthFieldNameList = nullptr; +std::vector<OUString>* pAuthFieldTypeList = nullptr; + +} + +// Finish UI + +void FinitUI() +{ + delete SwViewShell::GetShellRes(); + SwViewShell::SetShellRes( nullptr ); + + SwEditWin::FinitStaticData(); + + pGlossaries.reset(); + + delete SwFieldType::s_pFieldNames; + + delete pGlossaryList; + delete pAuthFieldNameList; + delete pAuthFieldTypeList; + +} + +// Initialise + +void InitUI() +{ + // ShellResource gives the CORE the possibility to work with resources. + SwViewShell::SetShellRes( new ShellResource ); + SwEditWin::InitStaticData(); +} + +const TranslateId FLD_DOCINFO_ARY[] = +{ + FLD_DOCINFO_TITLE, + FLD_DOCINFO_SUBJECT, + FLD_DOCINFO_KEYS, + FLD_DOCINFO_COMMENT, + FLD_DOCINFO_CREATE, + FLD_DOCINFO_CHANGE, + FLD_DOCINFO_PRINT, + FLD_DOCINFO_DOCNO, + FLD_DOCINFO_EDIT +}; + +ShellResource::ShellResource() + : aPostItAuthor( SwResId( STR_POSTIT_AUTHOR ) ), + aPostItPage( SwResId( STR_POSTIT_PAGE ) ), + aPostItLine( SwResId( STR_POSTIT_LINE ) ), + + aCalc_Syntax( SwResId( STR_CALC_SYNTAX ) ), + aCalc_ZeroDiv( SwResId( STR_CALC_ZERODIV ) ), + aCalc_Brack( SwResId( STR_CALC_BRACK ) ), + aCalc_Pow( SwResId( STR_CALC_POW ) ), + aCalc_Overflow( SwResId( STR_CALC_OVERFLOW ) ), + aCalc_Default( SwResId( STR_CALC_DEFAULT ) ), + aCalc_Error( SwResId( STR_CALC_ERROR ) ), + + // #i81002# + aGetRefField_RefItemNotFound( SwResId( STR_GETREFFLD_REFITEMNOTFOUND ) ), + aStrNone( SwResId( STR_TEMPLATE_NONE )), + aFixedStr( SwResId( STR_FIELD_FIXED )), + sDurationFormat( SwResId( STR_DURATION_FORMAT )), + + aTOXIndexName( SwResId(STR_TOI)), + aTOXUserName( SwResId(STR_TOU)), + aTOXContentName( SwResId(STR_TOC)), + aTOXIllustrationsName( SwResId(STR_TOX_ILL)), + aTOXObjectsName( SwResId(STR_TOX_OBJ)), + aTOXTablesName( SwResId(STR_TOX_TBL)), + aTOXAuthoritiesName( SwResId(STR_TOX_AUTH)), + aTOXCitationName( SwResId(STR_TOX_CITATION)), + sPageDescFirstName( SwResId(STR_PAGEDESC_FIRSTNAME)), + sPageDescFollowName( SwResId(STR_PAGEDESC_FOLLOWNAME)), + sPageDescName( SwResId(STR_PAGEDESC_NAME)) +{ + for (auto const& aID : FLD_DOCINFO_ARY) + aDocInfoLst.push_back(SwResId(aID)); +} + +OUString ShellResource::GetPageDescName(sal_uInt16 nNo, PageNameMode eMode) +{ + OUString sRet; + + switch (eMode) + { + case NORMAL_PAGE: + sRet = sPageDescName; + break; + case FIRST_PAGE: + sRet = sPageDescFirstName; + break; + case FOLLOW_PAGE: + sRet = sPageDescFollowName; + break; + } + + return sRet.replaceFirst( "$(ARG1)", OUString::number( nNo )); +} + +SwGlossaries* GetGlossaries() +{ + if (!pGlossaries) + pGlossaries.reset( new SwGlossaries ); + return pGlossaries.get(); +} + +bool HasGlossaryList() +{ + return pGlossaryList != nullptr; +} + +SwGlossaryList* GetGlossaryList() +{ + if(!pGlossaryList) + pGlossaryList = new SwGlossaryList(); + + return pGlossaryList; +} + +void ShellResource::GetAutoFormatNameLst_() const +{ + assert(!mxAutoFormatNameLst); + mxAutoFormatNameLst.emplace(); + mxAutoFormatNameLst->reserve(STR_AUTOFMTREDL_END); + + static_assert(SAL_N_ELEMENTS(RID_SHELLRES_AUTOFMTSTRS) == STR_AUTOFMTREDL_END); + for (sal_uInt16 n = 0; n < STR_AUTOFMTREDL_END; ++n) + { + OUString p(SwResId(RID_SHELLRES_AUTOFMTSTRS[n])); + if (STR_AUTOFMTREDL_TYPO == n) + { + const SvtSysLocale aSysLocale; + const LocaleDataWrapper& rLclD = aSysLocale.GetLocaleData(); + p = p.replaceFirst("%1", rLclD.getDoubleQuotationMarkStart()); + p = p.replaceFirst("%2", rLclD.getDoubleQuotationMarkEnd()); + } + mxAutoFormatNameLst->push_back(p); + } +} + +namespace +{ + const TranslateId STR_AUTH_FIELD_ARY[] = + { + STR_AUTH_FIELD_IDENTIFIER, + STR_AUTH_FIELD_AUTHORITY_TYPE, + STR_AUTH_FIELD_ADDRESS, + STR_AUTH_FIELD_ANNOTE, + STR_AUTH_FIELD_AUTHOR, + STR_AUTH_FIELD_BOOKTITLE, + STR_AUTH_FIELD_CHAPTER, + STR_AUTH_FIELD_EDITION, + STR_AUTH_FIELD_EDITOR, + STR_AUTH_FIELD_HOWPUBLISHED, + STR_AUTH_FIELD_INSTITUTION, + STR_AUTH_FIELD_JOURNAL, + STR_AUTH_FIELD_MONTH, + STR_AUTH_FIELD_NOTE, + STR_AUTH_FIELD_NUMBER, + STR_AUTH_FIELD_ORGANIZATIONS, + STR_AUTH_FIELD_PAGES, + STR_AUTH_FIELD_PUBLISHER, + STR_AUTH_FIELD_SCHOOL, + STR_AUTH_FIELD_SERIES, + STR_AUTH_FIELD_TITLE, + STR_AUTH_FIELD_TYPE, + STR_AUTH_FIELD_VOLUME, + STR_AUTH_FIELD_YEAR, + STR_AUTH_FIELD_URL, + STR_AUTH_FIELD_CUSTOM1, + STR_AUTH_FIELD_CUSTOM2, + STR_AUTH_FIELD_CUSTOM3, + STR_AUTH_FIELD_CUSTOM4, + STR_AUTH_FIELD_CUSTOM5, + STR_AUTH_FIELD_ISBN, + STR_AUTH_FIELD_LOCAL_URL, + STR_AUTH_FIELD_TARGET_TYPE, + STR_AUTH_FIELD_TARGET_URL, + }; +} + +OUString const & SwAuthorityFieldType::GetAuthFieldName(ToxAuthorityField eType) +{ + if(!pAuthFieldNameList) + { + pAuthFieldNameList = new std::vector<OUString>; + pAuthFieldNameList->reserve(AUTH_FIELD_END); + for (sal_uInt16 i = 0; i < AUTH_FIELD_END; ++i) + pAuthFieldNameList->push_back(SwResId(STR_AUTH_FIELD_ARY[i])); + } + return (*pAuthFieldNameList)[static_cast< sal_uInt16 >(eType)]; +} + +const TranslateId STR_AUTH_TYPE_ARY[] = +{ + STR_AUTH_TYPE_ARTICLE, + STR_AUTH_TYPE_BOOK, + STR_AUTH_TYPE_BOOKLET, + STR_AUTH_TYPE_CONFERENCE, + STR_AUTH_TYPE_INBOOK, + STR_AUTH_TYPE_INCOLLECTION, + STR_AUTH_TYPE_INPROCEEDINGS, + STR_AUTH_TYPE_JOURNAL, + STR_AUTH_TYPE_MANUAL, + STR_AUTH_TYPE_MASTERSTHESIS, + STR_AUTH_TYPE_MISC, + STR_AUTH_TYPE_PHDTHESIS, + STR_AUTH_TYPE_PROCEEDINGS, + STR_AUTH_TYPE_TECHREPORT, + STR_AUTH_TYPE_UNPUBLISHED, + STR_AUTH_TYPE_EMAIL, + STR_AUTH_TYPE_WWW, + STR_AUTH_TYPE_CUSTOM1, + STR_AUTH_TYPE_CUSTOM2, + STR_AUTH_TYPE_CUSTOM3, + STR_AUTH_TYPE_CUSTOM4, + STR_AUTH_TYPE_CUSTOM5 +}; + +OUString const & SwAuthorityFieldType::GetAuthTypeName(ToxAuthorityType eType) +{ + if(!pAuthFieldTypeList) + { + pAuthFieldTypeList = new std::vector<OUString>; + pAuthFieldTypeList->reserve(AUTH_TYPE_END); + for (sal_uInt16 i = 0; i < AUTH_TYPE_END; ++i) + pAuthFieldTypeList->push_back(SwResId(STR_AUTH_TYPE_ARY[i])); + } + return (*pAuthFieldTypeList)[static_cast< sal_uInt16 >(eType)]; +} + +void SwAuthorityFieldType::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwAuthorityFieldType")); + SwFieldType::dumpAsXml(pWriter); + + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("DataArr")); + for (const auto& xAuthEntry : m_DataArr) + { + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("AuthEntry")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", xAuthEntry.get()); + (void)xmlTextWriterEndElement(pWriter); + } + (void)xmlTextWriterEndElement(pWriter); + + (void)xmlTextWriterEndElement(pWriter); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/navicfg.cxx b/sw/source/uibase/utlui/navicfg.cxx new file mode 100644 index 0000000000..dfda882729 --- /dev/null +++ b/sw/source/uibase/utlui/navicfg.cxx @@ -0,0 +1,196 @@ +/* -*- 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 <swtypes.hxx> +#include <navicfg.hxx> +#include <swcont.hxx> +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <map> + +using namespace ::utl; +using namespace ::com::sun::star::uno; + +namespace { + std::map<OUString, ContentTypeId> mPropNameToContentTypeId + { + {"TableTracking", ContentTypeId::TABLE}, + {"FrameTracking", ContentTypeId::FRAME}, + {"ImageTracking", ContentTypeId::GRAPHIC}, + {"OLEobjectTracking", ContentTypeId::OLE}, + {"BookmarkTracking", ContentTypeId::BOOKMARK}, + {"SectionTracking", ContentTypeId::REGION}, + {"HyperlinkTracking", ContentTypeId::URLFIELD}, + {"ReferenceTracking", ContentTypeId::REFERENCE}, + {"IndexTracking", ContentTypeId::INDEX}, + {"CommentTracking", ContentTypeId::POSTIT}, + {"DrawingObjectTracking", ContentTypeId::DRAWOBJECT}, + {"FieldTracking", ContentTypeId::TEXTFIELD}, + {"FootnoteTracking", ContentTypeId::FOOTNOTE}, + {"EndnoteTracking", ContentTypeId::ENDNOTE} + }; +} + +Sequence<OUString> SwNavigationConfig::GetPropertyNames() +{ + return css::uno::Sequence<OUString>{ + OUString("RootType"), + OUString("SelectedPosition"), + OUString("OutlineLevel"), + OUString("InsertMode"), + OUString("ActiveBlock"), + OUString("ShowListBox"), + OUString("GlobalDocMode"), + OUString("OutlineTracking"), + OUString("TableTracking"), + OUString("SectionTracking"), + OUString("FrameTracking"), + OUString("ImageTracking"), + OUString("OLEobjectTracking"), + OUString("BookmarkTracking"), + OUString("HyperlinkTracking"), + OUString("ReferenceTracking"), + OUString("IndexTracking"), + OUString("CommentTracking"), + OUString("DrawingObjectTracking"), + OUString("FieldTracking"), + OUString("FootnoteTracking"), + OUString("EndnoteTracking"), + OUString("NavigateOnSelect"), + OUString("SortAlphabeticallyBlock")}; +} + +SwNavigationConfig::SwNavigationConfig() : + utl::ConfigItem("Office.Writer/Navigator"), + m_nRootType(ContentTypeId::UNKNOWN), + m_nSelectedPos(0), + m_nOutlineLevel(MAXLEVEL), + m_nRegionMode(RegionMode::NONE), + m_nActiveBlock(0), + m_bIsSmall(false), + m_bIsGlobalActive(true), + m_nOutlineTracking(1), + m_bIsNavigateOnSelect(false) +{ + Load(); + EnableNotification(GetPropertyNames()); +} + +void SwNavigationConfig::Load() +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(aNames); + const Any* pValues = aValues.getConstArray(); + OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() != aNames.getLength()) + return; + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case 0: + { + sal_Int32 nTmp = {}; // spurious -Werror=maybe-uninitialized + if (pValues[nProp] >>= nTmp) + { + if (nTmp < sal_Int32(ContentTypeId::UNKNOWN) + || nTmp > sal_Int32(ContentTypeId::LAST)) + { + SAL_WARN( + "sw", + "out-of-bounds ContentTypeId " << nTmp); + nTmp = sal_Int32(ContentTypeId::UNKNOWN); + } + m_nRootType = static_cast<ContentTypeId>(nTmp); + } + break; + } + case 1: pValues[nProp] >>= m_nSelectedPos; break; + case 2: pValues[nProp] >>= m_nOutlineLevel; break; + case 3: + { + sal_Int32 nTmp; + if (pValues[nProp] >>= nTmp) + m_nRegionMode = static_cast<RegionMode>(nTmp); + break; + } + case 4: pValues[nProp] >>= m_nActiveBlock; break; + case 5: m_bIsSmall = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 6: m_bIsGlobalActive = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 7: pValues[nProp] >>= m_nOutlineTracking; break; + case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: + case 17: case 18: case 19: case 20: case 21: + { + mContentTypeTrack[mPropNameToContentTypeId[aNames[nProp]]] = + *o3tl::doAccess<bool>(pValues[nProp]); + break; + } + case 22: m_bIsNavigateOnSelect = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 23: pValues[nProp] >>= m_nSortAlphabeticallyBlock; break; + } + } + } +} + +SwNavigationConfig::~SwNavigationConfig() +{ +} + +void SwNavigationConfig::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case 0: pValues[nProp] <<= static_cast<sal_Int32>(m_nRootType); break; + case 1: pValues[nProp] <<= m_nSelectedPos; break; + case 2: pValues[nProp] <<= m_nOutlineLevel; break; + case 3: pValues[nProp] <<= static_cast<sal_uInt16>(m_nRegionMode); break; + case 4: pValues[nProp] <<= m_nActiveBlock; break; + case 5: pValues[nProp] <<= m_bIsSmall; break; + case 6: pValues[nProp] <<= m_bIsGlobalActive; break; + case 7: pValues[nProp] <<= m_nOutlineTracking; break; + case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: + case 17: case 18: case 19: case 20: case 21: + { + pValues[nProp] <<= mContentTypeTrack[mPropNameToContentTypeId[aNames[nProp]]]; + break; + } + case 22: pValues[nProp] <<= m_bIsNavigateOnSelect; break; + case 23: pValues[nProp] <<= m_nSortAlphabeticallyBlock; break; + } + } + PutProperties(aNames, aValues); +} + +void SwNavigationConfig::Notify( const css::uno::Sequence< OUString >& ) +{ + Load(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/navipi.cxx b/sw/source/uibase/utlui/navipi.cxx new file mode 100644 index 0000000000..3fd5b7f9b6 --- /dev/null +++ b/sw/source/uibase/utlui/navipi.cxx @@ -0,0 +1,1277 @@ +/* -*- 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 <comphelper/string.hxx> +#include <svl/urlbmk.hxx> +#include <svl/stritem.hxx> +#include <vcl/graphicfilter.hxx> +#include <sot/formats.hxx> +#include <sot/filelist.hxx> +#include <sfx2/event.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <tools/urlobj.hxx> +#include <osl/diagnose.h> +#include <swtypes.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <navicfg.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <navipi.hxx> +#include <edtwin.hxx> +#include <sfx2/app.hxx> +#include <cmdid.h> +#include <helpids.h> + +#include <strings.hrc> +#include <bitmaps.hlst> + +#include <memory> + +#include <o3tl/enumrange.hxx> + +#include <workctrl.hxx> + +#include <comphelper/lok.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; + +// Filter the control characters out of the Outline-Entry +OUString SwNavigationPI::CleanEntry(const OUString& rEntry) +{ + if (rEntry.isEmpty()) + return rEntry; + + OUStringBuffer aEntry(rEntry); + for (sal_Int32 i = 0; i < rEntry.getLength(); ++i) + if(aEntry[i] == 10 || aEntry[i] == 9) + aEntry[i] = 0x20; + + return aEntry.makeStringAndClear(); +} + +// Execution of the drag operation with and without the children. + +void SwNavigationPI::MoveOutline(SwOutlineNodes::size_type nSource, SwOutlineNodes::size_type nTarget) +{ + SwView *pView = GetCreateView(); + SwWrtShell &rSh = pView->GetWrtShell(); + if(nTarget < nSource || nTarget == SwOutlineNodes::npos) + nTarget ++; + if ( !rSh.IsOutlineMovable( nSource )) + return; + + SwOutlineNodes::difference_type nMove = nTarget-nSource; //( nDir<0 ) ? 1 : 0 ; + rSh.GotoOutline(nSource); + rSh.MakeOutlineSel(nSource, nSource, true); + // While moving, the selected children does not counting. + const SwOutlineNodes::size_type nLastOutlinePos = rSh.GetOutlinePos(MAXLEVEL); + if(nMove > 1 && nLastOutlinePos < nTarget) + { + if(!rSh.IsCursorPtAtEnd()) + rSh.SwapPam(); + nMove -= nLastOutlinePos - nSource; + } + if( nMove < 1 || nLastOutlinePos < nTarget ) + rSh.MoveOutlinePara( nMove ); + rSh.ClearMark(); + rSh.GotoOutline( nSource + nMove); + +} + +// After goto cancel the status frame selection +static void lcl_UnSelectFrame(SwWrtShell *pSh) +{ + if (pSh->IsFrameSelected()) + { + pSh->UnSelectFrame(); + pSh->LeaveSelFrameMode(); + } +} + +// Select the document view +IMPL_LINK(SwNavigationPI, DocListBoxSelectHdl, weld::ComboBox&, rBox, void) +{ + int nEntryIdx = rBox.get_active(); + SwView *pView ; + pView = SwModule::GetFirstView(); + while (nEntryIdx-- && pView) + { + pView = SwModule::GetNextView(pView); + } + if(!pView) + { + nEntryIdx == 0 ? + m_xContentTree->ShowHiddenShell(): + m_xContentTree->ShowActualView(); + + } + else + { + m_xContentTree->SetConstantShell(pView->GetWrtShellPtr()); + } +} + +void SwNavigationPI::UpdateNavigateBy() +{ + if (!m_pNavigateByComboBox) + return; + SfxUInt32Item aParam(FN_NAV_ELEMENT, m_pNavigateByComboBox->get_active_id().toUInt32()); + const SfxPoolItem* aArgs[2]; + aArgs[0] = &aParam; + aArgs[1] = nullptr; + SfxDispatcher* pDispatcher = GetCreateView()->GetFrame()->GetDispatcher(); + pDispatcher->Execute(FN_NAV_ELEMENT, SfxCallMode::SYNCHRON, aArgs); +} + +IMPL_LINK(SwNavigationPI, NavigateByComboBoxSelectHdl, weld::ComboBox&, rComboBox, void) +{ + m_xContentTree->SelectContentType(rComboBox.get_active_text()); + UpdateNavigateBy(); +} + +// Filling of the list box for outline view or documents +// The PI will be set to full size +void SwNavigationPI::FillBox() +{ + if(m_pContentWrtShell) + { + m_xContentTree->SetHiddenShell( m_pContentWrtShell ); + m_xContentTree->Display( false ); + } + else + { + SwView *pView = GetCreateView(); + if(!pView) + { + m_xContentTree->SetActiveShell(nullptr); + } + else if( pView != m_pActContView) + { + SwWrtShell* pWrtShell = pView->GetWrtShellPtr(); + m_xContentTree->SetActiveShell(pWrtShell); + } + else + m_xContentTree->Display( true ); + m_pActContView = pView; + if (m_pActContView) + m_xContentTree->UpdateTracking(); + } +} + +void SwNavigationPI::UsePage() +{ + SwView *pView = GetCreateView(); + SwWrtShell *pSh = pView ? &pView->GetWrtShell() : nullptr; + m_xEdit->set_value(1); + if (pSh) + { + const sal_uInt16 nPageCnt = pSh->GetPageCnt(); + sal_uInt16 nPhyPage, nVirPage; + pSh->GetPageNum(nPhyPage, nVirPage); + + m_xEdit->set_max(nPageCnt); + m_xEdit->set_width_chars(3); + m_xEdit->set_value(nPhyPage); + } +} + +// Select handler of the toolboxes +IMPL_LINK(SwNavigationPI, ToolBoxSelectHdl, const OUString&, rCommand, void) +{ + SwView *pView = GetCreateView(); + if (!pView) + return; + SwWrtShell &rSh = pView->GetWrtShell(); + // Get MouseModifier for Outline-Move + + int nFuncId = 0; + bool bFocusToDoc = false; + if (rCommand == ".uno:ScrollToPrevious" || rCommand == ".uno:ScrollToNext") + { + bool *pbNext = new bool(true); + if (rCommand == ".uno:ScrollToPrevious") + *pbNext = false; + pView->MoveNavigationHdl(pbNext); + } + else if (rCommand == "root") + { + m_xContentTree->ToggleToRoot(); + } + else if (rCommand == "listbox") + { + if (ParentIsFloatingWindow(m_xNavigatorDlg)) + { + if (IsZoomedIn()) + { + ZoomOut(); + } + else + { + ZoomIn(); + } + } + return; + } + // Functions that will trigger a direct action. + else if (rCommand == "footer") + { + rSh.MoveCursor(); + const FrameTypeFlags eType = rSh.GetFrameType(nullptr,false); + if (eType & FrameTypeFlags::FOOTER) + { + if (rSh.EndPg()) + nFuncId = FN_END_OF_PAGE; + } + else if (rSh.GotoFooterText()) + nFuncId = FN_TO_FOOTER; + bFocusToDoc = true; + } + else if (rCommand == "header") + { + rSh.MoveCursor(); + const FrameTypeFlags eType = rSh.GetFrameType(nullptr,false); + if (eType & FrameTypeFlags::HEADER) + { + if (rSh.SttPg()) + nFuncId = FN_START_OF_PAGE; + } + else if (rSh.GotoHeaderText()) + nFuncId = FN_TO_HEADER; + bFocusToDoc = true; + } + else if (rCommand == "anchor") + { + rSh.MoveCursor(); + const FrameTypeFlags eFrameType = rSh.GetFrameType(nullptr,false); + // Jump from the footnote to the anchor. + if (eFrameType & FrameTypeFlags::FOOTNOTE) + { + if (rSh.GotoFootnoteAnchor()) + nFuncId = FN_FOOTNOTE_TO_ANCHOR; + } + // Otherwise, jump to the first footnote text; + // go to the next footnote if this is not possible; + // if this is also not possible got to the footnote before. + else + { + if (rSh.GotoFootnoteText()) + nFuncId = FN_FOOTNOTE_TO_ANCHOR; + else if (rSh.GotoNextFootnoteAnchor()) + nFuncId = FN_NEXT_FOOTNOTE; + else if (rSh.GotoPrevFootnoteAnchor()) + nFuncId = FN_PREV_FOOTNOTE; + } + bFocusToDoc = true; + } + else if (rCommand == "reminder") + { + rSh.GetView().GetViewFrame().GetDispatcher()->Execute(FN_SET_REMINDER, SfxCallMode::ASYNCHRON); + } + else if (rCommand == "chapterdown" || + rCommand == "movedown" || + rCommand == "chapterup" || + rCommand == "moveup" || + rCommand == "promote" || + rCommand == "demote" || + rCommand == "edit") + { + if (IsGlobalMode()) + m_xGlobalTree->ExecCommand(rCommand); + else + { + // Standard: sublevels are taken + // do not take sublevels with Ctrl + bool bOutlineWithChildren = (KEY_MOD1 != m_xContent6ToolBox->get_modifier_state()); + m_xContentTree->ExecCommand(rCommand, bOutlineWithChildren); + } + } + else if (rCommand == "contenttoggle" || rCommand == "globaltoggle") + { + ToggleTree(); + bool bGlobalMode = IsGlobalMode(); + m_pConfig->SetGlobalActive(bGlobalMode); + m_xGlobalToolBox->set_item_active("globaltoggle", bGlobalMode); + m_xContent1ToolBox->set_item_active("contenttoggle", bGlobalMode); + } + else if (rCommand == "save") + { + bool bSave = rSh.IsGlblDocSaveLinks(); + rSh.SetGlblDocSaveLinks( !bSave ); + m_xGlobalToolBox->set_item_active(rCommand, !bSave); + } + else if (rCommand == "dragmode") + m_xContent6ToolBox->set_menu_item_active("dragmode", !m_xContent6ToolBox->get_menu_item_active("dragmode")); + else if (rCommand == "headings") + m_xContent5ToolBox->set_menu_item_active("headings", !m_xContent5ToolBox->get_menu_item_active("headings")); + else if (rCommand == "update") + m_xGlobalToolBox->set_menu_item_active("update", !m_xGlobalToolBox->get_menu_item_active("update")); + else if (rCommand == "insert") + m_xGlobalToolBox->set_menu_item_active("insert", !m_xGlobalToolBox->get_menu_item_active("insert")); + + if (nFuncId) + lcl_UnSelectFrame(&rSh); + if (bFocusToDoc) + pView->GetEditWin().GrabFocus(); +} + +// Click handler of the toolboxes +IMPL_LINK(SwNavigationPI, ToolBoxClickHdl, const OUString&, rCommand, void) +{ + if (!m_xGlobalToolBox->get_menu_item_active(rCommand)) + return; + + if (rCommand == "update") + m_xGlobalTree->TbxMenuHdl(rCommand, *m_xUpdateMenu); + else if (rCommand == "insert") + m_xGlobalTree->TbxMenuHdl(rCommand, *m_xInsertMenu); +} + +IMPL_LINK(SwNavigationPI, ToolBox6DropdownClickHdl, const OUString&, rCommand, void) +{ + if (!m_xContent6ToolBox->get_menu_item_active(rCommand)) + return; + + if (rCommand != "dragmode") + return; + + switch (m_nRegionMode) + { + case RegionMode::NONE: + m_xDragModeMenu->set_active("hyperlink", true); + break; + case RegionMode::LINK: + m_xDragModeMenu->set_active("link", true); + break; + case RegionMode::EMBEDDED: + m_xDragModeMenu->set_active("copy", true); + break; + } +} + +IMPL_LINK(SwNavigationPI, DropModeMenuSelectHdl, const OUString&, rIdent, void) +{ + if (rIdent == "hyperlink") + SetRegionDropMode(RegionMode::NONE); + else if (rIdent == "link") + SetRegionDropMode(RegionMode::LINK); + else if (rIdent == "copy") + SetRegionDropMode(RegionMode::EMBEDDED); +} + +IMPL_LINK(SwNavigationPI, GlobalMenuSelectHdl, const OUString&, rIdent, void) +{ + m_xGlobalTree->ExecuteContextMenuAction(rIdent); +} + +IMPL_LINK(SwNavigationPI, ToolBox5DropdownClickHdl, const OUString&, rCommand, void) +{ + if (!m_xContent5ToolBox->get_menu_item_active(rCommand)) + return; + + if (rCommand == "headings") + m_xHeadingsMenu->set_active(OUString::number(m_xContentTree->GetOutlineLevel()), true); +} + +// Action-Handler Edit: +// Switches to the page if the structure view is not turned on. +bool SwNavigationPI::EditAction() +{ + SwView *pView = GetCreateView(); + if (!pView) + return false; + + if (m_aPageChgIdle.IsActive()) + m_aPageChgIdle.Stop(); + + // if the user has clicked into the document, forget about changing the page + if (pView->GetEditWin().HasFocus()) + return false; + + if (m_xEdit->get_text().isEmpty()) + return false; + sal_Int64 nNewPage = m_xEdit->get_text().toInt32(); + SwWrtShell& rSh = m_pCreateView->GetWrtShell(); + sal_Int64 max = rSh.GetPageCnt(); + if (nNewPage <= 0) + nNewPage = 1; + else if (nNewPage > max) + nNewPage = max; + m_xEdit->set_value(nNewPage); + m_xEdit->set_position(-1); + + rSh.GotoPage(nNewPage, true); + m_pCreateView->GetViewFrame().GetBindings().Invalidate(FN_STAT_PAGE); + + return true; +} + +void SwNavigationPI::ZoomOut() +{ + if (!IsZoomedIn()) + return; + SfxNavigator* pNav = m_xNavigatorDlg.get(); + if (!pNav) + return; + m_bIsZoomedIn = false; + FillBox(); + if (IsGlobalMode()) + { + m_xGlobalBox->show(); + m_xGlobalTree->ShowTree(); + } + else + { + m_xContentBox->show(); + m_xContentTree->ShowTree(); + m_xDocListBox->show(); + } + + pNav->InvalidateChildSizeCache(); + Size aOptimalSize(pNav->GetOptimalSize()); + Size aNewSize(pNav->GetOutputSizePixel()); + aNewSize.setHeight( m_aExpandedSize.Height() ); + pNav->SetMinOutputSizePixel(aOptimalSize); + pNav->SetOutputSizePixel(aNewSize); + + m_xContentTree->Select(); // Enable toolbox + m_pConfig->SetSmall(false); + m_xContent6ToolBox->set_item_active("listbox", true); +} + +void SwNavigationPI::ZoomIn() +{ + if (IsZoomedIn()) + return; + SfxNavigator* pNav = m_xNavigatorDlg.get(); + if (!pNav) + return; + + m_aExpandedSize = m_xNavigatorDlg->GetSizePixel(); + + m_xContentBox->hide(); + m_xContentTree->HideTree(); + m_xGlobalBox->hide(); + m_xGlobalTree->HideTree(); + m_xDocListBox->hide(); + m_bIsZoomedIn = true; + + pNav->InvalidateChildSizeCache(); + Size aOptimalSize(pNav->GetOptimalSize()); + Size aNewSize(pNav->GetOutputSizePixel()); + aNewSize.setHeight( aOptimalSize.Height() ); + pNav->SetMinOutputSizePixel(aOptimalSize); + pNav->SetOutputSizePixel(aNewSize); + + m_xContentTree->Select(); // Enable toolbox + + m_pConfig->SetSmall(true); + m_xContent6ToolBox->set_item_active("listbox", false); +} + +namespace { + +enum StatusIndex +{ + IDX_STR_HIDDEN = 0, + IDX_STR_ACTIVE = 1, + IDX_STR_INACTIVE = 2 +}; + +} + +std::unique_ptr<PanelLayout> SwNavigationPI::Create(weld::Widget* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* pBindings) +{ + if( pParent == nullptr ) + throw css::lang::IllegalArgumentException("no parent window given to SwNavigationPI::Create", nullptr, 0); + if( !rxFrame.is() ) + throw css::lang::IllegalArgumentException("no XFrame given to SwNavigationPI::Create", nullptr, 0); + if( pBindings == nullptr ) + throw css::lang::IllegalArgumentException("no SfxBindings given to SwNavigationPI::Create", nullptr, 0); + return std::make_unique<SwNavigationPI>(pParent, rxFrame, pBindings, nullptr); +} + +IMPL_LINK_NOARG(SwNavigationPI, PageModifiedHdl, weld::Entry&, void) +{ + SwView* pView = GetCreateView(); + if (!pView) + return; + if (m_xEdit->get_text().isEmpty()) + return; + sal_Int64 page_value = m_xEdit->get_text().toInt32(); + SwWrtShell& rSh = m_pCreateView->GetWrtShell(); + sal_Int64 max = rSh.GetPageCnt(); + if (page_value <= 0) + m_xEdit->set_value(1); + else if (page_value > max) + m_xEdit->set_value(max); + else + m_xEdit->set_value(page_value); + m_xEdit->set_position(-1); +} + +SwNavigationPI::SwNavigationPI(weld::Widget* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* _pBindings, SfxNavigator* pNavigatorDlg) + : PanelLayout(pParent, "NavigatorPanel", "modules/swriter/ui/navigatorpanel.ui") + , m_aDocFullName(SID_DOCFULLNAME, *_pBindings, *this) + , m_aPageStats(FN_STAT_PAGE, *_pBindings, *this) + , m_xContent1ToolBox(m_xBuilder->weld_toolbar("content1")) + , m_xContent2ToolBox(m_xBuilder->weld_toolbar("content2")) + , m_xContent3ToolBox(m_xBuilder->weld_toolbar("content3")) + , m_xContent4ToolBox(m_xBuilder->weld_toolbar("content4")) + , m_xContent5ToolBox(m_xBuilder->weld_toolbar("content5")) + , m_xContent6ToolBox(m_xBuilder->weld_toolbar("content6")) + , m_xContent2Dispatch(new ToolbarUnoDispatcher(*m_xContent2ToolBox, *m_xBuilder, rxFrame)) + , m_xContent3Dispatch(new ToolbarUnoDispatcher(*m_xContent3ToolBox, *m_xBuilder, rxFrame)) + , m_xHeadingsMenu(m_xBuilder->weld_menu("headingsmenu")) + , m_xDragModeMenu(m_xBuilder->weld_menu("dragmodemenu")) + , m_xUpdateMenu(m_xBuilder->weld_menu("updatemenu")) + , m_xInsertMenu(m_xBuilder->weld_menu("insertmenu")) + , m_xGlobalToolBox(m_xBuilder->weld_toolbar("global")) + , m_xEdit(m_xBuilder->weld_spin_button("spinbutton")) + , m_xContentBox(m_xBuilder->weld_widget("contentbox")) + , m_xContentTree(new SwContentTree(m_xBuilder->weld_tree_view("contenttree"), this)) + , m_xGlobalBox(m_xBuilder->weld_widget("globalbox")) + , m_xGlobalTree(new SwGlobalTree(m_xBuilder->weld_tree_view("globaltree"), this)) + , m_xDocListBox(m_xBuilder->weld_combo_box("documents")) + , m_aPageChgIdle("SwNavigationPI m_aPageChgIdle") + , m_xNavigatorDlg(pNavigatorDlg) + , m_pContentView(nullptr) + , m_pContentWrtShell(nullptr) + , m_pActContView(nullptr) + , m_pCreateView(nullptr) + , m_pConfig(SW_MOD()->GetNavigationConfig()) + , m_rBindings(*_pBindings) + , m_nRegionMode(RegionMode::NONE) + , m_bIsZoomedIn(false) + , m_bGlobalMode(false) +{ + m_xContainer->connect_container_focus_changed(LINK(this, SwNavigationPI, SetFocusChildHdl)); + + Reference<XToolbarController> xController = + m_xContent2Dispatch->GetControllerForCommand(".uno:NavElement"); + NavElementToolBoxControl* pToolBoxControl = + dynamic_cast<NavElementToolBoxControl*>(xController.get()); + + // In case of LOK, the xController may not a NavElementToolBoxControl + if (comphelper::LibreOfficeKit::isActive() && !pToolBoxControl) + { + m_pNavigateByComboBox = nullptr; + } + else + { + assert(pToolBoxControl); + m_pNavigateByComboBox = pToolBoxControl->GetComboBox(); + } + + // Restore content tree settings before calling UpdateInitShow. UpdateInitShow calls Fillbox, + // which calls Display and UpdateTracking. Incorrect outline levels could be displayed and + // unexpected content tracking could occur if these content tree settings are not done before. + m_xContentTree->SetOutlineLevel(static_cast<sal_uInt8>(m_pConfig->GetOutlineLevel())); + m_xContentTree->SetOutlineTracking(static_cast<sal_uInt8>(m_pConfig->GetOutlineTracking())); + for (ContentTypeId eCntTypeId : o3tl::enumrange<ContentTypeId>()) + { + if (eCntTypeId != ContentTypeId::OUTLINE) + m_xContentTree->SetContentTypeTracking( + eCntTypeId, m_pConfig->IsContentTypeTrack(eCntTypeId)); + } + + if (const ContentTypeId nRootType = m_pConfig->GetRootType(); + nRootType != ContentTypeId::UNKNOWN) + { + m_xContentTree->SetRootType(nRootType); + m_xContent5ToolBox->set_item_active("root", true); + if (nRootType == ContentTypeId::OUTLINE || nRootType == ContentTypeId::DRAWOBJECT) + m_xContentTree->set_selection_mode(SelectionMode::Multiple); + else + m_xContentTree->set_selection_mode(SelectionMode::Single); + } + else + m_xContentTree->set_selection_mode(SelectionMode::Single); + + UpdateInitShow(); + + GetCreateView(); + + m_xContent1ToolBox->set_help_id(HID_NAVIGATOR_TOOLBOX); + m_xContent2ToolBox->set_help_id(HID_NAVIGATOR_TOOLBOX); + m_xContent3ToolBox->set_help_id(HID_NAVIGATOR_TOOLBOX); + m_xContent4ToolBox->set_help_id(HID_NAVIGATOR_TOOLBOX); + m_xContent5ToolBox->set_help_id(HID_NAVIGATOR_TOOLBOX); + m_xContent6ToolBox->set_help_id(HID_NAVIGATOR_TOOLBOX); + m_xGlobalToolBox->set_help_id(HID_NAVIGATOR_GLOBAL_TOOLBOX); + m_xDocListBox->set_help_id(HID_NAVIGATOR_LISTBOX); + m_xDocListBox->set_size_request(42, -1); // set a nominal width so it takes width of surroundings + + // Insert the numeric field in the toolbox. + m_xEdit->set_accessible_name(m_xEdit->get_tooltip_text()); + m_xEdit->set_width_chars(3); + m_xEdit->connect_activate(LINK(this, SwNavigationPI, EditActionHdl)); + m_xEdit->connect_value_changed(LINK(this, SwNavigationPI, PageEditModifyHdl)); + m_xEdit->connect_changed(LINK(this, SwNavigationPI, PageModifiedHdl)); + m_xEdit->set_help_id("modules/swriter/ui/navigatorpanel/numericfield"); + + if (!IsGlobalDoc()) + { + m_xContent1ToolBox->set_item_visible("contenttoggle", false); + } + + const TranslateId REGIONNAME_ARY[] = + { + STR_HYPERLINK, + STR_LINK_REGION, + STR_COPY_REGION + }; + + const TranslateId REGIONMODE_ARY[] = + { + STR_HIDDEN, + STR_ACTIVE, + STR_INACTIVE + }; + + static_assert(SAL_N_ELEMENTS(REGIONNAME_ARY) == SAL_N_ELEMENTS(REGIONMODE_ARY), "### unexpected size!"); + static_assert(SAL_N_ELEMENTS(REGIONNAME_ARY) == static_cast<sal_uInt16>(RegionMode::EMBEDDED) + 1, "### unexpected size!"); + + for (sal_uInt16 i = 0; i <= static_cast<sal_uInt16>(RegionMode::EMBEDDED); ++i) + { + m_aStatusArr[i] = SwResId(REGIONMODE_ARY[i]); + } + + m_aStatusArr[3] = SwResId(STR_ACTIVE_VIEW); + + bool bFloatingNavigator = ParentIsFloatingWindow(m_xNavigatorDlg); + + SetRegionDropMode(m_pConfig->GetRegionMode()); + + m_xContentTree->ShowTree(); + m_xContent6ToolBox->set_item_active("listbox", true); + m_xContent6ToolBox->set_item_sensitive("listbox", bFloatingNavigator); + +// TreeListBox for global document + m_xGlobalTree->set_selection_mode(SelectionMode::Multiple); + +// Handler + Link<const OUString&, void> aLk = LINK(this, SwNavigationPI, ToolBoxSelectHdl); + m_xContent1ToolBox->connect_clicked(aLk); + m_xContent3ToolBox->connect_clicked(aLk); + m_xContent5ToolBox->connect_clicked(aLk); + m_xContent6ToolBox->connect_clicked(aLk); + m_xGlobalToolBox->connect_clicked(aLk); + m_xDocListBox->connect_changed(LINK(this, SwNavigationPI, DocListBoxSelectHdl)); + m_xContent5ToolBox->set_item_menu("headings", m_xHeadingsMenu.get()); + m_xHeadingsMenu->connect_activate(LINK(this, SwNavigationPI, HeadingsMenuSelectHdl)); + m_xContent5ToolBox->connect_menu_toggled(LINK(this, SwNavigationPI, ToolBox5DropdownClickHdl)); + m_xContent6ToolBox->set_item_menu("dragmode", m_xDragModeMenu.get()); + m_xDragModeMenu->connect_activate(LINK(this, SwNavigationPI, DropModeMenuSelectHdl)); + m_xContent6ToolBox->connect_menu_toggled(LINK(this, SwNavigationPI, ToolBox6DropdownClickHdl)); + m_xGlobalToolBox->set_item_menu("update", m_xUpdateMenu.get()); + m_xUpdateMenu->connect_activate(LINK(this, SwNavigationPI, GlobalMenuSelectHdl)); + m_xGlobalToolBox->set_item_menu("insert", m_xInsertMenu.get()); + m_xInsertMenu->connect_activate(LINK(this, SwNavigationPI, GlobalMenuSelectHdl)); + m_xGlobalToolBox->connect_menu_toggled(LINK(this, SwNavigationPI, ToolBoxClickHdl)); + m_xGlobalToolBox->set_item_active("globaltoggle", true); + if (m_pNavigateByComboBox) + m_pNavigateByComboBox->connect_changed( + LINK(this, SwNavigationPI, NavigateByComboBoxSelectHdl)); + +// set toolbar of both modes to widest of each + m_xGlobalToolBox->set_size_request(m_xContent1ToolBox->get_preferred_size().Width() + + m_xContent2ToolBox->get_preferred_size().Width() + + m_xContent3ToolBox->get_preferred_size().Width() + + m_xContent4ToolBox->get_preferred_size().Width(), -1); + + StartListening(*SfxGetpApp()); + + if(IsGlobalDoc()) + { + SwView *pActView = GetCreateView(); + if (pActView && pActView->GetWrtShellPtr()) + m_xGlobalToolBox->set_item_active("save", + pActView->GetWrtShellPtr()->IsGlblDocSaveLinks()); + if (m_pConfig->IsGlobalActive()) + ToggleTree(); + if (bFloatingNavigator) + m_xGlobalTree->grab_focus(); + } + else if (bFloatingNavigator) + m_xContentTree->grab_focus(); + UsePage(); + m_aPageChgIdle.SetInvokeHandler(LINK(this, SwNavigationPI, ChangePageHdl)); + m_aPageChgIdle.SetPriority(TaskPriority::LOWEST); + + m_xContentTree->set_accessible_name(SwResId(STR_ACCESS_TL_CONTENT)); + m_xGlobalTree->set_accessible_name(SwResId(STR_ACCESS_TL_GLOBAL)); + m_xDocListBox->set_accessible_name(m_aStatusArr[3]); + + m_aExpandedSize = m_xContainer->get_preferred_size(); + + if(comphelper::LibreOfficeKit::isActive()) + { + m_xBuilder->weld_container("gridcontent16")->hide(); + m_xDocListBox->hide(); + m_xGlobalBox->hide(); + m_xGlobalToolBox->hide(); + m_xGlobalTree->HideTree(); + + //Open Headings by default + SwView *pView = GetCreateView(); + if (pView->m_nNaviExpandedStatus < 0) + { + pView->m_nNaviExpandedStatus = 1; + m_xContentTree->ExpandAllHeadings(); + } + } +} + +weld::Window* SwNavigationPI::GetFrameWeld() const +{ + if (m_xNavigatorDlg) + return m_xNavigatorDlg->GetFrameWeld(); + return PanelLayout::GetFrameWeld(); +} + +SwNavigationPI::~SwNavigationPI() +{ + if (IsGlobalDoc() && !IsGlobalMode()) + { + SwView *pView = GetCreateView(); + SwWrtShell &rSh = pView->GetWrtShell(); + if (!rSh.IsAllProtect()) + pView->GetDocShell()->SetReadOnlyUI(false); + } + + EndListening(*SfxGetpApp()); + + if (m_oObjectShell) + { + if (m_oObjectShell->Is()) + (*m_oObjectShell)->DoClose(); + m_oObjectShell.reset(); + } + + m_xDocListBox.reset(); + m_xGlobalTree.reset(); + m_xGlobalBox.reset(); + m_xContentTree.reset(); + m_xContentBox.reset(); + m_xGlobalToolBox.reset(); + m_xEdit.reset(); + m_xHeadingsMenu.reset(); + m_xDragModeMenu.reset(); + m_xUpdateMenu.reset(); + m_xInsertMenu.reset(); + m_xContent2Dispatch.reset(); + m_xContent3Dispatch.reset(); + m_xContent1ToolBox.reset(); + m_xContent2ToolBox.reset(); + m_xContent3ToolBox.reset(); + m_xContent4ToolBox.reset(); + m_xContent5ToolBox.reset(); + m_xContent6ToolBox.reset(); + + m_aPageChgIdle.Stop(); + + m_aDocFullName.dispose(); + m_aPageStats.dispose(); +} + +void SwNavigationPI::NotifyItemUpdate(sal_uInt16 nSID, SfxItemState /*eState*/, + const SfxPoolItem* /*pState*/) +{ + if (nSID == SID_DOCFULLNAME) + { + SwView *pActView = GetCreateView(); + if(pActView) + { + SwWrtShell* pWrtShell = pActView->GetWrtShellPtr(); + m_xContentTree->SetActiveShell(pWrtShell); + bool bGlobal = IsGlobalDoc(); + m_xContent1ToolBox->set_item_visible("contenttoggle", bGlobal); + if ((!bGlobal && IsGlobalMode()) || (!IsGlobalMode() && m_pConfig->IsGlobalActive())) + { + ToggleTree(); + } + if (bGlobal) + { + m_xGlobalToolBox->set_item_active("save", pWrtShell->IsGlblDocSaveLinks()); + } + } + else + { + m_xContentTree->SetActiveShell(nullptr); + } + UpdateListBox(); + } + else if (nSID == FN_STAT_PAGE) + { + if(!comphelper::LibreOfficeKit::isActive()) + { + SwView *pActView = GetCreateView(); + if(pActView) + { + SwWrtShell &rSh = pActView->GetWrtShell(); + m_xEdit->set_max(rSh.GetPageCnt()); + m_xEdit->set_width_chars(3); + } + } + } +} + +void SwNavigationPI::UpdateInitShow() +{ + // if the parent isn't a float, then the navigator is displayed in + // the sidebar or is otherwise docked. While the navigator could change + // its size, the sidebar can not, and the navigator would just waste + // space. Therefore disable this button. + bool bParentIsFloatingWindow(ParentIsFloatingWindow(m_xNavigatorDlg)); + m_xContent6ToolBox->set_item_sensitive("listbox", bParentIsFloatingWindow); + // show content if docked + if (!bParentIsFloatingWindow && IsZoomedIn()) + ZoomOut(); + if (!IsZoomedIn()) + FillBox(); +} + +IMPL_LINK_NOARG(SwNavigationPI, SetFocusChildHdl, weld::Container&, void) +{ + // update documents listbox + UpdateListBox(); +} + +// Notification on modified DocInfo +void SwNavigationPI::Notify( SfxBroadcaster& rBrdc, const SfxHint& rHint ) +{ + if(&rBrdc == m_pCreateView) + { + if (rHint.GetId() == SfxHintId::Dying) + { + EndListening(*m_pCreateView); + m_pCreateView = nullptr; + m_xContentTree->SetActiveShell(nullptr); + } + } + else + { + if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint) + { + SfxEventHintId eEventId = static_cast<const SfxEventHint&>(rHint).GetEventId(); + if (eEventId == SfxEventHintId::OpenDoc) + { + SwView *pActView = GetCreateView(); + if(pActView) + { + SwWrtShell* pWrtShell = pActView->GetWrtShellPtr(); + m_xContentTree->SetActiveShell(pWrtShell); + if (m_xGlobalTree->get_visible()) + { + bool bUpdateAll = m_xGlobalTree->Update(false); + // If no update is needed, then update the font colors + // at the entries of broken links. + m_xGlobalTree->Display(!bUpdateAll); + } + } + } + } + } +} + +IMPL_LINK( SwNavigationPI, HeadingsMenuSelectHdl, const OUString&, rMenuId, void ) +{ + if (!rMenuId.isEmpty()) + m_xContentTree->SetOutlineLevel(rMenuId.toUInt32()); +} + +void SwNavigationPI::UpdateListBox() +{ + if (!m_xDocListBox) // disposed + return; + + m_xDocListBox->freeze(); + m_xDocListBox->clear(); + SwView *pActView = GetCreateView(); + bool bDisable = pActView == nullptr; + SwView *pView = SwModule::GetFirstView(); + sal_Int32 nCount = 0; + sal_Int32 nAct = 0; + sal_Int32 nConstPos = 0; + const SwView* pConstView = m_xContentTree->IsConstantView() && + m_xContentTree->GetActiveWrtShell() ? + &m_xContentTree->GetActiveWrtShell()->GetView(): + nullptr; + while (pView) + { + SfxObjectShell* pDoc = pView->GetDocShell(); + // #i53333# don't show help pages here + if ( !pDoc->IsHelpDocument() ) + { + OUString sEntry = pDoc->GetTitle() + " ("; + if (pView == pActView) + { + nAct = nCount; + sEntry += m_aStatusArr[IDX_STR_ACTIVE]; + } + else + sEntry += m_aStatusArr[IDX_STR_INACTIVE]; + sEntry += ")"; + m_xDocListBox->append_text(sEntry); + + if (pConstView && pView == pConstView) + nConstPos = nCount; + + nCount++; + } + pView = SwModule::GetNextView(pView); + } + m_xDocListBox->append_text(m_aStatusArr[3]); // "Active Window" + nCount++; + + if(m_xContentTree->GetHiddenWrtShell()) + { + OUString sEntry = m_xContentTree->GetHiddenWrtShell()->GetView(). + GetDocShell()->GetTitle() + + " (" + + m_aStatusArr[IDX_STR_HIDDEN] + + ")"; + m_xDocListBox->append_text(sEntry); + bDisable = false; + } + + m_xDocListBox->thaw(); + + if(m_xContentTree->IsActiveView()) + { + //Either the name of the current Document or "Active Document". + m_xDocListBox->set_active(pActView ? nAct : --nCount); + } + else if(m_xContentTree->IsHiddenView()) + { + m_xDocListBox->set_active(nCount); + } + else + m_xDocListBox->set_active(nConstPos); + + m_xDocListBox->set_sensitive(!bDisable); +} + +IMPL_LINK(SwNavigationPI, DoneLink, SfxPoolItem const *, pItem, void) +{ + const SfxViewFrameItem* pFrameItem = dynamic_cast<SfxViewFrameItem const *>( pItem ); + if( !pFrameItem ) + return; + + SfxViewFrame* pFrame = pFrameItem->GetFrame(); + if(pFrame) + { + m_xContentTree->clear(); + m_pContentView = dynamic_cast<SwView*>( pFrame->GetViewShell() ); + OSL_ENSURE(m_pContentView, "no SwView"); + if(m_pContentView) + m_pContentWrtShell = m_pContentView->GetWrtShellPtr(); + else + m_pContentWrtShell = nullptr; + m_oObjectShell.emplace( pFrame->GetObjectShell() ); + FillBox(); + } +} + +OUString SwNavigationPI::CreateDropFileName( const TransferableDataHelper& rData ) +{ + OUString sFileName; + SotClipboardFormatId nFormat; + if( rData.HasFormat( nFormat = SotClipboardFormatId::FILE_LIST )) + { + FileList aFileList; + rData.GetFileList( nFormat, aFileList ); + sFileName = aFileList.GetFile( 0 ); + } + else if( rData.HasFormat( nFormat = SotClipboardFormatId::STRING ) || + rData.HasFormat( nFormat = SotClipboardFormatId::SIMPLE_FILE ) || + rData.HasFormat( nFormat = SotClipboardFormatId::FILENAME )) + { + (void)rData.GetString(nFormat, sFileName); + } + else if( rData.HasFormat( nFormat = SotClipboardFormatId::SOLK ) || + rData.HasFormat( nFormat = SotClipboardFormatId::NETSCAPE_BOOKMARK )|| + rData.HasFormat( nFormat = SotClipboardFormatId::FILECONTENT ) || + rData.HasFormat( nFormat = SotClipboardFormatId::FILEGRPDESCRIPTOR ) || + rData.HasFormat( nFormat = SotClipboardFormatId::UNIFORMRESOURCELOCATOR )) + { + INetBookmark aBkmk { OUString(), OUString() }; + if (rData.GetINetBookmark(nFormat, aBkmk)) + sFileName = aBkmk.GetURL(); + } + if( !sFileName.isEmpty() ) + { + sFileName = INetURLObject( sFileName ).GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + return sFileName; +} + +sal_Int8 SwNavigationPI::AcceptDrop() +{ + return ( !m_xContentTree->IsInDrag() && + ( m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::STRING ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::SOLK ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK )|| + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::FILECONTENT ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::FILENAME ))) + ? DND_ACTION_COPY + : DND_ACTION_NONE; +} + +sal_Int8 SwNavigationPI::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + TransferableDataHelper aData( rEvt.maDropEvent.Transferable ); + sal_Int8 nRet = DND_ACTION_NONE; + if (m_xContentTree->IsInDrag()) + return nRet; + + OUString sFileName = SwNavigationPI::CreateDropFileName(aData); + if (sFileName.isEmpty()) + return nRet; + + INetURLObject aTemp(sFileName); + GraphicDescriptor aDesc(aTemp); + if (aDesc.Detect()) // accept no graphics + return nRet; + + if (-1 != sFileName.indexOf('#')) + return nRet; + + if (m_sContentFileName.isEmpty() || m_sContentFileName != sFileName) + { + nRet = rEvt.mnAction; + sFileName = comphelper::string::stripEnd(sFileName, 0); + m_sContentFileName = sFileName; + if(m_oObjectShell) + { + m_xContentTree->SetHiddenShell( nullptr ); + (*m_oObjectShell)->DoClose(); + m_oObjectShell.reset(); + } + SfxStringItem aFileItem(SID_FILE_NAME, sFileName ); + SfxStringItem aOptionsItem( SID_OPTIONS, "HRC" ); + SfxLinkItem aLink( SID_DONELINK, + LINK( this, SwNavigationPI, DoneLink ) ); + if (SwView* pView = GetActiveView()) + pView->GetViewFrame().GetDispatcher()->ExecuteList( + SID_OPENDOC, SfxCallMode::ASYNCHRON, + { &aFileItem, &aOptionsItem, &aLink }); + } + return nRet; +} + +void SwNavigationPI::SetRegionDropMode(RegionMode nNewMode) +{ + m_nRegionMode = nNewMode; + m_pConfig->SetRegionMode( m_nRegionMode ); + + OUString sImageId; + switch (nNewMode) + { + case RegionMode::NONE: + sImageId = RID_BMP_DROP_REGION; + break; + case RegionMode::LINK: + sImageId = RID_BMP_DROP_LINK; + break; + case RegionMode::EMBEDDED: + sImageId = RID_BMP_DROP_COPY; + break; + } + m_xContent6ToolBox->set_item_icon_name("dragmode", sImageId); +} + +void SwNavigationPI::ToggleTree() +{ + if (comphelper::LibreOfficeKit::isActive()) + { + m_xGlobalTree->HideTree(); + return; + } + + bool bGlobalDoc = IsGlobalDoc(); + if (!IsGlobalMode() && bGlobalDoc) + { + if (IsZoomedIn()) + ZoomOut(); + m_xGlobalBox->show(); + m_xGlobalTree->ShowTree(); + m_xGlobalToolBox->show(); + m_xContentBox->hide(); + m_xContentTree->HideTree(); + m_xContent1ToolBox->hide(); + m_xContent2ToolBox->hide(); + m_xContent3ToolBox->hide(); + m_xContent4ToolBox->hide(); + m_xContent5ToolBox->hide(); + m_xContent6ToolBox->hide(); + m_xDocListBox->hide(); + SetGlobalMode(true); + } + else + { + m_xGlobalBox->hide(); + m_xGlobalTree->HideTree(); + m_xGlobalToolBox->hide(); + if (!IsZoomedIn()) + { + m_xContentBox->show(); + m_xContentTree->ShowTree(); + m_xContent1ToolBox->show(); + m_xContent2ToolBox->show(); + m_xContent3ToolBox->show(); + m_xContent4ToolBox->show(); + m_xContent5ToolBox->show(); + m_xContent6ToolBox->show(); + m_xDocListBox->show(); + } + SetGlobalMode(false); + } +} + +bool SwNavigationPI::IsGlobalDoc() const +{ + bool bRet = false; + SwView *pView = GetCreateView(); + if (pView) + { + SwWrtShell &rSh = pView->GetWrtShell(); + bRet = rSh.IsGlobalDoc(); + } + return bRet; +} + +IMPL_LINK_NOARG(SwNavigationPI, ChangePageHdl, Timer *, void) +{ + if (!m_xDocListBox) // disposed + return; + // tdf#134959 if the SpinButton changed value this Timer was launched, now + // change to the desired page, but we leave focus where it currently is, + // i.e. typically remaining in the spinbutton, or whatever other widget the + // user moved to in the meantime + EditAction(); +} + +void SwNavigationPI::SelectNavigateByContentType(const OUString& rContentTypeName) +{ + if (!m_pNavigateByComboBox) + return; + if (auto nPos = m_pNavigateByComboBox->find_text(rContentTypeName); nPos != -1) + { + m_pNavigateByComboBox->set_active(nPos); + UpdateNavigateBy(); + } +} + +IMPL_LINK_NOARG(SwNavigationPI, EditActionHdl, weld::Entry&, bool) +{ + // tdf#134959 if the user presses enter to activate the Entry + // go to the page, and on success we move focus to the document + if (EditAction()) + m_pCreateView->GetEditWin().GrabFocus(); + return true; +} + +IMPL_LINK_NOARG(SwNavigationPI, PageEditModifyHdl, weld::SpinButton&, void) +{ + if (m_aPageChgIdle.IsActive()) + m_aPageChgIdle.Stop(); + m_aPageChgIdle.Start(); +} + +SwView* SwNavigationPI::GetCreateView() const +{ + if (!m_pCreateView) + { + SwView* pView = SwModule::GetFirstView(); + while (pView) + { + if(&pView->GetViewFrame().GetBindings() == &m_rBindings) + { + const_cast<SwNavigationPI*>(this)->m_pCreateView = pView; + const_cast<SwNavigationPI*>(this)->StartListening(*m_pCreateView); + break; + } + pView = SwModule::GetNextView(pView); + } + } + return m_pCreateView; +} + +class SwNavigatorWin : public SfxNavigator +{ +private: + std::unique_ptr<SwNavigationPI> m_xNavi; +public: + SwNavigatorWin(SfxBindings* _pBindings, SfxChildWindow* _pMgr, + vcl::Window* pParent, SfxChildWinInfo* pInfo); + virtual void StateChanged(StateChangedType nStateChange) override; + virtual void dispose() override + { + m_xNavi.reset(); + SfxNavigator::dispose(); + } + virtual ~SwNavigatorWin() override + { + disposeOnce(); + } +}; + +SwNavigatorWin::SwNavigatorWin(SfxBindings* _pBindings, SfxChildWindow* _pMgr, + vcl::Window* pParent, SfxChildWinInfo* pInfo) + : SfxNavigator(_pBindings, _pMgr, pParent, pInfo) + , m_xNavi(std::make_unique<SwNavigationPI>(m_xContainer.get(), _pBindings->GetActiveFrame(), _pBindings, this)) +{ + _pBindings->Invalidate(SID_NAVIGATOR); + + SwNavigationConfig* pNaviConfig = SW_MOD()->GetNavigationConfig(); + + SetMinOutputSizePixel(GetOptimalSize()); + if (pNaviConfig->IsSmall()) + m_xNavi->ZoomIn(); +} + +void SwNavigatorWin::StateChanged(StateChangedType nStateChange) +{ + SfxNavigator::StateChanged(nStateChange); + if (nStateChange == StateChangedType::InitShow) + m_xNavi->UpdateInitShow(); +} + +SFX_IMPL_DOCKINGWINDOW(SwNavigatorWrapper, SID_NAVIGATOR); + +SwNavigatorWrapper::SwNavigatorWrapper(vcl::Window *_pParent, sal_uInt16 nId, + SfxBindings* pBindings, SfxChildWinInfo* pInfo) + : SfxNavigatorWrapper(_pParent, nId) +{ + SetWindow(VclPtr<SwNavigatorWin>::Create(pBindings, this, _pParent, pInfo)); + Initialize(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/numfmtlb.cxx b/sw/source/uibase/utlui/numfmtlb.cxx new file mode 100644 index 0000000000..4dffed15d2 --- /dev/null +++ b/sw/source/uibase/utlui/numfmtlb.cxx @@ -0,0 +1,470 @@ +/* -*- 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 <i18nlangtag/lang.h> +#include <svl/numformat.hxx> +#include <svl/zformat.hxx> +#include <svl/eitem.hxx> +#include <svx/svxids.hrc> +#include <svx/numinf.hxx> +#include <svx/flagsdef.hxx> +#include <svl/itemset.hxx> +#include <osl/diagnose.h> +#include <docsh.hxx> +#include <swtypes.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <numfmtlb.hxx> +#include <strings.hrc> +#include <swabstdlg.hxx> +#include <memory> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +/** + * Description: + * nFormatType: Display the formats of this Type + * nDefaultFormat: Select this format and possibly insert it + */ + +namespace +{ + +bool lcl_isSystemFormat(sal_uInt32 nDefaultFormat, SvNumberFormatter* pFormatter, LanguageType eCurLanguage) +{ + const sal_uInt32 nSysNumFormat = pFormatter->GetFormatIndex(NF_NUMBER_SYSTEM, eCurLanguage); + if (nDefaultFormat == nSysNumFormat) + return true; + const sal_uInt32 nSysShortDateFormat = pFormatter->GetFormatIndex(NF_DATE_SYSTEM_SHORT, eCurLanguage); + if (nDefaultFormat == nSysShortDateFormat) + return true; + const sal_uInt32 nSysLongDateFormat = pFormatter->GetFormatIndex(NF_DATE_SYSTEM_LONG, eCurLanguage); + if (nDefaultFormat == nSysLongDateFormat) + return true; + + if ( eCurLanguage != GetAppLanguage() ) + return false; + + if (nDefaultFormat == pFormatter->GetFormatForLanguageIfBuiltIn(nSysNumFormat, LANGUAGE_SYSTEM)) + return true; + if (nDefaultFormat == pFormatter->GetFormatForLanguageIfBuiltIn(nSysShortDateFormat, LANGUAGE_SYSTEM)) + return true; + if (nDefaultFormat == pFormatter->GetFormatForLanguageIfBuiltIn(nSysLongDateFormat, LANGUAGE_SYSTEM)) + return true; + + return false; +} + +} + +double SwNumFormatBase::GetDefValue(const SvNumFormatType nFormatType) +{ + SvxNumValCategory nDefValue = SvxNumValCategory::Standard; + + switch (nFormatType) + { + case SvNumFormatType::DATE: + case SvNumFormatType::DATE|SvNumFormatType::TIME: + nDefValue = SvxNumValCategory::Date; + break; + + case SvNumFormatType::TIME: + nDefValue = SvxNumValCategory::Time; + break; + + case SvNumFormatType::TEXT: + case SvNumFormatType::UNDEFINED: + nDefValue = SvxNumValCategory::Standard; + break; + + case SvNumFormatType::CURRENCY: + nDefValue = SvxNumValCategory::Currency; + break; + + case SvNumFormatType::PERCENT: + nDefValue = SvxNumValCategory::Percent; + break; + + case SvNumFormatType::LOGICAL: + nDefValue = SvxNumValCategory::Boolean; + break; + + default: + nDefValue = SvxNumValCategory::Standard; + break; + } + + return fSvxNumValConst[nDefValue]; +} + +SwNumFormatBase::SwNumFormatBase() + : m_nStdEntry(0) + , m_nDefFormat(0) + , m_nCurrFormatType(SvNumFormatType::ALL) + , m_bOneArea(false) + , mbCurrFormatTypeNeedsInit(true) + , m_bShowLanguageControl(false) + , m_bUseAutomaticLanguage(true) +{ +} + +NumFormatListBox::NumFormatListBox(std::unique_ptr<weld::ComboBox> xControl) + : mxControl(std::move(xControl)) +{ + Init(); +} + +SwNumFormatTreeView::SwNumFormatTreeView(std::unique_ptr<weld::TreeView> xControl) + : mxControl(std::move(xControl)) +{ + Init(); +} + +void SwNumFormatBase::Init() +{ + if (SwView *pView = GetActiveView()) + m_eCurLanguage = pView->GetWrtShell().GetCurLang(); + else + m_eCurLanguage = SvtSysLocale().GetLanguageTag().getLanguageType(); + + SetFormatType(SvNumFormatType::NUMBER); + SetDefFormat(m_nDefFormat); +} + +void NumFormatListBox::Init() +{ + SwNumFormatBase::Init(); + + mxControl->connect_changed(LINK(this, NumFormatListBox, SelectHdl)); +} + +void SwNumFormatTreeView::Init() +{ + SwNumFormatBase::Init(); + + mxControl->connect_changed(LINK(this, SwNumFormatTreeView, SelectHdl)); +} + +void SwNumFormatBase::SetFormatType(const SvNumFormatType nFormatType) +{ + if (!mbCurrFormatTypeNeedsInit && + (m_nCurrFormatType & nFormatType)) // there are mixed formats, like for example DateTime + return; + + SwView *pView = GetActiveView(); + if(!pView) + return; + SwWrtShell &rSh = pView->GetWrtShell(); + SvNumberFormatter* pFormatter = rSh.GetNumberFormatter(); + + clear(); // Remove all entries from the Listbox + + NfIndexTableOffset eOffsetStart = NF_NUMBER_START; + NfIndexTableOffset eOffsetEnd = NF_NUMBER_START; + + switch( nFormatType ) + { + case SvNumFormatType::NUMBER: + eOffsetStart=NF_NUMBER_START; + eOffsetEnd=NF_NUMBER_END; + break; + + case SvNumFormatType::PERCENT: + eOffsetStart=NF_PERCENT_START; + eOffsetEnd=NF_PERCENT_END; + break; + + case SvNumFormatType::CURRENCY: + eOffsetStart=NF_CURRENCY_START; + eOffsetEnd=NF_CURRENCY_END; + break; + + case SvNumFormatType::DATETIME: + eOffsetStart=NF_DATE_START; + eOffsetEnd=NF_TIME_END; + break; + + case SvNumFormatType::DATE: + eOffsetStart=NF_DATE_START; + eOffsetEnd=NF_DATE_END; + break; + + case SvNumFormatType::TIME: + eOffsetStart=NF_TIME_START; + eOffsetEnd=NF_TIME_END; + break; + + case SvNumFormatType::SCIENTIFIC: + eOffsetStart=NF_SCIENTIFIC_START; + eOffsetEnd=NF_SCIENTIFIC_END; + break; + + case SvNumFormatType::FRACTION: + eOffsetStart=NF_FRACTION_START; + eOffsetEnd=NF_FRACTION_END; + break; + + case SvNumFormatType::LOGICAL: + eOffsetStart=NF_BOOLEAN; + eOffsetEnd=NF_BOOLEAN; + break; + + case SvNumFormatType::TEXT: + eOffsetStart=NF_TEXT; + eOffsetEnd=NF_TEXT; + break; + + case SvNumFormatType::ALL: + eOffsetStart=NF_NUMERIC_START; + eOffsetEnd = NfIndexTableOffset( NF_INDEX_TABLE_ENTRIES - 1 ); + break; + + default: + OSL_FAIL("what a format?"); + break; + } + + const SvNumberformat* pFormat; + sal_Int32 i = 0; + const Color* pCol; + double fVal = SwNumFormatBase::GetDefValue(nFormatType); + OUString sValue; + + const sal_uInt32 nSysNumFormat = pFormatter->GetFormatIndex( + NF_NUMBER_SYSTEM, m_eCurLanguage ); + const sal_uInt32 nSysShortDateFormat = pFormatter->GetFormatIndex( + NF_DATE_SYSTEM_SHORT, m_eCurLanguage ); + const sal_uInt32 nSysLongDateFormat = pFormatter->GetFormatIndex( + NF_DATE_SYSTEM_LONG, m_eCurLanguage ); + + for( tools::Long nIndex = eOffsetStart; nIndex <= eOffsetEnd; ++nIndex ) + { + const sal_uInt32 nFormat = pFormatter->GetFormatIndex( + static_cast<NfIndexTableOffset>(nIndex), m_eCurLanguage ); + pFormat = pFormatter->GetEntry( nFormat ); + + if( nFormat == pFormatter->GetFormatIndex( NF_NUMBER_STANDARD, + m_eCurLanguage ) + || const_cast<SvNumberformat*>(pFormat)->GetOutputString( fVal, sValue, &pCol ) + || nFormatType == SvNumFormatType::UNDEFINED ) + { + sValue = pFormat->GetFormatstring(); + } + else if( nFormatType == SvNumFormatType::TEXT ) + { + pFormatter->GetOutputString( "\"ABC\"", nFormat, sValue, &pCol); + } + + if (nFormat != nSysNumFormat && + nFormat != nSysShortDateFormat && + nFormat != nSysLongDateFormat) + { + append(OUString::number(nFormat), sValue); + + if( nFormat == pFormatter->GetStandardFormat( + nFormatType, m_eCurLanguage ) ) + m_nStdEntry = i; + ++i; + } + } + + append_text(SwResId(STR_DEFINE_NUMBERFORMAT)); + + set_active(m_nStdEntry); + + m_nCurrFormatType = nFormatType; + mbCurrFormatTypeNeedsInit = false; + +} + +void SwNumFormatBase::SetDefFormat(const sal_uInt32 nDefaultFormat) +{ + if (nDefaultFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + { + m_nDefFormat = nDefaultFormat; + return; + } + + SwView *pView = GetActiveView(); + if(!pView) + return; + SwWrtShell &rSh = pView->GetWrtShell(); + SvNumberFormatter* pFormatter = rSh.GetNumberFormatter(); + + SvNumFormatType nType = pFormatter->GetType(nDefaultFormat); + + SetFormatType(nType); + + sal_uInt32 nFormat = pFormatter->GetFormatForLanguageIfBuiltIn(nDefaultFormat, m_eCurLanguage); + + for (sal_Int32 i = 0, nCount = get_count(); i < nCount; ++i) + { + if (nFormat == get_id(i).toUInt32()) + { + set_active(i); + m_nStdEntry = i; + m_nDefFormat = GetFormat(); + return; + } + } + + // No entry found: + OUString sValue; + const Color* pCol = nullptr; + + if (nType == SvNumFormatType::TEXT) + { + pFormatter->GetOutputString("\"ABC\"", nDefaultFormat, sValue, &pCol); + } + else + { + pFormatter->GetOutputString(SwNumFormatBase::GetDefValue(nType), nDefaultFormat, sValue, &pCol); + } + + sal_Int32 nPos = 0; + while (get_id(nPos).toUInt32() == NUMBERFORMAT_ENTRY_NOT_FOUND) + nPos++; + + if ( lcl_isSystemFormat(nDefaultFormat, pFormatter, m_eCurLanguage) ) + { + sValue += SwResId(RID_STR_SYSTEM); + } + + insert_text(nPos, sValue); // Insert as first numeric entry + set_id(nPos, OUString::number(nDefaultFormat)); + set_active(nPos); + m_nDefFormat = GetFormat(); +} + +sal_uInt32 NumFormatListBox::GetFormat() const +{ + return mxControl->get_active_id().toUInt32(); +} + +sal_uInt32 SwNumFormatTreeView::GetFormat() const +{ + return mxControl->get_selected_id().toUInt32(); +} + +void SwNumFormatBase::CallSelectHdl() +{ + const sal_Int32 nPos = get_active(); + OUString sDefine(SwResId( STR_DEFINE_NUMBERFORMAT )); + SwView *pView = GetActiveView(); + + if (!pView || nPos != get_count() - 1 || get_text(nPos) != sDefine) + return; + + SwWrtShell &rSh = pView->GetWrtShell(); + SvNumberFormatter* pFormatter = rSh.GetNumberFormatter(); + + SfxItemSetFixed< + SID_ATTR_NUMBERFORMAT_VALUE, SID_ATTR_NUMBERFORMAT_INFO, + SID_ATTR_NUMBERFORMAT_ONE_AREA, SID_ATTR_NUMBERFORMAT_ONE_AREA, + SID_ATTR_NUMBERFORMAT_NOLANGUAGE, + SID_ATTR_NUMBERFORMAT_NOLANGUAGE, + SID_ATTR_NUMBERFORMAT_ADD_AUTO, + SID_ATTR_NUMBERFORMAT_ADD_AUTO> aCoreSet( rSh.GetAttrPool() ); + + double fValue = SwNumFormatBase::GetDefValue(m_nCurrFormatType); + + sal_uInt32 nFormat = pFormatter->GetStandardFormat( m_nCurrFormatType, m_eCurLanguage); + aCoreSet.Put( SfxUInt32Item( SID_ATTR_NUMBERFORMAT_VALUE, nFormat )); + + aCoreSet.Put( SvxNumberInfoItem( pFormatter, fValue, + SID_ATTR_NUMBERFORMAT_INFO ) ); + + if( (SvNumFormatType::DATE | SvNumFormatType::TIME) & m_nCurrFormatType ) + aCoreSet.Put(SfxBoolItem(SID_ATTR_NUMBERFORMAT_ONE_AREA, m_bOneArea)); + + aCoreSet.Put(SfxBoolItem(SID_ATTR_NUMBERFORMAT_NOLANGUAGE, !m_bShowLanguageControl)); + aCoreSet.Put(SfxBoolItem(SID_ATTR_NUMBERFORMAT_ADD_AUTO, m_bUseAutomaticLanguage)); + + // force deselect to break mouse lock on selected entry + set_active(-1); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateNumFormatDialog(&get_widget(), aCoreSet)); + + if (RET_OK == pDlg->Execute()) + { + const SvxNumberInfoItem* pFormatInfoItem = pView->GetDocShell()-> + GetItem( SID_ATTR_NUMBERFORMAT_INFO ); + + if( pFormatInfoItem ) + { + for ( sal_uInt32 key : pFormatInfoItem->GetDelFormats() ) + pFormatter->DeleteEntry( key ); + } + + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + if( const SfxUInt32Item* pFormatValueItem = pOutSet->GetItemIfSet( + SID_ATTR_NUMBERFORMAT_VALUE, false )) + { + sal_uInt32 nNumberFormat = pFormatValueItem->GetValue(); + // oj #105473# change order of calls + const SvNumberformat* pFormat = pFormatter->GetEntry(nNumberFormat); + if( pFormat ) + m_eCurLanguage = pFormat->GetLanguage(); + // SetDefFormat uses eCurLanguage to look for if this format already in the list + SetDefFormat(nNumberFormat); + } + const SfxBoolItem* pAddAutoItem; + if( m_bShowLanguageControl && (pAddAutoItem = pOutSet->GetItemIfSet( + SID_ATTR_NUMBERFORMAT_ADD_AUTO, false))) + { + m_bUseAutomaticLanguage = pAddAutoItem->GetValue(); + } + } + else + SetDefFormat(nFormat); + +} + +IMPL_LINK_NOARG(NumFormatListBox, SelectHdl, weld::ComboBox&, void) +{ + CallSelectHdl(); +} + +IMPL_LINK_NOARG(SwNumFormatTreeView, SelectHdl, weld::TreeView&, void) +{ + CallSelectHdl(); +} + +void SwNumFormatBase::clear() +{ + mbCurrFormatTypeNeedsInit = true; + m_nCurrFormatType = SvNumFormatType::ALL; +} + +void NumFormatListBox::clear() +{ + mxControl->clear(); + SwNumFormatBase::clear(); +} + +void SwNumFormatTreeView::clear() +{ + mxControl->clear(); + SwNumFormatBase::clear(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/prcntfld.cxx b/sw/source/uibase/utlui/prcntfld.cxx new file mode 100644 index 0000000000..4c8eede661 --- /dev/null +++ b/sw/source/uibase/utlui/prcntfld.cxx @@ -0,0 +1,231 @@ +/* -*- 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 <prcntfld.hxx> +#include <vcl/fieldvalues.hxx> + +SwPercentField::SwPercentField(std::unique_ptr<weld::MetricSpinButton> pControl) + : m_pField(std::move(pControl)) + , m_nOldMax(0) + , m_nOldMin(0) + , m_nLastPercent(-1) + , m_nLastValue(-1) + , m_nOldDigits(m_pField->get_digits()) + , m_eOldUnit(FieldUnit::NONE) + , m_bLockAutoCalculation(false) +{ + sal_Int64 nMin, nMax; + m_pField->get_range(nMin, nMax, FieldUnit::TWIP); + m_nRefValue = DenormalizePercent(nMax); + m_pField->get_increments(m_nOldSpinSize, m_nOldPageSize, FieldUnit::NONE); +} + +void SwPercentField::SetRefValue(sal_Int64 nValue) +{ + sal_Int64 nRealValue = GetRealValue(m_eOldUnit); + + m_nRefValue = nValue; + + if (!m_bLockAutoCalculation && (m_pField->get_unit() == FieldUnit::PERCENT)) + set_value(nRealValue, m_eOldUnit); +} + +void SwPercentField::ShowPercent(bool bPercent) +{ + if ((bPercent && m_pField->get_unit() == FieldUnit::PERCENT) + || (!bPercent && m_pField->get_unit() != FieldUnit::PERCENT)) + return; + + sal_Int64 nOldValue; + + if (bPercent) + { + nOldValue = get_value(); + + m_eOldUnit = m_pField->get_unit(); + m_nOldDigits = m_pField->get_digits(); + m_pField->get_range(m_nOldMin, m_nOldMax, FieldUnit::NONE); + m_pField->get_increments(m_nOldSpinSize, m_nOldPageSize, FieldUnit::NONE); + m_pField->set_unit(FieldUnit::PERCENT); + m_pField->set_digits(0); + + sal_Int64 nCurrentWidth + = vcl::ConvertValue(m_nOldMin, 0, m_nOldDigits, m_eOldUnit, FieldUnit::TWIP); + // round to 0.5 percent + int nPercent = m_nRefValue ? (((nCurrentWidth * 10) / m_nRefValue + 5) / 10) : 0; + + m_pField->set_range(std::max(1, nPercent), 100, FieldUnit::NONE); + m_pField->set_increments(5, 10, FieldUnit::NONE); + if (nOldValue != m_nLastValue) + { + nCurrentWidth + = vcl::ConvertValue(nOldValue, 0, m_nOldDigits, m_eOldUnit, FieldUnit::TWIP); + nPercent = m_nRefValue ? (((nCurrentWidth * 10) / m_nRefValue + 5) / 10) : 0; + m_pField->set_value(nPercent, FieldUnit::NONE); + m_nLastPercent = nPercent; + m_nLastValue = nOldValue; + } + else + m_pField->set_value(m_nLastPercent, FieldUnit::NONE); + } + else + { + sal_Int64 nOldPercent = get_value(FieldUnit::PERCENT); + + nOldValue = Convert(get_value(), m_pField->get_unit(), m_eOldUnit); + + m_pField->set_unit(m_eOldUnit); + m_pField->set_digits(m_nOldDigits); + m_pField->set_range(m_nOldMin, m_nOldMax, FieldUnit::NONE); + m_pField->set_increments(m_nOldSpinSize, m_nOldPageSize, FieldUnit::NONE); + + if (nOldPercent != m_nLastPercent) + { + set_value(nOldValue, m_eOldUnit); + m_nLastPercent = nOldPercent; + m_nLastValue = nOldValue; + } + else + set_value(m_nLastValue, m_eOldUnit); + } +} + +void SwPercentField::set_value(sal_Int64 nNewValue, FieldUnit eInUnit) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT || eInUnit == FieldUnit::PERCENT) + m_pField->set_value(Convert(nNewValue, eInUnit, m_pField->get_unit()), FieldUnit::NONE); + else + { + // Overwrite output value, do not restore later + sal_Int64 nPercent, nCurrentWidth; + if (eInUnit == FieldUnit::TWIP) + { + nCurrentWidth + = vcl::ConvertValue(nNewValue, 0, m_nOldDigits, FieldUnit::TWIP, FieldUnit::TWIP); + } + else + { + sal_Int64 nValue = Convert(nNewValue, eInUnit, m_eOldUnit); + nCurrentWidth = vcl::ConvertValue(nValue, 0, m_nOldDigits, m_eOldUnit, FieldUnit::TWIP); + } + nPercent = m_nRefValue ? (((nCurrentWidth * 10) / m_nRefValue + 5) / 10) : 0; + m_pField->set_value(nPercent, FieldUnit::NONE); + } +} + +sal_Int64 SwPercentField::get_value(FieldUnit eOutUnit) +{ + return Convert(m_pField->get_value(FieldUnit::NONE), m_pField->get_unit(), eOutUnit); +} + +void SwPercentField::set_min(sal_Int64 nNewMin, FieldUnit eInUnit) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT) + m_pField->set_min(nNewMin, eInUnit); + else + { + if (eInUnit == FieldUnit::NONE) + eInUnit = m_eOldUnit; + m_nOldMin = Convert(nNewMin, eInUnit, m_eOldUnit); + + int nPercent = Convert(nNewMin, eInUnit, FieldUnit::PERCENT); + m_pField->set_min(std::max(1, nPercent), FieldUnit::NONE); + } +} + +void SwPercentField::set_max(sal_Int64 nNewMax, FieldUnit eInUnit) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT) + m_pField->set_max(nNewMax, eInUnit); +} + +sal_Int64 SwPercentField::NormalizePercent(sal_Int64 nValue) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT) + nValue = m_pField->normalize(nValue); + else + nValue = nValue * ImpPower10(m_nOldDigits); + return nValue; +} + +sal_Int64 SwPercentField::DenormalizePercent(sal_Int64 nValue) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT) + nValue = m_pField->denormalize(nValue); + else + { + int nFactor = ImpPower10(m_nOldDigits); + nValue = ((nValue + (nFactor / 2)) / nFactor); + } + return nValue; +} + +int SwPercentField::ImpPower10(sal_uInt16 n) +{ + int nValue = 1; + + for (sal_uInt16 i = 0; i < n; ++i) + nValue *= 10; + + return nValue; +} + +sal_Int64 SwPercentField::GetRealValue(FieldUnit eOutUnit) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT) + return get_value(eOutUnit); + else + return Convert(get_value(), m_pField->get_unit(), eOutUnit); +} + +sal_Int64 SwPercentField::Convert(sal_Int64 nValue, FieldUnit eInUnit, FieldUnit eOutUnit) +{ + if (eInUnit == eOutUnit || (eInUnit == FieldUnit::NONE && eOutUnit == m_pField->get_unit()) + || (eOutUnit == FieldUnit::NONE && eInUnit == m_pField->get_unit())) + return nValue; + + if (eInUnit == FieldUnit::PERCENT) + { + // Convert to metric + sal_Int64 nTwipValue = (m_nRefValue * nValue + 50) / 100; + + if (eOutUnit == FieldUnit::TWIP) // Only convert if necessary + return NormalizePercent(nTwipValue); + else + return vcl::ConvertValue(NormalizePercent(nTwipValue), 0, m_nOldDigits, FieldUnit::TWIP, + eOutUnit); + } + + if (eOutUnit == FieldUnit::PERCENT) + { + // Convert to percent + sal_Int64 nCurrentWidth; + nValue = DenormalizePercent(nValue); + + if (eInUnit == FieldUnit::TWIP) // Only convert if necessary + nCurrentWidth = nValue; + else + nCurrentWidth = vcl::ConvertValue(nValue, 0, m_nOldDigits, eInUnit, FieldUnit::TWIP); + // Round to 0.5 percent + return m_nRefValue ? (((nCurrentWidth * 1000) / m_nRefValue + 5) / 10) : 0; + } + + return vcl::ConvertValue(nValue, 0, m_nOldDigits, eInUnit, eOutUnit); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/shdwcrsr.cxx b/sw/source/uibase/utlui/shdwcrsr.cxx new file mode 100644 index 0000000000..f897878408 --- /dev/null +++ b/sw/source/uibase/utlui/shdwcrsr.cxx @@ -0,0 +1,56 @@ +/* -*- 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 <com/sun/star/text/HoriOrientation.hpp> +#include <shdwcrsr.hxx> +#include <vcl/ptrstyle.hxx> + +using namespace ::com::sun::star; + +SwShadowCursor::~SwShadowCursor() +{ + if( USHRT_MAX != m_nOldMode ) + DrawCursor( m_nOldMode); +} + +void SwShadowCursor::SetPos( const Point& rPt, tools::Long nHeight, sal_uInt16 nMode ) +{ + Point aPt( m_pWin->LogicToPixel( rPt )); + nHeight = m_pWin->LogicToPixel( Size( 0, nHeight )).Height(); + if( m_aOldPt != aPt || m_nOldHeight != nHeight || m_nOldMode != nMode ) + { + if( USHRT_MAX != m_nOldMode ) + DrawCursor( m_nOldMode); + + DrawCursor( nMode); + m_nOldMode = nMode; + m_nOldHeight = nHeight; + m_aOldPt = aPt; + } +} + +void SwShadowCursor::DrawCursor( sal_uInt16 nMode ) +{ + if( text::HoriOrientation::LEFT == nMode ) // Arrow to the right + m_pWin->SetPointer(PointerStyle::AutoScrollE); + else // Arrow to the left + m_pWin->SetPointer(PointerStyle::AutoScrollW); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/tmplctrl.cxx b/sw/source/uibase/utlui/tmplctrl.cxx new file mode 100644 index 0000000000..2b68905cf4 --- /dev/null +++ b/sw/source/uibase/utlui/tmplctrl.cxx @@ -0,0 +1,121 @@ +/* -*- 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 <svl/style.hxx> +#include <svl/stritem.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/status.hxx> +#include <vcl/weldutils.hxx> + +#include <swtypes.hxx> +#include <strings.hrc> + +#include <wrtsh.hxx> +#include <view.hxx> +#include <swmodule.hxx> +#include <cmdid.h> +#include <docsh.hxx> +#include <tmplctrl.hxx> + +SFX_IMPL_STATUSBAR_CONTROL( SwTemplateControl, SfxStringItem ); + +SwTemplateControl::SwTemplateControl( sal_uInt16 _nSlotId, + sal_uInt16 _nId, + StatusBar& rStb ) : + SfxStatusBarControl( _nSlotId, _nId, rStb ) +{ +} + +SwTemplateControl::~SwTemplateControl() +{ +} + +void SwTemplateControl::StateChangedAtStatusBarControl( + sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState ) +{ + const SfxStringItem* pItem = nullptr; + if (SfxItemState::DEFAULT == eState && (pItem = dynamic_cast<const SfxStringItem*>(pState))) + { + m_sTemplate = pItem->GetValue(); + GetStatusBar().SetItemText(GetId(), m_sTemplate); + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_TMPLCTRL_HINT)); + } + else + { + GetStatusBar().SetItemText(GetId(), OUString()); + GetStatusBar().SetQuickHelpText(GetId(), u""_ustr); + } +} + +void SwTemplateControl::Paint( const UserDrawEvent& ) +{ +} + +void SwTemplateControl::Command( const CommandEvent& rCEvt ) +{ + if ( rCEvt.GetCommand() != CommandEventId::ContextMenu || + GetStatusBar().GetItemText( GetId() ).isEmpty()) + return; + + { + SwView* pView = ::GetActiveView(); + SwWrtShell *const pWrtShell(pView ? pView->GetWrtShellPtr() : nullptr); + if (nullptr != pWrtShell && + !pWrtShell->SwCursorShell::HasSelection()&& + !pWrtShell->IsSelFrameMode() && + !pWrtShell->IsObjSelected()) + { + SfxStyleSheetBasePool* pPool = pView->GetDocShell()-> + GetStyleSheetPool(); + auto xIter = pPool->CreateIterator(SfxStyleFamily::Page); + if (xIter->Count() > 1) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/swriter/ui/pagestylemenu.ui")); + std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu")); + + sal_uInt32 nCount = 0; + SfxStyleSheetBase* pStyle = xIter->First(); + while( pStyle ) + { + xPopup->append(OUString::number(++nCount), pStyle->GetName()); + pStyle = xIter->Next(); + } + + ::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1, 1)); + weld::Window* pParent = weld::GetPopupParent(GetStatusBar(), aRect); + OUString sResult = xPopup->popup_at_rect(pParent, aRect); + if (!sResult.isEmpty()) + { + sal_uInt32 nCurrId = sResult.toUInt32(); + // looks a bit awkward, but another way is not possible + pStyle = xIter->operator[]( nCurrId - 1 ); + SfxStringItem aStyle( FN_SET_PAGE_STYLE, pStyle->GetName() ); + pWrtShell->GetView().GetViewFrame().GetDispatcher()->ExecuteList( + FN_SET_PAGE_STYLE, + SfxCallMode::SLOT|SfxCallMode::RECORD, + { &aStyle }); + } + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/uiitems.cxx b/sw/source/uibase/utlui/uiitems.cxx new file mode 100644 index 0000000000..b88751abdd --- /dev/null +++ b/sw/source/uibase/utlui/uiitems.cxx @@ -0,0 +1,277 @@ +/* -*- 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 <comphelper/servicehelper.hxx> +#include <editeng/itemtype.hxx> +#include <tools/UnitConversion.hxx> +#include <unosett.hxx> + +#include <swtypes.hxx> +#include <cmdid.h> +#include <uiitems.hxx> + +#include <strings.hrc> +#include <unomid.h> +#include <numrule.hxx> + +#include <editeng/eerdll.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +SwPageFootnoteInfoItem::SwPageFootnoteInfoItem( SwPageFootnoteInfo const & rInfo) : + SfxPoolItem( FN_PARAM_FTN_INFO ), + m_aFootnoteInfo(rInfo) +{ +} + +SwPageFootnoteInfoItem::~SwPageFootnoteInfoItem() +{ +} + +SwPageFootnoteInfoItem* SwPageFootnoteInfoItem::Clone( SfxItemPool * /*pPool*/ ) const +{ + return new SwPageFootnoteInfoItem( *this ); +} + +bool SwPageFootnoteInfoItem::operator==( const SfxPoolItem& rAttr ) const +{ + return SfxPoolItem::operator==(rAttr) + && m_aFootnoteInfo == static_cast<const SwPageFootnoteInfoItem&>(rAttr).m_aFootnoteInfo; +} + +bool SwPageFootnoteInfoItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit eCoreUnit, + MapUnit ePresUnit, + OUString& rText, + const IntlWrapper& rIntl +) const +{ + const SwTwips nHght = GetPageFootnoteInfo().GetHeight(); + if ( nHght ) + { + rText = SwResId( STR_MAX_FTN_HEIGHT ) + " " + + ::GetMetricText( nHght, eCoreUnit, ePresUnit, &rIntl ) + " " + + EditResId( ::GetMetricId( ePresUnit ) ); + } + return true; +} + +bool SwPageFootnoteInfoItem::QueryValue( Any& rVal, sal_uInt8 nMemberId ) const +{ + bool bRet = true; + switch(nMemberId & ~CONVERT_TWIPS) + { + case MID_FTN_HEIGHT : rVal <<= static_cast<sal_Int32>(convertTwipToMm100(m_aFootnoteInfo.GetHeight()));break; + case MID_LINE_WEIGHT : rVal <<= static_cast<sal_Int16>(convertTwipToMm100(m_aFootnoteInfo.GetLineWidth()));break; + case MID_LINE_COLOR : rVal <<= m_aFootnoteInfo.GetLineColor();break; + case MID_LINE_RELWIDTH : + { + Fraction aTmp( 100, 1 ); + aTmp *= m_aFootnoteInfo.GetWidth(); + rVal <<= static_cast<sal_Int8>(static_cast<tools::Long>(aTmp)); + } + break; + case MID_LINE_ADJUST : rVal <<= static_cast<sal_Int16>(m_aFootnoteInfo.GetAdj());break;//text::HorizontalAdjust + case MID_LINE_TEXT_DIST : rVal <<= static_cast<sal_Int32>(convertTwipToMm100(m_aFootnoteInfo.GetTopDist()));break; + case MID_LINE_FOOTNOTE_DIST: rVal <<= static_cast<sal_Int32>(convertTwipToMm100(m_aFootnoteInfo.GetBottomDist()));break; + case MID_FTN_LINE_STYLE : + { + switch ( m_aFootnoteInfo.GetLineStyle( ) ) + { + default: + case SvxBorderLineStyle::NONE : rVal <<= sal_Int8(0); break; + case SvxBorderLineStyle::SOLID: rVal <<= sal_Int8(1); break; + case SvxBorderLineStyle::DOTTED: rVal <<= sal_Int8(2); break; + case SvxBorderLineStyle::DASHED: rVal <<= sal_Int8(3); break; + } + break; + } + default: + bRet = false; + } + return bRet; +} + +bool SwPageFootnoteInfoItem::PutValue(const Any& rVal, sal_uInt8 nMemberId) +{ + sal_Int32 nSet32 = 0; + Color aColor; + bool bRet = true; + switch(nMemberId & ~CONVERT_TWIPS) + { + case MID_LINE_COLOR : + rVal >>= aColor; + m_aFootnoteInfo.SetLineColor(aColor); + break; + case MID_FTN_HEIGHT: + case MID_LINE_TEXT_DIST : + case MID_LINE_FOOTNOTE_DIST: + rVal >>= nSet32; + if(nSet32 < 0) + bRet = false; + else + { + nSet32 = o3tl::toTwips(nSet32, o3tl::Length::mm100); + switch(nMemberId & ~CONVERT_TWIPS) + { + case MID_FTN_HEIGHT: m_aFootnoteInfo.SetHeight(nSet32); break; + case MID_LINE_TEXT_DIST: m_aFootnoteInfo.SetTopDist(nSet32);break; + case MID_LINE_FOOTNOTE_DIST: m_aFootnoteInfo.SetBottomDist(nSet32);break; + } + } + break; + case MID_LINE_WEIGHT : + { + sal_Int16 nSet = 0; + rVal >>= nSet; + if(nSet >= 0) + m_aFootnoteInfo.SetLineWidth(o3tl::toTwips(nSet, o3tl::Length::mm100)); + else + bRet = false; + } + break; + case MID_LINE_RELWIDTH : + { + sal_Int8 nSet = 0; + rVal >>= nSet; + if(nSet < 0) + bRet = false; + else + m_aFootnoteInfo.SetWidth(Fraction(nSet, 100)); + } + break; + case MID_LINE_ADJUST : + { + sal_Int16 nSet = 0; + rVal >>= nSet; + if(nSet >= 0 && nSet < 3) //text::HorizontalAdjust + m_aFootnoteInfo.SetAdj(static_cast<css::text::HorizontalAdjust>(nSet)); + else + bRet = false; + } + break; + case MID_FTN_LINE_STYLE: + { + SvxBorderLineStyle eStyle = SvxBorderLineStyle::NONE; + sal_Int8 nSet = 0; + rVal >>= nSet; + switch ( nSet ) + { + case 1: eStyle = SvxBorderLineStyle::SOLID; break; + case 2: eStyle = SvxBorderLineStyle::DOTTED; break; + case 3: eStyle = SvxBorderLineStyle::DASHED; break; + default: break; + } + m_aFootnoteInfo.SetLineStyle( eStyle ); + } + break; + default: + bRet = false; + } + return bRet; +} + +SwPtrItem::SwPtrItem( const sal_uInt16 nId, void* pPtr ) : + SfxPoolItem( nId ), + m_pMisc(pPtr) +{ +} + +// Cloning + +SwPtrItem* SwPtrItem::Clone( SfxItemPool * /*pPool*/ ) const +{ + return new SwPtrItem( *this ); +} + +bool SwPtrItem::operator==( const SfxPoolItem& rAttr ) const +{ + return SfxPoolItem::operator==(rAttr) + && m_pMisc == static_cast<const SwPtrItem&>(rAttr).m_pMisc; +} + +// SwUINumRuleItem for the NumTabPages of the FormatNumRule/Styleists + +SwUINumRuleItem::SwUINumRuleItem( const SwNumRule& rRul ) + : SfxPoolItem( FN_PARAM_ACT_NUMBER ), m_pRule( new SwNumRule( rRul ) ) +{ +} + +SwUINumRuleItem::SwUINumRuleItem( const SwUINumRuleItem& rItem ) + : SfxPoolItem( rItem ), + m_pRule( new SwNumRule( *rItem.m_pRule )) +{ +} + +SwUINumRuleItem::~SwUINumRuleItem() +{ +} + +SwUINumRuleItem* SwUINumRuleItem::Clone( SfxItemPool * /*pPool*/ ) const +{ + return new SwUINumRuleItem( *this ); +} + +bool SwUINumRuleItem::operator==( const SfxPoolItem& rAttr ) const +{ + return SfxPoolItem::operator==(rAttr) + && *m_pRule == *static_cast<const SwUINumRuleItem&>(rAttr).m_pRule; +} + +bool SwUINumRuleItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const +{ + uno::Reference< container::XIndexReplace >xRules = new SwXNumberingRules(*m_pRule); + rVal <<= xRules; + return true; +} +bool SwUINumRuleItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) +{ + uno::Reference< container::XIndexReplace> xRulesRef; + if(rVal >>= xRulesRef) + { + auto pSwXRules = dynamic_cast<SwXNumberingRules*>(xRulesRef.get()); + if(pSwXRules) + { + *m_pRule = *pSwXRules->GetNumRule(); + } + } + return true; +} + +SwPaMItem::SwPaMItem( const sal_uInt16 nId, SwPaM* pPaM ) : + SfxPoolItem( nId ), + m_pPaM(pPaM) +{ +} + +SwPaMItem* SwPaMItem::Clone( SfxItemPool * /*pPool*/ ) const +{ + return new SwPaMItem( *this ); +} + +bool SwPaMItem::operator==( const SfxPoolItem& rAttr ) const +{ + return SfxPoolItem::operator==(rAttr) + && m_pPaM == static_cast<const SwPaMItem&>(rAttr).m_pPaM; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/uitool.cxx b/sw/source/uibase/utlui/uitool.cxx new file mode 100644 index 0000000000..fd50bf6678 --- /dev/null +++ b/sw/source/uibase/utlui/uitool.cxx @@ -0,0 +1,921 @@ +/* -*- 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 <hintids.hxx> + +#include <osl/diagnose.h> +#include <tools/datetime.hxx> +#include <vcl/weld.hxx> +#include <unotools/collatorwrapper.hxx> +#include <svl/stritem.hxx> +#include <svl/grabbagitem.hxx> +#include <unotools/syslocale.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <editeng/pmdlitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/brushitem.hxx> +#include <svx/pageitem.hxx> +#include <editeng/lrspitem.hxx> +#include <svl/style.hxx> +#include <unotools/localedatawrapper.hxx> +#include <com/sun/star/awt/XPopupMenu.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <comphelper/processfactory.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <fmtornt.hxx> +#include <tabcol.hxx> +#include <fmtfsize.hxx> +#include <fmthdft.hxx> +#include <fmtpdsc.hxx> +#include <uiitems.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <uitool.hxx> +#include <frmatr.hxx> +#include <paratr.hxx> +#include <fmtcol.hxx> +#include <usrpref.hxx> + +#include <cmdid.h> +#include <doc.hxx> +#include <charfmt.hxx> +#include <SwStyleNameMapper.hxx> +#include <strings.hrc> +#include <docmodel/color/ComplexColor.hxx> + +// 50 cm 28350 +#define MAXHEIGHT 28350 +#define MAXWIDTH 28350 + +using namespace ::com::sun::star; + +// General list of string pointer + +// Set boxinfo attribute + +void PrepareBoxInfo(SfxItemSet& rSet, const SwWrtShell& rSh) +{ + std::shared_ptr<SvxBoxInfoItem> aBoxInfo(std::make_shared<SvxBoxInfoItem>(SID_ATTR_BORDER_INNER)); + + if ( const SvxBoxInfoItem *pBoxInfo = rSet.GetItemIfSet( SID_ATTR_BORDER_INNER )) + { + aBoxInfo.reset(pBoxInfo->Clone()); + } + + // Table variant: If more than one table cells are selected + rSh.GetCursor(); //So that GetCursorCnt() returns the right thing + aBoxInfo->SetTable (rSh.IsTableMode() && rSh.GetCursorCnt() > 1); + // Always show the distance field + aBoxInfo->SetDist (true); + // Set minimal size in tables and paragraphs + aBoxInfo->SetMinDist (rSh.IsTableMode() || rSh.GetSelectionType() & (SelectionType::Text | SelectionType::Table)); + // Set always the default distance + aBoxInfo->SetDefDist (MIN_BORDER_DIST); + // Single lines can have only in tables DontCare-Status + aBoxInfo->SetValid(SvxBoxInfoItemValidFlags::DISABLE, !rSh.IsTableMode()); + + rSet.Put(*aBoxInfo); +} + +void ConvertAttrCharToGen(SfxItemSet& rSet, bool bIsPara) +{ + // Background / highlight + { + // Always use the visible background + if( const SvxBrushItem *pTmpBrush = rSet.GetItemIfSet( RES_CHRATR_HIGHLIGHT ) ) + { + SvxBrushItem aTmpBrush( *pTmpBrush ); + if( aTmpBrush.GetColor() != COL_TRANSPARENT ) + { + aTmpBrush.SetWhich( RES_CHRATR_BACKGROUND ); + rSet.Put( aTmpBrush ); + } + } + } + + if ( bIsPara ) + return; + + // Tell dialogs to use character-specific slots/whichIds + // tdf#126684: We use RES_PARATR_GRABBAG, because RES_CHRATR_GRABBAG may be overwritten later in + // SwDocStyleSheet::GetItemSet when applying attributes from char format + assert(SfxItemState::SET != rSet.GetItemState(RES_PARATR_GRABBAG, false)); + SfxGrabBagItem aGrabBag(RES_PARATR_GRABBAG); + aGrabBag.GetGrabBag()["DialogUseCharAttr"] <<= true; + // Store initial ranges to allow restoring later + uno::Sequence<sal_uInt16> aOrigRanges(rSet.GetRanges().size() * 2 + 1); + int i = 0; + for (const auto& rPair : rSet.GetRanges()) + { + aOrigRanges.getArray()[i++] = rPair.first; + aOrigRanges.getArray()[i++] = rPair.second; + } + aOrigRanges.getArray()[i++] = 0; + aGrabBag.GetGrabBag()["OrigItemSetRanges"] <<= aOrigRanges; + rSet.MergeRange(RES_PARATR_GRABBAG, RES_PARATR_GRABBAG); + rSet.Put(aGrabBag); +} + +void ConvertAttrGenToChar(SfxItemSet& rSet, const SfxItemSet& rOrigSet, bool bIsPara) +{ + // Background / highlighting + if( SfxItemState::SET == rSet.GetItemState( RES_CHRATR_BACKGROUND, false ) ) + { + // Highlight is an MS specific thing, so remove it at the first time when LO modifies + // this part of the imported document. + rSet.Put( SvxBrushItem(RES_CHRATR_HIGHLIGHT) ); + + // Remove shading marker + if (const SfxGrabBagItem* pGrabBagItem = rOrigSet.GetItemIfSet(RES_CHRATR_GRABBAG, false)) + { + SfxGrabBagItem aGrabBag(*pGrabBagItem); + std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag(); + auto aIterator = rMap.find("CharShadingMarker"); + if( aIterator != rMap.end() ) + { + aIterator->second <<= false; + } + rSet.Put( aGrabBag ); + } + } + + if ( bIsPara ) + return; + + rSet.ClearItem( RES_BACKGROUND ); + + if (const SfxGrabBagItem* pGrabBagItem = rOrigSet.GetItemIfSet(RES_PARATR_GRABBAG, false)) + { + SfxGrabBagItem aGrabBag(*pGrabBagItem); + std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag(); + auto aIterator = rMap.find("OrigItemSetRanges"); + if (aIterator != rMap.end()) + { + uno::Sequence<sal_uInt16> aOrigRanges; + if ( aIterator->second >>= aOrigRanges ) + { + assert(aOrigRanges.getLength() % 2 == 1); + int numPairs = (aOrigRanges.getLength()-1)/2; + std::unique_ptr<WhichPair[]> xPairs(new WhichPair[numPairs]); + for(int i=0; i<aOrigRanges.getLength()-1; i += 2) + { + xPairs[i/2] = { aOrigRanges[i], aOrigRanges[i+1] }; + } + rSet.SetRanges(WhichRangesContainer(std::move(xPairs), numPairs)); + } + } + } + assert(SfxItemState::SET != rSet.GetItemState(RES_PARATR_GRABBAG, false)); +} + +void ApplyCharBackground(Color const& rBackgroundColor, model::ComplexColor const& rComplexColor, SwWrtShell& rShell) +{ + rShell.StartUndo(SwUndoId::INSATTR); + + SfxItemSetFixed<RES_CHRATR_GRABBAG, RES_CHRATR_GRABBAG> aCoreSet(rShell.GetView().GetPool()); + + rShell.GetCurAttr(aCoreSet); + + // Set char background + rShell.SetAttrItem(SvxBrushItem(rBackgroundColor, rComplexColor, RES_CHRATR_BACKGROUND)); + + // Highlight is an MS specific thing, so remove it at the first time when LO modifies + // this part of the imported document. + rShell.SetAttrItem(SvxBrushItem(RES_CHRATR_HIGHLIGHT)); + + // Remove shading marker + if (const SfxGrabBagItem* pGrabBagItem = aCoreSet.GetItemIfSet(RES_CHRATR_GRABBAG, false)) + { + SfxGrabBagItem aGrabBag(*pGrabBagItem); + std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag(); + auto aIterator = rMap.find("CharShadingMarker"); + if (aIterator != rMap.end()) + { + aIterator->second <<= false; + } + rShell.SetAttrItem(aGrabBag); + } + + rShell.EndUndo(SwUndoId::INSATTR); +} + +// Fill header footer + +static void FillHdFt(SwFrameFormat* pFormat, const SfxItemSet& rSet) +{ + SwAttrSet aSet(pFormat->GetAttrSet()); + aSet.Put(rSet); + + const SvxSizeItem& rSize = rSet.Get(SID_ATTR_PAGE_SIZE); + const SfxBoolItem& rDynamic = rSet.Get(SID_ATTR_PAGE_DYNAMIC); + + // Convert size + SwFormatFrameSize aFrameSize(rDynamic.GetValue() ? SwFrameSize::Minimum : SwFrameSize::Fixed, + rSize.GetSize().Width(), + rSize.GetSize().Height()); + aSet.Put(aFrameSize); + pFormat->SetFormatAttr(aSet); +} + +/// Convert from UseOnPage to SvxPageUsage. +static SvxPageUsage lcl_convertUseToSvx(UseOnPage nUse) +{ + SvxPageUsage nRet = SvxPageUsage::NONE; + if (nUse & UseOnPage::Left) + nRet = SvxPageUsage::Left; + if (nUse & UseOnPage::Right) + nRet = SvxPageUsage::Right; + if ((nUse & UseOnPage::All) == UseOnPage::All) + nRet = SvxPageUsage::All; + if ((nUse & UseOnPage::Mirror) == UseOnPage::Mirror) + nRet = SvxPageUsage::Mirror; + return nRet; +} + +/// Convert from SvxPageUsage to UseOnPage. +static UseOnPage lcl_convertUseFromSvx(SvxPageUsage nUse) +{ + UseOnPage nRet = UseOnPage::NONE; + if (nUse == SvxPageUsage::Left) + nRet = UseOnPage::Left; + else if (nUse == SvxPageUsage::Right) + nRet = UseOnPage::Right; + else if (nUse == SvxPageUsage::All) + nRet = UseOnPage::All; + else if (nUse == SvxPageUsage::Mirror) + nRet = UseOnPage::Mirror; + return nRet; +} + +// PageDesc <-> convert into sets and back + +void ItemSetToPageDesc( const SfxItemSet& rSet, SwPageDesc& rPageDesc ) +{ + SwFrameFormat& rMaster = rPageDesc.GetMaster(); + bool bFirstShare = false; + + // before SetFormatAttr() in case it contains RES_BACKGROUND_FULL_SIZE + // itself, as it does when called from SwXPageStyle + if (const SfxGrabBagItem* pGrabBag = rSet.GetItemIfSet(SID_ATTR_CHAR_GRABBAG)) + { + bool bValue; + const auto pGrabBagInner = pGrabBag->GetGrabBag(); + const auto iter = pGrabBagInner.find("BackgroundFullSize"); + assert(iter != pGrabBagInner.end()); + if (iter->second >>= bValue) + { + rMaster.SetFormatAttr(SfxBoolItem(RES_BACKGROUND_FULL_SIZE, bValue)); + } + auto it = pGrabBagInner.find("RtlGutter"); + if (it != pGrabBagInner.end() && (it->second >>= bValue)) + { + rMaster.SetFormatAttr(SfxBoolItem(RES_RTL_GUTTER, bValue)); + } + } + + // Transfer all general frame attributes + rMaster.SetFormatAttr(rSet); + + // PageData + if(rSet.GetItemState(SID_ATTR_PAGE) == SfxItemState::SET) + { + const SvxPageItem& rPageItem = rSet.Get(SID_ATTR_PAGE); + + const SvxPageUsage nUse = rPageItem.GetPageUsage(); + if(nUse != SvxPageUsage::NONE) + rPageDesc.SetUseOn( lcl_convertUseFromSvx(nUse) ); + rPageDesc.SetLandscape(rPageItem.IsLandscape()); + SvxNumberType aNumType; + aNumType.SetNumberingType( rPageItem.GetNumType() ); + rPageDesc.SetNumType(aNumType); + } + // Size + if(rSet.GetItemState(SID_ATTR_PAGE_SIZE) == SfxItemState::SET) + { + const SvxSizeItem& rSizeItem = rSet.Get(SID_ATTR_PAGE_SIZE); + SwFormatFrameSize aSize(SwFrameSize::Fixed); + aSize.SetSize(rSizeItem.GetSize()); + rMaster.SetFormatAttr(aSize); + } + // Evaluate header attributes + if( const SvxSetItem* pHeaderSetItem = rSet.GetItemIfSet( SID_ATTR_PAGE_HEADERSET, + false ) ) + { + const SfxItemSet& rHeaderSet = pHeaderSetItem->GetItemSet(); + const SfxBoolItem& rHeaderOn = rHeaderSet.Get(SID_ATTR_PAGE_ON); + + if(rHeaderOn.GetValue()) + { + // Take over values + if(!rMaster.GetHeader().IsActive()) + rMaster.SetFormatAttr(SwFormatHeader(true)); + + // Pick out everything and adapt the header format + SwFormatHeader aHeaderFormat(rMaster.GetHeader()); + SwFrameFormat *pHeaderFormat = aHeaderFormat.GetHeaderFormat(); + OSL_ENSURE(pHeaderFormat != nullptr, "no header format"); + + ::FillHdFt(pHeaderFormat, rHeaderSet); + + rPageDesc.ChgHeaderShare(rHeaderSet.Get(SID_ATTR_PAGE_SHARED).GetValue()); + rPageDesc.ChgFirstShare(static_cast<const SfxBoolItem&>( + rHeaderSet.Get(SID_ATTR_PAGE_SHARED_FIRST)).GetValue()); + bFirstShare = true; + } + else + { + // Disable header + if(rMaster.GetHeader().IsActive()) + { + rMaster.SetFormatAttr(SwFormatHeader(false)); + rPageDesc.ChgHeaderShare(false); + } + } + } + + // Evaluate footer attributes + if( const SvxSetItem* pFooterSetItem = rSet.GetItemIfSet( SID_ATTR_PAGE_FOOTERSET, + false ) ) + { + const SfxItemSet& rFooterSet = pFooterSetItem->GetItemSet(); + const SfxBoolItem& rFooterOn = rFooterSet.Get(SID_ATTR_PAGE_ON); + + if(rFooterOn.GetValue()) + { + // Take over values + if(!rMaster.GetFooter().IsActive()) + rMaster.SetFormatAttr(SwFormatFooter(true)); + + // Pick out everything and adapt the footer format + SwFormatFooter aFooterFormat(rMaster.GetFooter()); + SwFrameFormat *pFooterFormat = aFooterFormat.GetFooterFormat(); + OSL_ENSURE(pFooterFormat != nullptr, "no footer format"); + + ::FillHdFt(pFooterFormat, rFooterSet); + + rPageDesc.ChgFooterShare(rFooterSet.Get(SID_ATTR_PAGE_SHARED).GetValue()); + if (!bFirstShare) + { + rPageDesc.ChgFirstShare(static_cast<const SfxBoolItem&>( + rFooterSet.Get(SID_ATTR_PAGE_SHARED_FIRST)).GetValue()); + } + } + else + { + // Disable footer + if(rMaster.GetFooter().IsActive()) + { + rMaster.SetFormatAttr(SwFormatFooter(false)); + rPageDesc.ChgFooterShare(false); + } + } + } + + // Footnotes + + if( const SwPageFootnoteInfoItem* pFootnoteItem = rSet.GetItemIfSet( FN_PARAM_FTN_INFO, + false ) ) + rPageDesc.SetFootnoteInfo( pFootnoteItem->GetPageFootnoteInfo() ); + + // Columns + + // Register compliant + + const SfxBoolItem* pRegisterModeItem = rSet.GetItemIfSet( + SID_SWREGISTER_MODE, false); + if(!pRegisterModeItem) + return; + + bool bSet = pRegisterModeItem->GetValue(); + if(!bSet) + rPageDesc.SetRegisterFormatColl(nullptr); + else if(const SfxStringItem* pCollectionItem = rSet.GetItemIfSet( + SID_SWREGISTER_COLLECTION, false)) + { + const OUString& rColl = pCollectionItem->GetValue(); + SwDoc& rDoc = *rMaster.GetDoc(); + SwTextFormatColl* pColl = rDoc.FindTextFormatCollByName( rColl ); + if( !pColl ) + { + const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( + rColl, SwGetPoolIdFromName::TxtColl ); + if( USHRT_MAX != nId ) + pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( nId ); + else + pColl = rDoc.MakeTextFormatColl( rColl, + rDoc.GetDfltTextFormatColl() ); + } + if( pColl ) + pColl->SetFormatAttr( SwRegisterItem ( true )); + rPageDesc.SetRegisterFormatColl( pColl ); + } +} + +namespace +{ +bool IsOwnFormat(const SwDoc& rDoc) +{ + const SwDocShell* pDocShell = rDoc.GetDocShell(); + SfxMedium* pMedium = pDocShell->GetMedium(); + if (!pMedium) + { + return false; + } + + std::shared_ptr<const SfxFilter> pFilter = pMedium->GetFilter(); + if (!pFilter) + { + return false; + } + + return pFilter->IsOwnFormat(); +} +} + +void PageDescToItemSet( const SwPageDesc& rPageDesc, SfxItemSet& rSet) +{ + const SwFrameFormat& rMaster = rPageDesc.GetMaster(); + + // Page data + SvxPageItem aPageItem(SID_ATTR_PAGE); + aPageItem.SetDescName(rPageDesc.GetName()); + aPageItem.SetPageUsage(lcl_convertUseToSvx(rPageDesc.GetUseOn())); + aPageItem.SetLandscape(rPageDesc.GetLandscape()); + aPageItem.SetNumType(rPageDesc.GetNumType().GetNumberingType()); + rSet.Put(aPageItem); + + // Size + SvxSizeItem aSizeItem(SID_ATTR_PAGE_SIZE, rMaster.GetFrameSize().GetSize()); + rSet.Put(aSizeItem); + + // Maximum size + SvxSizeItem aMaxSizeItem(SID_ATTR_PAGE_MAXSIZE, Size(MAXWIDTH, MAXHEIGHT)); + rSet.Put(aMaxSizeItem); + + // Margins, border and the other stuff. + rSet.Put(rMaster.GetAttrSet()); + + std::shared_ptr<SvxBoxInfoItem> aBoxInfo(std::make_shared<SvxBoxInfoItem>(SID_ATTR_BORDER_INNER)); + + if ( const SvxBoxInfoItem *pBoxInfo = rSet.GetItemIfSet( SID_ATTR_BORDER_INNER ) ) + { + aBoxInfo.reset(pBoxInfo->Clone()); + } + + aBoxInfo->SetTable( false ); + // Show always the distance field + aBoxInfo->SetDist( true); + // Set minimal size in tables and paragraphs + aBoxInfo->SetMinDist( false ); + // Set always the default distance + aBoxInfo->SetDefDist( MIN_BORDER_DIST ); + // Single lines can have only in tables DontCare-Status + aBoxInfo->SetValid( SvxBoxInfoItemValidFlags::DISABLE ); + rSet.Put( *aBoxInfo ); + + SfxStringItem aFollow(SID_ATTR_PAGE_EXT1, OUString()); + if(rPageDesc.GetFollow()) + aFollow.SetValue(rPageDesc.GetFollow()->GetName()); + rSet.Put(aFollow); + + // Header + if(rMaster.GetHeader().IsActive()) + { + const SwFormatHeader &rHeaderFormat = rMaster.GetHeader(); + const SwFrameFormat *pHeaderFormat = rHeaderFormat.GetHeaderFormat(); + OSL_ENSURE(pHeaderFormat != nullptr, "no header format"); + + // HeaderInfo, margins, background, border + SfxItemSetFixed<RES_FRMATR_BEGIN,RES_FRMATR_END - 1, // [82 + + // FillAttribute support + XATTR_FILL_FIRST, XATTR_FILL_LAST, // [1014 + + SID_ATTR_BORDER_INNER,SID_ATTR_BORDER_INNER, // [10023 + SID_ATTR_PAGE_SIZE,SID_ATTR_PAGE_SIZE, // [10051 + SID_ATTR_PAGE_ON,SID_ATTR_PAGE_SHARED, // [10060 + SID_ATTR_PAGE_SHARED_FIRST,SID_ATTR_PAGE_SHARED_FIRST> aHeaderSet(*rSet.GetPool()); + + // set correct parent to get the XFILL_NONE FillStyle as needed + aHeaderSet.SetParent(&rMaster.GetDoc()->GetDfltFrameFormat()->GetAttrSet()); + + // Dynamic or fixed height + SfxBoolItem aOn(SID_ATTR_PAGE_ON, true); + aHeaderSet.Put(aOn); + + const SwFormatFrameSize &rFrameSize = pHeaderFormat->GetFrameSize(); + const SwFrameSize eSizeType = rFrameSize.GetHeightSizeType(); + SfxBoolItem aDynamic(SID_ATTR_PAGE_DYNAMIC, eSizeType != SwFrameSize::Fixed); + aHeaderSet.Put(aDynamic); + + // Left equal right + SfxBoolItem aShared(SID_ATTR_PAGE_SHARED, rPageDesc.IsHeaderShared()); + aHeaderSet.Put(aShared); + SfxBoolItem aFirstShared(SID_ATTR_PAGE_SHARED_FIRST, rPageDesc.IsFirstShared()); + aHeaderSet.Put(aFirstShared); + + // Size + SvxSizeItem aSize(SID_ATTR_PAGE_SIZE, rFrameSize.GetSize()); + aHeaderSet.Put(aSize); + + // Shifting frame attributes + aHeaderSet.Put(pHeaderFormat->GetAttrSet()); + aHeaderSet.Put( *aBoxInfo ); + + // Create SetItem + SvxSetItem aSetItem(SID_ATTR_PAGE_HEADERSET, aHeaderSet); + rSet.Put(aSetItem); + } + + // Footer + if(rMaster.GetFooter().IsActive()) + { + const SwFormatFooter &rFooterFormat = rMaster.GetFooter(); + const SwFrameFormat *pFooterFormat = rFooterFormat.GetFooterFormat(); + OSL_ENSURE(pFooterFormat != nullptr, "no footer format"); + + // FooterInfo, margins, background, border + SfxItemSetFixed<RES_FRMATR_BEGIN,RES_FRMATR_END - 1, // [82 + + // FillAttribute support + XATTR_FILL_FIRST, XATTR_FILL_LAST, // [1014 + + SID_ATTR_BORDER_INNER,SID_ATTR_BORDER_INNER, // [10023 + SID_ATTR_PAGE_SIZE,SID_ATTR_PAGE_SIZE, // [10051 + SID_ATTR_PAGE_ON,SID_ATTR_PAGE_SHARED, // [10060 + SID_ATTR_PAGE_SHARED_FIRST,SID_ATTR_PAGE_SHARED_FIRST> aFooterSet(*rSet.GetPool()); + + // set correct parent to get the XFILL_NONE FillStyle as needed + aFooterSet.SetParent(&rMaster.GetDoc()->GetDfltFrameFormat()->GetAttrSet()); + + // Dynamic or fixed height + SfxBoolItem aOn(SID_ATTR_PAGE_ON, true); + aFooterSet.Put(aOn); + + const SwFormatFrameSize &rFrameSize = pFooterFormat->GetFrameSize(); + const SwFrameSize eSizeType = rFrameSize.GetHeightSizeType(); + SfxBoolItem aDynamic(SID_ATTR_PAGE_DYNAMIC, eSizeType != SwFrameSize::Fixed); + aFooterSet.Put(aDynamic); + + // Left equal right + SfxBoolItem aShared(SID_ATTR_PAGE_SHARED, rPageDesc.IsFooterShared()); + aFooterSet.Put(aShared); + SfxBoolItem aFirstShared(SID_ATTR_PAGE_SHARED_FIRST, rPageDesc.IsFirstShared()); + aFooterSet.Put(aFirstShared); + + // Size + SvxSizeItem aSize(SID_ATTR_PAGE_SIZE, rFrameSize.GetSize()); + aFooterSet.Put(aSize); + + // Shifting Frame attributes + aFooterSet.Put(pFooterFormat->GetAttrSet()); + aFooterSet.Put( *aBoxInfo ); + + // Create SetItem + SvxSetItem aSetItem(SID_ATTR_PAGE_FOOTERSET, aFooterSet); + rSet.Put(aSetItem); + } + + // Integrate footnotes + SwPageFootnoteInfo& rInfo = const_cast<SwPageFootnoteInfo&>(rPageDesc.GetFootnoteInfo()); + SwPageFootnoteInfoItem aFootnoteItem(rInfo); + rSet.Put(aFootnoteItem); + + // Register compliant + const SwTextFormatColl* pCol = rPageDesc.GetRegisterFormatColl(); + SwRegisterItem aReg(pCol != nullptr); + aReg.SetWhich(SID_SWREGISTER_MODE); + rSet.Put(aReg); + if(pCol) + rSet.Put(SfxStringItem(SID_SWREGISTER_COLLECTION, pCol->GetName())); + + std::optional<SfxGrabBagItem> oGrabBag; + if (SfxGrabBagItem const* pItem = rSet.GetItemIfSet(SID_ATTR_CHAR_GRABBAG)) + { + oGrabBag.emplace(*pItem); + } + else + { + oGrabBag.emplace(SID_ATTR_CHAR_GRABBAG); + } + oGrabBag->GetGrabBag()["BackgroundFullSize"] <<= + rMaster.GetAttrSet().GetItem<SfxBoolItem>(RES_BACKGROUND_FULL_SIZE)->GetValue(); + + if (IsOwnFormat(*rMaster.GetDoc())) + { + oGrabBag->GetGrabBag()["RtlGutter"] + <<= rMaster.GetAttrSet().GetItem<SfxBoolItem>(RES_RTL_GUTTER)->GetValue(); + } + + rSet.Put(*oGrabBag); +} + +// Set DefaultTabs + +void MakeDefTabs(SwTwips nDefDist, SvxTabStopItem& rTabs) +{ + if( rTabs.Count() ) + return; + { + SvxTabStop aSwTabStop( nDefDist, SvxTabAdjust::Default ); + rTabs.Insert( aSwTabStop ); + } +} + +// Distance between two tabs + +SwTwips GetTabDist(const SvxTabStopItem& rTabs) +{ + return rTabs.Count() ? rTabs[0].GetTabPos() : 1134; // 1134 = 2 cm +} + +// Inquire if in the set is a Sfx-PageDesc combination present and return it. +void SfxToSwPageDescAttr( const SwWrtShell& rShell, SfxItemSet& rSet ) +{ + const SfxPoolItem* pItem; + SwFormatPageDesc aPgDesc; + + bool bChanged = false; + // Page number + switch (rSet.GetItemState(SID_ATTR_PARA_PAGENUM, false, &pItem)) + { + case SfxItemState::SET: + { + aPgDesc.SetNumOffset(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + bChanged = true; + break; + } + case SfxItemState::DISABLED: + { + bChanged = true; // default initialised aPgDesc clears the number + break; + } + case SfxItemState::UNKNOWN: + case SfxItemState::DEFAULT: + break; + default: + assert(false); // unexpected + break; + } + if( const SvxPageModelItem* pModelItem = rSet.GetItemIfSet( SID_ATTR_PARA_MODEL, false )) + { + const OUString& rDescName = pModelItem->GetValue(); + if( !rDescName.isEmpty() ) // No name -> disable PageDesc! + { + // Delete only, if PageDesc will be enabled! + rSet.ClearItem( RES_BREAK ); + SwPageDesc* pDesc = const_cast<SwWrtShell&>(rShell).FindPageDescByName( + rDescName, true ); + if( pDesc ) + aPgDesc.RegisterToPageDesc( *pDesc ); + } + rSet.ClearItem( SID_ATTR_PARA_MODEL ); + bChanged = true; + } + else + { + SfxItemSetFixed<RES_PAGEDESC, RES_PAGEDESC> aCoreSet(rShell.GetView().GetPool()); + rShell.GetCurAttr( aCoreSet ); + if(const SwFormatPageDesc* pPageDescItem = aCoreSet.GetItemIfSet( RES_PAGEDESC ) ) + { + const SwPageDesc* pPageDesc = pPageDescItem->GetPageDesc(); + if( pPageDesc ) + { + aPgDesc.RegisterToPageDesc( *const_cast<SwPageDesc*>(pPageDesc) ); + } + } + } + + if(bChanged) + rSet.Put( aPgDesc ); +} + +// Inquire if in the set is a Sfx-PageDesc combination present and return it. +void SwToSfxPageDescAttr( SfxItemSet& rCoreSet ) +{ + const SwFormatPageDesc* pPageDescItem = nullptr; + OUString aName; + ::std::optional<sal_uInt16> oNumOffset; + bool bPut = true; + switch( rCoreSet.GetItemState( RES_PAGEDESC, true, &pPageDescItem ) ) + { + case SfxItemState::SET: + { + if( pPageDescItem->GetPageDesc() ) + { + aName = pPageDescItem->GetPageDesc()->GetName(); + oNumOffset = pPageDescItem->GetNumOffset(); + } + rCoreSet.ClearItem( RES_PAGEDESC ); + // Page number + } + break; + + case SfxItemState::DEFAULT: + break; + + default: + bPut = false; + } + + if (oNumOffset) + { + SfxUInt16Item aPageNum( SID_ATTR_PARA_PAGENUM, *oNumOffset ); + rCoreSet.Put( aPageNum ); + } + + if(bPut) + rCoreSet.Put( SvxPageModelItem( aName, true, SID_ATTR_PARA_MODEL ) ); +} + +// Determine metric + +FieldUnit GetDfltMetric(bool bWeb) +{ + return SW_MOD()->GetUsrPref(bWeb)->GetMetric(); +} + +// Determine metric + +void SetDfltMetric( FieldUnit eMetric, bool bWeb ) +{ + SW_MOD()->ApplyUserMetric(eMetric, bWeb); +} + +void InsertStringSorted(const OUString& rId, const OUString& rEntry, weld::ComboBox& rToFill, int nOffset) +{ + CollatorWrapper& rCaseColl = ::GetAppCaseCollator(); + const int nCount = rToFill.get_count(); + while (nOffset < nCount) + { + if (0 < rCaseColl.compareString(rToFill.get_text(nOffset), rEntry)) + break; + ++nOffset; + } + rToFill.insert(nOffset, rEntry, &rId, nullptr, nullptr); +} + +void FillCharStyleListBox(weld::ComboBox& rToFill, SwDocShell* pDocSh, bool bSorted, bool bWithDefault) +{ + const int nOffset = rToFill.get_count() > 0 ? 1 : 0; + rToFill.freeze(); + SfxStyleSheetBasePool* pPool = pDocSh->GetStyleSheetPool(); + SwDoc* pDoc = pDocSh->GetDoc(); + const SfxStyleSheetBase* pBase = pPool->First(SfxStyleFamily::Char); + const OUString sStandard(SwResId(STR_POOLCHR_STANDARD)); + while(pBase) + { + if(bWithDefault || pBase->GetName() != sStandard) + { + sal_IntPtr nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( pBase->GetName(), SwGetPoolIdFromName::ChrFmt ); + OUString sId(OUString::number(nPoolId)); + if (bSorted) + InsertStringSorted(sId, pBase->GetName(), rToFill, nOffset); + else + rToFill.append(sId, pBase->GetName()); + } + pBase = pPool->Next(); + } + // non-pool styles + const SwCharFormats* pFormats = pDoc->GetCharFormats(); + for(size_t i = 0; i < pFormats->size(); ++i) + { + const SwCharFormat* pFormat = (*pFormats)[i]; + if(pFormat->IsDefault()) + continue; + const OUString& rName = pFormat->GetName(); + if (rToFill.find_text(rName) == -1) + { + OUString sId(OUString::number(USHRT_MAX)); + if (bSorted) + InsertStringSorted(sId, rName, rToFill, nOffset); + else + rToFill.append(sId, rName); + } + } + rToFill.thaw(); +}; + +SwTwips GetTableWidth( SwFrameFormat const * pFormat, SwTabCols const & rCols, sal_uInt16 *pPercent, + SwWrtShell* pSh ) +{ + // To get the width is slightly more complicated. + SwTwips nWidth = 0; + const sal_Int16 eOri = pFormat->GetHoriOrient().GetHoriOrient(); + switch(eOri) + { + case text::HoriOrientation::FULL: nWidth = rCols.GetRight(); break; + case text::HoriOrientation::LEFT_AND_WIDTH: + case text::HoriOrientation::LEFT: + case text::HoriOrientation::RIGHT: + case text::HoriOrientation::CENTER: + nWidth = pFormat->GetFrameSize().GetWidth(); + break; + default: + { + if(pSh) + { + if ( nullptr == pSh->GetFlyFrameFormat() ) + { + nWidth = pSh->GetAnyCurRect(CurRectType::PagePrt).Width(); + } + else + { + nWidth = pSh->GetAnyCurRect(CurRectType::FlyEmbeddedPrt).Width(); + } + } + else + { + OSL_FAIL("where to get the actual width from?"); + } + const SvxLRSpaceItem& rLRSpace = pFormat->GetLRSpace(); + nWidth -= (rLRSpace.GetRight() + rLRSpace.GetLeft()); + } + } + if (pPercent) + *pPercent = pFormat->GetFrameSize().GetWidthPercent(); + return nWidth; +} + +OUString GetAppLangDateTimeString( const DateTime& rDT ) +{ + const SvtSysLocale aSysLocale; + const LocaleDataWrapper& rAppLclData = aSysLocale.GetLocaleData(); + OUString sRet = rAppLclData.getDate( rDT ) + " " + rAppLclData.getTime( rDT ); + return sRet; +} + +// Add a new function which can get and set the current "SID_ATTR_APPLYCHARUNIT" value + +bool HasCharUnit( bool bWeb) +{ + return SW_MOD()->GetUsrPref(bWeb)->IsApplyCharUnit(); +} + +void SetApplyCharUnit(bool bApplyChar, bool bWeb) +{ + SW_MOD()->ApplyUserCharUnit(bApplyChar, bWeb); +} + +bool ExecuteMenuCommand(const css::uno::Reference<css::awt::XPopupMenu>& rMenu, const SfxViewFrame& rViewFrame, sal_uInt16 nId) +{ + bool bRet = false; + const sal_uInt16 nItemCount = rMenu->getItemCount(); + OUString sCommand; + for (sal_uInt16 nItem = 0; nItem < nItemCount; ++nItem) + { + sal_Int16 nItemId = rMenu->getItemId(nItem); + css::uno::Reference<css::awt::XPopupMenu> xPopup = rMenu->getPopupMenu(nItemId); + if (xPopup.is()) + { + sCommand = xPopup->getCommand(nId); + if(!sCommand.isEmpty()) + break; + } + } + if(!sCommand.isEmpty()) + { + uno::Reference< frame::XFrame > xFrame = rViewFrame.GetFrame().GetFrameInterface(); + uno::Reference < frame::XDispatchProvider > xProv( xFrame, uno::UNO_QUERY ); + util::URL aURL; + aURL.Complete = sCommand; + uno::Reference < util::XURLTransformer > xTrans( util::URLTransformer::create(::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict( aURL ); + uno::Reference< frame::XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 ); + if( xDisp.is() ) + { + uno::Sequence< beans::PropertyValue > aSeq; + xDisp->dispatch( aURL, aSeq ); + bRet = true; + } + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/unotools.cxx b/sw/source/uibase/utlui/unotools.cxx new file mode 100644 index 0000000000..93edfaad30 --- /dev/null +++ b/sw/source/uibase/utlui/unotools.cxx @@ -0,0 +1,496 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <swtypes.hxx> + +#include <strings.hrc> +#include <unotools.hxx> +#include <unoprnms.hxx> +#include <unotextcursor.hxx> +#include <i18nutil/unicode.hxx> +#include <o3tl/string_view.hxx> +#include <rtl/string.h> +#include <svtools/colorcfg.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/jobset.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/virdev.hxx> +#include <vcl/weld.hxx> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/text/XTextViewCursorSupplier.hpp> +#include <com/sun/star/view/XScreenCursor.hpp> +#include <com/sun/star/view/DocumentZoomType.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/view/XViewSettingsSupplier.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/string.hxx> +#include <docsh.hxx> +#include <editsh.hxx> +#include <wrtsh.hxx> +#include <swmodule.hxx> +#include <TextCursorHelper.hxx> +#include <doc.hxx> + +using namespace ::com::sun::star; + +constexpr OUStringLiteral cFactory = u"private:factory/swriter"; + +static void disableScrollBars(uno::Reference< beans::XPropertySet > const & xViewProps, + bool bEnableOnlineMode) +{ + //the scrollbar logic is kind of busted looking in writer, when the hori scrollbar + //property is changed then the hori scrollbar is enabled if the property is + //true or browse (online) mode is enabled. So... + //disable online mode + //turn off scrollbars + //turn back on online mode if that's what we want + //which subverts the (dodgy/buggy) scrollbar setting + + //To reproduce this problem, in edit->autotext and click through + //the examples and see if the preview gets a horizontal scrollbar + uno::Any aFalseSet(uno::Any(false)); + xViewProps->setPropertyValue(UNO_NAME_SHOW_ONLINE_LAYOUT, aFalseSet); + + xViewProps->setPropertyValue(UNO_NAME_SHOW_HORI_SCROLL_BAR, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_VERT_SCROLL_BAR, aFalseSet); + + if (bEnableOnlineMode) + { + xViewProps->setPropertyValue(UNO_NAME_SHOW_ONLINE_LAYOUT, uno::Any(true)); + } +} + +SwOneExampleFrame::SwOneExampleFrame(sal_uInt32 nFlags, + const Link<SwOneExampleFrame&,void>* pInitializedLink, + const OUString* pURL) + : m_aLoadedIdle("sw uibase SwOneExampleFrame Loaded") + , m_pModuleView(SW_MOD()->GetView()) + , m_nStyleFlags(nFlags) + , m_bIsInitialized(false) +{ + if (pURL && !pURL->isEmpty()) + m_sArgumentURL = *pURL; + + if( pInitializedLink ) + m_aInitializedLink = *pInitializedLink; + + // the controller is asynchronously set + m_aLoadedIdle.SetInvokeHandler(LINK(this, SwOneExampleFrame, TimeoutHdl)); + m_aLoadedIdle.SetPriority(TaskPriority::HIGH_IDLE); +} + +void SwOneExampleFrame::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + m_xVirDev = VclPtr<VirtualDevice>::Create(); + Size aSize(m_xVirDev->LogicToPixel(Size(150, 188), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); + CreateControl(); +} + +bool SwOneExampleFrame::Command(const CommandEvent& rCEvt) +{ + switch (rCEvt.GetCommand()) + { + case CommandEventId::ContextMenu: + { + //#125881# quickly clicking crashes because the control is not fully initialized + if (m_xController.is()) + return CreatePopup(rCEvt.GetMousePosPixel()); + } + break; + default:; + break; + } + return CustomWidgetController::Command(rCEvt); +} + +void SwOneExampleFrame::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + Size aSize(GetOutputSizePixel()); + // m_xVirDev instead of rRenderContext just to avoid overlays in writer re-triggering + // invalidate on rRenderContext if it is a vcl::Window, which is the "classic" gen mode + m_xVirDev->SetOutputSizePixel(aSize); + + Color aBgColor = SW_MOD()->GetColorConfig().GetColorValue(::svtools::DOCCOLOR).nColor; + m_xVirDev->DrawWallpaper(tools::Rectangle(Point(), aSize), aBgColor); + + if (m_xCursor) + { + uno::Reference<view::XViewSettingsSupplier> xSettings(m_xController, uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xViewProps = xSettings->getViewSettings(); + uno::Any aZoom = xViewProps->getPropertyValue(UNO_NAME_ZOOM_VALUE); + sal_Int16 nZoom = 100; + aZoom >>= nZoom; + + double fZoom = 100.0 / nZoom; + + m_xVirDev->Push(vcl::PushFlags::ALL); + m_xVirDev->SetMapMode(MapMode(MapUnit::MapTwip)); + SwDoc *pDoc = m_xCursor->GetDoc(); + SwDocShell* pShell = pDoc->GetDocShell(); + tools::Rectangle aRect(Point(), m_xVirDev->PixelToLogic(aSize)); + pShell->SetVisArea(tools::Rectangle(Point(), Size(aRect.GetWidth() * fZoom, + aRect.GetHeight() * fZoom))); + pShell->DoDraw(m_xVirDev.get(), aRect.TopLeft(), aRect.GetSize(), JobSetup(), ASPECT_CONTENT, true); + m_xVirDev->Pop(); + } + + rRenderContext.DrawOutDev(Point(), aSize, Point(), aSize, *m_xVirDev); +} + +SwOneExampleFrame::~SwOneExampleFrame() +{ + DisposeControl(); +} + +void SwOneExampleFrame::CreateControl() +{ + // create new doc + OUString sTempURL(cFactory); + if(!m_sArgumentURL.isEmpty()) + sTempURL = m_sArgumentURL; + + uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(::comphelper::getProcessComponentContext()); + uno::Sequence<beans::PropertyValue> args( comphelper::InitPropertySequence({ + { "DocumentService", uno::Any(OUString("com.sun.star.text.TextDocument")) }, + { "OpenFlags", uno::Any(OUString("-RB")) }, + { "Referer", uno::Any(OUString("private:user")) }, + { "ReadOnly", uno::Any(true) }, + { "Hidden", uno::Any(true) } + })); + + m_xModel.set(xDesktop->loadComponentFromURL(sTempURL, "_blank", 0, args), uno::UNO_QUERY); + + m_aLoadedIdle.Start(); +} + +void SwOneExampleFrame::DisposeControl() +{ + m_aLoadedIdle.Stop(); + m_xCursor = nullptr; + if (m_xModel) + { + m_xModel->dispose(); + m_xModel = nullptr; + } + m_xController = nullptr; +} + +IMPL_LINK( SwOneExampleFrame, TimeoutHdl, Timer*, pTimer, void ) +{ + if (!m_xModel.is()) + return; + + m_xController = m_xModel->getCurrentController(); + + if (m_xController.is()) + { + uno::Reference<frame::XFrame> xFrame = m_xController->getFrame(); + uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + uno::Reference< frame::XLayoutManager > xLayoutManager; + uno::Any aValue = xPropSet->getPropertyValue("LayoutManager"); + aValue >>= xLayoutManager; + if ( xLayoutManager.is() ) + xLayoutManager->setVisible( false ); + } + catch (const uno::Exception&) + { + } + } + + //now the ViewOptions should be set properly + uno::Reference< view::XViewSettingsSupplier > xSettings(m_xController, uno::UNO_QUERY); + uno::Reference< beans::XPropertySet > xViewProps = xSettings->getViewSettings(); + + const uno::Any aTrueSet( true ); + const uno::Any aFalseSet( false ); + + if( !m_bIsInitialized ) + { + xViewProps->setPropertyValue(UNO_NAME_SHOW_BREAKS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_DRAWINGS, aTrueSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_FIELD_COMMANDS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_GRAPHICS, aTrueSet); + xViewProps->setPropertyValue(UNO_NAME_HIDE_WHITESPACE, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_HIDDEN_PARAGRAPHS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_HIDDEN_TEXT, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_HORI_RULER, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_PARA_BREAKS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_PROTECTED_SPACES, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_SOFT_HYPHENS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_SPACES, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_TABLES, aTrueSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_TABSTOPS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_VERT_RULER, aFalseSet); + + if(0 ==(m_nStyleFlags&EX_SHOW_ONLINE_LAYOUT)) + { + uno::Any aZoom; + aZoom <<= sal_Int16(view::DocumentZoomType::PAGE_WIDTH_EXACT); + xViewProps->setPropertyValue(UNO_NAME_ZOOM_TYPE, aZoom); + } + else + { + uno::Any aZoom; + aZoom <<= sal_Int16(view::DocumentZoomType::BY_VALUE); + xViewProps->setPropertyValue(UNO_NAME_ZOOM_TYPE, aZoom); + + sal_Int16 nZoomValue = 75; + if(EX_SHOW_BUSINESS_CARDS == m_nStyleFlags) + { + nZoomValue = 80; + } + aZoom <<= nZoomValue; + xViewProps->setPropertyValue(UNO_NAME_ZOOM_VALUE, aZoom); + } + + // set onlinelayout property after setting the zoom + disableScrollBars(xViewProps, (m_nStyleFlags&EX_SHOW_ONLINE_LAYOUT) != 0); + m_bIsInitialized = true; + } + + uno::Reference< text::XTextDocument > xDoc(m_xModel, uno::UNO_QUERY); + uno::Reference< text::XText > xText = xDoc->getText(); + uno::Reference< text::XTextCursor > xTextCursor = xText->createTextCursor(); + m_xCursor = dynamic_cast<SwXTextCursor*>(xTextCursor.get()); + assert(bool(xTextCursor) == bool(m_xCursor) && "expect to get SwXTextCursor type here"); + + //From here, a cursor is defined, which goes through the template, + //and overwrites the template words where it is necessary. + + SwDoc *pDoc = m_xCursor ? m_xCursor->GetDoc() : nullptr; + if (pDoc && (m_nStyleFlags & EX_LOCALIZE_TOC_STRINGS)) + { + SwEditShell* pSh = pDoc->GetEditShell(); + + do + { + if (pSh->GetCurWord() == "HEADING1") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_HEADING1)); + } + else if (pSh->GetCurWord() == "ENTRY1") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_ENTRY1)); + } + else if (pSh->GetCurWord() == "HEADING11") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_HEADING11)); + } + else if (pSh->GetCurWord() == "ENTRY11") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_ENTRY11)); + } + else if (pSh->GetCurWord() == "HEADING12") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_HEADING12)); + } + else if (pSh->GetCurWord() == "ENTRY12") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_ENTRY12)); + } + else if (pSh->GetCurWord() == "TABLE1") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_TABLE1)); + } + else if (pSh->GetCurWord() == "IMAGE1") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_IMAGE1)); + } + } + while(pSh->Right(sal_uInt16(1), SwCursorSkipMode::Cells, true)); + + TOXTypes eTypes[] = { TOX_INDEX, TOX_USER, TOX_CONTENT }; + for (auto eType : eTypes) + { + const SwTOXType* pTOXType = pDoc->GetTOXType(eType, 0); + SwTOXMarks aMarks; + pTOXType->CollectTextMarks(aMarks); + for (auto pMark : aMarks) + { + if (pMark->GetAlternativeText() == "Chapter") + pMark->SetAlternativeText(SwResId(STR_IDXEXAMPLE_IDXMARK_CHAPTER)); + else if (pMark->GetAlternativeText() == "Keyword") + pMark->SetAlternativeText(SwResId(STR_IDXEXAMPLE_IDXMARK_KEYWORD)); + else if (pMark->GetAlternativeText() == "this") + pMark->SetAlternativeText(SwResId(STR_IDXEXAMPLE_IDXMARK_THIS)); + else if (pMark->GetAlternativeText() == "User Directory Entry") + pMark->SetAlternativeText(SwResId(STR_IDXEXAMPLE_IDXMARK_USER_DIR_ENTRY)); + else if (pMark->GetAlternativeText() == "Entry") + pMark->SetAlternativeText(SwResId(STR_IDXEXAMPLE_IDXMARK_ENTRY)); + + if (pMark->GetPrimaryKey() == "Primary key") + pMark->SetPrimaryKey(SwResId(STR_IDXEXAMPLE_IDXMARK_PRIMARY_KEY)); + + if (pMark->GetSecondaryKey() == "Secondary key") + pMark->SetSecondaryKey(SwResId(STR_IDXEXAMPLE_IDXMARK_SECONDARY_KEY)); + } + } + } + + uno::Any aPageStyle = m_xCursor->getPropertyValue(UNO_NAME_PAGE_STYLE_NAME); + OUString sPageStyle; + aPageStyle >>= sPageStyle; + + uno::Reference< style::XStyleFamiliesSupplier > xSSupp( xDoc, uno::UNO_QUERY); + uno::Reference< container::XNameAccess > xStyles = xSSupp->getStyleFamilies(); + uno::Any aPFamily = xStyles->getByName( "PageStyles" ); + uno::Reference< container::XNameContainer > xPFamily; + + if( EX_SHOW_DEFAULT_PAGE != m_nStyleFlags + && (aPFamily >>= xPFamily) && !sPageStyle.isEmpty() ) + { + uno::Any aPStyle = xPFamily->getByName( sPageStyle ); + uno::Reference< style::XStyle > xPStyle; + aPStyle >>= xPStyle; + uno::Reference< beans::XPropertySet > xPProp(xPStyle, uno::UNO_QUERY); + uno::Any aSize = xPProp->getPropertyValue(UNO_NAME_SIZE); + awt::Size aPSize; + aSize >>= aPSize; + //TODO: set page width to card width + aPSize.Width = 10000; + aSize <<= aPSize; + xPProp->setPropertyValue(UNO_NAME_SIZE, aSize); + + uno::Any aZero; aZero <<= sal_Int32(0); + xPProp->setPropertyValue(UNO_NAME_LEFT_MARGIN, aZero); + xPProp->setPropertyValue(UNO_NAME_RIGHT_MARGIN, aZero); + } + + uno::Reference<awt::XWindow> xWin = xFrame->getContainerWindow(); + Size aWinSize(GetOutputSizePixel()); + xWin->setPosSize(0, 0, aWinSize.Width(), aWinSize.Height(), awt::PosSize::SIZE); + + // can only be done here - the SFX changes the ScrollBar values + disableScrollBars(xViewProps, (m_nStyleFlags&EX_SHOW_ONLINE_LAYOUT) != 0); + + m_aInitializedLink.Call(*this); + + uno::Reference< text::XTextViewCursorSupplier > xCursorSupp(m_xController, uno::UNO_QUERY); + uno::Reference< view::XScreenCursor > xScrCursor(xCursorSupp->getViewCursor(), uno::UNO_QUERY); + if(xScrCursor.is()) + xScrCursor->screenUp(); + + if (pDoc) + { + SwEditShell* pSh = pDoc->GetEditShell(); + if( pSh->ActionCount() ) + { + pSh->EndAllAction(); + pSh->UnlockPaint(); + } + } + + SW_MOD()->SetView(m_pModuleView); + + Invalidate(); + } + else + pTimer->Start(); +} + +void SwOneExampleFrame::ClearDocument() +{ + if( !m_xCursor ) + return; + + SwDoc* pDoc = m_xCursor->GetDoc(); + SwEditShell* pSh = pDoc->GetEditShell(); + pSh->LockPaint(LockPaintReason::ExampleFrame); + pSh->StartAllAction(); + pSh->KillPams(); + pSh->ClearMark(); + pDoc->ClearDoc(); + pSh->ClearUpCursors(); + + if( m_aLoadedIdle.IsActive()) + { + pSh->EndAllAction(); + pSh->UnlockPaint(); + } + m_aLoadedIdle.Start(); +} + +bool SwOneExampleFrame::CreatePopup(const Point& rPt) +{ + if (EX_SHOW_ONLINE_LAYOUT != m_nStyleFlags) + return false; + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/swriter/ui/previewmenu.ui")); + std::unique_ptr<weld::Menu> xPop(xBuilder->weld_menu("previewmenu")); + + uno::Reference< view::XViewSettingsSupplier > xSettings(m_xController, uno::UNO_QUERY); + uno::Reference< beans::XPropertySet > xViewProps = xSettings->getViewSettings(); + + uno::Any aZoom = xViewProps->getPropertyValue(UNO_NAME_ZOOM_VALUE); + sal_Int16 nZoom = 0; + aZoom >>= nZoom; + + for (auto const nZoomPreset : { 20, 40, 50, 75, 100 }) + { + OUString sTemp = unicode::formatPercent(nZoomPreset, + Application::GetSettings().GetUILanguageTag()); + OUString sIdent = "zoom" + OUString::number(nZoomPreset); + xPop->set_label(sIdent, sTemp); + if (nZoom == nZoomPreset) + xPop->set_active(sIdent, true); + } + + PopupHdl(xPop->popup_at_rect(GetDrawingArea(), tools::Rectangle(rPt, Size(1, 1)))); + + return true; +} + +void SwOneExampleFrame::PopupHdl(std::u16string_view rId) +{ + std::u16string_view sZoomValue; + if (o3tl::starts_with(rId, u"zoom", &sZoomValue)) + { + sal_Int16 nZoom = o3tl::toInt32(sZoomValue); + uno::Reference< view::XViewSettingsSupplier > xSettings(m_xController, uno::UNO_QUERY); + uno::Reference< beans::XPropertySet > xViewProps = xSettings->getViewSettings(); + + uno::Any aZoom; + aZoom <<= nZoom; + xViewProps->setPropertyValue(UNO_NAME_ZOOM_VALUE, aZoom); + aZoom <<= sal_Int16(view::DocumentZoomType::BY_VALUE); + xViewProps->setPropertyValue(UNO_NAME_ZOOM_TYPE, aZoom); + } + Invalidate(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/viewlayoutctrl.cxx b/sw/source/uibase/utlui/viewlayoutctrl.cxx new file mode 100644 index 0000000000..89dcc30175 --- /dev/null +++ b/sw/source/uibase/utlui/viewlayoutctrl.cxx @@ -0,0 +1,213 @@ +/* -*- 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 <viewlayoutctrl.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <vcl/event.hxx> +#include <vcl/status.hxx> +#include <vcl/image.hxx> +#include <svx/viewlayoutitem.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <swtypes.hxx> + +SFX_IMPL_STATUSBAR_CONTROL( SwViewLayoutControl, SvxViewLayoutItem ); + +struct SwViewLayoutControl::SwViewLayoutControl_Impl +{ + sal_uInt16 mnState; // 0 = auto, 1= single, 2 = book, 3 = none, 4 = off + Image maImageSingleColumn; + Image maImageSingleColumn_Active; + Image maImageAutomatic; + Image maImageAutomatic_Active; + Image maImageBookMode; + Image maImageBookMode_Active; +}; + +SwViewLayoutControl::SwViewLayoutControl( sal_uInt16 _nSlotId, sal_uInt16 _nId, StatusBar& rStatusBar ) : + SfxStatusBarControl( _nSlotId, _nId, rStatusBar ), + mpImpl( new SwViewLayoutControl_Impl ) +{ + mpImpl->mnState = 1; + + mpImpl->maImageSingleColumn = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_SINGLECOLUMN); + mpImpl->maImageSingleColumn_Active = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_SINGLECOLUMN_ACTIVE); + mpImpl->maImageAutomatic = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_AUTOMATIC); + mpImpl->maImageAutomatic_Active = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_AUTOMATIC_ACTIVE); + mpImpl->maImageBookMode = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_BOOKMODE); + mpImpl->maImageBookMode_Active = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_BOOKMODE_ACTIVE); +} + +SwViewLayoutControl::~SwViewLayoutControl() +{ +} + +void SwViewLayoutControl::StateChangedAtStatusBarControl( sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState ) +{ + if (SfxItemState::DEFAULT != eState || SfxItemState::DISABLED == eState) + { + GetStatusBar().SetItemText( GetId(), OUString() ); + mpImpl->mnState = 4; //tdf#148441 switch off, if disabled + } + else + { + assert( dynamic_cast< const SvxViewLayoutItem *>( pState ) && "invalid item type" ); + const sal_uInt16 nColumns = static_cast<const SvxViewLayoutItem*>( pState )->GetValue(); + const bool bBookMode = static_cast<const SvxViewLayoutItem*>( pState )->IsBookMode(); + + // SingleColumn Mode + if ( 1 == nColumns ) + mpImpl->mnState = 0; + // Automatic Mode + else if ( 0 == nColumns ) + mpImpl->mnState = 1; + // Book Mode + else if ( bBookMode && 2 == nColumns ) + mpImpl->mnState = 2; + else + mpImpl->mnState = 3; + } + + GetStatusBar().SetItemData( GetId(), nullptr ); // force repaint +} + +void SwViewLayoutControl::Paint( const UserDrawEvent& rUsrEvt ) +{ + vcl::RenderContext* pDev = rUsrEvt.GetRenderContext(); + tools::Rectangle aRect(rUsrEvt.GetRect()); + + const tools::Rectangle aControlRect = getControlRect(); + + if (mpImpl->mnState < 4) + { + const bool bSingleColumn = 0 == mpImpl->mnState; + const bool bAutomatic = 1 == mpImpl->mnState; + const bool bBookMode = 2 == mpImpl->mnState; + const int nDistance = 6; + + const tools::Long nImageWidthSum = mpImpl->maImageSingleColumn.GetSizePixel().Width() + + mpImpl->maImageAutomatic.GetSizePixel().Width() + + mpImpl->maImageBookMode.GetSizePixel().Width(); + + const tools::Long nXOffset = (aRect.GetWidth() - nImageWidthSum - (2* nDistance)) / 2; + const tools::Long nYOffset = (aControlRect.GetHeight() - mpImpl->maImageSingleColumn.GetSizePixel().Height()) / 2; + + aRect.AdjustLeft( nXOffset ); + aRect.AdjustTop( nYOffset ); + + // draw single column image: + pDev->DrawImage( aRect.TopLeft(), bSingleColumn ? mpImpl->maImageSingleColumn_Active : mpImpl->maImageSingleColumn ); + + // draw automatic image: + aRect.AdjustLeft(mpImpl->maImageSingleColumn.GetSizePixel().Width() + nDistance); + pDev->DrawImage( aRect.TopLeft(), bAutomatic ? mpImpl->maImageAutomatic_Active : mpImpl->maImageAutomatic ); + + // draw bookmode image: + aRect.AdjustLeft(mpImpl->maImageAutomatic.GetSizePixel().Width() + nDistance); + pDev->DrawImage( aRect.TopLeft(), bBookMode ? mpImpl->maImageBookMode_Active : mpImpl->maImageBookMode ); + } + else + { + pDev->DrawRect( aControlRect ); + } +} + +bool SwViewLayoutControl::MouseButtonDown( const MouseEvent & rEvt ) +{ + if (mpImpl->mnState < 4) + { + const tools::Rectangle aRect = getControlRect(); + const Point aPoint = rEvt.GetPosPixel(); + const tools::Long nXDiff = aPoint.X() - aRect.Left(); + + sal_uInt16 nColumns = 1; + bool bBookMode = false; + + const tools::Long nImageWidthSingle = mpImpl->maImageSingleColumn.GetSizePixel().Width(); + const tools::Long nImageWidthAuto = mpImpl->maImageAutomatic.GetSizePixel().Width(); + const tools::Long nImageWidthBook = mpImpl->maImageBookMode.GetSizePixel().Width(); + const tools::Long nImageWidthSum = nImageWidthSingle + nImageWidthAuto + nImageWidthBook; + + const tools::Long nXOffset = (aRect.GetWidth() - nImageWidthSum)/2; + + if ( nXDiff < nXOffset + nImageWidthSingle ) + { + mpImpl->mnState = 0; // single + nColumns = 1; + } + else if ( nXDiff < nXOffset + nImageWidthSingle + nImageWidthAuto ) + { + mpImpl->mnState = 1; // auto + nColumns = 0; + } + else + { + mpImpl->mnState = 2; // book + nColumns = 2; + bBookMode = true; + } + + // commit state change + SvxViewLayoutItem aViewLayout( nColumns, bBookMode ); + + css::uno::Any a; + aViewLayout.QueryValue( a ); + + css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue("ViewLayout", + a) }; + execute( aArgs ); + } + return true; +} + +bool SwViewLayoutControl::MouseMove( const MouseEvent & rEvt ) +{ + if (mpImpl->mnState < 4) + { + const tools::Rectangle aRect = getControlRect(); + const Point aPoint = rEvt.GetPosPixel(); + const tools::Long nXDiff = aPoint.X() - aRect.Left(); + + const tools::Long nImageWidthSingle = mpImpl->maImageSingleColumn.GetSizePixel().Width(); + const tools::Long nImageWidthAuto = mpImpl->maImageAutomatic.GetSizePixel().Width(); + const tools::Long nImageWidthBook = mpImpl->maImageBookMode.GetSizePixel().Width(); + const tools::Long nImageWidthSum = nImageWidthSingle + nImageWidthAuto + nImageWidthBook; + + const tools::Long nXOffset = (aRect.GetWidth() - nImageWidthSum)/2; + + if ( nXDiff < nXOffset + nImageWidthSingle ) + { + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_VIEWLAYOUT_ONE)); + } + else if ( nXDiff < nXOffset + nImageWidthSingle + nImageWidthAuto ) + { + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_VIEWLAYOUT_MULTI)); + } + else + { + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_VIEWLAYOUT_BOOK)); + } + } + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/wordcountctrl.cxx b/sw/source/uibase/utlui/wordcountctrl.cxx new file mode 100644 index 0000000000..4185969189 --- /dev/null +++ b/sw/source/uibase/utlui/wordcountctrl.cxx @@ -0,0 +1,45 @@ +/* -*- 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 <swtypes.hxx> +#include <strings.hrc> +#include <wordcountctrl.hxx> +#include <svl/stritem.hxx> +#include <vcl/status.hxx> + +SFX_IMPL_STATUSBAR_CONTROL(SwWordCountStatusBarControl, SfxStringItem); + +SwWordCountStatusBarControl::SwWordCountStatusBarControl( + sal_uInt16 _nSlotId, + sal_uInt16 _nId, + StatusBar& rStb) : + SfxStatusBarControl(_nSlotId, _nId, rStb) +{ +} + +SwWordCountStatusBarControl::~SwWordCountStatusBarControl() +{ +} + +void SwWordCountStatusBarControl::StateChangedAtStatusBarControl( + sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState ) +{ + if (eState == SfxItemState::DEFAULT) // Can access pState + { + GetStatusBar().SetItemText( GetId(), static_cast<const SfxStringItem*>(pState)->GetValue() ); + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_WORDCOUNT_HINT)); + } + else + { + GetStatusBar().SetItemText(GetId(), u""_ustr); + GetStatusBar().SetQuickHelpText(GetId(), u""_ustr); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/zoomctrl.cxx b/sw/source/uibase/utlui/zoomctrl.cxx new file mode 100644 index 0000000000..4489432464 --- /dev/null +++ b/sw/source/uibase/utlui/zoomctrl.cxx @@ -0,0 +1,65 @@ +/* -*- 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 <vcl/status.hxx> +#include <svl/stritem.hxx> +#include <sfx2/zoomitem.hxx> + +#include <zoomctrl.hxx> + +SFX_IMPL_STATUSBAR_CONTROL( SwZoomControl, SvxZoomItem ); + +SwZoomControl::SwZoomControl( sal_uInt16 _nSlotId, + sal_uInt16 _nId, + StatusBar& rStb ) : + SvxZoomStatusBarControl( _nSlotId, _nId, rStb ) +{ +} + +SwZoomControl::~SwZoomControl() +{ +} + +void SwZoomControl::StateChangedAtStatusBarControl( sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState ) +{ + const SfxStringItem* pItem = nullptr; + if (SfxItemState::DEFAULT == eState && (pItem = dynamic_cast<const SfxStringItem*>(pState))) + { + m_sPreviewZoom = pItem->GetValue(); + GetStatusBar().SetItemText(GetId(), m_sPreviewZoom); + } + else + { + m_sPreviewZoom.clear(); + SvxZoomStatusBarControl::StateChangedAtStatusBarControl(nSID, eState, pState); + } +} + +void SwZoomControl::Paint( const UserDrawEvent& ) +{ +} + +void SwZoomControl::Command( const CommandEvent& rCEvt ) +{ + if (m_sPreviewZoom.isEmpty()) + SvxZoomStatusBarControl::Command(rCEvt); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |