sw/inc/bitmaps.hlst | 1 sw/inc/crsrsh.hxx | 3 sw/inc/strings.hrc | 4 + sw/source/core/crsr/crstrvl.cxx | 18 +++++ sw/source/uibase/inc/content.hxx | 16 ++++ sw/source/uibase/inc/swcont.hxx | 5 - sw/source/uibase/inc/wrtsh.hxx | 4 - sw/source/uibase/utlui/content.cxx | 127 ++++++++++++++++++++++++++++++++++++- sw/source/uibase/wrtsh/move.cxx | 8 ++ 9 files changed, 179 insertions(+), 7 deletions(-)
New commits: commit fc44168ebe840fa2e772f034232fed15460e4977 Author: Jim Raykowski <rayk...@gmail.com> AuthorDate: Fri Oct 1 15:51:08 2021 -0800 Commit: Jim Raykowski <rayk...@gmail.com> CommitDate: Mon Oct 4 01:18:20 2021 +0200 tdf#144788 SwNavigator: Add footnotes and endnotes to content tree This patch introduces lcl_SelectByContentTypeAndAddress function which is useful for content types that can have non unique string entries which can be selected incorrectly by use of the lcl_SelectByContentTypeAndName function. Although footnotes and endnotes names are always unique, lcl_SelectByContentTypeAndAddress is used here in preference of lcl_SelectByContentTypeAndName. Change-Id: Ia118f717f72877cddb932ef19f6d155a7227845d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/122970 Tested-by: Jenkins Reviewed-by: Jim Raykowski <rayk...@gmail.com> diff --git a/sw/inc/bitmaps.hlst b/sw/inc/bitmaps.hlst index 7643989dd2e9..b3353862bee0 100644 --- a/sw/inc/bitmaps.hlst +++ b/sw/inc/bitmaps.hlst @@ -88,6 +88,7 @@ #define RID_BMP_NAVI_POSTIT "sw/res/nc20010.png" #define RID_BMP_NAVI_DRAWOBJECT "sw/res/nc20011.png" #define RID_BMP_NAVI_TEXTFIELD "sw/res/nc20005.png" +#define RID_BMP_NAVI_FOOTNOTE "sw/res/nc20000.png" #define RID_BMP_DROP_REGION "sw/res/sc20235.png" #define RID_BMP_DROP_LINK "sw/res/sc20238.png" #define RID_BMP_DROP_COPY "sw/res/sc20239.png" diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx index 9f60a87345d5..14507f20097c 100644 --- a/sw/inc/crsrsh.hxx +++ b/sw/inc/crsrsh.hxx @@ -55,6 +55,7 @@ class SwRangeRedline; class SwBlockCursor; class SwPostItField; class SwTextField; +class SwTextFootnote; namespace i18nutil { struct SearchOptions2; @@ -812,6 +813,8 @@ public: const SwRangeRedline* SelPrevRedline(); const SwRangeRedline* GotoRedline( SwRedlineTable::size_type nArrPos, bool bSelect ); + bool GotoFootnoteAnchor(const SwTextFootnote& rTextFootnote); + SAL_DLLPRIVATE SvxFrameDirection GetTextDirection( const Point* pPt = nullptr ) const; // is cursor or the point in/over a vertical formatted text? bool IsInVerticalText( const Point* pPt = nullptr ) const; diff --git a/sw/inc/strings.hrc b/sw/inc/strings.hrc index 359e25b49312..395cfc63bd53 100644 --- a/sw/inc/strings.hrc +++ b/sw/inc/strings.hrc @@ -371,6 +371,7 @@ #define STR_CONTENT_TYPE_INDEX NC_("STR_CONTENT_TYPE_INDEX", "Indexes") #define STR_CONTENT_TYPE_DRAWOBJECT NC_("STR_CONTENT_TYPE_DRAWOBJECT", "Drawing objects") #define STR_CONTENT_TYPE_TEXTFIELD NC_("STR_CONTENT_TYPE_TEXTFIELD", "Fields") +#define STR_CONTENT_TYPE_FOOTNOTE NC_("STR_CONTENT_TYPE_FOOTNOTE", "Footnotes and Endnotes") #define STR_CONTENT_TYPE_POSTIT NC_("STR_CONTENT_TYPE_POSTIT", "Comments") #define STR_IDXEXAMPLE_IDXTXT_HEADING1 NC_("STR_IDXEXAMPLE_IDXTXT_HEADING1", "Heading 1") #define STR_IDXEXAMPLE_IDXTXT_ENTRY1 NC_("STR_IDXEXAMPLE_IDXTXT_ENTRY1", "This is the content from the first chapter. This is a user directory entry.") @@ -400,6 +401,9 @@ #define STR_CONTENT_TYPE_SINGLE_POSTIT NC_("STR_CONTENT_TYPE_SINGLE_POSTIT", "Comment") #define STR_CONTENT_TYPE_SINGLE_DRAWOBJECT NC_("STR_CONTENT_TYPE_SINGLE_DRAWOBJECT", "Draw object") #define STR_CONTENT_TYPE_SINGLE_TEXTFIELD NC_("STR_CONTENT_TYPE_SINGLE_TEXTFIELD", "Field") +#define STR_CONTENT_TYPE_SINGLE_FOOTNOTE NC_("STR_CONTENT_TYPE_SINGLE_FOOTNOTE", "Footnote or Endnote") +#define STR_CONTENT_FOOTNOTE NC_("STR_CONTENT_FOOTNOTE", "Footnote") +#define STR_CONTENT_ENDNOTE NC_("STR_CONTENT_ENDNOTE", "Endnote") #define STR_DEFINE_NUMBERFORMAT NC_("STR_DEFINE_NUMBERFORMAT", "Additional formats...") #define RID_STR_SYSTEM NC_("RID_STR_SYSTEM", "[System]") #define STR_MULT_INTERACT_HYPH_WARN NC_("STR_MULT_INTERACT_HYPH_WARN", "The interactive hyphenation is already active\nin a different document") diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx index 87ab068e6ded..03f94624aae2 100644 --- a/sw/source/core/crsr/crstrvl.cxx +++ b/sw/source/core/crsr/crstrvl.cxx @@ -831,6 +831,24 @@ bool SwCursorShell::MoveFieldType( return bRet; } +bool SwCursorShell::GotoFootnoteAnchor(const SwTextFootnote& rTextFootnote) +{ + bool bRet = false; + SwCursor* pCursor = getShellCursor(true); + + CurrShell aCurr(this); + SwCallLink aLk(*this); // watch Cursor-Moves + SwCursorSaveState aSaveState(*pCursor); + + pCursor->GetPoint()->nNode = rTextFootnote.GetTextNode(); + pCursor->GetPoint()->nContent.Assign(const_cast<SwTextNode*>(&rTextFootnote.GetTextNode()), + rTextFootnote.GetStart()); + bRet = !pCursor->IsSelOvr(); + if (bRet) + UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY); + return bRet; +} + bool SwCursorShell::GotoFormatField( const SwFormatField& rField ) { bool bRet = false; diff --git a/sw/source/uibase/inc/content.hxx b/sw/source/uibase/inc/content.hxx index 64b1e265f10b..195dfd5ff6f3 100644 --- a/sw/source/uibase/inc/content.hxx +++ b/sw/source/uibase/inc/content.hxx @@ -31,6 +31,7 @@ class SwFormatField; class SwTextINetFormat; class SwTOXBase; class SwRangeRedline; +class SwTextFootnote; // helper classes @@ -104,6 +105,21 @@ public: virtual bool IsProtect() const override; }; +class SwTextFootnoteContent final : public SwContent +{ + const SwTextFootnote* m_pTextFootnote; +public: + SwTextFootnoteContent(const SwContentType* pCnt, + const OUString& rName, + const SwTextFootnote* pTextFootnote, + tools::Long nYPos) + : SwContent(pCnt, rName, nYPos), + m_pTextFootnote(pTextFootnote) + {} + + const SwTextFootnote* GetTextFootnote() const {return m_pTextFootnote;} +}; + class SwPostItContent final : public SwContent { const SwFormatField* pField; diff --git a/sw/source/uibase/inc/swcont.hxx b/sw/source/uibase/inc/swcont.hxx index d65c4fa59aca..2e32944efb48 100644 --- a/sw/source/uibase/inc/swcont.hxx +++ b/sw/source/uibase/inc/swcont.hxx @@ -40,8 +40,9 @@ enum class ContentTypeId INDEX = 9, POSTIT = 10, DRAWOBJECT = 11, - TEXTFIELD = 12, - LAST = TEXTFIELD, + TEXTFIELD = 12, + FOOTNOTE = 13, + LAST = FOOTNOTE, UNKNOWN = -1 }; diff --git a/sw/source/uibase/inc/wrtsh.hxx b/sw/source/uibase/inc/wrtsh.hxx index 727d4cfb64c9..8e2573916e8b 100644 --- a/sw/source/uibase/inc/wrtsh.hxx +++ b/sw/source/uibase/inc/wrtsh.hxx @@ -114,7 +114,7 @@ private: SELECTFUNC m_fnKillSel = &SwWrtShell::Ignore; public: - + using SwCursorShell::GotoFootnoteAnchor; using SwEditShell::Insert; tools::Long CallSetCursor(const Point* pPt, bool bProp) { return (this->*m_fnSetCursor)(pPt, bProp); } @@ -486,7 +486,7 @@ typedef bool (SwWrtShell::*FNSimpleMove)(); void GotoFormatField( const SwFormatField& rField ); const SwRangeRedline* GotoRedline( SwRedlineTable::size_type nArrPos, bool bSelect); bool GotoDrawingObject(std::u16string_view rName); - + void GotoFootnoteAnchor(const SwTextFootnote& rTextFootnote); void ChangeHeaderOrFooter(std::u16string_view rStyleName, bool bHeader, bool bOn, bool bShowWarning); virtual void SetShowHeaderFooterSeparator( FrameControlType eControl, bool bShow ) override; diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx index 8f8e1531f45c..f6caf32c5aed 100644 --- a/sw/source/uibase/utlui/content.cxx +++ b/sw/source/uibase/utlui/content.cxx @@ -103,6 +103,10 @@ #include <com/sun/star/text/XTextRange.hpp> #include <com/sun/star/text/XTextRangeCompare.hpp> +#include <ftnidx.hxx> +#include <txtftn.hxx> +#include <fmtftn.hxx> + #define CTYPE_CNT 0 #define CTYPE_CTT 1 @@ -258,7 +262,8 @@ const TranslateId STR_CONTENT_TYPE_ARY[] = STR_CONTENT_TYPE_INDEX, STR_CONTENT_TYPE_POSTIT, STR_CONTENT_TYPE_DRAWOBJECT, - STR_CONTENT_TYPE_TEXTFIELD + STR_CONTENT_TYPE_TEXTFIELD, + STR_CONTENT_TYPE_FOOTNOTE }; const TranslateId STR_CONTENT_TYPE_SINGLE_ARY[] = @@ -275,7 +280,8 @@ const TranslateId STR_CONTENT_TYPE_SINGLE_ARY[] = STR_CONTENT_TYPE_SINGLE_INDEX, STR_CONTENT_TYPE_SINGLE_POSTIT, STR_CONTENT_TYPE_SINGLE_DRAWOBJECT, - STR_CONTENT_TYPE_SINGLE_TEXTFIELD + STR_CONTENT_TYPE_SINGLE_TEXTFIELD, + STR_CONTENT_TYPE_SINGLE_FOOTNOTE }; namespace @@ -409,6 +415,16 @@ void SwContentType::Init(bool* pbInvalidateWindow) } } break; + case ContentTypeId::FOOTNOTE: + { + m_nMemberCount = 0; + m_sTypeToken.clear(); + m_bEdit = true; + m_bDelete = false; + const SwFootnoteIdxs& rFootnoteIdxs = m_pWrtShell->GetDoc()->GetFootnoteIdxs(); + m_nMemberCount = rFootnoteIdxs.size(); + } + break; case ContentTypeId::BOOKMARK: { IDocumentMarkAccess* const pMarkAccess = m_pWrtShell->getIDocumentMarkAccess(); @@ -845,7 +861,26 @@ void SwContentType::FillMemberList(bool* pbLevelOrVisibilityChanged) m_nMemberCount = m_pMember->size(); } break; - + case ContentTypeId::FOOTNOTE: + { + const SwFootnoteIdxs& rFootnoteIdxs = m_pWrtShell->GetDoc()->GetFootnoteIdxs(); + for (SwTextFootnote* pTextFootnote : rFootnoteIdxs) + { + const SwFormatFootnote& rFormatFootnote = pTextFootnote->GetFootnote(); + const OUString& sText = + rFormatFootnote.GetViewNumStr(*m_pWrtShell->GetDoc(), + m_pWrtShell->GetLayout(), true) + " " + + rFormatFootnote.GetFootnoteText(*m_pWrtShell->GetLayout()); + std::unique_ptr<SwTextFootnoteContent> pCnt(new SwTextFootnoteContent( + this, sText, pTextFootnote, + rFormatFootnote.GetNumber())); + if (!pTextFootnote->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout())) + pCnt->SetInvisible(); + m_pMember->insert(std::move(pCnt)); + } + m_nMemberCount = m_pMember->size(); + } + break; case ContentTypeId::REGION : { m_nMemberCount = m_pWrtShell->GetSectionFormatCount(); @@ -2122,6 +2157,9 @@ namespace case ContentTypeId::TEXTFIELD: sResId = RID_BMP_NAVI_TEXTFIELD; break; + case ContentTypeId::FOOTNOTE: + sResId = RID_BMP_NAVI_FOOTNOTE; + break; case ContentTypeId::UNKNOWN: SAL_WARN("sw.ui", "ContentTypeId::UNKNOWN has no bitmap preview"); break; @@ -3283,6 +3321,66 @@ void SwContentTree::HideTree() m_xTreeView->hide(); } +static void lcl_SelectByContentTypeAndAddress(SwContentTree* pThis, weld::TreeView& rContentTree, + ContentTypeId nType, const void* ptr) +{ + if (!ptr) + 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 = reinterpret_cast<void*>(rContentTree.get_id(*xIter).toInt64()); + assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData))); + if (nType == static_cast<SwContentType*>(pUserData)->GetType()) + break; + bFoundEntry = rContentTree.iter_next_sibling(*xIter); + } + + if (bFoundEntry) + { + // 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 = reinterpret_cast<void*>(rContentTree.get_id(*xIter).toInt64()); + switch( nType ) + { + case ContentTypeId::FOOTNOTE: + { + assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData))); + SwTextFootnoteContent* pCnt = static_cast<SwTextFootnoteContent*>(pUserData); + p = pCnt->GetTextFootnote(); + 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 || + rContentTree.iter_compare(*xIter, *xFirstSelected) != 0) + { + // unselect all entries and make passed entry visible and selected + rContentTree.set_cursor(*xIter); + pThis->Select(); + } + return; + } + } + } +} + static void lcl_SelectByContentTypeAndName(SwContentTree* pThis, weld::TreeView& rContentTree, std::u16string_view rContentTypeName, std::u16string_view rName) { @@ -3400,6 +3498,15 @@ void SwContentTree::UpdateTracking() return; } + // footnotes and endnotes + if (SwContentAtPos aContentAtPos(IsAttrAtPos::Ftn); + m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos) && + !(m_bIsRoot && m_nRootType != ContentTypeId::FOOTNOTE)) + { + lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::FOOTNOTE, + aContentAtPos.pFndTextAttr); + return; + } // bookmarks - track first bookmark at cursor SwDoc* pDoc = m_pActiveShell->GetDoc(); uno::Reference<text::XBookmarksSupplier> xBookmarksSupplier(pDoc->GetDocShell()->GetBaseModel(), @@ -4080,6 +4187,12 @@ IMPL_LINK(SwContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUStrin } } break; + case ContentTypeId::FOOTNOTE: + assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwTextFootnoteContent*>(pUserData)->GetTextFootnote()-> + GetFootnote().IsEndNote() ? SwResId(STR_CONTENT_ENDNOTE) : + SwResId(STR_CONTENT_FOOTNOTE); + break; default: break; } if(static_cast<SwContent*>(pUserData)->IsInvisible()) @@ -4658,6 +4771,10 @@ void SwContentTree::EditEntry(const weld::TreeIter& rEntry, EditEntryMode nMode) else if(nMode == EditEntryMode::RENAME) nSlot = FN_NAME_SHAPE; break; + case ContentTypeId::FOOTNOTE: + if (EditEntryMode::EDIT == nMode) + nSlot = FN_FORMAT_FOOTNOTE_DLG; + break; default: break; } if(nSlot) @@ -4788,6 +4905,10 @@ void SwContentTree::GotoContent(const SwContent* pCnt) m_pActiveShell->GotoDrawingObject(pCnt->GetName()); } break; + case ContentTypeId::FOOTNOTE: + m_pActiveShell->GotoFootnoteAnchor( + *static_cast<const SwTextFootnoteContent*>(pCnt)->GetTextFootnote()); + break; default: break; } diff --git a/sw/source/uibase/wrtsh/move.cxx b/sw/source/uibase/wrtsh/move.cxx index 95443ba16ca7..a5f10d5f8800 100644 --- a/sw/source/uibase/wrtsh/move.cxx +++ b/sw/source/uibase/wrtsh/move.cxx @@ -706,6 +706,14 @@ void SwWrtShell::GotoFormatField( const SwFormatField& rField ) { m_aNavigationMgr.addEntry(aPos); } +void SwWrtShell::GotoFootnoteAnchor(const SwTextFootnote& rTextFootnote) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoFootnoteAnchor(rTextFootnote); + if (bRet) + m_aNavigationMgr.addEntry(aPos); +} + const SwRangeRedline* SwWrtShell::GotoRedline( SwRedlineTable::size_type nArrPos, bool bSelect ) { SwPosition aPos = *GetCursor()->GetPoint(); const SwRangeRedline *pRedline = SwCursorShell::GotoRedline(nArrPos, bSelect);