Resolves: rhbz#2069486 classic help index entry autocompletes on del/backspace
[LibreOffice.git] / sfx2 / source / appl / newhelp.cxx
blob9312a83ccd0448f69e469671bbcbd9dc839ac554
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "newhelp.hxx"
22 #include <sfx2/sfxresid.hxx>
23 #include "helpinterceptor.hxx"
24 #include <helper.hxx>
25 #include <srchdlg.hxx>
26 #include <sfx2/sfxhelp.hxx>
27 #include <sal/log.hxx>
28 #include <osl/diagnose.h>
29 #include <tools/debug.hxx>
30 #include <tools/diagnose_ex.h>
32 #include <sfx2/strings.hrc>
33 #include <helpids.h>
34 #include <bitmaps.hlst>
36 #include <rtl/ustrbuf.hxx>
37 #include <comphelper/configurationhelper.hxx>
38 #include <comphelper/processfactory.hxx>
39 #include <comphelper/string.hxx>
40 #include <toolkit/helper/vclunohelper.hxx>
41 #include <com/sun/star/beans/PropertyValue.hpp>
42 #include <com/sun/star/beans/XPropertySetInfo.hpp>
43 #include <com/sun/star/container/XIndexAccess.hpp>
44 #include <com/sun/star/frame/XComponentLoader.hpp>
45 #include <com/sun/star/frame/XTitle.hpp>
46 #include <com/sun/star/frame/XLayoutManager.hpp>
47 #include <com/sun/star/frame/XController.hpp>
48 #include <com/sun/star/frame/XDispatch.hpp>
49 #include <com/sun/star/frame/XDispatchProvider.hpp>
50 #include <com/sun/star/frame/Frame.hpp>
51 #include <com/sun/star/i18n/XBreakIterator.hpp>
52 #include <com/sun/star/i18n/WordType.hpp>
53 #include <com/sun/star/lang/XComponent.hpp>
54 #include <com/sun/star/style/XStyle.hpp>
55 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
56 #include <com/sun/star/text/XText.hpp>
57 #include <com/sun/star/text/XTextCursor.hpp>
58 #include <com/sun/star/text/XTextDocument.hpp>
59 #include <com/sun/star/text/XTextViewCursor.hpp>
60 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
61 #include <com/sun/star/ucb/CommandAbortedException.hpp>
62 #include <com/sun/star/util/URL.hpp>
63 #include <com/sun/star/util/XSearchable.hpp>
64 #include <com/sun/star/util/XSearchDescriptor.hpp>
65 #include <com/sun/star/util/URLTransformer.hpp>
66 #include <com/sun/star/util/XURLTransformer.hpp>
67 #include <com/sun/star/util/XModifiable.hpp>
68 #include <com/sun/star/util/XCloseable.hpp>
69 #include <com/sun/star/util/CloseVetoException.hpp>
70 #include <com/sun/star/view/XSelectionSupplier.hpp>
71 #include <com/sun/star/view/XViewSettingsSupplier.hpp>
72 #include <unotools/historyoptions.hxx>
73 #include <unotools/viewoptions.hxx>
74 #include <tools/urlobj.hxx>
75 #include <svtools/imagemgr.hxx>
76 #include <svtools/miscopt.hxx>
77 #include <vcl/commandevent.hxx>
78 #include <vcl/event.hxx>
79 #include <vcl/i18nhelp.hxx>
80 #include <vcl/settings.hxx>
81 #include <vcl/svapp.hxx>
82 #include <vcl/unohelp.hxx>
83 #include <vcl/weld.hxx>
85 #include <ucbhelper/content.hxx>
86 #include <unotools/ucbhelper.hxx>
88 #include <string_view>
89 #include <unordered_map>
90 #include <vector>
92 using namespace ::ucbhelper;
93 using namespace ::com::sun::star::ucb;
95 using namespace ::com::sun::star;
96 using namespace ::com::sun::star::beans;
97 using namespace ::com::sun::star::container;
98 using namespace ::com::sun::star::frame;
99 using namespace ::com::sun::star::i18n;
100 using namespace ::com::sun::star::lang;
101 using namespace ::com::sun::star::style;
102 using namespace ::com::sun::star::text;
103 using namespace ::com::sun::star::uno;
104 using namespace ::com::sun::star::util;
105 using namespace ::com::sun::star::view;
106 using namespace ::com::sun::star::ui;
108 using namespace ::comphelper;
110 // defines ---------------------------------------------------------------
112 constexpr OUStringLiteral CONFIGNAME_HELPWIN = u"OfficeHelp";
113 constexpr OUStringLiteral CONFIGNAME_INDEXWIN = u"OfficeHelpIndex";
114 constexpr OUStringLiteral CONFIGNAME_SEARCHPAGE = u"OfficeHelpSearch";
115 constexpr OUStringLiteral IMAGE_URL = u"private:factory/";
117 constexpr OUStringLiteral PROPERTY_KEYWORDLIST = u"KeywordList";
118 constexpr OUStringLiteral PROPERTY_KEYWORDREF = u"KeywordRef";
119 constexpr OUStringLiteral PROPERTY_ANCHORREF = u"KeywordAnchorForRef";
120 constexpr OUStringLiteral PROPERTY_TITLEREF = u"KeywordTitleForRef";
121 constexpr OUStringLiteral PROPERTY_TITLE = u"Title";
122 constexpr OUStringLiteral HELP_URL = u"vnd.sun.star.help://";
123 constexpr OUStringLiteral HELP_SEARCH_TAG = u"/?Query=";
124 constexpr OUStringLiteral USERITEM_NAME = u"UserItem";
126 constexpr OUStringLiteral PACKAGE_SETUP = u"/org.openoffice.Setup";
127 constexpr OUStringLiteral PATH_OFFICE_FACTORIES = u"Office/Factories/";
128 constexpr OUStringLiteral KEY_HELP_ON_OPEN = u"ooSetupFactoryHelpOnOpen";
129 constexpr OUStringLiteral KEY_UI_NAME = u"ooSetupFactoryUIName";
131 namespace sfx2
135 /** Prepare a search string for searching or selecting.
136 For searching every search word needs the postfix '*' and the delimiter ' ' if necessary.
137 For selecting the delimiter '|' is required to search with regular expressions.
138 Samples:
139 search string | output for searching | output for selecting
140 -----------------------------------------------------------
141 "text" | "text*" | "text"
142 "text*" | "text*" | "text"
143 "text menu" | "text* menu*" | "text|menu"
145 static OUString PrepareSearchString( const OUString& rSearchString,
146 const Reference< XBreakIterator >& xBreak, bool bForSearch )
148 OUStringBuffer sSearchStr;
149 sal_Int32 nStartPos = 0;
150 const lang::Locale aLocale = Application::GetSettings().GetUILanguageTag().getLocale();
151 Boundary aBoundary = xBreak->getWordBoundary(
152 rSearchString, nStartPos, aLocale, WordType::ANYWORD_IGNOREWHITESPACES, true );
154 while ( aBoundary.startPos < aBoundary.endPos )
156 nStartPos = aBoundary.endPos;
157 OUString sSearchToken( rSearchString.copy(
158 static_cast<sal_uInt16>(aBoundary.startPos), static_cast<sal_uInt16>(aBoundary.endPos) - static_cast<sal_uInt16>(aBoundary.startPos) ) );
159 if ( !sSearchToken.isEmpty() && ( sSearchToken.getLength() > 1 || sSearchToken[0] != '.' ) )
161 if ( bForSearch && sSearchToken[ sSearchToken.getLength() - 1 ] != '*' )
162 sSearchToken += "*";
164 if ( sSearchToken.getLength() > 1 ||
165 ( sSearchToken.getLength() > 0 && sSearchToken[ 0 ] != '*' ) )
167 if ( !sSearchStr.isEmpty() )
169 if ( bForSearch )
170 sSearchStr.append(" ");
171 else
172 sSearchStr.append("|");
174 sSearchStr.append(sSearchToken);
177 aBoundary = xBreak->nextWord( rSearchString, nStartPos,
178 aLocale, WordType::ANYWORD_IGNOREWHITESPACES );
181 return sSearchStr.makeStringAndClear();
184 // namespace sfx2
188 // struct IndexEntry_Impl ------------------------------------------------
190 namespace {
192 struct IndexEntry_Impl
194 bool m_bSubEntry;
195 OUString m_aURL;
197 IndexEntry_Impl( const OUString& rURL, bool bSubEntry ) :
198 m_bSubEntry( bSubEntry ), m_aURL( rURL ) {}
201 // struct ContentEntry_Impl ----------------------------------------------
203 struct ContentEntry_Impl
205 OUString aURL;
206 bool bIsFolder;
208 ContentEntry_Impl( const OUString& rURL, bool bFolder ) :
209 aURL( rURL ), bIsFolder( bFolder ) {}
214 void ContentTabPage_Impl::InitRoot()
216 std::vector< OUString > aList =
217 SfxContentHelper::GetHelpTreeViewContents( "vnd.sun.star.hier://com.sun.star.help.TreeView/" );
219 for (const OUString & aRow : aList)
221 sal_Int32 nIdx = 0;
222 OUString aTitle = aRow.getToken( 0, '\t', nIdx );
223 OUString aURL = aRow.getToken( 0, '\t', nIdx );
224 sal_Unicode cFolder = aRow.getToken( 0, '\t', nIdx )[0];
225 bool bIsFolder = ( '1' == cFolder );
226 OUString sId;
227 if (bIsFolder)
228 sId = OUString::number(reinterpret_cast<sal_Int64>(new ContentEntry_Impl(aURL, true)));
229 m_xContentBox->insert(nullptr, -1, &aTitle, &sId, nullptr, nullptr, true, m_xScratchIter.get());
230 m_xContentBox->set_image(*m_xScratchIter, aClosedBookImage);
234 void ContentTabPage_Impl::ClearChildren(const weld::TreeIter* pParent)
236 std::unique_ptr<weld::TreeIter> xEntry = m_xContentBox->make_iterator(pParent);
237 bool bEntry = m_xContentBox->iter_children(*xEntry);
238 while (bEntry)
240 ClearChildren(xEntry.get());
241 delete reinterpret_cast<ContentEntry_Impl*>(m_xContentBox->get_id(*xEntry).toInt64());
242 bEntry = m_xContentBox->iter_next_sibling(*xEntry);
247 IMPL_LINK(ContentTabPage_Impl, ExpandingHdl, const weld::TreeIter&, rIter, bool)
249 ContentEntry_Impl* pContentEntry = reinterpret_cast<ContentEntry_Impl*>(m_xContentBox->get_id(rIter).toInt64());
250 if (!m_xContentBox->iter_has_child(rIter))
254 if (pContentEntry)
256 std::vector<OUString > aList = SfxContentHelper::GetHelpTreeViewContents(pContentEntry->aURL);
258 for (const OUString & aRow : aList)
260 sal_Int32 nIdx = 0;
261 OUString aTitle = aRow.getToken( 0, '\t', nIdx );
262 OUString aURL = aRow.getToken( 0, '\t', nIdx );
263 sal_Unicode cFolder = aRow.getToken( 0, '\t', nIdx )[0];
264 bool bIsFolder = ( '1' == cFolder );
265 if ( bIsFolder )
267 OUString sId = OUString::number(reinterpret_cast<sal_Int64>(new ContentEntry_Impl(aURL, true)));
268 m_xContentBox->insert(&rIter, -1, &aTitle, &sId, nullptr, nullptr, true, m_xScratchIter.get());
269 m_xContentBox->set_image(*m_xScratchIter, aClosedBookImage);
271 else
273 Any aAny( ::utl::UCBContentHelper::GetProperty( aURL, "TargetURL" ) );
274 OUString sId;
275 OUString aTargetURL;
276 if ( aAny >>= aTargetURL )
277 sId = OUString::number(reinterpret_cast<sal_Int64>(new ContentEntry_Impl(aTargetURL, false)));
278 m_xContentBox->insert(&rIter, -1, &aTitle, &sId, nullptr, nullptr, false, m_xScratchIter.get());
279 m_xContentBox->set_image(*m_xScratchIter, aDocumentImage);
284 catch (const Exception&)
286 TOOLS_WARN_EXCEPTION( "sfx.appl", "ContentListBox_Impl::RequestingChildren(): unexpected exception" );
290 if (!pContentEntry || pContentEntry->bIsFolder)
291 m_xContentBox->set_image(rIter, aOpenBookImage);
293 return true;
296 IMPL_LINK(ContentTabPage_Impl, CollapsingHdl, const weld::TreeIter&, rIter, bool)
298 ContentEntry_Impl* pContentEntry = reinterpret_cast<ContentEntry_Impl*>(m_xContentBox->get_id(rIter).toInt64());
299 if (!pContentEntry || pContentEntry->bIsFolder)
300 m_xContentBox->set_image(rIter, aClosedBookImage);
302 return true;
305 OUString ContentTabPage_Impl::GetSelectedEntry() const
307 OUString aRet;
308 ContentEntry_Impl* pEntry = reinterpret_cast<ContentEntry_Impl*>(m_xContentBox->get_selected_id().toInt64());
309 if (pEntry && !pEntry->bIsFolder)
310 aRet = pEntry->aURL;
311 return aRet;
314 // class HelpTabPage_Impl ------------------------------------------------
315 HelpTabPage_Impl::HelpTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin,
316 const OString& rID, const OUString& rUIXMLDescription)
317 : BuilderPage(pParent, nullptr, rUIXMLDescription, rID)
318 , m_pIdxWin(pIdxWin)
322 HelpTabPage_Impl::~HelpTabPage_Impl()
326 // class ContentTabPage_Impl ---------------------------------------------
327 ContentTabPage_Impl::ContentTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin)
328 : HelpTabPage_Impl(pParent, pIdxWin, "HelpContentPage",
329 "sfx/ui/helpcontentpage.ui")
330 , m_xContentBox(m_xBuilder->weld_tree_view("content"))
331 , m_xScratchIter(m_xContentBox->make_iterator())
332 , aOpenBookImage(BMP_HELP_CONTENT_BOOK_OPEN)
333 , aClosedBookImage(BMP_HELP_CONTENT_BOOK_CLOSED)
334 , aDocumentImage(BMP_HELP_CONTENT_DOC)
336 m_xContentBox->set_size_request(m_xContentBox->get_approximate_digit_width() * 30,
337 m_xContentBox->get_height_rows(20));
338 m_xContentBox->connect_row_activated(LINK(this, ContentTabPage_Impl, DoubleClickHdl));
339 m_xContentBox->connect_expanding(LINK(this, ContentTabPage_Impl, ExpandingHdl));
340 m_xContentBox->connect_collapsing(LINK(this, ContentTabPage_Impl, CollapsingHdl));
342 InitRoot();
345 IMPL_LINK_NOARG(ContentTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
347 aDoubleClickHdl.Call(nullptr);
348 return false;
351 void ContentTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
353 aDoubleClickHdl = rLink;
356 ContentTabPage_Impl::~ContentTabPage_Impl()
358 std::unique_ptr<weld::TreeIter> xEntry = m_xContentBox->make_iterator();
359 bool bEntry = m_xContentBox->get_iter_first(*xEntry);
360 while (bEntry)
362 ClearChildren(xEntry.get());
363 delete reinterpret_cast<ContentEntry_Impl*>(m_xContentBox->get_id(*xEntry).toInt64());
364 bEntry = m_xContentBox->iter_next_sibling(*xEntry);
368 void IndexTabPage_Impl::SelectExecutableEntry()
370 sal_Int32 nPos = m_xIndexList->find_text(m_xIndexEntry->get_text());
371 if (nPos == -1)
372 return;
374 sal_Int32 nOldPos = nPos;
375 OUString aEntryText;
376 IndexEntry_Impl* pEntry = reinterpret_cast<IndexEntry_Impl*>(m_xIndexList->get_id(nPos).toInt64());
377 sal_Int32 nCount = m_xIndexList->n_children();
378 while ( nPos < nCount && ( !pEntry || pEntry->m_aURL.isEmpty() ) )
380 pEntry = reinterpret_cast<IndexEntry_Impl*>(m_xIndexList->get_id(++nPos).toInt64());
381 aEntryText = m_xIndexList->get_text(nPos);
384 if ( nOldPos != nPos )
385 m_xIndexEntry->set_text(aEntryText);
388 // class IndexTabPage_Impl -----------------------------------------------
389 IndexTabPage_Impl::IndexTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin)
390 : HelpTabPage_Impl(pParent, pIdxWin, "HelpIndexPage", "sfx/ui/helpindexpage.ui")
391 , m_xIndexEntry(m_xBuilder->weld_entry("termentry"))
392 , m_xIndexList(m_xBuilder->weld_tree_view("termlist"))
393 , m_xOpenBtn(m_xBuilder->weld_button("display"))
394 , aFactoryIdle("sfx2 appl IndexTabPage_Impl Factory")
395 , aAutoCompleteIdle("sfx2 appl IndexTabPage_Impl AutoComplete")
396 , aKeywordTimer("sfx2::IndexTabPage_Impl aKeywordTimer")
397 , bIsActivated(false)
398 , nRowHeight(m_xIndexList->get_height_rows(1))
399 , nAllHeight(0)
400 , nLastCharCode(0)
402 m_xIndexList->set_size_request(m_xIndexList->get_approximate_digit_width() * 30, -1);
404 m_xOpenBtn->connect_clicked(LINK(this, IndexTabPage_Impl, OpenHdl));
405 aFactoryIdle.SetInvokeHandler( LINK(this, IndexTabPage_Impl, IdleHdl ));
406 aAutoCompleteIdle.SetInvokeHandler( LINK(this, IndexTabPage_Impl, AutoCompleteHdl ));
407 aKeywordTimer.SetInvokeHandler( LINK( this, IndexTabPage_Impl, TimeoutHdl ) );
408 m_xIndexList->connect_row_activated(LINK(this, IndexTabPage_Impl, DoubleClickHdl));
409 m_xIndexList->connect_changed(LINK(this, IndexTabPage_Impl, TreeChangeHdl));
410 m_xIndexList->connect_custom_get_size(LINK(this, IndexTabPage_Impl, CustomGetSizeHdl));
411 m_xIndexList->connect_custom_render(LINK(this, IndexTabPage_Impl, CustomRenderHdl));
412 m_xIndexList->set_column_custom_renderer(0, true);
413 m_xIndexList->connect_size_allocate(LINK(this, IndexTabPage_Impl, ResizeHdl));
414 m_xIndexEntry->connect_key_press(LINK(this, IndexTabPage_Impl, KeyInputHdl));
415 m_xIndexEntry->connect_changed(LINK(this, IndexTabPage_Impl, EntryChangeHdl));
416 m_xIndexEntry->connect_activate(LINK(this, IndexTabPage_Impl, ActivateHdl));
419 IMPL_LINK(IndexTabPage_Impl, ResizeHdl, const Size&, rSize, void)
421 nAllHeight = rSize.Height();
424 IMPL_LINK_NOARG(IndexTabPage_Impl, CustomGetSizeHdl, weld::TreeView::get_size_args, Size)
426 return Size(m_xIndexList->get_size_request().Width(), nRowHeight);
429 IMPL_LINK(IndexTabPage_Impl, CustomRenderHdl, weld::TreeView::render_args, aPayload, void)
431 vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
432 const ::tools::Rectangle& rRect = std::get<1>(aPayload);
433 bool bSelected = std::get<2>(aPayload);
434 const OUString& rId = std::get<3>(aPayload);
436 rRenderContext.Push(vcl::PushFlags::TEXTCOLOR);
437 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
438 if (bSelected)
439 rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
440 else
441 rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor());
443 Point aPos(rRect.TopLeft());
444 aPos.AdjustY((rRect.GetHeight() - rRenderContext.GetTextHeight()) / 2);
446 int nIndex = m_xIndexList->find_id(rId);
447 OUString aEntry(m_xIndexList->get_text(nIndex));
449 IndexEntry_Impl* pEntry = reinterpret_cast<IndexEntry_Impl*>(rId.toInt64());
450 if (pEntry && pEntry->m_bSubEntry)
452 // indent sub entries
453 aPos.AdjustX(8);
454 sal_Int32 nPos = aEntry.indexOf(';');
455 rRenderContext.DrawText(aPos, (nPos !=-1) ? aEntry.copy(nPos + 1) : aEntry);
457 else
458 rRenderContext.DrawText(aPos, aEntry);
460 rRenderContext.Pop();
463 IMPL_LINK_NOARG(IndexTabPage_Impl, TreeChangeHdl, weld::TreeView&, void)
465 m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
468 IMPL_LINK_NOARG(IndexTabPage_Impl, EntryChangeHdl, weld::Entry&, void)
470 switch (nLastCharCode)
472 case css::awt::Key::DELETE_WORD_BACKWARD:
473 case css::awt::Key::DELETE_WORD_FORWARD:
474 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
475 case css::awt::Key::DELETE_TO_END_OF_LINE:
476 case KEY_BACKSPACE:
477 case KEY_DELETE:
478 break;
479 default:
480 aAutoCompleteIdle.Start();
481 break;
485 IMPL_LINK(IndexTabPage_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
487 const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
488 if (rKCode.GetModifier()) // only with no modifiers held
489 return false;
491 sal_uInt16 nCode = rKCode.GetCode();
493 if (nCode == KEY_UP || nCode == KEY_PAGEUP ||
494 nCode == KEY_DOWN || nCode == KEY_PAGEDOWN)
496 // disable_notify_events();
497 sal_Int32 nIndex = m_xIndexList->get_selected_index();
498 sal_Int32 nOrigIndex = nIndex;
499 sal_Int32 nCount = m_xIndexList->n_children();
500 if (nIndex == -1)
502 m_xIndexList->set_cursor(0);
503 m_xIndexList->select(0);
504 m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
506 else
508 if (nCode == KEY_UP)
509 --nIndex;
510 else if (nCode == KEY_DOWN)
511 ++nIndex;
512 else if (nCode == KEY_PAGEUP)
514 int nVisRows = nAllHeight / nRowHeight;
515 nIndex -= nVisRows;
517 else if (nCode == KEY_PAGEDOWN)
519 int nVisRows = nAllHeight / nRowHeight;
520 nIndex += nVisRows;
523 if (nIndex < 0)
524 nIndex = 0;
525 if (nIndex >= nCount)
526 nIndex = nCount - 1;
528 if (nIndex != nOrigIndex)
530 m_xIndexList->set_cursor(nIndex);
531 m_xIndexList->select(nIndex);
532 m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
535 // m_xIndexList->grab_focus();
536 // g_signal_emit_by_name(pWidget, "key-press-event", pEvent, &ret);
537 // m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
538 // m_xIndexEntry->grab_focus();
540 m_xIndexEntry->select_region(0, -1);
541 // enable_notify_events();
542 // m_bTreeChange = true;
543 // m_pEntry->fire_signal_changed();
544 // m_bTreeChange = false;
545 return true;
548 nLastCharCode = nCode;
549 return false;
552 IndexTabPage_Impl::~IndexTabPage_Impl()
554 ClearIndex();
557 namespace sfx2 {
559 typedef std::unordered_map< OUString, int > KeywordInfo;
562 void IndexTabPage_Impl::InitializeIndex()
564 weld::WaitObject aWaitCursor(m_pIdxWin->GetFrameWeld());
566 // By now more than 256 equal entries are not allowed
567 sal_Unicode append[256];
568 for(sal_Unicode & k : append)
569 k = ' ';
571 sfx2::KeywordInfo aInfo;
572 m_xIndexList->freeze();
576 OUStringBuffer aURL(HELP_URL);
577 aURL.append(sFactory);
578 AppendConfigToken(aURL, true);
580 Content aCnt( aURL.makeStringAndClear(), Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
581 css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties();
582 if ( xInfo->hasPropertyByName( PROPERTY_ANCHORREF ) )
584 css::uno::Sequence< OUString > aPropSeq{ PROPERTY_KEYWORDLIST, PROPERTY_KEYWORDREF,
585 PROPERTY_ANCHORREF, PROPERTY_TITLEREF };
587 // abi: use one possibly remote call only
588 css::uno::Sequence< css::uno::Any > aAnySeq =
589 aCnt.getPropertyValues( aPropSeq );
591 css::uno::Sequence< OUString > aKeywordList;
592 css::uno::Sequence< css::uno::Sequence< OUString > > aKeywordRefList;
593 css::uno::Sequence< css::uno::Sequence< OUString > > aAnchorRefList;
594 css::uno::Sequence< css::uno::Sequence< OUString > > aTitleRefList;
596 if ( ( aAnySeq[0] >>= aKeywordList ) && ( aAnySeq[1] >>= aKeywordRefList ) &&
597 ( aAnySeq[2] >>= aAnchorRefList ) && ( aAnySeq[3] >>= aTitleRefList ) )
599 int ndx,tmp;
600 OUString aIndex, aTempString;
601 OUStringBuffer aData( 128 ); // Capacity of up to 128 characters
602 sfx2::KeywordInfo::iterator it;
604 for ( int i = 0; i < aKeywordList.getLength(); ++i )
606 // abi: Do not copy, but use references
607 const OUString& aKeywordPair = aKeywordList[i];
608 DBG_ASSERT( !aKeywordPair.isEmpty(), "invalid help index" );
609 const css::uno::Sequence< OUString >& aRefList = aKeywordRefList[i];
610 const css::uno::Sequence< OUString >& aAnchorList = aAnchorRefList[i];
611 const css::uno::Sequence< OUString >& aTitleList = aTitleRefList[i];
613 DBG_ASSERT( aRefList.getLength() == aAnchorList.getLength(),"reference list and title list of different length" );
615 ndx = aKeywordPair.indexOf( ';' );
616 const bool insert = ndx != -1;
618 OUString sId;
620 if ( insert )
622 aTempString = aKeywordPair.copy( 0, ndx );
623 if ( aIndex != aTempString )
625 aIndex = aTempString;
626 it = aInfo.emplace(aTempString, 0).first;
627 sId = OUString::number(reinterpret_cast<sal_Int64>(new IndexEntry_Impl(OUString(), false)));
628 if ( (tmp = it->second++) != 0)
629 m_xIndexList->append(
630 sId, aTempString + std::u16string_view(append, tmp));
631 else
632 m_xIndexList->append(sId, aTempString);
635 else
636 aIndex.clear();
638 sal_uInt32 nRefListLen = aRefList.getLength();
640 DBG_ASSERT( aAnchorList.hasElements(), "*IndexTabPage_Impl::InitializeIndex(): AnchorList is empty!" );
641 DBG_ASSERT( nRefListLen, "*IndexTabPage_Impl::InitializeIndex(): RefList is empty!" );
643 if ( aAnchorList.hasElements() && nRefListLen )
645 if ( aAnchorList[0].getLength() > 0 )
647 aData.append( aRefList[0] ).append( '#' ).append( aAnchorList[0] );
648 sId = OUString::number(reinterpret_cast<sal_Int64>(new IndexEntry_Impl(aData.makeStringAndClear(), insert)));
650 else
651 sId = OUString::number(reinterpret_cast<sal_Int64>(new IndexEntry_Impl(aRefList[0], insert)));
654 // Assume the token is trimmed
655 it = aInfo.emplace(aKeywordPair, 0).first;
656 if ((tmp = it->second++) != 0)
657 m_xIndexList->append(sId, aKeywordPair + std::u16string_view(append, tmp));
658 else
659 m_xIndexList->append(sId, aKeywordPair);
661 for ( sal_uInt32 j = 1; j < nRefListLen ; ++j )
663 aData
664 .append( aKeywordPair )
665 .append( ' ' )
666 .append( '-' )
667 .append( ' ' )
668 .append( aTitleList[j] );
670 aTempString = aData.makeStringAndClear();
672 if ( aAnchorList[j].getLength() > 0 )
674 aData.append( aRefList[j] ).append( '#' ).append( aAnchorList[j] );
675 sId = OUString::number(reinterpret_cast<sal_Int64>(new IndexEntry_Impl(aData.makeStringAndClear(), insert)));
677 else
678 sId = OUString::number(reinterpret_cast<sal_Int64>(new IndexEntry_Impl(aRefList[j], insert)));
680 it = aInfo.emplace(aTempString, 0).first;
681 if ( (tmp = it->second++) != 0 )
682 m_xIndexList->append(
683 sId, aTempString + std::u16string_view(append, tmp));
684 else
685 m_xIndexList->append(sId, aTempString);
691 catch( Exception& )
693 TOOLS_WARN_EXCEPTION( "sfx.appl", "IndexTabPage_Impl::InitializeIndex(): unexpected exception" );
696 m_xIndexList->thaw();
698 if ( !sKeyword.isEmpty() )
699 aKeywordLink.Call( *this );
702 void IndexTabPage_Impl::ClearIndex()
704 const sal_Int32 nCount = m_xIndexList->n_children();
705 for ( sal_Int32 i = 0; i < nCount; ++i )
706 delete reinterpret_cast<IndexEntry_Impl*>(m_xIndexList->get_id(i).toInt64());
707 m_xIndexList->clear();
710 IMPL_LINK_NOARG(IndexTabPage_Impl, OpenHdl, weld::Button&, void)
712 aDoubleClickHdl.Call(nullptr);
715 IMPL_LINK_NOARG(IndexTabPage_Impl, ActivateHdl, weld::Entry&, bool)
717 aDoubleClickHdl.Call(nullptr);
718 return true;
721 IMPL_LINK_NOARG(IndexTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
723 aDoubleClickHdl.Call(nullptr);
724 return true;
727 IMPL_LINK_NOARG(IndexTabPage_Impl, IdleHdl, Timer*, void)
729 InitializeIndex();
732 int IndexTabPage_Impl::starts_with(const OUString& rStr, int nStartRow, bool bCaseSensitive)
734 const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
736 int nRet = nStartRow;
737 int nCount = m_xIndexList->n_children();
738 while (nRet < nCount)
740 OUString aStr(m_xIndexList->get_text(nRet));
741 const bool bMatch = !bCaseSensitive ? rI18nHelper.MatchString(rStr, aStr) : aStr.startsWith(rStr);
742 if (bMatch)
743 return nRet;
744 ++nRet;
747 return -1;
750 IMPL_LINK_NOARG(IndexTabPage_Impl, AutoCompleteHdl, Timer*, void)
752 OUString aStartText = m_xIndexEntry->get_text();
753 int nStartPos, nEndPos;
754 m_xIndexEntry->get_selection_bounds(nStartPos, nEndPos);
755 int nMaxSelection = std::max(nStartPos, nEndPos);
756 if (nMaxSelection != aStartText.getLength())
757 return;
759 int nActive = m_xIndexList->get_selected_index();
760 int nStart = nActive;
762 if (nStart == -1)
763 nStart = 0;
765 // Try match case insensitive from current position
766 int nPos = starts_with(aStartText, nStart, false);
767 if (nPos == -1 && nStart != 0)
769 // Try match case insensitive, but from start
770 nPos = starts_with(aStartText, 0, false);
773 if (nPos == -1)
775 // Try match case sensitive from current position
776 nPos = starts_with(aStartText, nStart, true);
777 if (nPos == -1 && nStart != 0)
779 // Try match case sensitive, but from start
780 nPos = starts_with(aStartText, 0, true);
784 if (nPos != -1)
786 m_xIndexList->set_cursor(nPos);
787 m_xIndexList->select(nPos);
788 OUString aText = m_xIndexList->get_text(nPos);
789 if (aText != aStartText)
790 m_xIndexEntry->set_text(aText);
791 m_xIndexEntry->select_region(aText.getLength(), aStartText.getLength());
795 IMPL_LINK( IndexTabPage_Impl, TimeoutHdl, Timer*, pTimer, void)
797 if(&aKeywordTimer == pTimer && !sKeyword.isEmpty())
798 aKeywordLink.Call(*this);
801 void IndexTabPage_Impl::Activate()
803 if ( !bIsActivated )
805 bIsActivated = true;
806 aFactoryIdle.Start();
810 void IndexTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
812 aDoubleClickHdl = rLink;
815 void IndexTabPage_Impl::SetFactory( const OUString& rFactory )
817 OUString sNewFactory( rFactory );
818 DBG_ASSERT( !sNewFactory.isEmpty(), "empty factory" );
819 bool bValid = m_pIdxWin->IsValidFactory( rFactory );
821 if ( sFactory.isEmpty() && !bValid )
823 sNewFactory = SfxHelp::GetDefaultHelpModule();
824 bValid = true;
827 if ( sNewFactory != sFactory && bValid )
829 sFactory = sNewFactory;
830 ClearIndex();
831 if ( bIsActivated )
832 aFactoryIdle.Start();
836 OUString IndexTabPage_Impl::GetSelectedEntry() const
838 OUString aRet;
839 IndexEntry_Impl* pEntry = reinterpret_cast<IndexEntry_Impl*>(m_xIndexList->get_id(m_xIndexList->find_text(m_xIndexEntry->get_text())).toInt64());
840 if (pEntry)
841 aRet = pEntry->m_aURL;
842 return aRet;
845 void IndexTabPage_Impl::SetKeyword( const OUString& rKeyword )
847 sKeyword = rKeyword;
849 if (m_xIndexList->n_children() > 0)
850 aKeywordTimer.Start();
851 else if ( !bIsActivated )
852 aFactoryIdle.Start();
856 bool IndexTabPage_Impl::HasKeyword() const
858 bool bRet = false;
859 if ( !sKeyword.isEmpty() )
861 sal_Int32 nPos = m_xIndexList->find_text( sKeyword );
862 bRet = nPos != -1;
865 return bRet;
869 bool IndexTabPage_Impl::HasKeywordIgnoreCase()
871 bool bRet = false;
872 if ( !sKeyword.isEmpty() )
874 sal_Int32 nEntries = m_xIndexList->n_children();
875 const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetLocaleI18nHelper();
876 for ( sal_Int32 n = 0; n < nEntries; n++)
878 const OUString sIndexItem {m_xIndexList->get_text(n)};
879 if (rI18nHelper.MatchString( sIndexItem, sKeyword ))
881 sKeyword = sIndexItem;
882 bRet = true;
887 return bRet;
890 void IndexTabPage_Impl::OpenKeyword()
892 if ( !sKeyword.isEmpty() )
894 m_xIndexEntry->set_text(sKeyword);
895 aDoubleClickHdl.Call(nullptr);
896 sKeyword.clear();
900 IMPL_LINK_NOARG(SearchTabPage_Impl, ActivateHdl, weld::ComboBox&, bool)
902 Search();
903 return true;
906 // class SearchTabPage_Impl ----------------------------------------------
908 SearchTabPage_Impl::SearchTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin)
909 : HelpTabPage_Impl(pParent, pIdxWin, "HelpSearchPage",
910 "sfx/ui/helpsearchpage.ui")
911 , m_xSearchED(m_xBuilder->weld_combo_box("search"))
912 , m_xSearchBtn(m_xBuilder->weld_button("find"))
913 , m_xFullWordsCB(m_xBuilder->weld_check_button("completewords"))
914 , m_xScopeCB(m_xBuilder->weld_check_button("headings"))
915 , m_xResultsLB(m_xBuilder->weld_tree_view("results"))
916 , m_xOpenBtn(m_xBuilder->weld_button("display"))
917 , xBreakIterator(vcl::unohelper::CreateBreakIterator())
919 m_xResultsLB->set_size_request(m_xResultsLB->get_approximate_digit_width() * 30,
920 m_xResultsLB->get_height_rows(15));
922 m_xSearchBtn->connect_clicked(LINK(this, SearchTabPage_Impl, ClickHdl));
923 m_xSearchED->connect_changed(LINK(this, SearchTabPage_Impl, ModifyHdl));
924 m_xSearchED->connect_entry_activate(LINK(this, SearchTabPage_Impl, ActivateHdl));
925 m_xOpenBtn->connect_clicked(LINK(this, SearchTabPage_Impl, OpenHdl));
926 m_xResultsLB->connect_row_activated(LINK(this, SearchTabPage_Impl, DoubleClickHdl));
928 SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE );
929 if ( aViewOpt.Exists() )
931 OUString aUserData;
932 Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
933 if ( aUserItem >>= aUserData )
935 sal_Int32 nIdx {0};
936 bool bChecked = aUserData.getToken(0, ';', nIdx).toInt32() == 1;
937 m_xFullWordsCB->set_active(bChecked);
938 bChecked = aUserData.getToken(0, ';', nIdx).toInt32() == 1;
939 m_xScopeCB->set_active(bChecked);
941 while ( nIdx > 0 )
943 m_xSearchED->append_text( INetURLObject::decode(
944 aUserData.getToken(0, ';', nIdx),
945 INetURLObject::DecodeMechanism::WithCharset ) );
950 ModifyHdl(*m_xSearchED);
953 SearchTabPage_Impl::~SearchTabPage_Impl()
955 SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE );
956 OUStringBuffer aUserData =
957 OUString::number(m_xFullWordsCB->get_active() ? 1 : 0) +
958 ";" +
959 OUString::number(m_xScopeCB->get_active() ? 1 : 0);
960 sal_Int32 nCount = std::min(m_xSearchED->get_count(), 10); // save only 10 entries
962 for ( sal_Int32 i = 0; i < nCount; ++i )
964 aUserData.append(";" +
965 INetURLObject::encode(
966 m_xSearchED->get_text(i),
967 INetURLObject::PART_UNO_PARAM_VALUE,
968 INetURLObject::EncodeMechanism::All ));
971 Any aUserItem = makeAny( aUserData.makeStringAndClear() );
972 aViewOpt.SetUserItem( USERITEM_NAME, aUserItem );
974 m_xSearchED.reset();
975 m_xSearchBtn.reset();
976 m_xFullWordsCB.reset();
977 m_xScopeCB.reset();
978 m_xResultsLB.reset();
979 m_xOpenBtn.reset();
982 void SearchTabPage_Impl::ClearSearchResults()
984 m_xResultsLB->clear();
987 void SearchTabPage_Impl::RememberSearchText( const OUString& rSearchText )
989 for (sal_Int32 i = 0, nEntryCount = m_xSearchED->get_count(); i < nEntryCount; ++i)
991 if (rSearchText == m_xSearchED->get_text(i))
993 m_xSearchED->remove(i);
994 break;
998 m_xSearchED->insert_text(0, rSearchText);
1001 IMPL_LINK_NOARG(SearchTabPage_Impl, ClickHdl, weld::Button&, void)
1003 Search();
1006 void SearchTabPage_Impl::Search()
1008 OUString aSearchText = comphelper::string::strip(m_xSearchED->get_active_text(), ' ');
1009 if ( aSearchText.isEmpty() )
1010 return;
1012 std::unique_ptr<weld::WaitObject> xWaitCursor(new weld::WaitObject(m_pIdxWin->GetFrameWeld()));
1013 ClearSearchResults();
1014 RememberSearchText( aSearchText );
1015 OUStringBuffer aSearchURL(HELP_URL);
1016 aSearchURL.append(aFactory);
1017 aSearchURL.append(HELP_SEARCH_TAG);
1018 if (!m_xFullWordsCB->get_active())
1019 aSearchText = sfx2::PrepareSearchString( aSearchText, xBreakIterator, true );
1020 aSearchURL.append(aSearchText);
1021 AppendConfigToken(aSearchURL, false);
1022 if (m_xScopeCB->get_active())
1023 aSearchURL.append("&Scope=Heading");
1024 std::vector< OUString > aFactories = SfxContentHelper::GetResultSet(aSearchURL.makeStringAndClear());
1025 for (const OUString & rRow : aFactories)
1027 sal_Int32 nIdx = 0;
1028 OUString aTitle = rRow.getToken(0, '\t', nIdx);
1029 OUString sURL(rRow.getToken(1, '\t', nIdx));
1030 m_xResultsLB->append(sURL, aTitle);
1032 xWaitCursor.reset();
1034 if ( aFactories.empty() )
1036 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xContainer.get(),
1037 VclMessageType::Info, VclButtonsType::Ok,
1038 SfxResId(STR_INFO_NOSEARCHRESULTS)));
1039 xBox->run();
1043 IMPL_LINK_NOARG(SearchTabPage_Impl, OpenHdl, weld::Button&, void)
1045 aDoubleClickHdl.Call(nullptr);
1048 IMPL_LINK(SearchTabPage_Impl, ModifyHdl, weld::ComboBox&, rComboBox, void)
1050 OUString aSearchText = comphelper::string::strip(m_xSearchED->get_active_text(), ' ');
1051 m_xSearchBtn->set_sensitive(!aSearchText.isEmpty());
1053 if (rComboBox.changed_by_direct_pick())
1054 Search();
1057 IMPL_LINK_NOARG(SearchTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
1059 aDoubleClickHdl.Call(nullptr);
1060 return true;
1063 void SearchTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
1065 aDoubleClickHdl = rLink;
1068 OUString SearchTabPage_Impl::GetSelectedEntry() const
1070 return m_xResultsLB->get_selected_id();
1073 void SearchTabPage_Impl::ClearPage()
1075 ClearSearchResults();
1076 m_xSearchED->set_entry_text(OUString());
1079 bool SearchTabPage_Impl::OpenKeyword( const OUString& rKeyword )
1081 bool bRet = false;
1082 m_xSearchED->set_entry_text(rKeyword);
1083 Search();
1084 if (m_xResultsLB->n_children() > 0)
1086 // found keyword -> open it
1087 m_xResultsLB->select(0);
1088 OpenHdl(*m_xOpenBtn);
1089 bRet = true;
1091 return bRet;
1094 // class BookmarksTabPage_Impl -------------------------------------------
1096 void BookmarksTabPage_Impl::DoAction(std::string_view rAction)
1098 if (rAction == "display")
1099 aDoubleClickHdl.Call(nullptr);
1100 else if (rAction == "rename")
1102 sal_Int32 nPos = m_xBookmarksBox->get_selected_index();
1103 if (nPos != -1)
1105 SfxAddHelpBookmarkDialog_Impl aDlg(m_xBookmarksBox.get(), true);
1106 aDlg.SetTitle(m_xBookmarksBox->get_text(nPos));
1107 if (aDlg.run() == RET_OK)
1109 OUString sURL = m_xBookmarksBox->get_id(nPos);
1110 m_xBookmarksBox->remove(nPos);
1111 m_xBookmarksBox->append(sURL, aDlg.GetTitle(), SvFileInformationManager::GetImageId(INetURLObject(IMAGE_URL+INetURLObject(sURL).GetHost())));
1112 m_xBookmarksBox->select(m_xBookmarksBox->n_children() - 1);
1116 else if (rAction == "delete")
1118 sal_Int32 nPos = m_xBookmarksBox->get_selected_index();
1119 if (nPos != -1)
1121 m_xBookmarksBox->remove(nPos);
1122 const sal_Int32 nCount = m_xBookmarksBox->n_children();
1123 if (nCount)
1125 if (nPos >= nCount)
1126 nPos = nCount - 1;
1127 m_xBookmarksBox->select(nPos);
1133 IMPL_LINK(BookmarksTabPage_Impl, CommandHdl, const CommandEvent&, rCEvt, bool)
1135 if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
1136 return false;
1138 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xBookmarksBox.get(), "sfx/ui/bookmarkmenu.ui"));
1139 std::unique_ptr<weld::Menu> xMenu = xBuilder->weld_menu("menu");
1141 OString sIdent = xMenu->popup_at_rect(m_xBookmarksBox.get(), ::tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
1142 if (!sIdent.isEmpty())
1143 DoAction(sIdent);
1144 return true;
1147 IMPL_LINK(BookmarksTabPage_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
1149 bool bHandled = false;
1150 sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
1151 if (KEY_DELETE == nCode && m_xBookmarksBox->n_children() > 0)
1153 DoAction("delete");
1154 bHandled = true;
1156 return bHandled;
1159 // class BookmarksTabPage_Impl -------------------------------------------
1160 BookmarksTabPage_Impl::BookmarksTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* _pIdxWin)
1161 : HelpTabPage_Impl(pParent, _pIdxWin, "HelpBookmarkPage",
1162 "sfx/ui/helpbookmarkpage.ui")
1163 , m_xBookmarksBox(m_xBuilder->weld_tree_view("bookmarks"))
1164 , m_xBookmarksPB(m_xBuilder->weld_button("display"))
1166 m_xBookmarksBox->set_size_request(m_xBookmarksBox->get_approximate_digit_width() * 30,
1167 m_xBookmarksBox->get_height_rows(20));
1169 m_xBookmarksPB->connect_clicked( LINK(this, BookmarksTabPage_Impl, OpenHdl));
1170 m_xBookmarksBox->connect_row_activated(LINK(this, BookmarksTabPage_Impl, DoubleClickHdl));
1171 m_xBookmarksBox->connect_popup_menu(LINK(this, BookmarksTabPage_Impl, CommandHdl));
1172 m_xBookmarksBox->connect_key_press(LINK(this, BookmarksTabPage_Impl, KeyInputHdl));
1174 // load bookmarks from configuration
1175 const std::vector< SvtHistoryOptions::HistoryItem > aBookmarkSeq = SvtHistoryOptions::GetList( EHistoryType::HelpBookmarks );
1176 for ( const auto& rBookmark : aBookmarkSeq )
1178 AddBookmarks( rBookmark.sTitle, rBookmark.sURL );
1182 BookmarksTabPage_Impl::~BookmarksTabPage_Impl()
1184 // save bookmarks to configuration
1185 SvtHistoryOptions::Clear( EHistoryType::HelpBookmarks );
1186 const sal_Int32 nCount = m_xBookmarksBox->n_children();
1187 for (sal_Int32 i = 0; i < nCount; ++i)
1188 SvtHistoryOptions::AppendItem(EHistoryType::HelpBookmarks, m_xBookmarksBox->get_id(i), "", m_xBookmarksBox->get_text(i), std::nullopt);
1190 m_xBookmarksBox.reset();
1191 m_xBookmarksPB.reset();
1194 IMPL_LINK_NOARG(BookmarksTabPage_Impl, OpenHdl, weld::Button&, void)
1196 aDoubleClickHdl.Call(nullptr);
1199 IMPL_LINK_NOARG(BookmarksTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
1201 aDoubleClickHdl.Call(nullptr);
1202 return true;
1205 void BookmarksTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
1207 aDoubleClickHdl = rLink;
1210 OUString BookmarksTabPage_Impl::GetSelectedEntry() const
1212 return m_xBookmarksBox->get_selected_id();
1215 void BookmarksTabPage_Impl::AddBookmarks(const OUString& rTitle, const OUString& rURL)
1217 const OUString aImageURL {IMAGE_URL + INetURLObject(rURL).GetHost()};
1218 m_xBookmarksBox->append(rURL, rTitle, SvFileInformationManager::GetImageId(INetURLObject(aImageURL)));
1221 OUString SfxHelpWindow_Impl::buildHelpURL(std::u16string_view sFactory ,
1222 std::u16string_view sContent ,
1223 std::u16string_view sAnchor)
1225 OUStringBuffer sHelpURL(256);
1226 sHelpURL.append(HELP_URL);
1227 sHelpURL.append(sFactory);
1228 sHelpURL.append(sContent);
1229 AppendConfigToken(sHelpURL, true/*bUseQuestionMark*/);
1230 if (!sAnchor.empty())
1231 sHelpURL.append(sAnchor);
1232 return sHelpURL.makeStringAndClear();
1235 void SfxHelpWindow_Impl::loadHelpContent(const OUString& sHelpURL, bool bAddToHistory)
1237 Reference< XComponentLoader > xLoader(getTextFrame(), UNO_QUERY);
1238 if (!xLoader.is())
1239 return;
1241 // If a print job runs do not open a new page
1242 Reference< XFrame2 > xTextFrame = pTextWin->getFrame();
1243 Reference< XController > xTextController ;
1244 if (xTextFrame.is())
1245 xTextController = xTextFrame->getController ();
1246 if ( xTextController.is() && !xTextController->suspend( true ) )
1248 xTextController->suspend( false );
1249 return;
1252 // save url to history
1253 if (bAddToHistory)
1254 pHelpInterceptor->addURL(sHelpURL);
1256 if ( !IsWait() )
1257 EnterWait();
1258 bool bSuccess = false;
1259 // TODO implement locale fallback ... see below while(true)
1263 Reference< XComponent > xContent = xLoader->loadComponentFromURL(sHelpURL, "_self", 0, Sequence< PropertyValue >());
1264 if (xContent.is())
1266 bSuccess = true;
1269 catch(const RuntimeException&)
1270 { throw; }
1271 catch(const Exception&)
1272 { /*break;*/ }
1274 /* TODO try next locale ...
1275 no further locale available? => break loop and show error page
1278 openDone(sHelpURL, bSuccess);
1279 if ( IsWait() )
1280 LeaveWait();
1283 IMPL_LINK(SfxHelpIndexWindow_Impl, ActivatePageHdl, const OString&, rPage, void)
1285 GetPage(rPage)->Activate();
1288 SfxHelpIndexWindow_Impl::SfxHelpIndexWindow_Impl(SfxHelpWindow_Impl* _pParent, weld::Container* pContainer)
1289 : m_xBuilder(Application::CreateBuilder(pContainer, "sfx/ui/helpcontrol.ui"))
1290 , m_xContainer(m_xBuilder->weld_container("HelpControl"))
1291 , m_xActiveLB(m_xBuilder->weld_combo_box("active"))
1292 , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol"))
1293 , aIdle("sfx2 appl SfxHelpIndexWindow_Impl")
1294 , aIndexKeywordLink(LINK(this, SfxHelpIndexWindow_Impl, KeywordHdl))
1295 , pParentWin(_pParent)
1296 , bIsInitDone(false)
1298 // create the pages
1299 GetContentPage();
1300 GetIndexPage();
1301 GetSearchPage();
1302 GetBookmarksPage();
1304 OString sPageId("index");
1305 SvtViewOptions aViewOpt( EViewType::TabDialog, CONFIGNAME_INDEXWIN );
1306 if ( aViewOpt.Exists() )
1307 sPageId = aViewOpt.GetPageID();
1308 m_xTabCtrl->set_current_page(sPageId);
1309 ActivatePageHdl(sPageId);
1310 m_xActiveLB->connect_changed(LINK(this, SfxHelpIndexWindow_Impl, SelectHdl));
1312 m_xTabCtrl->connect_enter_page(LINK(this, SfxHelpIndexWindow_Impl, ActivatePageHdl));
1314 aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, InitHdl ) );
1315 aIdle.Start();
1317 m_xContainer->show();
1320 SfxHelpIndexWindow_Impl::~SfxHelpIndexWindow_Impl()
1322 SvtViewOptions aViewOpt(EViewType::TabDialog, CONFIGNAME_INDEXWIN);
1323 aViewOpt.SetPageID(m_xTabCtrl->get_current_page_ident());
1325 xCPage.reset();
1326 xIPage.reset();
1327 xSPage.reset();
1328 xBPage.reset();
1331 void SfxHelpIndexWindow_Impl::Initialize()
1333 OUStringBuffer aHelpURL(HELP_URL);
1334 AppendConfigToken(aHelpURL, true);
1335 std::vector<OUString> aFactories = SfxContentHelper::GetResultSet(aHelpURL.makeStringAndClear());
1336 for (const OUString & rRow : aFactories)
1338 sal_Int32 nIdx = 0;
1339 OUString aTitle = rRow.getToken( 0, '\t', nIdx ); // token 0
1340 OUString aURL = rRow.getToken( 1, '\t', nIdx ); // token 2
1341 OUString aFactory(INetURLObject(aURL).GetHost());
1342 m_xActiveLB->append(aFactory, aTitle);
1345 if (m_xActiveLB->get_active() == -1)
1346 SetActiveFactory();
1349 void SfxHelpIndexWindow_Impl::SetActiveFactory()
1351 DBG_ASSERT( xIPage, "index page not initialized" );
1352 if (!bIsInitDone && !m_xActiveLB->get_count())
1354 aIdle.Stop();
1355 InitHdl( nullptr );
1358 for (sal_Int32 i = 0, nEntryCount = m_xActiveLB->get_count(); i < nEntryCount; ++i)
1360 OUString aFactory = m_xActiveLB->get_id(i);
1361 aFactory = aFactory.toAsciiLowerCase();
1362 if (aFactory == xIPage->GetFactory())
1364 if (m_xActiveLB->get_active() != i)
1366 m_xActiveLB->set_active(i);
1367 aSelectFactoryLink.Call(nullptr);
1369 break;
1374 HelpTabPage_Impl* SfxHelpIndexWindow_Impl::GetPage(std::string_view rName)
1376 HelpTabPage_Impl* pPage = nullptr;
1378 if (rName == "contents")
1379 pPage = GetContentPage();
1380 else if (rName == "index")
1381 pPage = GetIndexPage();
1382 else if (rName == "find")
1383 pPage = GetSearchPage();
1384 else if (rName == "bookmarks")
1385 pPage = GetBookmarksPage();
1387 assert(pPage && "SfxHelpIndexWindow_Impl::GetCurrentPage(): no current page");
1389 return pPage;
1392 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectHdl, weld::ComboBox&, void)
1394 aIdle.Start();
1397 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, InitHdl, Timer *, void)
1399 bIsInitDone = true;
1400 Initialize();
1402 // now use the timer for selection
1403 aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, SelectFactoryHdl ) );
1404 aIdle.SetPriority( TaskPriority::LOWEST );
1407 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectFactoryHdl, Timer *, void)
1409 OUString aFactory = m_xActiveLB->get_active_id();
1410 if (!aFactory.isEmpty())
1412 SetFactory(aFactory.toAsciiLowerCase(), false);
1413 aSelectFactoryLink.Call(this);
1417 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, KeywordHdl, IndexTabPage_Impl&, void)
1419 // keyword found on index?
1420 bool bIndex = xIPage->HasKeyword();
1422 if( !bIndex)
1423 bIndex = xIPage->HasKeywordIgnoreCase();
1424 // then set index or search page as current.
1425 OString sPageId = bIndex ? "index" : "find";
1426 if (sPageId != m_xTabCtrl->get_current_page_ident())
1427 m_xTabCtrl->set_current_page(sPageId);
1429 // at last we open the keyword
1430 if ( bIndex )
1431 xIPage->OpenKeyword();
1432 else if ( !xSPage->OpenKeyword( sKeyword ) )
1433 pParentWin->ShowStartPage();
1436 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, IndexTabPageDoubleClickHdl, LinkParamNone*, void)
1438 aPageDoubleClickLink.Call(nullptr);
1441 void SfxHelpIndexWindow_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
1443 aPageDoubleClickLink = rLink;
1446 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, ContentTabPageDoubleClickHdl, LinkParamNone*, void)
1448 aPageDoubleClickLink.Call(nullptr);
1451 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, TabPageDoubleClickHdl, LinkParamNone*, void)
1453 aPageDoubleClickLink.Call(nullptr);
1456 void SfxHelpIndexWindow_Impl::SetFactory( const OUString& rFactory, bool bActive )
1458 if ( !rFactory.isEmpty() )
1460 GetIndexPage()->SetFactory( rFactory );
1461 // the index page made a check if rFactory is valid,
1462 // so the index page always returns a valid factory
1463 GetSearchPage()->SetFactory( GetIndexPage()->GetFactory() );
1464 if ( bActive )
1465 SetActiveFactory();
1469 OUString SfxHelpIndexWindow_Impl::GetSelectedEntry() const
1471 OUString sRet;
1473 OString sName(m_xTabCtrl->get_current_page_ident());
1475 if (sName == "contents")
1477 sRet = xCPage->GetSelectedEntry();
1479 else if (sName == "index")
1481 sRet = xIPage->GetSelectedEntry();
1483 else if (sName == "find")
1485 sRet = xSPage->GetSelectedEntry();
1487 else if (sName == "bookmarks")
1489 sRet = xBPage->GetSelectedEntry();
1492 return sRet;
1495 void SfxHelpIndexWindow_Impl::AddBookmarks( const OUString& rTitle, const OUString& rURL )
1497 GetBookmarksPage()->AddBookmarks( rTitle, rURL );
1500 bool SfxHelpIndexWindow_Impl::IsValidFactory( std::u16string_view _rFactory )
1502 bool bValid = false;
1503 for (sal_Int32 i = 0, nEntryCount = m_xActiveLB->get_count(); i < nEntryCount; ++i)
1505 OUString aFactory = m_xActiveLB->get_id(i);
1506 if (aFactory == _rFactory)
1508 bValid = true;
1509 break;
1512 return bValid;
1515 void SfxHelpIndexWindow_Impl::ClearSearchPage()
1517 if ( xSPage )
1518 xSPage->ClearPage();
1521 void SfxHelpIndexWindow_Impl::GrabFocusBack()
1523 OString sName(m_xTabCtrl->get_current_page_ident());
1525 if (sName == "contents" && xCPage)
1526 xCPage->SetFocusOnBox();
1527 else if (sName == "index" && xIPage)
1528 xIPage->SetFocusOnBox();
1529 else if (sName == "find" && xSPage)
1530 xSPage->SetFocusOnBox();
1531 else if (sName == "bookmarks" && xBPage)
1532 xBPage->SetFocusOnBox();
1535 bool SfxHelpIndexWindow_Impl::HasFocusOnEdit() const
1537 bool bRet = false;
1538 OString sName(m_xTabCtrl->get_current_page_ident());
1539 if (sName == "index" && xIPage)
1540 bRet = xIPage->HasFocusOnEdit();
1541 else if (sName == "find" && xSPage)
1542 bRet = xSPage->HasFocusOnEdit();
1543 return bRet;
1546 OUString SfxHelpIndexWindow_Impl::GetSearchText() const
1548 OUString sRet;
1549 OString sName(m_xTabCtrl->get_current_page_ident());
1550 if (sName == "find" && xSPage)
1551 sRet = xSPage->GetSearchText();
1552 return sRet;
1555 bool SfxHelpIndexWindow_Impl::IsFullWordSearch() const
1557 bool bRet = false;
1558 OString sName(m_xTabCtrl->get_current_page_ident());
1559 if (sName == "find" && xSPage)
1560 bRet = xSPage->IsFullWordSearch();
1561 return bRet;
1564 void SfxHelpIndexWindow_Impl::OpenKeyword( const OUString& rKeyword )
1566 sKeyword = rKeyword;
1567 DBG_ASSERT( xIPage, "invalid index page" );
1568 xIPage->SetKeyword( sKeyword );
1571 void SfxHelpIndexWindow_Impl::SelectExecutableEntry()
1573 OString sName(m_xTabCtrl->get_current_page_ident());
1574 if (sName == "index" && xIPage )
1575 xIPage->SelectExecutableEntry();
1578 weld::Window* SfxHelpIndexWindow_Impl::GetFrameWeld() const
1580 return pParentWin->GetFrameWeld();
1583 // class TextWin_Impl ----------------------------------------------------
1584 TextWin_Impl::TextWin_Impl( vcl::Window* p ) : DockingWindow( p, 0 )
1588 bool TextWin_Impl::EventNotify( NotifyEvent& rNEvt )
1590 if( ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT ) && rNEvt.GetKeyEvent()->GetKeyCode().GetCode() == KEY_TAB )
1591 return GetParent()->EventNotify( rNEvt );
1592 else
1593 return DockingWindow::EventNotify( rNEvt );
1597 // remove docking area acceptor from layoutmanager, so it will not layout anything further .-)
1598 static void lcl_disableLayoutOfFrame(const Reference< XFrame2 >& xFrame)
1600 xFrame->setLayoutManager( Reference< XLayoutManager >() );
1603 // class SfxHelpTextWindow_Impl ------------------------------------------
1605 SfxHelpTextWindow_Impl::SfxHelpTextWindow_Impl(SfxHelpWindow_Impl* pHelpWin, weld::Builder& rBuilder, vcl::Window* pParent) :
1607 Window( pParent, WB_CLIPCHILDREN | WB_TABSTOP | WB_DIALOGCONTROL ),
1609 xToolBox ( rBuilder.weld_toolbar("toolbar") ),
1610 xOnStartupCB ( rBuilder.weld_check_button("checkbutton") ),
1611 xMenu ( rBuilder.weld_menu("menu") ),
1612 aSelectIdle ( "sfx2 appl SfxHelpTextWindow_Impl Select" ),
1613 aIndexOnImage ( BMP_HELP_TOOLBOX_INDEX_ON ),
1614 aIndexOffImage ( BMP_HELP_TOOLBOX_INDEX_OFF ),
1615 aIndexOnText ( SfxResId( STR_HELP_BUTTON_INDEX_ON ) ),
1616 aIndexOffText ( SfxResId( STR_HELP_BUTTON_INDEX_OFF ) ),
1617 aOnStartupText ( SfxResId( RID_HELP_ONSTARTUP_TEXT ) ),
1618 xHelpWin ( pHelpWin ),
1619 pTextWin ( VclPtr<TextWin_Impl>::Create( this ) ),
1620 bIsDebug ( false ),
1621 bIsIndexOn ( false ),
1622 bIsInClose ( false ),
1623 bIsFullWordSearch ( false )
1625 xFrame = Frame::create( ::comphelper::getProcessComponentContext() );
1626 xFrame->initialize( VCLUnoHelper::GetInterface ( pTextWin ) );
1627 xFrame->setName( "OFFICE_HELP" );
1628 lcl_disableLayoutOfFrame(xFrame);
1630 xToolBox->set_help_id(HID_HELP_TOOLBOX);
1632 xToolBox->set_item_tooltip_text("index", aIndexOffText );
1633 xToolBox->set_item_help_id("index", HID_HELP_TOOLBOXITEM_INDEX);
1634 xToolBox->set_item_help_id("backward", HID_HELP_TOOLBOXITEM_BACKWARD);
1635 xToolBox->set_item_help_id("forward", HID_HELP_TOOLBOXITEM_FORWARD);
1636 xToolBox->set_item_help_id("start", HID_HELP_TOOLBOXITEM_START);
1637 xToolBox->set_item_help_id("print", HID_HELP_TOOLBOXITEM_PRINT);
1638 xToolBox->set_item_help_id("bookmarks", HID_HELP_TOOLBOXITEM_BOOKMARKS );
1639 xToolBox->set_item_help_id("searchdialog", HID_HELP_TOOLBOXITEM_SEARCHDIALOG);
1641 InitToolBoxImages();
1642 InitOnStartupBox();
1643 xOnStartupCB->connect_toggled(LINK(this, SfxHelpTextWindow_Impl, CheckHdl));
1645 aSelectIdle.SetInvokeHandler( LINK( this, SfxHelpTextWindow_Impl, SelectHdl ) );
1646 aSelectIdle.SetPriority( TaskPriority::LOWEST );
1648 char* pEnv = getenv( "help_debug" );
1649 if ( pEnv )
1650 bIsDebug = true;
1652 SvtMiscOptions().AddListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) );
1655 SfxHelpTextWindow_Impl::~SfxHelpTextWindow_Impl()
1657 disposeOnce();
1660 void SfxHelpTextWindow_Impl::dispose()
1662 bIsInClose = true;
1663 SvtMiscOptions().RemoveListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) );
1664 m_xSrchDlg.reset();
1665 xToolBox.reset();
1666 xOnStartupCB.reset();
1667 xHelpWin.clear();
1668 pTextWin.disposeAndClear();
1669 vcl::Window::dispose();
1672 bool SfxHelpTextWindow_Impl::HasSelection() const
1674 // is there any selection in the text and not only a cursor?
1675 bool bRet = false;
1676 Reference < XTextRange > xRange = getCursor();
1677 if ( xRange.is() )
1679 Reference < XText > xText = xRange->getText();
1680 Reference < XTextCursor > xCursor = xText->createTextCursorByRange( xRange );
1681 bRet = !xCursor->isCollapsed();
1684 return bRet;
1687 void SfxHelpTextWindow_Impl::InitToolBoxImages()
1689 xToolBox->set_item_icon_name("index", bIsIndexOn ? aIndexOffImage : aIndexOnImage);
1692 void SfxHelpTextWindow_Impl::InitOnStartupBox()
1694 sCurrentFactory = SfxHelp::GetCurrentModuleIdentifier();
1696 Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
1697 const OUString sPath { PATH_OFFICE_FACTORIES + sCurrentFactory };
1699 // Attention: This check boy knows two states:
1700 // 1) Reading of the config key fails with an exception or by getting an empty Any (!) => check box must be hidden
1701 // 2) We read sal_True/sal_False => check box must be shown and enabled/disabled
1703 bool bHideBox = true;
1704 bool bHelpAtStartup = false;
1707 xConfiguration = ConfigurationHelper::openConfig(
1708 xContext, PACKAGE_SETUP, EConfigurationModes::Standard );
1709 if ( xConfiguration.is() )
1711 Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, KEY_HELP_ON_OPEN );
1712 if (aAny >>= bHelpAtStartup)
1713 bHideBox = false;
1716 catch( Exception& )
1718 bHideBox = true;
1721 if ( bHideBox )
1722 xOnStartupCB->hide();
1723 else
1725 // detect module name
1726 OUString sModuleName;
1728 if ( xConfiguration.is() )
1730 OUString sTemp;
1733 Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, KEY_UI_NAME );
1734 aAny >>= sTemp;
1736 catch( Exception const & )
1738 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::InitOnStartupBox()" );
1740 sModuleName = sTemp;
1743 if ( !sModuleName.isEmpty() )
1745 // set module name in checkbox text
1746 xOnStartupCB->set_label(aOnStartupText.replaceFirst("%MODULENAME", sModuleName));
1747 // and show it
1748 xOnStartupCB->show();
1749 // set check state
1750 xOnStartupCB->set_active(bHelpAtStartup);
1751 xOnStartupCB->save_state();
1756 Reference< XBreakIterator > const & SfxHelpTextWindow_Impl::GetBreakIterator()
1758 if ( !xBreakIterator.is() )
1759 xBreakIterator = vcl::unohelper::CreateBreakIterator();
1760 DBG_ASSERT( xBreakIterator.is(), "Could not create BreakIterator" );
1761 return xBreakIterator;
1764 Reference< XTextRange > SfxHelpTextWindow_Impl::getCursor() const
1766 // return the current cursor
1767 Reference< XTextRange > xCursor;
1771 Reference < XSelectionSupplier > xSelSup( xFrame->getController(), UNO_QUERY );
1772 if ( xSelSup.is() )
1774 Any aAny = xSelSup->getSelection();
1775 Reference < XIndexAccess > xSelection;
1776 if ( aAny >>= xSelection )
1778 if ( xSelection->getCount() == 1 )
1780 aAny = xSelection->getByIndex(0);
1781 aAny >>= xCursor;
1786 catch( Exception& )
1788 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::getCursor(): unexpected exception" );
1791 return xCursor;
1795 bool SfxHelpTextWindow_Impl::isHandledKey( const vcl::KeyCode& _rKeyCode )
1797 bool bRet = false;
1798 sal_uInt16 nCode = _rKeyCode.GetCode();
1800 // the keys <CTRL><A> (select all), <CTRL><C> (copy),
1801 // <CTRL><F> (find), <CTRL><P> (print) and <CTRL><W> (close window)
1802 // were handled in help
1803 if ( _rKeyCode.IsMod1() &&
1804 ( KEY_A == nCode || KEY_C == nCode || KEY_F == nCode || KEY_P == nCode || KEY_W == nCode ) )
1806 if ( KEY_F == nCode )
1807 DoSearch();
1808 else
1809 bRet = true;
1812 return bRet;
1816 IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, SelectHdl, Timer *, void)
1820 // select the words, which are equal to the search text of the search page
1821 Reference < XController > xController = xFrame->getController();
1822 if ( xController.is() )
1824 // get document
1825 Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY );
1826 if ( xSearchable.is() )
1828 // create descriptor, set string and find all words
1829 Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor();
1830 xSrchDesc->setPropertyValue( "SearchRegularExpression", makeAny( true ) );
1831 if ( bIsFullWordSearch )
1832 xSrchDesc->setPropertyValue( "SearchWords", makeAny( true ) );
1834 xSrchDesc->setSearchString( sfx2::PrepareSearchString( aSearchText, GetBreakIterator(), false ) );
1835 Reference< XIndexAccess > xSelection = xSearchable->findAll( xSrchDesc );
1837 // then select all found words
1838 Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY );
1839 if ( xSelectionSup.is() )
1841 xSelectionSup->select( Any(xSelection) );
1846 catch( Exception& )
1848 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" );
1853 IMPL_LINK_NOARG( SfxHelpTextWindow_Impl, NotifyHdl, LinkParamNone*, void )
1855 InitToolBoxImages();
1856 Resize();
1859 IMPL_LINK( SfxHelpTextWindow_Impl, FindHdl, sfx2::SearchDialog&, rDlg, void )
1861 FindHdl(&rDlg);
1863 void SfxHelpTextWindow_Impl::FindHdl(sfx2::SearchDialog* pDlg)
1865 bool bWrapAround = ( nullptr == pDlg );
1866 if ( bWrapAround )
1867 pDlg = m_xSrchDlg.get();
1868 DBG_ASSERT( pDlg, "invalid search dialog" );
1871 // select the words, which are equal to the search text of the search page
1872 Reference < XController > xController = xFrame->getController();
1873 if ( xController.is() )
1875 // get document
1876 Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY );
1877 if ( xSearchable.is() )
1879 // create descriptor, set string and find all words
1880 Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor();
1881 xSrchDesc->setPropertyValue( "SearchWords", makeAny(pDlg->IsOnlyWholeWords()) );
1882 xSrchDesc->setPropertyValue( "SearchCaseSensitive", makeAny(pDlg->IsMarchCase()) );
1883 xSrchDesc->setPropertyValue( "SearchBackwards", makeAny(pDlg->IsSearchBackwards()) );
1884 xSrchDesc->setSearchString( pDlg->GetSearchText() );
1885 Reference< XInterface > xSelection;
1886 Reference< XTextRange > xCursor = getCursor();
1888 if ( xCursor.is() )
1890 if ( pDlg->IsSearchBackwards() )
1891 xCursor = xCursor->getStart();
1892 xSelection = xSearchable->findNext( xCursor, xSrchDesc );
1894 else
1895 xSelection = xSearchable->findFirst( xSrchDesc );
1897 // then select the found word
1898 if ( xSelection.is() )
1900 Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY );
1901 if ( xSelectionSup.is() )
1903 xSelectionSup->select( Any(xSelection) );
1906 else if ( pDlg->IsWrapAround() && !bWrapAround )
1908 Reference < text::XTextViewCursorSupplier > xCrsrSupp( xController, uno::UNO_QUERY );
1909 Reference < text::XTextViewCursor > xTVCrsr = xCrsrSupp->getViewCursor();
1910 if ( xTVCrsr.is() )
1912 Reference < text::XTextDocument > xDoc( xController->getModel(), uno::UNO_QUERY );
1913 Reference < text::XText > xText = xDoc->getText();
1914 if ( xText.is() )
1916 if ( pDlg->IsSearchBackwards() )
1917 xTVCrsr->gotoRange( xText->getEnd(), false );
1918 else
1919 xTVCrsr->gotoRange( xText->getStart(), false );
1920 FindHdl( nullptr );
1924 else
1926 assert(m_xSrchDlg && "no search dialog");
1927 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xSrchDlg->getDialog(),
1928 VclMessageType::Info, VclButtonsType::Ok, SfxResId(STR_INFO_NOSEARCHTEXTFOUND)));
1929 xBox->run();
1930 m_xSrchDlg->SetFocusOnEdit();
1935 catch( Exception& )
1937 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" );
1941 IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, CloseHdl, LinkParamNone*, void)
1943 m_xSrchDlg.reset();
1946 IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, CheckHdl, weld::Toggleable&, void)
1948 if ( !xConfiguration.is() )
1949 return;
1951 bool bChecked = xOnStartupCB->get_active();
1954 ConfigurationHelper::writeRelativeKey(
1955 xConfiguration, PATH_OFFICE_FACTORIES + sCurrentFactory, KEY_HELP_ON_OPEN, makeAny( bChecked ) );
1956 ConfigurationHelper::flush( xConfiguration );
1958 catch( Exception const & )
1960 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::CheckHdl()" );
1964 void SfxHelpTextWindow_Impl::Resize()
1966 Size aSize = GetOutputSizePixel();
1967 pTextWin->SetPosSizePixel( Point(0, 0), aSize );
1970 bool SfxHelpTextWindow_Impl::PreNotify( NotifyEvent& rNEvt )
1972 bool bDone = false;
1973 MouseNotifyEvent nType = rNEvt.GetType();
1974 if ( MouseNotifyEvent::COMMAND == nType && rNEvt.GetCommandEvent() )
1976 const CommandEvent* pCmdEvt = rNEvt.GetCommandEvent();
1977 vcl::Window* pCmdWin = rNEvt.GetWindow();
1979 if ( pCmdEvt->GetCommand() == CommandEventId::ContextMenu && pCmdWin != this )
1981 Point aPos;
1982 if ( pCmdEvt->IsMouseEvent() )
1983 aPos = pCmdEvt->GetMousePosPixel();
1984 else
1985 aPos = Point( pTextWin->GetPosPixel().X() + 20, 20 );
1987 xMenu->clear();
1989 if (bIsIndexOn)
1990 xMenu->append("index", aIndexOffText, BMP_HELP_TOOLBOX_INDEX_OFF);
1991 else
1992 xMenu->append("index", aIndexOnText, BMP_HELP_TOOLBOX_INDEX_ON);
1994 xMenu->append_separator("separator1");
1995 xMenu->append("backward", SfxResId(STR_HELP_BUTTON_PREV), BMP_HELP_TOOLBOX_PREV);
1996 xMenu->set_sensitive("backward", xHelpWin->HasHistoryPredecessor());
1997 xMenu->append("forward", SfxResId(STR_HELP_BUTTON_NEXT), BMP_HELP_TOOLBOX_NEXT);
1998 xMenu->set_sensitive("forward", xHelpWin->HasHistorySuccessor());
1999 xMenu->append("start", SfxResId(STR_HELP_BUTTON_START), BMP_HELP_TOOLBOX_START);
2000 xMenu->append_separator("separator2");
2001 xMenu->append("print", SfxResId(STR_HELP_BUTTON_PRINT), BMP_HELP_TOOLBOX_PRINT);
2002 xMenu->append("bookmarks", SfxResId(STR_HELP_BUTTON_ADDBOOKMARK), BMP_HELP_TOOLBOX_BOOKMARKS);
2003 xMenu->append("searchdialog", SfxResId(STR_HELP_BUTTON_SEARCHDIALOG), BMP_HELP_TOOLBOX_SEARCHDIALOG);
2004 xMenu->append_separator("separator3");
2005 xMenu->append_check("selectionmode", SfxResId(STR_HELP_MENU_TEXT_SELECTION_MODE));
2006 URL aURL;
2007 aURL.Complete = ".uno:SelectTextMode";
2008 Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
2009 xTrans->parseStrict(aURL);
2010 Reference < XDispatch > xDisp = xFrame->queryDispatch( aURL, OUString(), 0 );
2011 if(xDisp.is())
2013 rtl::Reference<HelpStatusListener_Impl> pStateListener =
2014 new HelpStatusListener_Impl(xDisp, aURL );
2015 FeatureStateEvent rEvent = pStateListener->GetStateEvent();
2016 bool bCheck = false;
2017 rEvent.State >>= bCheck;
2018 xMenu->set_active("selectionmode", bCheck);
2020 xMenu->append_separator("separator4");
2021 xMenu->append("copy", SfxResId(STR_HELP_MENU_TEXT_COPY), BMP_HELP_TOOLBOX_COPY);
2022 xMenu->set_sensitive("copy", HasSelection());
2024 if ( bIsDebug )
2026 xMenu->append_separator("separator5");
2027 xMenu->append("sourceview", SfxResId(STR_HELP_BUTTON_SOURCEVIEW));
2030 int x, y, width, height;
2031 weld::Window* pTopLevel = GetFrameWeld();
2032 xHelpWin->GetContainer()->get_extents_relative_to(*pTopLevel, x, y, width, height);
2033 aPos.AdjustX(x);
2034 aPos.AdjustY(y);
2036 xHelpWin->DoAction(xMenu->popup_at_rect(pTopLevel, tools::Rectangle(aPos, Size(1,1))));
2037 bDone = true;
2040 else if ( MouseNotifyEvent::KEYINPUT == nType && rNEvt.GetKeyEvent() )
2042 const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
2043 const vcl::KeyCode& rKeyCode = pKEvt->GetKeyCode();
2044 sal_uInt16 nKeyGroup = rKeyCode.GetGroup();
2045 sal_uInt16 nKey = rKeyCode.GetCode();
2046 if ( KEYGROUP_ALPHA == nKeyGroup && !isHandledKey( rKeyCode ) )
2048 // do nothing disables the writer accelerators
2049 bDone = true;
2051 else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) )
2053 // <CTRL><F4> or <CTRL><W> -> close top frame
2054 xHelpWin->CloseWindow();
2055 bDone = true;
2057 else if ( KEY_TAB == nKey && xOnStartupCB->has_focus() )
2059 xToolBox->grab_focus();
2060 bDone = true;
2064 return bDone || Window::PreNotify( rNEvt );
2068 void SfxHelpTextWindow_Impl::GetFocus()
2070 if ( bIsInClose )
2071 return;
2075 if( xFrame.is() )
2077 Reference< css::awt::XWindow > xWindow = xFrame->getComponentWindow();
2078 if( xWindow.is() )
2079 xWindow->setFocus();
2082 catch( Exception const & )
2084 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::GetFocus()" );
2089 void SfxHelpTextWindow_Impl::DataChanged( const DataChangedEvent& rDCEvt )
2091 Window::DataChanged( rDCEvt );
2093 if ( ( ( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) ||
2094 ( rDCEvt.GetType() == DataChangedEventType::DISPLAY ) ) &&
2095 ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) )
2097 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFaceColor() ) );
2098 InitToolBoxImages();
2102 void SfxHelpTextWindow_Impl::ToggleIndex( bool bOn )
2104 bIsIndexOn = bOn;
2105 if ( bIsIndexOn )
2107 xToolBox->set_item_icon_name("index", aIndexOffImage);
2108 xToolBox->set_item_tooltip_text("index", aIndexOffText);
2110 else
2112 xToolBox->set_item_icon_name("index", aIndexOnImage);
2113 xToolBox->set_item_tooltip_text("index", aIndexOnText);
2117 void SfxHelpTextWindow_Impl::SelectSearchText( const OUString& rSearchText, bool _bIsFullWordSearch )
2119 aSearchText = rSearchText;
2120 bIsFullWordSearch = _bIsFullWordSearch;
2121 aSelectIdle.Start();
2125 void SfxHelpTextWindow_Impl::SetPageStyleHeaderOff() const
2127 bool bSetOff = false;
2128 // set off the pagestyle header to prevent print output of the help URL
2131 Reference < XController > xController = xFrame->getController();
2132 Reference < XSelectionSupplier > xSelSup( xController, UNO_QUERY );
2133 if ( xSelSup.is() )
2135 Reference < XIndexAccess > xSelection;
2136 if ( xSelSup->getSelection() >>= xSelection )
2138 Reference < XTextRange > xRange;
2139 if ( xSelection->getByIndex(0) >>= xRange )
2141 Reference < XText > xText = xRange->getText();
2142 Reference < XPropertySet > xProps( xText->createTextCursorByRange( xRange ), UNO_QUERY );
2143 OUString sStyleName;
2144 if ( xProps->getPropertyValue( "PageStyleName" ) >>= sStyleName )
2146 Reference < XStyleFamiliesSupplier > xStyles( xController->getModel(), UNO_QUERY );
2147 Reference < XNameContainer > xContainer;
2148 if ( xStyles->getStyleFamilies()->getByName( "PageStyles" )
2149 >>= xContainer )
2151 Reference < XStyle > xStyle;
2152 if ( xContainer->getByName( sStyleName ) >>= xStyle )
2154 Reference < XPropertySet > xPropSet( xStyle, UNO_QUERY );
2155 xPropSet->setPropertyValue( "HeaderIsOn", makeAny( false ) );
2157 Reference< XModifiable > xReset(xStyles, UNO_QUERY);
2158 xReset->setModified(false);
2159 bSetOff = true;
2167 catch( Exception const & )
2169 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff()" );
2172 SAL_WARN_IF( !bSetOff, "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff(): set off failed" );
2176 void SfxHelpTextWindow_Impl::CloseFrame()
2178 bIsInClose = true;
2181 css::uno::Reference< css::util::XCloseable > xCloseable ( xFrame, css::uno::UNO_QUERY );
2182 if (xCloseable.is())
2183 xCloseable->close(true);
2185 catch( css::util::CloseVetoException& )
2191 void SfxHelpTextWindow_Impl::DoSearch()
2193 if (m_xSrchDlg)
2194 return;
2196 // create the search dialog
2197 m_xSrchDlg = std::make_shared<sfx2::SearchDialog>(pTextWin->GetFrameWeld(), "HelpSearchDialog");
2198 // set handler
2199 m_xSrchDlg->SetFindHdl( LINK( this, SfxHelpTextWindow_Impl, FindHdl ) );
2200 m_xSrchDlg->SetCloseHdl( LINK( this, SfxHelpTextWindow_Impl, CloseHdl ) );
2201 // get selected text of the help page to set it as the search text
2202 Reference< XTextRange > xCursor = getCursor();
2203 if ( xCursor.is() )
2205 OUString sText = xCursor->getString();
2206 if ( !sText.isEmpty() )
2207 m_xSrchDlg->SetSearchText( sText );
2209 sfx2::SearchDialog::runAsync(m_xSrchDlg);
2212 void SfxHelpWindow_Impl::GetFocus()
2214 if (pTextWin)
2215 pTextWin->GrabFocus();
2216 else
2217 ResizableDockingWindow::GetFocus();
2220 void SfxHelpWindow_Impl::MakeLayout()
2222 Split();
2224 m_xHelpPaneWindow->set_visible(bIndex);
2227 IMPL_LINK(SfxHelpWindow_Impl, ResizeHdl, const Size&, rSize, void)
2229 int nNewWidth = rSize.Width();
2230 if (!nNewWidth)
2231 return;
2232 if (bSplit)
2233 nIndexSize = round(m_xContainer->get_position() * 100.0 / nNewWidth);
2234 nWidth = nNewWidth;
2235 Split();
2236 nIndexSize = round(m_xContainer->get_position() * 100.0 / nWidth);
2239 void SfxHelpWindow_Impl::Split()
2241 if (!nWidth)
2242 return;
2243 m_xContainer->set_position(nWidth * nIndexSize / 100);
2244 bSplit = true;
2247 void SfxHelpWindow_Impl::LoadConfig()
2249 SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN );
2250 if ( !aViewOpt.Exists() )
2251 return;
2252 bIndex = aViewOpt.IsVisible();
2254 Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
2255 OUString aUserData;
2256 if ( aUserItem >>= aUserData )
2258 DBG_ASSERT( comphelper::string::getTokenCount(aUserData, ';') == 6, "invalid user data" );
2259 sal_Int32 nIdx = 0;
2260 nIndexSize = aUserData.getToken( 0, ';', nIdx ).toInt32();
2261 aUserData.getToken(0, ';', nIdx); // ignore nTextSize
2262 sal_Int32 nOldWidth = aUserData.getToken( 0, ';', nIdx ).toInt32();
2263 sal_Int32 nOldHeight = aUserData.getToken( 0, ';', nIdx ).toInt32();
2264 aWinSize = Size(nOldWidth, nOldHeight);
2265 aWinPos.setX( aUserData.getToken( 0, ';', nIdx ).toInt32() );
2266 aWinPos.setY( aUserData.getToken( 0, ';', nIdx ).toInt32() );
2269 pTextWin->ToggleIndex( bIndex );
2272 void SfxHelpWindow_Impl::SaveConfig()
2274 SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN );
2275 sal_Int32 nW = 0, nH = 0;
2277 if ( xWindow.is() )
2279 css::awt::Rectangle aRect = xWindow->getPosSize();
2280 nW = aRect.Width;
2281 nH = aRect.Height;
2284 aViewOpt.SetVisible( bIndex );
2285 VclPtr<vcl::Window> pScreenWin = VCLUnoHelper::GetWindow( xWindow );
2286 aWinPos = pScreenWin->GetWindowExtentsRelative( nullptr ).TopLeft();
2287 if (bSplit)
2288 nIndexSize = round(m_xContainer->get_position() * 100.0 / nWidth);
2289 const OUString aUserData = OUString::number( nIndexSize )
2290 + ";" + OUString::number( 100 - nIndexSize )
2291 + ";" + OUString::number( nW )
2292 + ";" + OUString::number( nH )
2293 + ";" + OUString::number( aWinPos.X() )
2294 + ";" + OUString::number( aWinPos.Y() );
2296 aViewOpt.SetUserItem( USERITEM_NAME, makeAny( aUserData ) );
2299 void SfxHelpWindow_Impl::ShowStartPage()
2301 loadHelpContent(SfxHelpWindow_Impl::buildHelpURL(xIndexWin->GetFactory(), u"/start", u""));
2304 IMPL_LINK(SfxHelpWindow_Impl, SelectHdl, const OString&, rCurItem, void)
2306 bGrabFocusToToolBox = pTextWin->GetToolBox().has_focus();
2307 DoAction(rCurItem);
2310 IMPL_LINK_NOARG(SfxHelpWindow_Impl, OpenHdl, LinkParamNone*, void)
2312 xIndexWin->SelectExecutableEntry();
2313 OUString aEntry = xIndexWin->GetSelectedEntry();
2315 if ( aEntry.isEmpty() )
2316 return;
2318 OUString sHelpURL;
2320 bool bComplete = aEntry.toAsciiLowerCase().match("vnd.sun.star.help");
2322 if (bComplete)
2323 sHelpURL = aEntry;
2324 else
2326 OUString aId;
2327 OUString aAnchor('#');
2328 if ( comphelper::string::getTokenCount(aEntry, '#') == 2 )
2330 sal_Int32 nIdx{ 0 };
2331 aId = aEntry.getToken( 0, '#', nIdx );
2332 aAnchor += aEntry.getToken( 0, '#', nIdx );
2334 else
2335 aId = aEntry;
2337 sHelpURL = SfxHelpWindow_Impl::buildHelpURL(xIndexWin->GetFactory(), OUStringConcatenation("/" + aId), aAnchor);
2340 loadHelpContent(sHelpURL);
2343 IMPL_LINK( SfxHelpWindow_Impl, SelectFactoryHdl, SfxHelpIndexWindow_Impl* , pWin, void )
2345 if ( sTitle.isEmpty() )
2346 sTitle = GetParent()->GetText();
2348 Reference< XTitle > xTitle(xFrame, UNO_QUERY);
2349 if (xTitle.is ())
2350 xTitle->setTitle(sTitle + " - " + xIndexWin->GetActiveFactoryTitle());
2352 if ( pWin )
2353 ShowStartPage();
2354 xIndexWin->ClearSearchPage();
2358 IMPL_LINK( SfxHelpWindow_Impl, ChangeHdl, HelpListener_Impl&, rListener, void )
2360 SetFactory( rListener.GetFactory() );
2364 void SfxHelpWindow_Impl::openDone(const OUString& sURL ,
2365 bool bSuccess)
2367 INetURLObject aObj( sURL );
2368 if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp )
2369 SetFactory( aObj.GetHost() );
2370 if ( IsWait() )
2371 LeaveWait();
2372 if ( bGrabFocusToToolBox )
2374 pTextWin->GetToolBox().grab_focus();
2375 bGrabFocusToToolBox = false;
2377 else
2378 xIndexWin->GrabFocusBack();
2379 if ( !bSuccess )
2380 return;
2382 // set some view settings: "prevent help tips" and "helpid == 68245"
2385 Reference < XController > xController = pTextWin->getFrame()->getController();
2386 if ( xController.is() )
2388 Reference < XViewSettingsSupplier > xSettings( xController, UNO_QUERY );
2389 Reference < XPropertySet > xViewProps = xSettings->getViewSettings();
2390 Reference< XPropertySetInfo > xInfo = xViewProps->getPropertySetInfo();
2391 xViewProps->setPropertyValue( "ShowContentTips", makeAny( false ) );
2392 xViewProps->setPropertyValue( "ShowGraphics", makeAny( true ) );
2393 xViewProps->setPropertyValue( "ShowTables", makeAny( true ) );
2394 xViewProps->setPropertyValue( "HelpURL", makeAny( OUString("HID:SFX2_HID_HELP_ONHELP") ) );
2395 OUString sProperty( "IsExecuteHyperlinks" );
2396 if ( xInfo->hasPropertyByName( sProperty ) )
2397 xViewProps->setPropertyValue( sProperty, makeAny( true ) );
2398 xController->restoreViewData(Any());
2401 catch( Exception& )
2403 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::OpenDoneHdl(): unexpected exception" );
2406 // When the SearchPage opens the help doc, then select all words, which are equal to its text
2407 OUString sSearchText = comphelper::string::strip(xIndexWin->GetSearchText(), ' ');
2408 if ( !sSearchText.isEmpty() )
2409 pTextWin->SelectSearchText( sSearchText, xIndexWin->IsFullWordSearch() );
2411 // no page style header -> this prevents a print output of the URL
2412 pTextWin->SetPageStyleHeaderOff();
2416 SfxHelpWindow_Impl::SfxHelpWindow_Impl(
2417 const css::uno::Reference < css::frame::XFrame2 >& rFrame,
2418 vcl::Window* pParent ) :
2420 ResizableDockingWindow(pParent),
2422 xFrame ( rFrame ),
2423 pTextWin ( nullptr ),
2424 pHelpInterceptor ( new HelpInterceptor_Impl() ),
2425 pHelpListener ( new HelpListener_Impl( pHelpInterceptor ) ),
2426 bIndex ( true ),
2427 bGrabFocusToToolBox ( false ),
2428 bSplit ( false ),
2429 nWidth ( 0 ),
2430 nIndexSize ( 40 ), // % of width
2431 aWinPos ( 0, 0 ),
2432 aWinSize ( 0, 0 ),
2433 sTitle ( pParent->GetText() )
2435 SetStyle(GetStyle() & ~WB_DOCKABLE);
2437 SetHelpId( HID_HELP_WINDOW );
2439 m_xBuilder = Application::CreateInterimBuilder(m_xBox.get(), "sfx/ui/helpwindow.ui", false);
2440 m_xContainer = m_xBuilder->weld_paned("HelpWindow");
2441 m_xContainer->connect_size_allocate(LINK(this, SfxHelpWindow_Impl, ResizeHdl));
2442 m_xHelpPaneWindow = m_xBuilder->weld_container("helppanewindow");
2443 m_xHelpTextWindow = m_xBuilder->weld_container("helptextwindow");
2444 m_xHelpTextXWindow = m_xHelpTextWindow->CreateChildFrame();
2446 pHelpInterceptor->InitWaiter( this );
2447 xIndexWin.reset(new SfxHelpIndexWindow_Impl(this, m_xHelpPaneWindow.get()));
2448 xIndexWin->SetDoubleClickHdl( LINK( this, SfxHelpWindow_Impl, OpenHdl ) );
2449 xIndexWin->SetSelectFactoryHdl( LINK( this, SfxHelpWindow_Impl, SelectFactoryHdl ) );
2451 pTextWin = VclPtr<SfxHelpTextWindow_Impl>::Create(this, *m_xBuilder, VCLUnoHelper::GetWindow(m_xHelpTextXWindow));
2452 Reference < XFrames > xFrames = rFrame->getFrames();
2453 xFrames->append( Reference<XFrame>(pTextWin->getFrame(), UNO_QUERY_THROW) );
2454 pTextWin->SetSelectHdl( LINK( this, SfxHelpWindow_Impl, SelectHdl ) );
2455 pTextWin->Show();
2456 pHelpInterceptor->setInterception( Reference<XFrame>(pTextWin->getFrame(), UNO_QUERY_THROW) );
2457 pHelpListener->SetChangeHdl( LINK( this, SfxHelpWindow_Impl, ChangeHdl ) );
2458 LoadConfig();
2461 SfxHelpWindow_Impl::~SfxHelpWindow_Impl()
2463 disposeOnce();
2466 void SfxHelpWindow_Impl::dispose()
2468 SaveConfig();
2469 xIndexWin.reset();
2470 pTextWin->CloseFrame();
2471 pTextWin.disposeAndClear();
2473 m_xHelpTextXWindow->dispose();
2474 m_xHelpTextXWindow.clear();
2475 m_xHelpTextWindow.reset();
2476 m_xHelpPaneWindow.reset();
2477 m_xContainer.reset();
2478 m_xBuilder.reset();
2480 ResizableDockingWindow::dispose();
2483 bool SfxHelpWindow_Impl::PreNotify( NotifyEvent& rNEvt )
2485 bool bHandled = false;
2486 if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
2488 // Backward == <ALT><LEFT> or <BACKSPACE> Forward == <ALT><RIGHT>
2489 const vcl::KeyCode& rKeyCode = rNEvt.GetKeyEvent()->GetKeyCode();
2490 sal_uInt16 nKey = rKeyCode.GetCode();
2491 if ( ( rKeyCode.IsMod2() && ( KEY_LEFT == nKey || KEY_RIGHT == nKey ) ) ||
2492 ( !rKeyCode.GetModifier() && KEY_BACKSPACE == nKey && !xIndexWin->HasFocusOnEdit() ) )
2494 DoAction( rKeyCode.GetCode() == KEY_RIGHT ? "forward" : "backward" );
2495 bHandled = true;
2497 else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) )
2499 // <CTRL><F4> or <CTRL><W> -> close top frame
2500 CloseWindow();
2501 bHandled = true;
2504 return bHandled || Window::PreNotify( rNEvt );
2507 void SfxHelpWindow_Impl::setContainerWindow( const Reference < css::awt::XWindow >& xWin )
2509 xWindow = xWin;
2510 MakeLayout();
2511 if (xWindow.is())
2513 VclPtr<vcl::Window> pScreenWin = VCLUnoHelper::GetWindow(xWindow);
2514 if (aWinSize.Width() && aWinSize.Height())
2515 pScreenWin->SetPosSizePixel(aWinPos, aWinSize);
2516 else
2517 pScreenWin->SetPosPixel(aWinPos);
2521 void SfxHelpWindow_Impl::SetFactory( const OUString& rFactory )
2523 xIndexWin->SetFactory( rFactory, true );
2526 void SfxHelpWindow_Impl::SetHelpURL( const OUString& rURL )
2528 INetURLObject aObj( rURL );
2529 if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp )
2530 SetFactory( aObj.GetHost() );
2533 void SfxHelpWindow_Impl::DoAction(std::string_view rActionId)
2535 if (rActionId == "index")
2537 bIndex = !bIndex;
2538 MakeLayout();
2539 pTextWin->ToggleIndex( bIndex );
2541 else if (rActionId == "start")
2543 ShowStartPage();
2545 else if (rActionId == "backward" || rActionId == "forward")
2547 URL aURL;
2548 aURL.Complete = ".uno:Backward";
2549 if (rActionId == "forward")
2550 aURL.Complete = ".uno:Forward";
2551 Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
2552 xTrans->parseStrict(aURL);
2553 pHelpInterceptor->dispatch( aURL, Sequence < PropertyValue >() );
2555 else if (rActionId == "searchdialog")
2557 pTextWin->DoSearch();
2559 else if (rActionId == "print" || rActionId == "sourceview" || rActionId == "copy" || rActionId == "selectionmode")
2561 Reference < XDispatchProvider > xProv = pTextWin->getFrame();
2562 if ( xProv.is() )
2564 URL aURL;
2565 if (rActionId == "print")
2566 aURL.Complete = ".uno:Print";
2567 else if (rActionId == "sourceview")
2568 aURL.Complete = ".uno:SourceView";
2569 else if (rActionId == "copy")
2570 aURL.Complete = ".uno:Copy";
2571 else // rActionId == "selectionmode"
2572 aURL.Complete = ".uno:SelectTextMode";
2573 Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
2574 xTrans->parseStrict(aURL);
2575 Reference < XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
2576 if ( xDisp.is() )
2577 xDisp->dispatch( aURL, Sequence < PropertyValue >() );
2580 else if (rActionId == "bookmarks")
2582 OUString aURL = pHelpInterceptor->GetCurrentURL();
2583 if ( !aURL.isEmpty() )
2587 Content aCnt( aURL, Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
2588 css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties();
2589 if ( xInfo->hasPropertyByName( PROPERTY_TITLE ) )
2591 css::uno::Any aAny = aCnt.getPropertyValue( PROPERTY_TITLE );
2592 OUString aValue;
2593 if ( aAny >>= aValue )
2595 SfxAddHelpBookmarkDialog_Impl aDlg(GetFrameWeld(), false);
2596 aDlg.SetTitle(aValue);
2597 if (aDlg.run() == RET_OK )
2599 xIndexWin->AddBookmarks( aDlg.GetTitle(), aURL );
2604 catch( Exception& )
2606 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::DoAction(): unexpected exception" );
2612 void SfxHelpWindow_Impl::CloseWindow()
2616 // search for top frame
2617 Reference< XFramesSupplier > xCreator = getTextFrame()->getCreator();
2618 while ( xCreator.is() && !xCreator->isTop() )
2620 xCreator = xCreator->getCreator();
2623 // when found, close it
2624 if ( xCreator.is() && xCreator->isTop() )
2626 Reference < XCloseable > xCloser( xCreator, UNO_QUERY );
2627 if ( xCloser.is() )
2628 xCloser->close( false );
2631 catch( Exception const & )
2633 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::CloseWindow()" );
2638 void SfxHelpWindow_Impl::UpdateToolbox()
2640 pTextWin->GetToolBox().set_item_sensitive("backward", pHelpInterceptor->HasHistoryPred());
2641 pTextWin->GetToolBox().set_item_sensitive("forward", pHelpInterceptor->HasHistorySucc());
2645 bool SfxHelpWindow_Impl::HasHistoryPredecessor() const
2647 return pHelpInterceptor->HasHistoryPred();
2651 bool SfxHelpWindow_Impl::HasHistorySuccessor() const
2653 return pHelpInterceptor->HasHistorySucc();
2656 // class SfxAddHelpBookmarkDialog_Impl -----------------------------------
2658 SfxAddHelpBookmarkDialog_Impl::SfxAddHelpBookmarkDialog_Impl(weld::Widget* pParent, bool bRename)
2659 : GenericDialogController(pParent, "sfx/ui/bookmarkdialog.ui", "BookmarkDialog")
2660 , m_xTitleED(m_xBuilder->weld_entry("entry"))
2661 , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
2663 if (bRename)
2664 m_xDialog->set_title(m_xAltTitle->get_label());
2667 void SfxAddHelpBookmarkDialog_Impl::SetTitle(const OUString& rTitle)
2669 m_xTitleED->set_text(rTitle);
2670 m_xTitleED->select_region(0, -1);
2673 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */