From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- sfx2/source/appl/newhelp.cxx | 2681 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2681 insertions(+) create mode 100644 sfx2/source/appl/newhelp.cxx (limited to 'sfx2/source/appl/newhelp.cxx') diff --git a/sfx2/source/appl/newhelp.cxx b/sfx2/source/appl/newhelp.cxx new file mode 100644 index 000000000..d9fdc9d0c --- /dev/null +++ b/sfx2/source/appl/newhelp.cxx @@ -0,0 +1,2681 @@ +/* -*- 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 "newhelp.hxx" +#include +#include "helpinterceptor.hxx" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace ::ucbhelper; +using namespace ::com::sun::star::ucb; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::ui; + +using namespace ::comphelper; + +// defines --------------------------------------------------------------- + +constexpr OUStringLiteral CONFIGNAME_HELPWIN = u"OfficeHelp"; +constexpr OUStringLiteral CONFIGNAME_INDEXWIN = u"OfficeHelpIndex"; +constexpr OUStringLiteral CONFIGNAME_SEARCHPAGE = u"OfficeHelpSearch"; +constexpr OUStringLiteral IMAGE_URL = u"private:factory/"; + +constexpr OUStringLiteral PROPERTY_KEYWORDLIST = u"KeywordList"; +constexpr OUStringLiteral PROPERTY_KEYWORDREF = u"KeywordRef"; +constexpr OUStringLiteral PROPERTY_ANCHORREF = u"KeywordAnchorForRef"; +constexpr OUStringLiteral PROPERTY_TITLEREF = u"KeywordTitleForRef"; +constexpr OUStringLiteral PROPERTY_TITLE = u"Title"; +constexpr OUStringLiteral HELP_URL = u"vnd.sun.star.help://"; +constexpr OUStringLiteral HELP_SEARCH_TAG = u"/?Query="; +constexpr OUStringLiteral USERITEM_NAME = u"UserItem"; + +constexpr OUStringLiteral PACKAGE_SETUP = u"/org.openoffice.Setup"; +constexpr OUStringLiteral PATH_OFFICE_FACTORIES = u"Office/Factories/"; +constexpr OUStringLiteral KEY_HELP_ON_OPEN = u"ooSetupFactoryHelpOnOpen"; +constexpr OUStringLiteral KEY_UI_NAME = u"ooSetupFactoryUIName"; + +namespace sfx2 +{ + + + /** Prepare a search string for searching or selecting. + For searching every search word needs the postfix '*' and the delimiter ' ' if necessary. + For selecting the delimiter '|' is required to search with regular expressions. + Samples: + search string | output for searching | output for selecting + ----------------------------------------------------------- + "text" | "text*" | "text" + "text*" | "text*" | "text" + "text menu" | "text* menu*" | "text|menu" + */ + static OUString PrepareSearchString( const OUString& rSearchString, + const Reference< XBreakIterator >& xBreak, bool bForSearch ) + { + OUStringBuffer sSearchStr; + sal_Int32 nStartPos = 0; + const lang::Locale aLocale = Application::GetSettings().GetUILanguageTag().getLocale(); + Boundary aBoundary = xBreak->getWordBoundary( + rSearchString, nStartPos, aLocale, WordType::ANYWORD_IGNOREWHITESPACES, true ); + + while ( aBoundary.startPos < aBoundary.endPos ) + { + nStartPos = aBoundary.endPos; + OUString sSearchToken( rSearchString.copy( + static_cast(aBoundary.startPos), static_cast(aBoundary.endPos) - static_cast(aBoundary.startPos) ) ); + if ( !sSearchToken.isEmpty() && ( sSearchToken.getLength() > 1 || sSearchToken[0] != '.' ) ) + { + if ( bForSearch && sSearchToken[ sSearchToken.getLength() - 1 ] != '*' ) + sSearchToken += "*"; + + if ( sSearchToken.getLength() > 1 || + ( sSearchToken.getLength() > 0 && sSearchToken[ 0 ] != '*' ) ) + { + if ( !sSearchStr.isEmpty() ) + { + if ( bForSearch ) + sSearchStr.append(" "); + else + sSearchStr.append("|"); + } + sSearchStr.append(sSearchToken); + } + } + aBoundary = xBreak->nextWord( rSearchString, nStartPos, + aLocale, WordType::ANYWORD_IGNOREWHITESPACES ); + } + + return sSearchStr.makeStringAndClear(); + } + +// namespace sfx2 +} + + +// struct IndexEntry_Impl ------------------------------------------------ + +namespace { + +struct IndexEntry_Impl +{ + bool m_bSubEntry; + OUString m_aURL; + + IndexEntry_Impl( const OUString& rURL, bool bSubEntry ) : + m_bSubEntry( bSubEntry ), m_aURL( rURL ) {} +}; + +// struct ContentEntry_Impl ---------------------------------------------- + +struct ContentEntry_Impl +{ + OUString aURL; + bool bIsFolder; + + ContentEntry_Impl( const OUString& rURL, bool bFolder ) : + aURL( rURL ), bIsFolder( bFolder ) {} +}; + +} + +void ContentTabPage_Impl::InitRoot() +{ + std::vector< OUString > aList = + SfxContentHelper::GetHelpTreeViewContents( "vnd.sun.star.hier://com.sun.star.help.TreeView/" ); + + for (const OUString & aRow : aList) + { + sal_Int32 nIdx = 0; + OUString aTitle = aRow.getToken( 0, '\t', nIdx ); + OUString aURL = aRow.getToken( 0, '\t', nIdx ); + sal_Unicode cFolder = o3tl::getToken(aRow, 0, '\t', nIdx )[0]; + bool bIsFolder = ( '1' == cFolder ); + OUString sId; + if (bIsFolder) + sId = weld::toId(new ContentEntry_Impl(aURL, true)); + m_xContentBox->insert(nullptr, -1, &aTitle, &sId, nullptr, nullptr, true, m_xScratchIter.get()); + m_xContentBox->set_image(*m_xScratchIter, aClosedBookImage); + } +} + +void ContentTabPage_Impl::ClearChildren(const weld::TreeIter* pParent) +{ + std::unique_ptr xEntry = m_xContentBox->make_iterator(pParent); + bool bEntry = m_xContentBox->iter_children(*xEntry); + while (bEntry) + { + ClearChildren(xEntry.get()); + delete weld::fromId(m_xContentBox->get_id(*xEntry)); + bEntry = m_xContentBox->iter_next_sibling(*xEntry); + } + +} + +IMPL_LINK(ContentTabPage_Impl, ExpandingHdl, const weld::TreeIter&, rIter, bool) +{ + ContentEntry_Impl* pContentEntry = weld::fromId(m_xContentBox->get_id(rIter)); + if (!m_xContentBox->iter_has_child(rIter)) + { + try + { + if (pContentEntry) + { + std::vector aList = SfxContentHelper::GetHelpTreeViewContents(pContentEntry->aURL); + + for (const OUString & aRow : aList) + { + sal_Int32 nIdx = 0; + OUString aTitle = aRow.getToken( 0, '\t', nIdx ); + OUString aURL = aRow.getToken( 0, '\t', nIdx ); + sal_Unicode cFolder = o3tl::getToken(aRow, 0, '\t', nIdx )[0]; + bool bIsFolder = ( '1' == cFolder ); + if ( bIsFolder ) + { + OUString sId = weld::toId(new ContentEntry_Impl(aURL, true)); + m_xContentBox->insert(&rIter, -1, &aTitle, &sId, nullptr, nullptr, true, m_xScratchIter.get()); + m_xContentBox->set_image(*m_xScratchIter, aClosedBookImage); + } + else + { + Any aAny( ::utl::UCBContentHelper::GetProperty( aURL, "TargetURL" ) ); + OUString sId; + OUString aTargetURL; + if ( aAny >>= aTargetURL ) + sId = weld::toId(new ContentEntry_Impl(aTargetURL, false)); + m_xContentBox->insert(&rIter, -1, &aTitle, &sId, nullptr, nullptr, false, m_xScratchIter.get()); + m_xContentBox->set_image(*m_xScratchIter, aDocumentImage); + } + } + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "ContentListBox_Impl::RequestingChildren(): unexpected exception" ); + } + } + + if (!pContentEntry || pContentEntry->bIsFolder) + m_xContentBox->set_image(rIter, aOpenBookImage); + + return true; +} + +IMPL_LINK(ContentTabPage_Impl, CollapsingHdl, const weld::TreeIter&, rIter, bool) +{ + ContentEntry_Impl* pContentEntry = weld::fromId(m_xContentBox->get_id(rIter)); + if (!pContentEntry || pContentEntry->bIsFolder) + m_xContentBox->set_image(rIter, aClosedBookImage); + + return true; +} + +OUString ContentTabPage_Impl::GetSelectedEntry() const +{ + OUString aRet; + ContentEntry_Impl* pEntry = weld::fromId(m_xContentBox->get_selected_id()); + if (pEntry && !pEntry->bIsFolder) + aRet = pEntry->aURL; + return aRet; +} + +// class HelpTabPage_Impl ------------------------------------------------ +HelpTabPage_Impl::HelpTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin, + const OString& rID, const OUString& rUIXMLDescription) + : BuilderPage(pParent, nullptr, rUIXMLDescription, rID) + , m_pIdxWin(pIdxWin) +{ +} + +HelpTabPage_Impl::~HelpTabPage_Impl() +{ +} + +// class ContentTabPage_Impl --------------------------------------------- +ContentTabPage_Impl::ContentTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin) + : HelpTabPage_Impl(pParent, pIdxWin, "HelpContentPage", + "sfx/ui/helpcontentpage.ui") + , m_xContentBox(m_xBuilder->weld_tree_view("content")) + , m_xScratchIter(m_xContentBox->make_iterator()) + , aOpenBookImage(BMP_HELP_CONTENT_BOOK_OPEN) + , aClosedBookImage(BMP_HELP_CONTENT_BOOK_CLOSED) + , aDocumentImage(BMP_HELP_CONTENT_DOC) +{ + m_xContentBox->set_size_request(m_xContentBox->get_approximate_digit_width() * 30, + m_xContentBox->get_height_rows(20)); + m_xContentBox->connect_row_activated(LINK(this, ContentTabPage_Impl, DoubleClickHdl)); + m_xContentBox->connect_expanding(LINK(this, ContentTabPage_Impl, ExpandingHdl)); + m_xContentBox->connect_collapsing(LINK(this, ContentTabPage_Impl, CollapsingHdl)); + + InitRoot(); +} + +IMPL_LINK_NOARG(ContentTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool) +{ + aDoubleClickHdl.Call(nullptr); + return false; +} + +void ContentTabPage_Impl::SetDoubleClickHdl(const Link& rLink) +{ + aDoubleClickHdl = rLink; +} + +ContentTabPage_Impl::~ContentTabPage_Impl() +{ + std::unique_ptr xEntry = m_xContentBox->make_iterator(); + bool bEntry = m_xContentBox->get_iter_first(*xEntry); + while (bEntry) + { + ClearChildren(xEntry.get()); + delete weld::fromId(m_xContentBox->get_id(*xEntry)); + bEntry = m_xContentBox->iter_next_sibling(*xEntry); + } +} + +void IndexTabPage_Impl::SelectExecutableEntry() +{ + sal_Int32 nPos = m_xIndexList->find_text(m_xIndexEntry->get_text()); + if (nPos == -1) + return; + + sal_Int32 nOldPos = nPos; + OUString aEntryText; + IndexEntry_Impl* pEntry = weld::fromId(m_xIndexList->get_id(nPos)); + sal_Int32 nCount = m_xIndexList->n_children(); + while ( nPos < nCount && ( !pEntry || pEntry->m_aURL.isEmpty() ) ) + { + pEntry = weld::fromId(m_xIndexList->get_id(++nPos)); + aEntryText = m_xIndexList->get_text(nPos); + } + + if ( nOldPos != nPos ) + m_xIndexEntry->set_text(aEntryText); +} + +// class IndexTabPage_Impl ----------------------------------------------- +IndexTabPage_Impl::IndexTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin) + : HelpTabPage_Impl(pParent, pIdxWin, "HelpIndexPage", "sfx/ui/helpindexpage.ui") + , m_xIndexEntry(m_xBuilder->weld_entry("termentry")) + , m_xIndexList(m_xBuilder->weld_tree_view("termlist")) + , m_xOpenBtn(m_xBuilder->weld_button("display")) + , aFactoryIdle("sfx2 appl IndexTabPage_Impl Factory") + , aAutoCompleteIdle("sfx2 appl IndexTabPage_Impl AutoComplete") + , aKeywordTimer("sfx2::IndexTabPage_Impl aKeywordTimer") + , bIsActivated(false) + , nRowHeight(m_xIndexList->get_height_rows(1)) + , nAllHeight(0) + , nLastCharCode(0) +{ + m_xIndexList->set_size_request(m_xIndexList->get_approximate_digit_width() * 30, -1); + + m_xOpenBtn->connect_clicked(LINK(this, IndexTabPage_Impl, OpenHdl)); + aFactoryIdle.SetInvokeHandler( LINK(this, IndexTabPage_Impl, IdleHdl )); + aAutoCompleteIdle.SetInvokeHandler( LINK(this, IndexTabPage_Impl, AutoCompleteHdl )); + aKeywordTimer.SetInvokeHandler( LINK( this, IndexTabPage_Impl, TimeoutHdl ) ); + m_xIndexList->connect_row_activated(LINK(this, IndexTabPage_Impl, DoubleClickHdl)); + m_xIndexList->connect_changed(LINK(this, IndexTabPage_Impl, TreeChangeHdl)); + m_xIndexList->connect_custom_get_size(LINK(this, IndexTabPage_Impl, CustomGetSizeHdl)); + m_xIndexList->connect_custom_render(LINK(this, IndexTabPage_Impl, CustomRenderHdl)); + m_xIndexList->set_column_custom_renderer(0, true); + m_xIndexList->connect_size_allocate(LINK(this, IndexTabPage_Impl, ResizeHdl)); + m_xIndexEntry->connect_key_press(LINK(this, IndexTabPage_Impl, KeyInputHdl)); + m_xIndexEntry->connect_changed(LINK(this, IndexTabPage_Impl, EntryChangeHdl)); + m_xIndexEntry->connect_activate(LINK(this, IndexTabPage_Impl, ActivateHdl)); +} + +IMPL_LINK(IndexTabPage_Impl, ResizeHdl, const Size&, rSize, void) +{ + nAllHeight = rSize.Height(); +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, CustomGetSizeHdl, weld::TreeView::get_size_args, Size) +{ + return Size(m_xIndexList->get_size_request().Width(), nRowHeight); +} + +IMPL_LINK(IndexTabPage_Impl, CustomRenderHdl, weld::TreeView::render_args, aPayload, void) +{ + vcl::RenderContext& rRenderContext = std::get<0>(aPayload); + const ::tools::Rectangle& rRect = std::get<1>(aPayload); + bool bSelected = std::get<2>(aPayload); + const OUString& rId = std::get<3>(aPayload); + + rRenderContext.Push(vcl::PushFlags::TEXTCOLOR); + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + if (bSelected) + rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor()); + else + rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor()); + + Point aPos(rRect.TopLeft()); + aPos.AdjustY((rRect.GetHeight() - rRenderContext.GetTextHeight()) / 2); + + int nIndex = m_xIndexList->find_id(rId); + OUString aEntry(m_xIndexList->get_text(nIndex)); + + IndexEntry_Impl* pEntry = weld::fromId(rId); + if (pEntry && pEntry->m_bSubEntry) + { + // indent sub entries + aPos.AdjustX(8); + sal_Int32 nPos = aEntry.indexOf(';'); + rRenderContext.DrawText(aPos, (nPos !=-1) ? aEntry.copy(nPos + 1) : aEntry); + } + else + rRenderContext.DrawText(aPos, aEntry); + + rRenderContext.Pop(); +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, TreeChangeHdl, weld::TreeView&, void) +{ + m_xIndexEntry->set_text(m_xIndexList->get_selected_text()); +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, EntryChangeHdl, weld::Entry&, void) +{ + switch (nLastCharCode) + { + case css::awt::Key::DELETE_WORD_BACKWARD: + case css::awt::Key::DELETE_WORD_FORWARD: + case css::awt::Key::DELETE_TO_BEGIN_OF_LINE: + case css::awt::Key::DELETE_TO_END_OF_LINE: + case KEY_BACKSPACE: + case KEY_DELETE: + aAutoCompleteIdle.Stop(); + break; + default: + aAutoCompleteIdle.Start(); + break; + } +} + +IMPL_LINK(IndexTabPage_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + const vcl::KeyCode& rKCode = rKEvt.GetKeyCode(); + if (rKCode.GetModifier()) // only with no modifiers held + return false; + + sal_uInt16 nCode = rKCode.GetCode(); + + if (nCode == KEY_UP || nCode == KEY_PAGEUP || + nCode == KEY_DOWN || nCode == KEY_PAGEDOWN) + { +// disable_notify_events(); + sal_Int32 nIndex = m_xIndexList->get_selected_index(); + sal_Int32 nOrigIndex = nIndex; + sal_Int32 nCount = m_xIndexList->n_children(); + if (nIndex == -1) + { + m_xIndexList->set_cursor(0); + m_xIndexList->select(0); + m_xIndexEntry->set_text(m_xIndexList->get_selected_text()); + } + else + { + if (nCode == KEY_UP) + --nIndex; + else if (nCode == KEY_DOWN) + ++nIndex; + else if (nCode == KEY_PAGEUP) + { + int nVisRows = nAllHeight / nRowHeight; + nIndex -= nVisRows; + } + else if (nCode == KEY_PAGEDOWN) + { + int nVisRows = nAllHeight / nRowHeight; + nIndex += nVisRows; + } + + if (nIndex < 0) + nIndex = 0; + if (nIndex >= nCount) + nIndex = nCount - 1; + + if (nIndex != nOrigIndex) + { + m_xIndexList->set_cursor(nIndex); + m_xIndexList->select(nIndex); + m_xIndexEntry->set_text(m_xIndexList->get_selected_text()); + } + +// m_xIndexList->grab_focus(); +// g_signal_emit_by_name(pWidget, "key-press-event", pEvent, &ret); +// m_xIndexEntry->set_text(m_xIndexList->get_selected_text()); +// m_xIndexEntry->grab_focus(); + } + m_xIndexEntry->select_region(0, -1); +// enable_notify_events(); +// m_bTreeChange = true; +// m_pEntry->fire_signal_changed(); +// m_bTreeChange = false; + return true; + } + + nLastCharCode = nCode; + return false; +} + +IndexTabPage_Impl::~IndexTabPage_Impl() +{ + ClearIndex(); +} + +namespace sfx2 { + + typedef std::unordered_map< OUString, int > KeywordInfo; +} + +void IndexTabPage_Impl::InitializeIndex() +{ + weld::WaitObject aWaitCursor(m_pIdxWin->GetFrameWeld()); + + // By now more than 256 equal entries are not allowed + sal_Unicode append[256]; + for(sal_Unicode & k : append) + k = ' '; + + sfx2::KeywordInfo aInfo; + m_xIndexList->freeze(); + + try + { + OUStringBuffer aURL(HELP_URL); + aURL.append(sFactory); + AppendConfigToken(aURL, true); + + Content aCnt( aURL.makeStringAndClear(), Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties(); + if ( xInfo->hasPropertyByName( PROPERTY_ANCHORREF ) ) + { + css::uno::Sequence< OUString > aPropSeq{ PROPERTY_KEYWORDLIST, PROPERTY_KEYWORDREF, + PROPERTY_ANCHORREF, PROPERTY_TITLEREF }; + + // abi: use one possibly remote call only + css::uno::Sequence< css::uno::Any > aAnySeq = + aCnt.getPropertyValues( aPropSeq ); + + css::uno::Sequence< OUString > aKeywordList; + css::uno::Sequence< css::uno::Sequence< OUString > > aKeywordRefList; + css::uno::Sequence< css::uno::Sequence< OUString > > aAnchorRefList; + css::uno::Sequence< css::uno::Sequence< OUString > > aTitleRefList; + + if ( ( aAnySeq[0] >>= aKeywordList ) && ( aAnySeq[1] >>= aKeywordRefList ) && + ( aAnySeq[2] >>= aAnchorRefList ) && ( aAnySeq[3] >>= aTitleRefList ) ) + { + int ndx,tmp; + OUString aIndex, aTempString; + OUStringBuffer aData( 128 ); // Capacity of up to 128 characters + sfx2::KeywordInfo::iterator it; + + for ( int i = 0; i < aKeywordList.getLength(); ++i ) + { + // abi: Do not copy, but use references + const OUString& aKeywordPair = aKeywordList[i]; + DBG_ASSERT( !aKeywordPair.isEmpty(), "invalid help index" ); + const css::uno::Sequence< OUString >& aRefList = aKeywordRefList[i]; + const css::uno::Sequence< OUString >& aAnchorList = aAnchorRefList[i]; + const css::uno::Sequence< OUString >& aTitleList = aTitleRefList[i]; + + DBG_ASSERT( aRefList.getLength() == aAnchorList.getLength(),"reference list and title list of different length" ); + + ndx = aKeywordPair.indexOf( ';' ); + const bool insert = ndx != -1; + + OUString sId; + + if ( insert ) + { + aTempString = aKeywordPair.copy( 0, ndx ); + if ( aIndex != aTempString ) + { + aIndex = aTempString; + it = aInfo.emplace(aTempString, 0).first; + sId = weld::toId(new IndexEntry_Impl(OUString(), false)); + if ( (tmp = it->second++) != 0) + m_xIndexList->append( + sId, aTempString + std::u16string_view(append, tmp)); + else + m_xIndexList->append(sId, aTempString); + } + } + else + aIndex.clear(); + + sal_uInt32 nRefListLen = aRefList.getLength(); + + DBG_ASSERT( aAnchorList.hasElements(), "*IndexTabPage_Impl::InitializeIndex(): AnchorList is empty!" ); + DBG_ASSERT( nRefListLen, "*IndexTabPage_Impl::InitializeIndex(): RefList is empty!" ); + + if ( aAnchorList.hasElements() && nRefListLen ) + { + if ( aAnchorList[0].getLength() > 0 ) + { + aData.append( aRefList[0] ).append( '#' ).append( aAnchorList[0] ); + sId = weld::toId(new IndexEntry_Impl(aData.makeStringAndClear(), insert)); + } + else + sId = weld::toId(new IndexEntry_Impl(aRefList[0], insert)); + } + + // Assume the token is trimmed + it = aInfo.emplace(aKeywordPair, 0).first; + if ((tmp = it->second++) != 0) + m_xIndexList->append(sId, aKeywordPair + std::u16string_view(append, tmp)); + else + m_xIndexList->append(sId, aKeywordPair); + + for ( sal_uInt32 j = 1; j < nRefListLen ; ++j ) + { + aData + .append( aKeywordPair ) + .append( ' ' ) + .append( '-' ) + .append( ' ' ) + .append( aTitleList[j] ); + + aTempString = aData.makeStringAndClear(); + + if ( aAnchorList[j].getLength() > 0 ) + { + aData.append( aRefList[j] ).append( '#' ).append( aAnchorList[j] ); + sId = weld::toId(new IndexEntry_Impl(aData.makeStringAndClear(), insert)); + } + else + sId = weld::toId(new IndexEntry_Impl(aRefList[j], insert)); + + it = aInfo.emplace(aTempString, 0).first; + if ( (tmp = it->second++) != 0 ) + m_xIndexList->append( + sId, aTempString + std::u16string_view(append, tmp)); + else + m_xIndexList->append(sId, aTempString); + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "IndexTabPage_Impl::InitializeIndex(): unexpected exception" ); + } + + m_xIndexList->thaw(); + + if ( !sKeyword.isEmpty() ) + aKeywordLink.Call( *this ); +} + +void IndexTabPage_Impl::ClearIndex() +{ + const sal_Int32 nCount = m_xIndexList->n_children(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + delete weld::fromId(m_xIndexList->get_id(i)); + m_xIndexList->clear(); +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, OpenHdl, weld::Button&, void) +{ + aDoubleClickHdl.Call(nullptr); +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, ActivateHdl, weld::Entry&, bool) +{ + aDoubleClickHdl.Call(nullptr); + return true; +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool) +{ + aDoubleClickHdl.Call(nullptr); + return true; +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, IdleHdl, Timer*, void) +{ + InitializeIndex(); +} + +int IndexTabPage_Impl::starts_with(const OUString& rStr, int nStartRow, bool bCaseSensitive) +{ + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + + int nRet = nStartRow; + int nCount = m_xIndexList->n_children(); + while (nRet < nCount) + { + OUString aStr(m_xIndexList->get_text(nRet)); + const bool bMatch = !bCaseSensitive ? rI18nHelper.MatchString(rStr, aStr) : aStr.startsWith(rStr); + if (bMatch) + return nRet; + ++nRet; + } + + return -1; +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, AutoCompleteHdl, Timer*, void) +{ + OUString aStartText = m_xIndexEntry->get_text(); + int nStartPos, nEndPos; + m_xIndexEntry->get_selection_bounds(nStartPos, nEndPos); + int nMaxSelection = std::max(nStartPos, nEndPos); + if (nMaxSelection != aStartText.getLength()) + return; + + int nActive = m_xIndexList->get_selected_index(); + int nStart = nActive; + + if (nStart == -1) + nStart = 0; + + // Try match case insensitive from current position + int nPos = starts_with(aStartText, nStart, false); + if (nPos == -1 && nStart != 0) + { + // Try match case insensitive, but from start + nPos = starts_with(aStartText, 0, false); + } + + if (nPos == -1) + { + // Try match case sensitive from current position + nPos = starts_with(aStartText, nStart, true); + if (nPos == -1 && nStart != 0) + { + // Try match case sensitive, but from start + nPos = starts_with(aStartText, 0, true); + } + } + + if (nPos != -1) + { + m_xIndexList->set_cursor(nPos); + m_xIndexList->select(nPos); + OUString aText = m_xIndexList->get_text(nPos); + if (aText != aStartText) + m_xIndexEntry->set_text(aText); + m_xIndexEntry->select_region(aText.getLength(), aStartText.getLength()); + } +} + +IMPL_LINK( IndexTabPage_Impl, TimeoutHdl, Timer*, pTimer, void) +{ + if(&aKeywordTimer == pTimer && !sKeyword.isEmpty()) + aKeywordLink.Call(*this); +} + +void IndexTabPage_Impl::Activate() +{ + if ( !bIsActivated ) + { + bIsActivated = true; + aFactoryIdle.Start(); + } +} + +void IndexTabPage_Impl::SetDoubleClickHdl(const Link& rLink) +{ + aDoubleClickHdl = rLink; +} + +void IndexTabPage_Impl::SetFactory( const OUString& rFactory ) +{ + OUString sNewFactory( rFactory ); + DBG_ASSERT( !sNewFactory.isEmpty(), "empty factory" ); + bool bValid = m_pIdxWin->IsValidFactory( rFactory ); + + if ( sFactory.isEmpty() && !bValid ) + { + sNewFactory = SfxHelp::GetDefaultHelpModule(); + bValid = true; + } + + if ( sNewFactory != sFactory && bValid ) + { + sFactory = sNewFactory; + ClearIndex(); + if ( bIsActivated ) + aFactoryIdle.Start(); + } +} + +OUString IndexTabPage_Impl::GetSelectedEntry() const +{ + OUString aRet; + IndexEntry_Impl* pEntry = weld::fromId(m_xIndexList->get_id(m_xIndexList->find_text(m_xIndexEntry->get_text()))); + if (pEntry) + aRet = pEntry->m_aURL; + return aRet; +} + +void IndexTabPage_Impl::SetKeyword( const OUString& rKeyword ) +{ + sKeyword = rKeyword; + + if (m_xIndexList->n_children() > 0) + aKeywordTimer.Start(); + else if ( !bIsActivated ) + aFactoryIdle.Start(); +} + + +bool IndexTabPage_Impl::HasKeyword() const +{ + bool bRet = false; + if ( !sKeyword.isEmpty() ) + { + sal_Int32 nPos = m_xIndexList->find_text( sKeyword ); + bRet = nPos != -1; + } + + return bRet; +} + + +bool IndexTabPage_Impl::HasKeywordIgnoreCase() +{ + bool bRet = false; + if ( !sKeyword.isEmpty() ) + { + sal_Int32 nEntries = m_xIndexList->n_children(); + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetLocaleI18nHelper(); + for ( sal_Int32 n = 0; n < nEntries; n++) + { + const OUString sIndexItem {m_xIndexList->get_text(n)}; + if (rI18nHelper.MatchString( sIndexItem, sKeyword )) + { + sKeyword = sIndexItem; + bRet = true; + } + } + } + + return bRet; +} + +void IndexTabPage_Impl::OpenKeyword() +{ + if ( !sKeyword.isEmpty() ) + { + m_xIndexEntry->set_text(sKeyword); + aDoubleClickHdl.Call(nullptr); + sKeyword.clear(); + } +} + +IMPL_LINK_NOARG(SearchTabPage_Impl, ActivateHdl, weld::ComboBox&, bool) +{ + Search(); + return true; +} + +// class SearchTabPage_Impl ---------------------------------------------- + +SearchTabPage_Impl::SearchTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin) + : HelpTabPage_Impl(pParent, pIdxWin, "HelpSearchPage", + "sfx/ui/helpsearchpage.ui") + , m_xSearchED(m_xBuilder->weld_combo_box("search")) + , m_xSearchBtn(m_xBuilder->weld_button("find")) + , m_xFullWordsCB(m_xBuilder->weld_check_button("completewords")) + , m_xScopeCB(m_xBuilder->weld_check_button("headings")) + , m_xResultsLB(m_xBuilder->weld_tree_view("results")) + , m_xOpenBtn(m_xBuilder->weld_button("display")) + , xBreakIterator(vcl::unohelper::CreateBreakIterator()) +{ + m_xResultsLB->set_size_request(m_xResultsLB->get_approximate_digit_width() * 30, + m_xResultsLB->get_height_rows(15)); + + m_xSearchBtn->connect_clicked(LINK(this, SearchTabPage_Impl, ClickHdl)); + m_xSearchED->connect_changed(LINK(this, SearchTabPage_Impl, ModifyHdl)); + m_xSearchED->connect_entry_activate(LINK(this, SearchTabPage_Impl, ActivateHdl)); + m_xOpenBtn->connect_clicked(LINK(this, SearchTabPage_Impl, OpenHdl)); + m_xResultsLB->connect_row_activated(LINK(this, SearchTabPage_Impl, DoubleClickHdl)); + + SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE ); + if ( aViewOpt.Exists() ) + { + OUString aUserData; + Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME ); + if ( aUserItem >>= aUserData ) + { + sal_Int32 nIdx {0}; + bool bChecked = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx)) == 1; + m_xFullWordsCB->set_active(bChecked); + bChecked = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx)) == 1; + m_xScopeCB->set_active(bChecked); + + while ( nIdx > 0 ) + { + m_xSearchED->append_text( INetURLObject::decode( + o3tl::getToken(aUserData, 0, ';', nIdx), + INetURLObject::DecodeMechanism::WithCharset ) ); + } + } + } + + ModifyHdl(*m_xSearchED); +} + +SearchTabPage_Impl::~SearchTabPage_Impl() +{ + SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE ); + OUStringBuffer aUserData = + OUString::number(m_xFullWordsCB->get_active() ? 1 : 0) + + ";" + + OUString::number(m_xScopeCB->get_active() ? 1 : 0); + sal_Int32 nCount = std::min(m_xSearchED->get_count(), 10); // save only 10 entries + + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + aUserData.append(";" + + INetURLObject::encode( + m_xSearchED->get_text(i), + INetURLObject::PART_UNO_PARAM_VALUE, + INetURLObject::EncodeMechanism::All )); + } + + Any aUserItem( aUserData.makeStringAndClear() ); + aViewOpt.SetUserItem( USERITEM_NAME, aUserItem ); + + m_xSearchED.reset(); + m_xSearchBtn.reset(); + m_xFullWordsCB.reset(); + m_xScopeCB.reset(); + m_xResultsLB.reset(); + m_xOpenBtn.reset(); +} + +void SearchTabPage_Impl::ClearSearchResults() +{ + m_xResultsLB->clear(); +} + +void SearchTabPage_Impl::RememberSearchText( const OUString& rSearchText ) +{ + for (sal_Int32 i = 0, nEntryCount = m_xSearchED->get_count(); i < nEntryCount; ++i) + { + if (rSearchText == m_xSearchED->get_text(i)) + { + m_xSearchED->remove(i); + break; + } + } + + m_xSearchED->insert_text(0, rSearchText); +} + +IMPL_LINK_NOARG(SearchTabPage_Impl, ClickHdl, weld::Button&, void) +{ + Search(); +} + +void SearchTabPage_Impl::Search() +{ + OUString aSearchText = comphelper::string::strip(m_xSearchED->get_active_text(), ' '); + if ( aSearchText.isEmpty() ) + return; + + std::unique_ptr xWaitCursor(new weld::WaitObject(m_pIdxWin->GetFrameWeld())); + ClearSearchResults(); + RememberSearchText( aSearchText ); + OUStringBuffer aSearchURL(HELP_URL); + aSearchURL.append(aFactory); + aSearchURL.append(HELP_SEARCH_TAG); + if (!m_xFullWordsCB->get_active()) + aSearchText = sfx2::PrepareSearchString( aSearchText, xBreakIterator, true ); + aSearchURL.append(aSearchText); + AppendConfigToken(aSearchURL, false); + if (m_xScopeCB->get_active()) + aSearchURL.append("&Scope=Heading"); + std::vector< OUString > aFactories = SfxContentHelper::GetResultSet(aSearchURL.makeStringAndClear()); + for (const OUString & rRow : aFactories) + { + sal_Int32 nIdx = 0; + OUString aTitle = rRow.getToken(0, '\t', nIdx); + OUString sURL(rRow.getToken(1, '\t', nIdx)); + m_xResultsLB->append(sURL, aTitle); + } + xWaitCursor.reset(); + + if ( aFactories.empty() ) + { + std::unique_ptr xBox(Application::CreateMessageDialog(m_xContainer.get(), + VclMessageType::Info, VclButtonsType::Ok, + SfxResId(STR_INFO_NOSEARCHRESULTS))); + xBox->run(); + } +} + +IMPL_LINK_NOARG(SearchTabPage_Impl, OpenHdl, weld::Button&, void) +{ + aDoubleClickHdl.Call(nullptr); +} + +IMPL_LINK(SearchTabPage_Impl, ModifyHdl, weld::ComboBox&, rComboBox, void) +{ + OUString aSearchText = comphelper::string::strip(m_xSearchED->get_active_text(), ' '); + m_xSearchBtn->set_sensitive(!aSearchText.isEmpty()); + + if (rComboBox.changed_by_direct_pick()) + Search(); +} + +IMPL_LINK_NOARG(SearchTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool) +{ + aDoubleClickHdl.Call(nullptr); + return true; +} + +void SearchTabPage_Impl::SetDoubleClickHdl(const Link& rLink) +{ + aDoubleClickHdl = rLink; +} + +OUString SearchTabPage_Impl::GetSelectedEntry() const +{ + return m_xResultsLB->get_selected_id(); +} + +void SearchTabPage_Impl::ClearPage() +{ + ClearSearchResults(); + m_xSearchED->set_entry_text(OUString()); +} + +bool SearchTabPage_Impl::OpenKeyword( const OUString& rKeyword ) +{ + bool bRet = false; + m_xSearchED->set_entry_text(rKeyword); + Search(); + if (m_xResultsLB->n_children() > 0) + { + // found keyword -> open it + m_xResultsLB->select(0); + OpenHdl(*m_xOpenBtn); + bRet = true; + } + return bRet; +} + +// class BookmarksTabPage_Impl ------------------------------------------- + +void BookmarksTabPage_Impl::DoAction(std::string_view rAction) +{ + if (rAction == "display") + aDoubleClickHdl.Call(nullptr); + else if (rAction == "rename") + { + sal_Int32 nPos = m_xBookmarksBox->get_selected_index(); + if (nPos != -1) + { + SfxAddHelpBookmarkDialog_Impl aDlg(m_xBookmarksBox.get(), true); + aDlg.SetTitle(m_xBookmarksBox->get_text(nPos)); + if (aDlg.run() == RET_OK) + { + OUString sURL = m_xBookmarksBox->get_id(nPos); + m_xBookmarksBox->remove(nPos); + m_xBookmarksBox->append(sURL, aDlg.GetTitle(), + SvFileInformationManager::GetImageId(INetURLObject(rtl::OUStringConcatenation(IMAGE_URL+INetURLObject(sURL).GetHost())))); + m_xBookmarksBox->select(m_xBookmarksBox->n_children() - 1); + } + } + } + else if (rAction == "delete") + { + sal_Int32 nPos = m_xBookmarksBox->get_selected_index(); + if (nPos != -1) + { + m_xBookmarksBox->remove(nPos); + const sal_Int32 nCount = m_xBookmarksBox->n_children(); + if (nCount) + { + if (nPos >= nCount) + nPos = nCount - 1; + m_xBookmarksBox->select(nPos); + } + } + } +} + +IMPL_LINK(BookmarksTabPage_Impl, CommandHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + std::unique_ptr xBuilder(Application::CreateBuilder(m_xBookmarksBox.get(), "sfx/ui/bookmarkmenu.ui")); + std::unique_ptr xMenu = xBuilder->weld_menu("menu"); + + OString sIdent = xMenu->popup_at_rect(m_xBookmarksBox.get(), ::tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + if (!sIdent.isEmpty()) + DoAction(sIdent); + return true; +} + +IMPL_LINK(BookmarksTabPage_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = false; + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + if (KEY_DELETE == nCode && m_xBookmarksBox->n_children() > 0) + { + DoAction("delete"); + bHandled = true; + } + return bHandled; +} + +// class BookmarksTabPage_Impl ------------------------------------------- +BookmarksTabPage_Impl::BookmarksTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* _pIdxWin) + : HelpTabPage_Impl(pParent, _pIdxWin, "HelpBookmarkPage", + "sfx/ui/helpbookmarkpage.ui") + , m_xBookmarksBox(m_xBuilder->weld_tree_view("bookmarks")) + , m_xBookmarksPB(m_xBuilder->weld_button("display")) +{ + m_xBookmarksBox->set_size_request(m_xBookmarksBox->get_approximate_digit_width() * 30, + m_xBookmarksBox->get_height_rows(20)); + + m_xBookmarksPB->connect_clicked( LINK(this, BookmarksTabPage_Impl, OpenHdl)); + m_xBookmarksBox->connect_row_activated(LINK(this, BookmarksTabPage_Impl, DoubleClickHdl)); + m_xBookmarksBox->connect_popup_menu(LINK(this, BookmarksTabPage_Impl, CommandHdl)); + m_xBookmarksBox->connect_key_press(LINK(this, BookmarksTabPage_Impl, KeyInputHdl)); + + // load bookmarks from configuration + const std::vector< SvtHistoryOptions::HistoryItem > aBookmarkSeq = SvtHistoryOptions::GetList( EHistoryType::HelpBookmarks ); + for ( const auto& rBookmark : aBookmarkSeq ) + { + AddBookmarks( rBookmark.sTitle, rBookmark.sURL ); + } +} + +BookmarksTabPage_Impl::~BookmarksTabPage_Impl() +{ + // save bookmarks to configuration + SvtHistoryOptions::Clear( EHistoryType::HelpBookmarks ); + const sal_Int32 nCount = m_xBookmarksBox->n_children(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + SvtHistoryOptions::AppendItem(EHistoryType::HelpBookmarks, m_xBookmarksBox->get_id(i), "", m_xBookmarksBox->get_text(i), std::nullopt, std::nullopt); + } + + m_xBookmarksBox.reset(); + m_xBookmarksPB.reset(); +} + +IMPL_LINK_NOARG(BookmarksTabPage_Impl, OpenHdl, weld::Button&, void) +{ + aDoubleClickHdl.Call(nullptr); +} + +IMPL_LINK_NOARG(BookmarksTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool) +{ + aDoubleClickHdl.Call(nullptr); + return true; +} + +void BookmarksTabPage_Impl::SetDoubleClickHdl(const Link& rLink) +{ + aDoubleClickHdl = rLink; +} + +OUString BookmarksTabPage_Impl::GetSelectedEntry() const +{ + return m_xBookmarksBox->get_selected_id(); +} + +void BookmarksTabPage_Impl::AddBookmarks(const OUString& rTitle, const OUString& rURL) +{ + const OUString aImageURL {IMAGE_URL + INetURLObject(rURL).GetHost()}; + m_xBookmarksBox->append(rURL, rTitle, SvFileInformationManager::GetImageId(INetURLObject(aImageURL))); +} + +OUString SfxHelpWindow_Impl::buildHelpURL(std::u16string_view sFactory , + std::u16string_view sContent , + std::u16string_view sAnchor) +{ + OUStringBuffer sHelpURL(256); + sHelpURL.append(HELP_URL); + sHelpURL.append(sFactory); + sHelpURL.append(sContent); + AppendConfigToken(sHelpURL, true/*bUseQuestionMark*/); + if (!sAnchor.empty()) + sHelpURL.append(sAnchor); + return sHelpURL.makeStringAndClear(); +} + +void SfxHelpWindow_Impl::loadHelpContent(const OUString& sHelpURL, bool bAddToHistory) +{ + Reference< XComponentLoader > xLoader(getTextFrame(), UNO_QUERY); + if (!xLoader.is()) + return; + + // If a print job runs do not open a new page + Reference< XFrame2 > xTextFrame = pTextWin->getFrame(); + Reference< XController > xTextController ; + if (xTextFrame.is()) + xTextController = xTextFrame->getController (); + if ( xTextController.is() && !xTextController->suspend( true ) ) + { + xTextController->suspend( false ); + return; + } + + // save url to history + if (bAddToHistory) + pHelpInterceptor->addURL(sHelpURL); + + if ( !IsWait() ) + EnterWait(); + bool bSuccess = false; +// TODO implement locale fallback ... see below while(true) + { + try + { + Reference< XComponent > xContent = xLoader->loadComponentFromURL(sHelpURL, "_self", 0, Sequence< PropertyValue >()); + if (xContent.is()) + { + bSuccess = true; + } + } + catch(const RuntimeException&) + { throw; } + catch(const Exception&) + { /*break;*/ } + + /* TODO try next locale ... + no further locale available? => break loop and show error page + */ + } + openDone(sHelpURL, bSuccess); + if ( IsWait() ) + LeaveWait(); +} + +IMPL_LINK(SfxHelpIndexWindow_Impl, ActivatePageHdl, const OString&, rPage, void) +{ + GetPage(rPage)->Activate(); +} + +SfxHelpIndexWindow_Impl::SfxHelpIndexWindow_Impl(SfxHelpWindow_Impl* _pParent, weld::Container* pContainer) + : m_xBuilder(Application::CreateBuilder(pContainer, "sfx/ui/helpcontrol.ui")) + , m_xContainer(m_xBuilder->weld_container("HelpControl")) + , m_xActiveLB(m_xBuilder->weld_combo_box("active")) + , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol")) + , aIdle("sfx2 appl SfxHelpIndexWindow_Impl") + , aIndexKeywordLink(LINK(this, SfxHelpIndexWindow_Impl, KeywordHdl)) + , pParentWin(_pParent) + , bIsInitDone(false) +{ + // create the pages + GetContentPage(); + GetIndexPage(); + GetSearchPage(); + GetBookmarksPage(); + + OString sPageId("index"); + SvtViewOptions aViewOpt( EViewType::TabDialog, CONFIGNAME_INDEXWIN ); + if ( aViewOpt.Exists() ) + { + OString sSavedPageId = aViewOpt.GetPageID(); + if (m_xTabCtrl->get_page_index(sSavedPageId) != -1) + sPageId = sSavedPageId; + } + m_xTabCtrl->set_current_page(sPageId); + ActivatePageHdl(sPageId); + m_xActiveLB->connect_changed(LINK(this, SfxHelpIndexWindow_Impl, SelectHdl)); + + m_xTabCtrl->connect_enter_page(LINK(this, SfxHelpIndexWindow_Impl, ActivatePageHdl)); + + aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, InitHdl ) ); + aIdle.Start(); + + m_xContainer->show(); +} + +SfxHelpIndexWindow_Impl::~SfxHelpIndexWindow_Impl() +{ + SvtViewOptions aViewOpt(EViewType::TabDialog, CONFIGNAME_INDEXWIN); + aViewOpt.SetPageID(m_xTabCtrl->get_current_page_ident()); + + xCPage.reset(); + xIPage.reset(); + xSPage.reset(); + xBPage.reset(); +} + +void SfxHelpIndexWindow_Impl::Initialize() +{ + OUStringBuffer aHelpURL(HELP_URL); + AppendConfigToken(aHelpURL, true); + std::vector aFactories = SfxContentHelper::GetResultSet(aHelpURL.makeStringAndClear()); + for (const OUString & rRow : aFactories) + { + sal_Int32 nIdx = 0; + OUString aTitle = rRow.getToken( 0, '\t', nIdx ); // token 0 + std::u16string_view aURL = o3tl::getToken(rRow, 1, '\t', nIdx ); // token 2 + OUString aFactory(INetURLObject(aURL).GetHost()); + m_xActiveLB->append(aFactory, aTitle); + } + + if (m_xActiveLB->get_active() == -1) + SetActiveFactory(); +} + +void SfxHelpIndexWindow_Impl::SetActiveFactory() +{ + DBG_ASSERT( xIPage, "index page not initialized" ); + if (!bIsInitDone && !m_xActiveLB->get_count()) + { + aIdle.Stop(); + InitHdl( nullptr ); + } + + for (sal_Int32 i = 0, nEntryCount = m_xActiveLB->get_count(); i < nEntryCount; ++i) + { + OUString aFactory = m_xActiveLB->get_id(i); + aFactory = aFactory.toAsciiLowerCase(); + if (aFactory == xIPage->GetFactory()) + { + if (m_xActiveLB->get_active() != i) + { + m_xActiveLB->set_active(i); + aSelectFactoryLink.Call(nullptr); + } + break; + } + } +} + +HelpTabPage_Impl* SfxHelpIndexWindow_Impl::GetPage(std::string_view rName) +{ + HelpTabPage_Impl* pPage = nullptr; + + if (rName == "contents") + pPage = GetContentPage(); + else if (rName == "index") + pPage = GetIndexPage(); + else if (rName == "find") + pPage = GetSearchPage(); + else if (rName == "bookmarks") + pPage = GetBookmarksPage(); + + assert(pPage && "SfxHelpIndexWindow_Impl::GetCurrentPage(): no current page"); + + return pPage; +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectHdl, weld::ComboBox&, void) +{ + aIdle.Start(); +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, InitHdl, Timer *, void) +{ + bIsInitDone = true; + Initialize(); + + // now use the timer for selection + aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, SelectFactoryHdl ) ); + aIdle.SetPriority( TaskPriority::LOWEST ); +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectFactoryHdl, Timer *, void) +{ + OUString aFactory = m_xActiveLB->get_active_id(); + if (!aFactory.isEmpty()) + { + SetFactory(aFactory.toAsciiLowerCase(), false); + aSelectFactoryLink.Call(this); + } +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, KeywordHdl, IndexTabPage_Impl&, void) +{ + // keyword found on index? + bool bIndex = xIPage->HasKeyword(); + + if( !bIndex) + bIndex = xIPage->HasKeywordIgnoreCase(); + // then set index or search page as current. + OString sPageId = bIndex ? "index" : "find"; + if (sPageId != m_xTabCtrl->get_current_page_ident()) + m_xTabCtrl->set_current_page(sPageId); + + // at last we open the keyword + if ( bIndex ) + xIPage->OpenKeyword(); + else if ( !xSPage->OpenKeyword( sKeyword ) ) + pParentWin->ShowStartPage(); +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, IndexTabPageDoubleClickHdl, LinkParamNone*, void) +{ + aPageDoubleClickLink.Call(nullptr); +} + +void SfxHelpIndexWindow_Impl::SetDoubleClickHdl(const Link& rLink) +{ + aPageDoubleClickLink = rLink; +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, ContentTabPageDoubleClickHdl, LinkParamNone*, void) +{ + aPageDoubleClickLink.Call(nullptr); +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, TabPageDoubleClickHdl, LinkParamNone*, void) +{ + aPageDoubleClickLink.Call(nullptr); +} + +void SfxHelpIndexWindow_Impl::SetFactory( const OUString& rFactory, bool bActive ) +{ + if ( !rFactory.isEmpty() ) + { + GetIndexPage()->SetFactory( rFactory ); + // the index page made a check if rFactory is valid, + // so the index page always returns a valid factory + GetSearchPage()->SetFactory( GetIndexPage()->GetFactory() ); + if ( bActive ) + SetActiveFactory(); + } +} + +OUString SfxHelpIndexWindow_Impl::GetSelectedEntry() const +{ + OUString sRet; + + OString sName(m_xTabCtrl->get_current_page_ident()); + + if (sName == "contents") + { + sRet = xCPage->GetSelectedEntry(); + } + else if (sName == "index") + { + sRet = xIPage->GetSelectedEntry(); + } + else if (sName == "find") + { + sRet = xSPage->GetSelectedEntry(); + } + else if (sName == "bookmarks") + { + sRet = xBPage->GetSelectedEntry(); + } + + return sRet; +} + +void SfxHelpIndexWindow_Impl::AddBookmarks( const OUString& rTitle, const OUString& rURL ) +{ + GetBookmarksPage()->AddBookmarks( rTitle, rURL ); +} + +bool SfxHelpIndexWindow_Impl::IsValidFactory( std::u16string_view _rFactory ) +{ + bool bValid = false; + for (sal_Int32 i = 0, nEntryCount = m_xActiveLB->get_count(); i < nEntryCount; ++i) + { + OUString aFactory = m_xActiveLB->get_id(i); + if (aFactory == _rFactory) + { + bValid = true; + break; + } + } + return bValid; +} + +void SfxHelpIndexWindow_Impl::ClearSearchPage() +{ + if ( xSPage ) + xSPage->ClearPage(); +} + +void SfxHelpIndexWindow_Impl::GrabFocusBack() +{ + OString sName(m_xTabCtrl->get_current_page_ident()); + + if (sName == "contents" && xCPage) + xCPage->SetFocusOnBox(); + else if (sName == "index" && xIPage) + xIPage->SetFocusOnBox(); + else if (sName == "find" && xSPage) + xSPage->SetFocusOnBox(); + else if (sName == "bookmarks" && xBPage) + xBPage->SetFocusOnBox(); +} + +bool SfxHelpIndexWindow_Impl::HasFocusOnEdit() const +{ + bool bRet = false; + OString sName(m_xTabCtrl->get_current_page_ident()); + if (sName == "index" && xIPage) + bRet = xIPage->HasFocusOnEdit(); + else if (sName == "find" && xSPage) + bRet = xSPage->HasFocusOnEdit(); + return bRet; +} + +OUString SfxHelpIndexWindow_Impl::GetSearchText() const +{ + OUString sRet; + OString sName(m_xTabCtrl->get_current_page_ident()); + if (sName == "find" && xSPage) + sRet = xSPage->GetSearchText(); + return sRet; +} + +bool SfxHelpIndexWindow_Impl::IsFullWordSearch() const +{ + bool bRet = false; + OString sName(m_xTabCtrl->get_current_page_ident()); + if (sName == "find" && xSPage) + bRet = xSPage->IsFullWordSearch(); + return bRet; +} + +void SfxHelpIndexWindow_Impl::OpenKeyword( const OUString& rKeyword ) +{ + sKeyword = rKeyword; + DBG_ASSERT( xIPage, "invalid index page" ); + xIPage->SetKeyword( sKeyword ); +} + +void SfxHelpIndexWindow_Impl::SelectExecutableEntry() +{ + OString sName(m_xTabCtrl->get_current_page_ident()); + if (sName == "index" && xIPage ) + xIPage->SelectExecutableEntry(); +} + +weld::Window* SfxHelpIndexWindow_Impl::GetFrameWeld() const +{ + return pParentWin->GetFrameWeld(); +} + +// class TextWin_Impl ---------------------------------------------------- +TextWin_Impl::TextWin_Impl( vcl::Window* p ) : DockingWindow( p, 0 ) +{ +} + +bool TextWin_Impl::EventNotify( NotifyEvent& rNEvt ) +{ + if( ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT ) && rNEvt.GetKeyEvent()->GetKeyCode().GetCode() == KEY_TAB ) + return GetParent()->EventNotify( rNEvt ); + else + return DockingWindow::EventNotify( rNEvt ); +} + + +// remove docking area acceptor from layoutmanager, so it will not layout anything further .-) +static void lcl_disableLayoutOfFrame(const Reference< XFrame2 >& xFrame) +{ + xFrame->setLayoutManager( Reference< XLayoutManager >() ); +} + +// class SfxHelpTextWindow_Impl ------------------------------------------ + +SfxHelpTextWindow_Impl::SfxHelpTextWindow_Impl(SfxHelpWindow_Impl* pHelpWin, weld::Builder& rBuilder, vcl::Window* pParent) : + + Window( pParent, WB_CLIPCHILDREN | WB_TABSTOP | WB_DIALOGCONTROL ), + + xToolBox ( rBuilder.weld_toolbar("toolbar") ), + xOnStartupCB ( rBuilder.weld_check_button("checkbutton") ), + xMenu ( rBuilder.weld_menu("menu") ), + aSelectIdle ( "sfx2 appl SfxHelpTextWindow_Impl Select" ), + aIndexOnImage ( BMP_HELP_TOOLBOX_INDEX_ON ), + aIndexOffImage ( BMP_HELP_TOOLBOX_INDEX_OFF ), + aIndexOnText ( SfxResId( STR_HELP_BUTTON_INDEX_ON ) ), + aIndexOffText ( SfxResId( STR_HELP_BUTTON_INDEX_OFF ) ), + aOnStartupText ( SfxResId( RID_HELP_ONSTARTUP_TEXT ) ), + xHelpWin ( pHelpWin ), + pTextWin ( VclPtr::Create( this ) ), + bIsDebug ( false ), + bIsIndexOn ( false ), + bIsInClose ( false ), + bIsFullWordSearch ( false ) +{ + xFrame = Frame::create( ::comphelper::getProcessComponentContext() ); + xFrame->initialize( VCLUnoHelper::GetInterface ( pTextWin ) ); + xFrame->setName( "OFFICE_HELP" ); + lcl_disableLayoutOfFrame(xFrame); + + xToolBox->set_help_id(HID_HELP_TOOLBOX); + + xToolBox->set_item_tooltip_text("index", aIndexOffText ); + xToolBox->set_item_help_id("index", HID_HELP_TOOLBOXITEM_INDEX); + xToolBox->set_item_help_id("backward", HID_HELP_TOOLBOXITEM_BACKWARD); + xToolBox->set_item_help_id("forward", HID_HELP_TOOLBOXITEM_FORWARD); + xToolBox->set_item_help_id("start", HID_HELP_TOOLBOXITEM_START); + xToolBox->set_item_help_id("print", HID_HELP_TOOLBOXITEM_PRINT); + xToolBox->set_item_help_id("bookmarks", HID_HELP_TOOLBOXITEM_BOOKMARKS ); + xToolBox->set_item_help_id("searchdialog", HID_HELP_TOOLBOXITEM_SEARCHDIALOG); + + InitToolBoxImages(); + InitOnStartupBox(); + xOnStartupCB->connect_toggled(LINK(this, SfxHelpTextWindow_Impl, CheckHdl)); + + aSelectIdle.SetInvokeHandler( LINK( this, SfxHelpTextWindow_Impl, SelectHdl ) ); + aSelectIdle.SetPriority( TaskPriority::LOWEST ); + + char* pEnv = getenv( "help_debug" ); + if ( pEnv ) + bIsDebug = true; + + SvtMiscOptions().AddListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) ); +} + +SfxHelpTextWindow_Impl::~SfxHelpTextWindow_Impl() +{ + disposeOnce(); +} + +void SfxHelpTextWindow_Impl::dispose() +{ + bIsInClose = true; + SvtMiscOptions().RemoveListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) ); + m_xSrchDlg.reset(); + xToolBox.reset(); + xOnStartupCB.reset(); + xHelpWin.clear(); + pTextWin.disposeAndClear(); + vcl::Window::dispose(); +} + +bool SfxHelpTextWindow_Impl::HasSelection() const +{ + // is there any selection in the text and not only a cursor? + bool bRet = false; + Reference < XTextRange > xRange = getCursor(); + if ( xRange.is() ) + { + Reference < XText > xText = xRange->getText(); + Reference < XTextCursor > xCursor = xText->createTextCursorByRange( xRange ); + bRet = !xCursor->isCollapsed(); + } + + return bRet; +} + +void SfxHelpTextWindow_Impl::InitToolBoxImages() +{ + xToolBox->set_item_icon_name("index", bIsIndexOn ? aIndexOffImage : aIndexOnImage); +} + +void SfxHelpTextWindow_Impl::InitOnStartupBox() +{ + sCurrentFactory = SfxHelp::GetCurrentModuleIdentifier(); + + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + const OUString sPath { PATH_OFFICE_FACTORIES + sCurrentFactory }; + + // Attention: This check boy knows two states: + // 1) Reading of the config key fails with an exception or by getting an empty Any (!) => check box must be hidden + // 2) We read sal_True/sal_False => check box must be shown and enabled/disabled + + bool bHideBox = true; + bool bHelpAtStartup = false; + try + { + xConfiguration = ConfigurationHelper::openConfig( + xContext, PACKAGE_SETUP, EConfigurationModes::Standard ); + if ( xConfiguration.is() ) + { + Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, KEY_HELP_ON_OPEN ); + if (aAny >>= bHelpAtStartup) + bHideBox = false; + } + } + catch( Exception& ) + { + bHideBox = true; + } + + if ( bHideBox ) + xOnStartupCB->hide(); + else + { + // detect module name + OUString sModuleName; + + if ( xConfiguration.is() ) + { + OUString sTemp; + try + { + Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, KEY_UI_NAME ); + aAny >>= sTemp; + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::InitOnStartupBox()" ); + } + sModuleName = sTemp; + } + + if ( !sModuleName.isEmpty() ) + { + // set module name in checkbox text + xOnStartupCB->set_label(aOnStartupText.replaceFirst("%MODULENAME", sModuleName)); + // and show it + xOnStartupCB->show(); + // set check state + xOnStartupCB->set_active(bHelpAtStartup); + xOnStartupCB->save_state(); + } + } +} + +Reference< XBreakIterator > const & SfxHelpTextWindow_Impl::GetBreakIterator() +{ + if ( !xBreakIterator.is() ) + xBreakIterator = vcl::unohelper::CreateBreakIterator(); + DBG_ASSERT( xBreakIterator.is(), "Could not create BreakIterator" ); + return xBreakIterator; +} + +Reference< XTextRange > SfxHelpTextWindow_Impl::getCursor() const +{ + // return the current cursor + Reference< XTextRange > xCursor; + + try + { + Reference < XSelectionSupplier > xSelSup( xFrame->getController(), UNO_QUERY ); + if ( xSelSup.is() ) + { + Any aAny = xSelSup->getSelection(); + Reference < XIndexAccess > xSelection; + if ( aAny >>= xSelection ) + { + if ( xSelection->getCount() == 1 ) + { + aAny = xSelection->getByIndex(0); + aAny >>= xCursor; + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::getCursor(): unexpected exception" ); + } + + return xCursor; +} + + +bool SfxHelpTextWindow_Impl::isHandledKey( const vcl::KeyCode& _rKeyCode ) +{ + bool bRet = false; + sal_uInt16 nCode = _rKeyCode.GetCode(); + + // the keys (select all), (copy), + // (find),

(print) and (close window) + // were handled in help + if ( _rKeyCode.IsMod1() && + ( KEY_A == nCode || KEY_C == nCode || KEY_F == nCode || KEY_P == nCode || KEY_W == nCode ) ) + { + if ( KEY_F == nCode ) + DoSearch(); + else + bRet = true; + } + + return bRet; +} + + +IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, SelectHdl, Timer *, void) +{ + try + { + // select the words, which are equal to the search text of the search page + Reference < XController > xController = xFrame->getController(); + if ( xController.is() ) + { + // get document + Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY ); + if ( xSearchable.is() ) + { + // create descriptor, set string and find all words + Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor(); + xSrchDesc->setPropertyValue( "SearchRegularExpression", Any( true ) ); + if ( bIsFullWordSearch ) + xSrchDesc->setPropertyValue( "SearchWords", Any( true ) ); + + xSrchDesc->setSearchString( sfx2::PrepareSearchString( aSearchText, GetBreakIterator(), false ) ); + Reference< XIndexAccess > xSelection = xSearchable->findAll( xSrchDesc ); + + // then select all found words + Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY ); + if ( xSelectionSup.is() ) + { + xSelectionSup->select( Any(xSelection) ); + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" ); + } +} + + +IMPL_LINK_NOARG( SfxHelpTextWindow_Impl, NotifyHdl, LinkParamNone*, void ) +{ + InitToolBoxImages(); + Resize(); +} + +IMPL_LINK( SfxHelpTextWindow_Impl, FindHdl, sfx2::SearchDialog&, rDlg, void ) +{ + FindHdl(&rDlg); +} +void SfxHelpTextWindow_Impl::FindHdl(sfx2::SearchDialog* pDlg) +{ + bool bWrapAround = ( nullptr == pDlg ); + if ( bWrapAround ) + pDlg = m_xSrchDlg.get(); + DBG_ASSERT( pDlg, "invalid search dialog" ); + try + { + // select the words, which are equal to the search text of the search page + Reference < XController > xController = xFrame->getController(); + if ( xController.is() ) + { + // get document + Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY ); + if ( xSearchable.is() ) + { + // create descriptor, set string and find all words + Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor(); + xSrchDesc->setPropertyValue( "SearchWords", Any(pDlg->IsOnlyWholeWords()) ); + xSrchDesc->setPropertyValue( "SearchCaseSensitive", Any(pDlg->IsMarchCase()) ); + xSrchDesc->setPropertyValue( "SearchBackwards", Any(pDlg->IsSearchBackwards()) ); + xSrchDesc->setSearchString( pDlg->GetSearchText() ); + Reference< XInterface > xSelection; + Reference< XTextRange > xCursor = getCursor(); + + if ( xCursor.is() ) + { + if ( pDlg->IsSearchBackwards() ) + xCursor = xCursor->getStart(); + xSelection = xSearchable->findNext( xCursor, xSrchDesc ); + } + else + xSelection = xSearchable->findFirst( xSrchDesc ); + + // then select the found word + if ( xSelection.is() ) + { + Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY ); + if ( xSelectionSup.is() ) + { + xSelectionSup->select( Any(xSelection) ); + } + } + else if ( pDlg->IsWrapAround() && !bWrapAround ) + { + Reference < text::XTextViewCursorSupplier > xCrsrSupp( xController, uno::UNO_QUERY ); + Reference < text::XTextViewCursor > xTVCrsr = xCrsrSupp->getViewCursor(); + if ( xTVCrsr.is() ) + { + Reference < text::XTextDocument > xDoc( xController->getModel(), uno::UNO_QUERY ); + Reference < text::XText > xText = xDoc->getText(); + if ( xText.is() ) + { + if ( pDlg->IsSearchBackwards() ) + xTVCrsr->gotoRange( xText->getEnd(), false ); + else + xTVCrsr->gotoRange( xText->getStart(), false ); + FindHdl( nullptr ); + } + } + } + else + { + assert(m_xSrchDlg && "no search dialog"); + std::unique_ptr xBox(Application::CreateMessageDialog(m_xSrchDlg->getDialog(), + VclMessageType::Info, VclButtonsType::Ok, SfxResId(STR_INFO_NOSEARCHTEXTFOUND))); + xBox->run(); + m_xSrchDlg->SetFocusOnEdit(); + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" ); + } +} + +IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, CloseHdl, LinkParamNone*, void) +{ + m_xSrchDlg.reset(); +} + +IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, CheckHdl, weld::Toggleable&, void) +{ + if ( !xConfiguration.is() ) + return; + + bool bChecked = xOnStartupCB->get_active(); + try + { + ConfigurationHelper::writeRelativeKey( + xConfiguration, PATH_OFFICE_FACTORIES + sCurrentFactory, KEY_HELP_ON_OPEN, Any( bChecked ) ); + ConfigurationHelper::flush( xConfiguration ); + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::CheckHdl()" ); + } +} + +void SfxHelpTextWindow_Impl::Resize() +{ + Size aSize = GetOutputSizePixel(); + pTextWin->SetPosSizePixel( Point(0, 0), aSize ); +} + +bool SfxHelpTextWindow_Impl::PreNotify( NotifyEvent& rNEvt ) +{ + bool bDone = false; + MouseNotifyEvent nType = rNEvt.GetType(); + if ( MouseNotifyEvent::COMMAND == nType && rNEvt.GetCommandEvent() ) + { + const CommandEvent* pCmdEvt = rNEvt.GetCommandEvent(); + vcl::Window* pCmdWin = rNEvt.GetWindow(); + + if ( pCmdEvt->GetCommand() == CommandEventId::ContextMenu && pCmdWin != this ) + { + Point aPos; + if ( pCmdEvt->IsMouseEvent() ) + aPos = pCmdEvt->GetMousePosPixel(); + else + aPos = Point( pTextWin->GetPosPixel().X() + 20, 20 ); + + xMenu->clear(); + + if (bIsIndexOn) + xMenu->append("index", aIndexOffText, BMP_HELP_TOOLBOX_INDEX_OFF); + else + xMenu->append("index", aIndexOnText, BMP_HELP_TOOLBOX_INDEX_ON); + + xMenu->append_separator("separator1"); + xMenu->append("backward", SfxResId(STR_HELP_BUTTON_PREV), BMP_HELP_TOOLBOX_PREV); + xMenu->set_sensitive("backward", xHelpWin->HasHistoryPredecessor()); + xMenu->append("forward", SfxResId(STR_HELP_BUTTON_NEXT), BMP_HELP_TOOLBOX_NEXT); + xMenu->set_sensitive("forward", xHelpWin->HasHistorySuccessor()); + xMenu->append("start", SfxResId(STR_HELP_BUTTON_START), BMP_HELP_TOOLBOX_START); + xMenu->append_separator("separator2"); + xMenu->append("print", SfxResId(STR_HELP_BUTTON_PRINT), BMP_HELP_TOOLBOX_PRINT); + xMenu->append("bookmarks", SfxResId(STR_HELP_BUTTON_ADDBOOKMARK), BMP_HELP_TOOLBOX_BOOKMARKS); + xMenu->append("searchdialog", SfxResId(STR_HELP_BUTTON_SEARCHDIALOG), BMP_HELP_TOOLBOX_SEARCHDIALOG); + xMenu->append_separator("separator3"); + xMenu->append_check("selectionmode", SfxResId(STR_HELP_MENU_TEXT_SELECTION_MODE)); + URL aURL; + aURL.Complete = ".uno:SelectTextMode"; + Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict(aURL); + Reference < XDispatch > xDisp = xFrame->queryDispatch( aURL, OUString(), 0 ); + if(xDisp.is()) + { + rtl::Reference pStateListener = + new HelpStatusListener_Impl(xDisp, aURL ); + FeatureStateEvent rEvent = pStateListener->GetStateEvent(); + bool bCheck = false; + rEvent.State >>= bCheck; + xMenu->set_active("selectionmode", bCheck); + } + xMenu->append_separator("separator4"); + xMenu->append("copy", SfxResId(STR_HELP_MENU_TEXT_COPY), BMP_HELP_TOOLBOX_COPY); + xMenu->set_sensitive("copy", HasSelection()); + + if ( bIsDebug ) + { + xMenu->append_separator("separator5"); + xMenu->append("sourceview", SfxResId(STR_HELP_BUTTON_SOURCEVIEW)); + } + + int x, y, width, height; + weld::Window* pTopLevel = GetFrameWeld(); + xHelpWin->GetContainer()->get_extents_relative_to(*pTopLevel, x, y, width, height); + aPos.AdjustX(x); + aPos.AdjustY(y); + + xHelpWin->DoAction(xMenu->popup_at_rect(pTopLevel, tools::Rectangle(aPos, Size(1,1)))); + bDone = true; + } + } + else if ( MouseNotifyEvent::KEYINPUT == nType && rNEvt.GetKeyEvent() ) + { + const KeyEvent* pKEvt = rNEvt.GetKeyEvent(); + const vcl::KeyCode& rKeyCode = pKEvt->GetKeyCode(); + sal_uInt16 nKeyGroup = rKeyCode.GetGroup(); + sal_uInt16 nKey = rKeyCode.GetCode(); + if ( KEYGROUP_ALPHA == nKeyGroup && !isHandledKey( rKeyCode ) ) + { + // do nothing disables the writer accelerators + bDone = true; + } + else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) ) + { + // or -> close top frame + xHelpWin->CloseWindow(); + bDone = true; + } + else if ( KEY_TAB == nKey && xOnStartupCB->has_focus() ) + { + xToolBox->grab_focus(); + bDone = true; + } + } + + return bDone || Window::PreNotify( rNEvt ); +} + + +void SfxHelpTextWindow_Impl::GetFocus() +{ + if ( bIsInClose ) + return; + + try + { + if( xFrame.is() ) + { + Reference< css::awt::XWindow > xWindow = xFrame->getComponentWindow(); + if( xWindow.is() ) + xWindow->setFocus(); + } + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::GetFocus()" ); + } +} + + +void SfxHelpTextWindow_Impl::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + if ( ( ( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) || + ( rDCEvt.GetType() == DataChangedEventType::DISPLAY ) ) && + ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) ) + { + SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFaceColor() ) ); + InitToolBoxImages(); + } +} + +void SfxHelpTextWindow_Impl::ToggleIndex( bool bOn ) +{ + bIsIndexOn = bOn; + if ( bIsIndexOn ) + { + xToolBox->set_item_icon_name("index", aIndexOffImage); + xToolBox->set_item_tooltip_text("index", aIndexOffText); + } + else + { + xToolBox->set_item_icon_name("index", aIndexOnImage); + xToolBox->set_item_tooltip_text("index", aIndexOnText); + } +} + +void SfxHelpTextWindow_Impl::SelectSearchText( const OUString& rSearchText, bool _bIsFullWordSearch ) +{ + aSearchText = rSearchText; + bIsFullWordSearch = _bIsFullWordSearch; + aSelectIdle.Start(); +} + + +void SfxHelpTextWindow_Impl::SetPageStyleHeaderOff() const +{ + bool bSetOff = false; + // set off the pagestyle header to prevent print output of the help URL + try + { + Reference < XController > xController = xFrame->getController(); + Reference < XSelectionSupplier > xSelSup( xController, UNO_QUERY ); + if ( xSelSup.is() ) + { + Reference < XIndexAccess > xSelection; + if ( xSelSup->getSelection() >>= xSelection ) + { + Reference < XTextRange > xRange; + if ( xSelection->getByIndex(0) >>= xRange ) + { + Reference < XText > xText = xRange->getText(); + Reference < XPropertySet > xProps( xText->createTextCursorByRange( xRange ), UNO_QUERY ); + OUString sStyleName; + if ( xProps->getPropertyValue( "PageStyleName" ) >>= sStyleName ) + { + Reference < XStyleFamiliesSupplier > xStyles( xController->getModel(), UNO_QUERY ); + Reference < XNameContainer > xContainer; + if ( xStyles->getStyleFamilies()->getByName( "PageStyles" ) + >>= xContainer ) + { + Reference < XStyle > xStyle; + if ( xContainer->getByName( sStyleName ) >>= xStyle ) + { + Reference < XPropertySet > xPropSet( xStyle, UNO_QUERY ); + xPropSet->setPropertyValue( "HeaderIsOn", Any( false ) ); + + Reference< XModifiable > xReset(xStyles, UNO_QUERY); + xReset->setModified(false); + bSetOff = true; + } + } + } + } + } + } + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff()" ); + } + + SAL_WARN_IF( !bSetOff, "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff(): set off failed" ); +} + + +void SfxHelpTextWindow_Impl::CloseFrame() +{ + bIsInClose = true; + try + { + css::uno::Reference< css::util::XCloseable > xCloseable ( xFrame, css::uno::UNO_QUERY ); + if (xCloseable.is()) + xCloseable->close(true); + } + catch( css::util::CloseVetoException& ) + { + } +} + + +void SfxHelpTextWindow_Impl::DoSearch() +{ + if (m_xSrchDlg) + return; + + // create the search dialog + m_xSrchDlg = std::make_shared(pTextWin->GetFrameWeld(), "HelpSearchDialog"); + // set handler + m_xSrchDlg->SetFindHdl( LINK( this, SfxHelpTextWindow_Impl, FindHdl ) ); + m_xSrchDlg->SetCloseHdl( LINK( this, SfxHelpTextWindow_Impl, CloseHdl ) ); + // get selected text of the help page to set it as the search text + Reference< XTextRange > xCursor = getCursor(); + if ( xCursor.is() ) + { + OUString sText = xCursor->getString(); + if ( !sText.isEmpty() ) + m_xSrchDlg->SetSearchText( sText ); + } + sfx2::SearchDialog::runAsync(m_xSrchDlg); +} + +void SfxHelpWindow_Impl::GetFocus() +{ + if (pTextWin) + pTextWin->GrabFocus(); + else + ResizableDockingWindow::GetFocus(); +} + +void SfxHelpWindow_Impl::MakeLayout() +{ + Split(); + + m_xHelpPaneWindow->set_visible(bIndex); +} + +IMPL_LINK(SfxHelpWindow_Impl, ResizeHdl, const Size&, rSize, void) +{ + int nNewWidth = rSize.Width(); + if (!nNewWidth) + return; + if (bSplit) + nIndexSize = round(m_xContainer->get_position() * 100.0 / nNewWidth); + nWidth = nNewWidth; + Split(); + nIndexSize = round(m_xContainer->get_position() * 100.0 / nWidth); +} + +void SfxHelpWindow_Impl::Split() +{ + if (!nWidth) + return; + m_xContainer->set_position(nWidth * nIndexSize / 100); + bSplit = true; +} + +void SfxHelpWindow_Impl::LoadConfig() +{ + SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN ); + if ( !aViewOpt.Exists() ) + return; + bIndex = aViewOpt.IsVisible(); + + Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME ); + OUString aUserData; + if ( aUserItem >>= aUserData ) + { + DBG_ASSERT( comphelper::string::getTokenCount(aUserData, ';') == 6, "invalid user data" ); + sal_Int32 nIdx = 0; + nIndexSize = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )); + o3tl::getToken(aUserData, 0, ';', nIdx); // ignore nTextSize + sal_Int32 nOldWidth = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )); + sal_Int32 nOldHeight = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )); + aWinSize = Size(nOldWidth, nOldHeight); + aWinPos.setX( o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )) ); + aWinPos.setY( o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )) ); + } + + pTextWin->ToggleIndex( bIndex ); +} + +void SfxHelpWindow_Impl::SaveConfig() +{ + SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN ); + sal_Int32 nW = 0, nH = 0; + + if ( xWindow.is() ) + { + css::awt::Rectangle aRect = xWindow->getPosSize(); + nW = aRect.Width; + nH = aRect.Height; + } + + aViewOpt.SetVisible( bIndex ); + VclPtr pScreenWin = VCLUnoHelper::GetWindow( xWindow ); + aWinPos = pScreenWin->GetWindowExtentsRelative( nullptr ).TopLeft(); + if (bSplit) + nIndexSize = round(m_xContainer->get_position() * 100.0 / nWidth); + const OUString aUserData = OUString::number( nIndexSize ) + + ";" + OUString::number( 100 - nIndexSize ) + + ";" + OUString::number( nW ) + + ";" + OUString::number( nH ) + + ";" + OUString::number( aWinPos.X() ) + + ";" + OUString::number( aWinPos.Y() ); + + aViewOpt.SetUserItem( USERITEM_NAME, Any( aUserData ) ); +} + +void SfxHelpWindow_Impl::ShowStartPage() +{ + loadHelpContent(SfxHelpWindow_Impl::buildHelpURL(xIndexWin->GetFactory(), u"/start", u"")); +} + +IMPL_LINK(SfxHelpWindow_Impl, SelectHdl, const OString&, rCurItem, void) +{ + bGrabFocusToToolBox = pTextWin->GetToolBox().has_focus(); + DoAction(rCurItem); +} + +IMPL_LINK_NOARG(SfxHelpWindow_Impl, OpenHdl, LinkParamNone*, void) +{ + xIndexWin->SelectExecutableEntry(); + OUString aEntry = xIndexWin->GetSelectedEntry(); + + if ( aEntry.isEmpty() ) + return; + + OUString sHelpURL; + + bool bComplete = aEntry.toAsciiLowerCase().match("vnd.sun.star.help"); + + if (bComplete) + sHelpURL = aEntry; + else + { + std::u16string_view aId; + OUString aAnchor('#'); + if ( comphelper::string::getTokenCount(aEntry, '#') == 2 ) + { + sal_Int32 nIdx{ 0 }; + aId = o3tl::getToken(aEntry, 0, '#', nIdx ); + aAnchor += o3tl::getToken(aEntry, 0, '#', nIdx ); + } + else + aId = aEntry; + + sHelpURL = SfxHelpWindow_Impl::buildHelpURL(xIndexWin->GetFactory(), OUStringConcatenation(OUString::Concat("/") + aId), aAnchor); + } + + loadHelpContent(sHelpURL); +} + +IMPL_LINK( SfxHelpWindow_Impl, SelectFactoryHdl, SfxHelpIndexWindow_Impl* , pWin, void ) +{ + if ( sTitle.isEmpty() ) + sTitle = GetParent()->GetText(); + + Reference< XTitle > xTitle(xFrame, UNO_QUERY); + if (xTitle.is ()) + xTitle->setTitle(sTitle + " - " + xIndexWin->GetActiveFactoryTitle()); + + if ( pWin ) + ShowStartPage(); + xIndexWin->ClearSearchPage(); +} + + +IMPL_LINK( SfxHelpWindow_Impl, ChangeHdl, HelpListener_Impl&, rListener, void ) +{ + SetFactory( rListener.GetFactory() ); +} + + +void SfxHelpWindow_Impl::openDone(std::u16string_view sURL , + bool bSuccess) +{ + INetURLObject aObj( sURL ); + if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp ) + SetFactory( aObj.GetHost() ); + if ( IsWait() ) + LeaveWait(); + if ( bGrabFocusToToolBox ) + { + pTextWin->GetToolBox().grab_focus(); + bGrabFocusToToolBox = false; + } + else + xIndexWin->GrabFocusBack(); + if ( !bSuccess ) + return; + + // set some view settings: "prevent help tips" and "helpid == 68245" + try + { + Reference < XController > xController = pTextWin->getFrame()->getController(); + if ( xController.is() ) + { + Reference < XViewSettingsSupplier > xSettings( xController, UNO_QUERY ); + Reference < XPropertySet > xViewProps = xSettings->getViewSettings(); + Reference< XPropertySetInfo > xInfo = xViewProps->getPropertySetInfo(); + xViewProps->setPropertyValue( "ShowContentTips", Any( false ) ); + xViewProps->setPropertyValue( "ShowGraphics", Any( true ) ); + xViewProps->setPropertyValue( "ShowTables", Any( true ) ); + xViewProps->setPropertyValue( "HelpURL", Any( OUString("HID:SFX2_HID_HELP_ONHELP") ) ); + OUString sProperty( "IsExecuteHyperlinks" ); + if ( xInfo->hasPropertyByName( sProperty ) ) + xViewProps->setPropertyValue( sProperty, Any( true ) ); + xController->restoreViewData(Any()); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::OpenDoneHdl(): unexpected exception" ); + } + + // When the SearchPage opens the help doc, then select all words, which are equal to its text + OUString sSearchText = comphelper::string::strip(xIndexWin->GetSearchText(), ' '); + if ( !sSearchText.isEmpty() ) + pTextWin->SelectSearchText( sSearchText, xIndexWin->IsFullWordSearch() ); + + // no page style header -> this prevents a print output of the URL + pTextWin->SetPageStyleHeaderOff(); +} + + +SfxHelpWindow_Impl::SfxHelpWindow_Impl( + const css::uno::Reference < css::frame::XFrame2 >& rFrame, + vcl::Window* pParent ) : + + ResizableDockingWindow(pParent), + + xFrame ( rFrame ), + pTextWin ( nullptr ), + pHelpInterceptor ( new HelpInterceptor_Impl() ), + pHelpListener ( new HelpListener_Impl( pHelpInterceptor ) ), + bIndex ( true ), + bGrabFocusToToolBox ( false ), + bSplit ( false ), + nWidth ( 0 ), + nIndexSize ( 40 ), // % of width + aWinPos ( 0, 0 ), + aWinSize ( 0, 0 ), + sTitle ( pParent->GetText() ) +{ + SetStyle(GetStyle() & ~WB_DOCKABLE); + + SetHelpId( HID_HELP_WINDOW ); + + m_xBuilder = Application::CreateInterimBuilder(m_xBox.get(), "sfx/ui/helpwindow.ui", false); + m_xContainer = m_xBuilder->weld_paned("HelpWindow"); + m_xContainer->connect_size_allocate(LINK(this, SfxHelpWindow_Impl, ResizeHdl)); + m_xHelpPaneWindow = m_xBuilder->weld_container("helppanewindow"); + m_xHelpTextWindow = m_xBuilder->weld_container("helptextwindow"); + m_xHelpTextXWindow = m_xHelpTextWindow->CreateChildFrame(); + + pHelpInterceptor->InitWaiter( this ); + xIndexWin.reset(new SfxHelpIndexWindow_Impl(this, m_xHelpPaneWindow.get())); + xIndexWin->SetDoubleClickHdl( LINK( this, SfxHelpWindow_Impl, OpenHdl ) ); + xIndexWin->SetSelectFactoryHdl( LINK( this, SfxHelpWindow_Impl, SelectFactoryHdl ) ); + + pTextWin = VclPtr::Create(this, *m_xBuilder, VCLUnoHelper::GetWindow(m_xHelpTextXWindow)); + Reference < XFrames > xFrames = rFrame->getFrames(); + xFrames->append( Reference(pTextWin->getFrame(), UNO_QUERY_THROW) ); + pTextWin->SetSelectHdl( LINK( this, SfxHelpWindow_Impl, SelectHdl ) ); + pTextWin->Show(); + pHelpInterceptor->setInterception( Reference(pTextWin->getFrame(), UNO_QUERY_THROW) ); + pHelpListener->SetChangeHdl( LINK( this, SfxHelpWindow_Impl, ChangeHdl ) ); + LoadConfig(); +} + +SfxHelpWindow_Impl::~SfxHelpWindow_Impl() +{ + disposeOnce(); +} + +void SfxHelpWindow_Impl::dispose() +{ + SaveConfig(); + xIndexWin.reset(); + pTextWin->CloseFrame(); + pTextWin.disposeAndClear(); + + m_xHelpTextXWindow->dispose(); + m_xHelpTextXWindow.clear(); + m_xHelpTextWindow.reset(); + m_xHelpPaneWindow.reset(); + m_xContainer.reset(); + m_xBuilder.reset(); + + ResizableDockingWindow::dispose(); +} + +bool SfxHelpWindow_Impl::PreNotify( NotifyEvent& rNEvt ) +{ + bool bHandled = false; + if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT ) + { + // Backward == or Forward == + const vcl::KeyCode& rKeyCode = rNEvt.GetKeyEvent()->GetKeyCode(); + sal_uInt16 nKey = rKeyCode.GetCode(); + if ( ( rKeyCode.IsMod2() && ( KEY_LEFT == nKey || KEY_RIGHT == nKey ) ) || + ( !rKeyCode.GetModifier() && KEY_BACKSPACE == nKey && !xIndexWin->HasFocusOnEdit() ) ) + { + DoAction( rKeyCode.GetCode() == KEY_RIGHT ? "forward" : "backward" ); + bHandled = true; + } + else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) ) + { + // or -> close top frame + CloseWindow(); + bHandled = true; + } + } + return bHandled || Window::PreNotify( rNEvt ); +} + +void SfxHelpWindow_Impl::setContainerWindow( const Reference < css::awt::XWindow >& xWin ) +{ + xWindow = xWin; + MakeLayout(); + if (xWindow.is()) + { + VclPtr pScreenWin = VCLUnoHelper::GetWindow(xWindow); + if (aWinSize.Width() && aWinSize.Height()) + pScreenWin->SetPosSizePixel(aWinPos, aWinSize); + else + pScreenWin->SetPosPixel(aWinPos); + } +} + +void SfxHelpWindow_Impl::SetFactory( const OUString& rFactory ) +{ + xIndexWin->SetFactory( rFactory, true ); +} + +void SfxHelpWindow_Impl::SetHelpURL( std::u16string_view rURL ) +{ + INetURLObject aObj( rURL ); + if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp ) + SetFactory( aObj.GetHost() ); +} + +void SfxHelpWindow_Impl::DoAction(std::string_view rActionId) +{ + if (rActionId == "index") + { + bIndex = !bIndex; + MakeLayout(); + pTextWin->ToggleIndex( bIndex ); + } + else if (rActionId == "start") + { + ShowStartPage(); + } + else if (rActionId == "backward" || rActionId == "forward") + { + URL aURL; + aURL.Complete = ".uno:Backward"; + if (rActionId == "forward") + aURL.Complete = ".uno:Forward"; + Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict(aURL); + pHelpInterceptor->dispatch( aURL, Sequence < PropertyValue >() ); + } + else if (rActionId == "searchdialog") + { + pTextWin->DoSearch(); + } + else if (rActionId == "print" || rActionId == "sourceview" || rActionId == "copy" || rActionId == "selectionmode") + { + Reference < XDispatchProvider > xProv = pTextWin->getFrame(); + if ( xProv.is() ) + { + URL aURL; + if (rActionId == "print") + aURL.Complete = ".uno:Print"; + else if (rActionId == "sourceview") + aURL.Complete = ".uno:SourceView"; + else if (rActionId == "copy") + aURL.Complete = ".uno:Copy"; + else // rActionId == "selectionmode" + aURL.Complete = ".uno:SelectTextMode"; + Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict(aURL); + Reference < XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 ); + if ( xDisp.is() ) + xDisp->dispatch( aURL, Sequence < PropertyValue >() ); + } + } + else if (rActionId == "bookmarks") + { + OUString aURL = pHelpInterceptor->GetCurrentURL(); + if ( !aURL.isEmpty() ) + { + try + { + Content aCnt( aURL, Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties(); + if ( xInfo->hasPropertyByName( PROPERTY_TITLE ) ) + { + css::uno::Any aAny = aCnt.getPropertyValue( PROPERTY_TITLE ); + OUString aValue; + if ( aAny >>= aValue ) + { + SfxAddHelpBookmarkDialog_Impl aDlg(GetFrameWeld(), false); + aDlg.SetTitle(aValue); + if (aDlg.run() == RET_OK ) + { + xIndexWin->AddBookmarks( aDlg.GetTitle(), aURL ); + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::DoAction(): unexpected exception" ); + } + } + } +} + +void SfxHelpWindow_Impl::CloseWindow() +{ + try + { + // search for top frame + Reference< XFramesSupplier > xCreator = getTextFrame()->getCreator(); + while ( xCreator.is() && !xCreator->isTop() ) + { + xCreator = xCreator->getCreator(); + } + + // when found, close it + if ( xCreator.is() && xCreator->isTop() ) + { + Reference < XCloseable > xCloser( xCreator, UNO_QUERY ); + if ( xCloser.is() ) + xCloser->close( false ); + } + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::CloseWindow()" ); + } +} + + +void SfxHelpWindow_Impl::UpdateToolbox() +{ + pTextWin->GetToolBox().set_item_sensitive("backward", pHelpInterceptor->HasHistoryPred()); + pTextWin->GetToolBox().set_item_sensitive("forward", pHelpInterceptor->HasHistorySucc()); +} + + +bool SfxHelpWindow_Impl::HasHistoryPredecessor() const +{ + return pHelpInterceptor->HasHistoryPred(); +} + + +bool SfxHelpWindow_Impl::HasHistorySuccessor() const +{ + return pHelpInterceptor->HasHistorySucc(); +} + +// class SfxAddHelpBookmarkDialog_Impl ----------------------------------- + +SfxAddHelpBookmarkDialog_Impl::SfxAddHelpBookmarkDialog_Impl(weld::Widget* pParent, bool bRename) + : GenericDialogController(pParent, "sfx/ui/bookmarkdialog.ui", "BookmarkDialog") + , m_xTitleED(m_xBuilder->weld_entry("entry")) + , m_xAltTitle(m_xBuilder->weld_label("alttitle")) +{ + if (bRename) + m_xDialog->set_title(m_xAltTitle->get_label()); +} + +void SfxAddHelpBookmarkDialog_Impl::SetTitle(const OUString& rTitle) +{ + m_xTitleED->set_text(rTitle); + m_xTitleED->select_region(0, -1); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3