1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
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>
31 
32 #include <sfx2/strings.hrc>
33 #include <helpids.h>
34 #include <bitmaps.hlst>
35 
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>
84 
85 #include <ucbhelper/content.hxx>
86 #include <unotools/ucbhelper.hxx>
87 
88 #include <string_view>
89 #include <unordered_map>
90 #include <vector>
91 
92 using namespace ::ucbhelper;
93 using namespace ::com::sun::star::ucb;
94 
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;
107 
108 using namespace ::comphelper;
109 
110 // defines ---------------------------------------------------------------
111 
112 #define CONFIGNAME_HELPWIN      "OfficeHelp"
113 #define CONFIGNAME_INDEXWIN     "OfficeHelpIndex"
114 #define CONFIGNAME_SEARCHPAGE   "OfficeHelpSearch"
115 #define IMAGE_URL               "private:factory/"
116 
117 #define PROPERTY_KEYWORDLIST    "KeywordList"
118 #define PROPERTY_KEYWORDREF     "KeywordRef"
119 #define PROPERTY_ANCHORREF      "KeywordAnchorForRef"
120 #define PROPERTY_TITLEREF       "KeywordTitleForRef"
121 #define PROPERTY_TITLE          "Title"
122 #define HELP_URL                "vnd.sun.star.help://"
123 #define HELP_SEARCH_TAG         "/?Query="
124 #define USERITEM_NAME           "UserItem"
125 
126 #define PACKAGE_SETUP           "/org.openoffice.Setup"
127 #define PATH_OFFICE_FACTORIES   "Office/Factories/"
128 #define KEY_HELP_ON_OPEN        "ooSetupFactoryHelpOnOpen"
129 #define KEY_UI_NAME             "ooSetupFactoryUIName"
130 
131 namespace sfx2
132 {
133 
134 
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"
144     */
PrepareSearchString(const OUString & rSearchString,const Reference<XBreakIterator> & xBreak,bool bForSearch)145     static OUString PrepareSearchString( const OUString& rSearchString,
146                                 const Reference< XBreakIterator >& xBreak, bool bForSearch )
147     {
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 );
153 
154         while ( aBoundary.startPos < aBoundary.endPos )
155         {
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] != '.' ) )
160             {
161                 if ( bForSearch && sSearchToken[ sSearchToken.getLength() - 1 ] != '*' )
162                     sSearchToken += "*";
163 
164                 if ( sSearchToken.getLength() > 1 ||
165                      ( sSearchToken.getLength() > 0 && sSearchToken[ 0 ] != '*' ) )
166                 {
167                     if ( !sSearchStr.isEmpty() )
168                     {
169                         if ( bForSearch )
170                             sSearchStr.append(" ");
171                         else
172                             sSearchStr.append("|");
173                     }
174                     sSearchStr.append(sSearchToken);
175                 }
176             }
177             aBoundary = xBreak->nextWord( rSearchString, nStartPos,
178                                           aLocale, WordType::ANYWORD_IGNOREWHITESPACES );
179         }
180 
181         return sSearchStr.makeStringAndClear();
182     }
183 
184 // namespace sfx2
185 }
186 
187 
188 // struct IndexEntry_Impl ------------------------------------------------
189 
190 namespace {
191 
192 struct IndexEntry_Impl
193 {
194     bool            m_bSubEntry;
195     OUString        m_aURL;
196 
IndexEntry_Impl__anon68e9030f0111::IndexEntry_Impl197     IndexEntry_Impl( const OUString& rURL, bool bSubEntry ) :
198         m_bSubEntry( bSubEntry ), m_aURL( rURL ) {}
199 };
200 
201 // struct ContentEntry_Impl ----------------------------------------------
202 
203 struct ContentEntry_Impl
204 {
205     OUString    aURL;
206     bool        bIsFolder;
207 
ContentEntry_Impl__anon68e9030f0111::ContentEntry_Impl208     ContentEntry_Impl( const OUString& rURL, bool bFolder ) :
209         aURL( rURL ), bIsFolder( bFolder ) {}
210 };
211 
212 }
213 
InitRoot()214 void ContentTabPage_Impl::InitRoot()
215 {
216     std::vector< OUString > aList =
217         SfxContentHelper::GetHelpTreeViewContents( "vnd.sun.star.hier://com.sun.star.help.TreeView/" );
218 
219     for (const OUString & aRow : aList)
220     {
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);
231     }
232 }
233 
ClearChildren(const weld::TreeIter * pParent)234 void ContentTabPage_Impl::ClearChildren(const weld::TreeIter* pParent)
235 {
236     std::unique_ptr<weld::TreeIter> xEntry = m_xContentBox->make_iterator(pParent);
237     bool bEntry = m_xContentBox->iter_children(*xEntry);
238     while (bEntry)
239     {
240         ClearChildren(xEntry.get());
241         delete reinterpret_cast<ContentEntry_Impl*>(m_xContentBox->get_id(*xEntry).toInt64());
242         bEntry = m_xContentBox->iter_next_sibling(*xEntry);
243     }
244 
245 }
246 
IMPL_LINK(ContentTabPage_Impl,ExpandingHdl,const weld::TreeIter &,rIter,bool)247 IMPL_LINK(ContentTabPage_Impl, ExpandingHdl, const weld::TreeIter&, rIter, bool)
248 {
249     ContentEntry_Impl* pContentEntry = reinterpret_cast<ContentEntry_Impl*>(m_xContentBox->get_id(rIter).toInt64());
250     if (!m_xContentBox->iter_has_child(rIter))
251     {
252         try
253         {
254             if (pContentEntry)
255             {
256                 std::vector<OUString > aList = SfxContentHelper::GetHelpTreeViewContents(pContentEntry->aURL);
257 
258                 for (const OUString & aRow : aList)
259                 {
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 )
266                     {
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);
270                     }
271                     else
272                     {
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);
280                     }
281                 }
282             }
283         }
284         catch (const Exception&)
285         {
286             TOOLS_WARN_EXCEPTION( "sfx.appl", "ContentListBox_Impl::RequestingChildren(): unexpected exception" );
287         }
288     }
289 
290     if (!pContentEntry || pContentEntry->bIsFolder)
291         m_xContentBox->set_image(rIter, aOpenBookImage);
292 
293     return true;
294 }
295 
IMPL_LINK(ContentTabPage_Impl,CollapsingHdl,const weld::TreeIter &,rIter,bool)296 IMPL_LINK(ContentTabPage_Impl, CollapsingHdl, const weld::TreeIter&, rIter, bool)
297 {
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);
301 
302     return true;
303 }
304 
GetSelectedEntry() const305 OUString ContentTabPage_Impl::GetSelectedEntry() const
306 {
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;
312 }
313 
314 // class HelpTabPage_Impl ------------------------------------------------
HelpTabPage_Impl(weld::Widget * pParent,SfxHelpIndexWindow_Impl * pIdxWin,const OString & rID,const OUString & rUIXMLDescription)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)
319 {
320 }
321 
~HelpTabPage_Impl()322 HelpTabPage_Impl::~HelpTabPage_Impl()
323 {
324 }
325 
326 // class ContentTabPage_Impl ---------------------------------------------
ContentTabPage_Impl(weld::Widget * pParent,SfxHelpIndexWindow_Impl * pIdxWin)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)
335 {
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));
341 
342     InitRoot();
343 }
344 
IMPL_LINK_NOARG(ContentTabPage_Impl,DoubleClickHdl,weld::TreeView &,bool)345 IMPL_LINK_NOARG(ContentTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
346 {
347     aDoubleClickHdl.Call(nullptr);
348     return false;
349 }
350 
SetDoubleClickHdl(const Link<LinkParamNone *,void> & rLink)351 void ContentTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
352 {
353     aDoubleClickHdl = rLink;
354 }
355 
~ContentTabPage_Impl()356 ContentTabPage_Impl::~ContentTabPage_Impl()
357 {
358     std::unique_ptr<weld::TreeIter> xEntry = m_xContentBox->make_iterator();
359     bool bEntry = m_xContentBox->get_iter_first(*xEntry);
360     while (bEntry)
361     {
362         ClearChildren(xEntry.get());
363         delete reinterpret_cast<ContentEntry_Impl*>(m_xContentBox->get_id(*xEntry).toInt64());
364         bEntry = m_xContentBox->iter_next_sibling(*xEntry);
365     }
366 }
367 
SelectExecutableEntry()368 void IndexTabPage_Impl::SelectExecutableEntry()
369 {
370     sal_Int32 nPos = m_xIndexList->find_text(m_xIndexEntry->get_text());
371     if (nPos == -1)
372         return;
373 
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() ) )
379     {
380         pEntry = reinterpret_cast<IndexEntry_Impl*>(m_xIndexList->get_id(++nPos).toInt64());
381         aEntryText = m_xIndexList->get_text(nPos);
382     }
383 
384     if ( nOldPos != nPos )
385         m_xIndexEntry->set_text(aEntryText);
386 }
387 
388 // class IndexTabPage_Impl -----------------------------------------------
IndexTabPage_Impl(weld::Widget * pParent,SfxHelpIndexWindow_Impl * pIdxWin)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     , bIsActivated(false)
397     , nRowHeight(m_xIndexList->get_height_rows(1))
398     , nAllHeight(0)
399 {
400     m_xIndexList->set_size_request(m_xIndexList->get_approximate_digit_width() * 30, -1);
401 
402     m_xOpenBtn->connect_clicked(LINK(this, IndexTabPage_Impl, OpenHdl));
403     aFactoryIdle.SetInvokeHandler( LINK(this, IndexTabPage_Impl, IdleHdl ));
404     aAutoCompleteIdle.SetInvokeHandler( LINK(this, IndexTabPage_Impl, AutoCompleteHdl ));
405     aKeywordTimer.SetInvokeHandler( LINK( this, IndexTabPage_Impl, TimeoutHdl ) );
406     m_xIndexList->connect_row_activated(LINK(this, IndexTabPage_Impl, DoubleClickHdl));
407     m_xIndexList->connect_changed(LINK(this, IndexTabPage_Impl, TreeChangeHdl));
408     m_xIndexList->connect_custom_get_size(LINK(this, IndexTabPage_Impl, CustomGetSizeHdl));
409     m_xIndexList->connect_custom_render(LINK(this, IndexTabPage_Impl, CustomRenderHdl));
410     m_xIndexList->set_column_custom_renderer(0, true);
411     m_xIndexList->connect_size_allocate(LINK(this, IndexTabPage_Impl, ResizeHdl));
412     m_xIndexEntry->connect_key_press(LINK(this, IndexTabPage_Impl, KeyInputHdl));
413     m_xIndexEntry->connect_changed(LINK(this, IndexTabPage_Impl, EntryChangeHdl));
414     m_xIndexEntry->connect_activate(LINK(this, IndexTabPage_Impl, ActivateHdl));
415 }
416 
IMPL_LINK(IndexTabPage_Impl,ResizeHdl,const Size &,rSize,void)417 IMPL_LINK(IndexTabPage_Impl, ResizeHdl, const Size&, rSize, void)
418 {
419     nAllHeight = rSize.Height();
420 }
421 
IMPL_LINK_NOARG(IndexTabPage_Impl,CustomGetSizeHdl,weld::TreeView::get_size_args,Size)422 IMPL_LINK_NOARG(IndexTabPage_Impl, CustomGetSizeHdl, weld::TreeView::get_size_args, Size)
423 {
424     return Size(m_xIndexList->get_size_request().Width(), nRowHeight);
425 }
426 
IMPL_LINK(IndexTabPage_Impl,CustomRenderHdl,weld::TreeView::render_args,aPayload,void)427 IMPL_LINK(IndexTabPage_Impl, CustomRenderHdl, weld::TreeView::render_args, aPayload, void)
428 {
429     vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
430     const ::tools::Rectangle& rRect = std::get<1>(aPayload);
431     bool bSelected = std::get<2>(aPayload);
432     const OUString& rId = std::get<3>(aPayload);
433 
434     rRenderContext.Push(PushFlags::TEXTCOLOR);
435     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
436     if (bSelected)
437         rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
438     else
439         rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor());
440 
441     Point aPos(rRect.TopLeft());
442     aPos.AdjustY((rRect.GetHeight() - rRenderContext.GetTextHeight()) / 2);
443 
444     int nIndex = m_xIndexList->find_id(rId);
445     OUString aEntry(m_xIndexList->get_text(nIndex));
446 
447     IndexEntry_Impl* pEntry = reinterpret_cast<IndexEntry_Impl*>(rId.toInt64());
448     if (pEntry && pEntry->m_bSubEntry)
449     {
450         // indent sub entries
451         aPos.AdjustX(8);
452         sal_Int32 nPos = aEntry.indexOf(';');
453         rRenderContext.DrawText(aPos, (nPos !=-1) ? aEntry.copy(nPos + 1) : aEntry);
454     }
455     else
456         rRenderContext.DrawText(aPos, aEntry);
457 
458     rRenderContext.Pop();
459 }
460 
IMPL_LINK_NOARG(IndexTabPage_Impl,TreeChangeHdl,weld::TreeView &,void)461 IMPL_LINK_NOARG(IndexTabPage_Impl, TreeChangeHdl, weld::TreeView&, void)
462 {
463     m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
464 }
465 
IMPL_LINK_NOARG(IndexTabPage_Impl,EntryChangeHdl,weld::Entry &,void)466 IMPL_LINK_NOARG(IndexTabPage_Impl, EntryChangeHdl, weld::Entry&, void)
467 {
468     aAutoCompleteIdle.Start();
469 }
470 
IMPL_LINK(IndexTabPage_Impl,KeyInputHdl,const KeyEvent &,rKEvt,bool)471 IMPL_LINK(IndexTabPage_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
472 {
473     const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
474     if (rKCode.GetModifier()) // only with no modifiers held
475         return false;
476 
477     sal_uInt16 nCode = rKCode.GetCode();
478 
479     if (nCode == KEY_UP || nCode == KEY_PAGEUP ||
480         nCode == KEY_DOWN || nCode == KEY_PAGEDOWN)
481     {
482 //        disable_notify_events();
483         sal_Int32 nIndex = m_xIndexList->get_selected_index();
484         sal_Int32 nOrigIndex = nIndex;
485         sal_Int32 nCount = m_xIndexList->n_children();
486         if (nIndex == -1)
487         {
488             m_xIndexList->set_cursor(0);
489             m_xIndexList->select(0);
490             m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
491         }
492         else
493         {
494             if (nCode == KEY_UP)
495                 --nIndex;
496             else if (nCode == KEY_DOWN)
497                 ++nIndex;
498             else if (nCode == KEY_PAGEUP)
499             {
500                 int nVisRows = nAllHeight / nRowHeight;
501                 nIndex -= nVisRows;
502             }
503             else if (nCode == KEY_PAGEDOWN)
504             {
505                 int nVisRows = nAllHeight / nRowHeight;
506                 nIndex += nVisRows;
507             }
508 
509             if (nIndex < 0)
510                 nIndex = 0;
511             if (nIndex >= nCount)
512                 nIndex = nCount - 1;
513 
514             if (nIndex != nOrigIndex)
515             {
516                 m_xIndexList->set_cursor(nIndex);
517                 m_xIndexList->select(nIndex);
518                 m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
519             }
520 
521 //            m_xIndexList->grab_focus();
522 //            g_signal_emit_by_name(pWidget, "key-press-event", pEvent, &ret);
523 //            m_xIndexEntry->set_text(m_xIndexList->get_selected_text());
524 //            m_xIndexEntry->grab_focus();
525         }
526         m_xIndexEntry->select_region(0, -1);
527 //        enable_notify_events();
528 //        m_bTreeChange = true;
529 //        m_pEntry->fire_signal_changed();
530 //        m_bTreeChange = false;
531         return true;
532     }
533     return false;
534 }
535 
~IndexTabPage_Impl()536 IndexTabPage_Impl::~IndexTabPage_Impl()
537 {
538     ClearIndex();
539 }
540 
541 namespace sfx2 {
542 
543     typedef std::unordered_map< OUString, int > KeywordInfo;
544 }
545 
InitializeIndex()546 void IndexTabPage_Impl::InitializeIndex()
547 {
548     weld::WaitObject aWaitCursor(m_pIdxWin->GetFrameWeld());
549 
550     // By now more than 256 equal entries are not allowed
551     sal_Unicode append[256];
552     for(sal_Unicode & k : append)
553         k =  ' ';
554 
555     sfx2::KeywordInfo aInfo;
556     m_xIndexList->freeze();
557 
558     try
559     {
560         OUStringBuffer aURL = HELP_URL;
561         aURL.append(sFactory);
562         AppendConfigToken(aURL, true);
563 
564         Content aCnt( aURL.makeStringAndClear(), Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
565         css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties();
566         if ( xInfo->hasPropertyByName( PROPERTY_ANCHORREF ) )
567         {
568             css::uno::Sequence< OUString > aPropSeq( 4 );
569             aPropSeq[0] = PROPERTY_KEYWORDLIST;
570             aPropSeq[1] = PROPERTY_KEYWORDREF;
571             aPropSeq[2] = PROPERTY_ANCHORREF;
572             aPropSeq[3] = PROPERTY_TITLEREF;
573 
574             // abi: use one possibly remote call only
575             css::uno::Sequence< css::uno::Any > aAnySeq =
576                   aCnt.getPropertyValues( aPropSeq );
577 
578             css::uno::Sequence< OUString > aKeywordList;
579             css::uno::Sequence< css::uno::Sequence< OUString > > aKeywordRefList;
580             css::uno::Sequence< css::uno::Sequence< OUString > > aAnchorRefList;
581             css::uno::Sequence< css::uno::Sequence< OUString > > aTitleRefList;
582 
583             if ( ( aAnySeq[0] >>= aKeywordList ) && ( aAnySeq[1] >>= aKeywordRefList ) &&
584                  ( aAnySeq[2] >>= aAnchorRefList ) && ( aAnySeq[3] >>= aTitleRefList ) )
585             {
586                 int ndx,tmp;
587                 OUString aIndex, aTempString;
588                 OUStringBuffer aData( 128 );            // Capacity of up to 128 characters
589                 sfx2::KeywordInfo::iterator it;
590 
591                 for ( int i = 0; i < aKeywordList.getLength(); ++i )
592                 {
593                     // abi: Do not copy, but use references
594                     const OUString& aKeywordPair = aKeywordList[i];
595                     DBG_ASSERT( !aKeywordPair.isEmpty(), "invalid help index" );
596                     const css::uno::Sequence< OUString >& aRefList = aKeywordRefList[i];
597                     const css::uno::Sequence< OUString >& aAnchorList = aAnchorRefList[i];
598                     const css::uno::Sequence< OUString >& aTitleList = aTitleRefList[i];
599 
600                     DBG_ASSERT( aRefList.getLength() == aAnchorList.getLength(),"reference list and title list of different length" );
601 
602                     ndx = aKeywordPair.indexOf( ';' );
603                     const bool insert = ndx != -1;
604 
605                     OUString sId;
606 
607                     if ( insert )
608                     {
609                         aTempString = aKeywordPair.copy( 0, ndx );
610                         if ( aIndex != aTempString )
611                         {
612                             aIndex = aTempString;
613                             it = aInfo.emplace(aTempString, 0).first;
614                             sId = OUString::number(reinterpret_cast<sal_Int64>(new IndexEntry_Impl(OUString(), false)));
615                             if ( (tmp = it->second++) != 0)
616                                 m_xIndexList->append(
617                                     sId, aTempString + std::u16string_view(append, tmp));
618                             else
619                                 m_xIndexList->append(sId, aTempString);
620                         }
621                     }
622                     else
623                         aIndex.clear();
624 
625                     sal_uInt32 nRefListLen = aRefList.getLength();
626 
627                     DBG_ASSERT( aAnchorList.hasElements(), "*IndexTabPage_Impl::InitializeIndex(): AnchorList is empty!" );
628                     DBG_ASSERT( nRefListLen, "*IndexTabPage_Impl::InitializeIndex(): RefList is empty!" );
629 
630                     if ( aAnchorList.hasElements() && nRefListLen )
631                     {
632                         if ( aAnchorList[0].getLength() > 0 )
633                         {
634                             aData.append( aRefList[0] ).append( '#' ).append( aAnchorList[0] );
635                             sId = OUString::number(reinterpret_cast<sal_Int64>(new IndexEntry_Impl(aData.makeStringAndClear(), insert)));
636                         }
637                         else
638                             sId = OUString::number(reinterpret_cast<sal_Int64>(new IndexEntry_Impl(aRefList[0], insert)));
639                     }
640 
641                     // Assume the token is trimmed
642                     it = aInfo.emplace(aKeywordPair, 0).first;
643                     if ((tmp = it->second++) != 0)
644                         m_xIndexList->append(sId, aKeywordPair + std::u16string_view(append, tmp));
645                     else
646                         m_xIndexList->append(sId, aKeywordPair);
647 
648                     for ( sal_uInt32 j = 1; j < nRefListLen ; ++j )
649                     {
650                         aData
651                             .append( aKeywordPair )
652                             .append( ' ' )
653                             .append( '-' )
654                             .append( ' ' )
655                             .append( aTitleList[j] );
656 
657                         aTempString = aData.makeStringAndClear();
658 
659                         if ( aAnchorList[j].getLength() > 0 )
660                         {
661                             aData.append( aRefList[j] ).append( '#' ).append( aAnchorList[j] );
662                             sId = OUString::number(reinterpret_cast<sal_Int64>(new IndexEntry_Impl(aData.makeStringAndClear(), insert)));
663                         }
664                         else
665                             sId = OUString::number(reinterpret_cast<sal_Int64>(new IndexEntry_Impl(aRefList[j], insert)));
666 
667                         it = aInfo.emplace(aTempString, 0).first;
668                         if ( (tmp = it->second++) != 0 )
669                             m_xIndexList->append(
670                                 sId, aTempString + std::u16string_view(append, tmp));
671                         else
672                             m_xIndexList->append(sId, aTempString);
673                     }
674                 }
675             }
676         }
677     }
678     catch( Exception& )
679     {
680         TOOLS_WARN_EXCEPTION( "sfx.appl", "IndexTabPage_Impl::InitializeIndex(): unexpected exception" );
681     }
682 
683     m_xIndexList->thaw();
684 
685     if ( !sKeyword.isEmpty() )
686         aKeywordLink.Call( *this );
687 }
688 
ClearIndex()689 void IndexTabPage_Impl::ClearIndex()
690 {
691     const sal_Int32 nCount = m_xIndexList->n_children();
692     for ( sal_Int32 i = 0; i < nCount; ++i )
693         delete reinterpret_cast<IndexEntry_Impl*>(m_xIndexList->get_id(i).toInt64());
694     m_xIndexList->clear();
695 }
696 
IMPL_LINK_NOARG(IndexTabPage_Impl,OpenHdl,weld::Button &,void)697 IMPL_LINK_NOARG(IndexTabPage_Impl, OpenHdl, weld::Button&, void)
698 {
699     aDoubleClickHdl.Call(nullptr);
700 }
701 
IMPL_LINK_NOARG(IndexTabPage_Impl,ActivateHdl,weld::Entry &,bool)702 IMPL_LINK_NOARG(IndexTabPage_Impl, ActivateHdl, weld::Entry&, bool)
703 {
704     aDoubleClickHdl.Call(nullptr);
705     return true;
706 }
707 
IMPL_LINK_NOARG(IndexTabPage_Impl,DoubleClickHdl,weld::TreeView &,bool)708 IMPL_LINK_NOARG(IndexTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
709 {
710     aDoubleClickHdl.Call(nullptr);
711     return true;
712 }
713 
IMPL_LINK_NOARG(IndexTabPage_Impl,IdleHdl,Timer *,void)714 IMPL_LINK_NOARG(IndexTabPage_Impl, IdleHdl, Timer*, void)
715 {
716     InitializeIndex();
717 }
718 
starts_with(const OUString & rStr,int nStartRow,bool bCaseSensitive)719 int IndexTabPage_Impl::starts_with(const OUString& rStr, int nStartRow, bool bCaseSensitive)
720 {
721     const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
722 
723     int nRet = nStartRow;
724     int nCount = m_xIndexList->n_children();
725     while (nRet < nCount)
726     {
727         OUString aStr(m_xIndexList->get_text(nRet));
728         const bool bMatch = !bCaseSensitive ? rI18nHelper.MatchString(rStr, aStr) : aStr.startsWith(rStr);
729         if (bMatch)
730             return nRet;
731         ++nRet;
732     }
733 
734     return -1;
735 }
736 
IMPL_LINK_NOARG(IndexTabPage_Impl,AutoCompleteHdl,Timer *,void)737 IMPL_LINK_NOARG(IndexTabPage_Impl, AutoCompleteHdl, Timer*, void)
738 {
739     OUString aStartText = m_xIndexEntry->get_text();
740     int nStartPos, nEndPos;
741     m_xIndexEntry->get_selection_bounds(nStartPos, nEndPos);
742     int nMaxSelection = std::max(nStartPos, nEndPos);
743     if (nMaxSelection != aStartText.getLength())
744         return;
745 
746     int nActive = m_xIndexList->get_selected_index();
747     int nStart = nActive;
748 
749     if (nStart == -1)
750         nStart = 0;
751 
752     // Try match case insensitive from current position
753     int nPos = starts_with(aStartText, nStart, false);
754     if (nPos == -1 && nStart != 0)
755     {
756         // Try match case insensitive, but from start
757         nPos = starts_with(aStartText, 0, false);
758     }
759 
760     if (nPos == -1)
761     {
762         // Try match case sensitive from current position
763         nPos = starts_with(aStartText, nStart, true);
764         if (nPos == -1 && nStart != 0)
765         {
766             // Try match case sensitive, but from start
767             nPos = starts_with(aStartText, 0, true);
768         }
769     }
770 
771     if (nPos != -1)
772     {
773         m_xIndexList->set_cursor(nPos);
774         m_xIndexList->select(nPos);
775         OUString aText = m_xIndexList->get_text(nPos);
776         if (aText != aStartText)
777             m_xIndexEntry->set_text(aText);
778         m_xIndexEntry->select_region(aText.getLength(), aStartText.getLength());
779     }
780 }
781 
IMPL_LINK(IndexTabPage_Impl,TimeoutHdl,Timer *,pTimer,void)782 IMPL_LINK( IndexTabPage_Impl, TimeoutHdl, Timer*, pTimer, void)
783 {
784     if(&aKeywordTimer == pTimer && !sKeyword.isEmpty())
785         aKeywordLink.Call(*this);
786 }
787 
Activate()788 void IndexTabPage_Impl::Activate()
789 {
790     if ( !bIsActivated )
791     {
792         bIsActivated = true;
793         aFactoryIdle.Start();
794     }
795 }
796 
SetDoubleClickHdl(const Link<LinkParamNone *,void> & rLink)797 void IndexTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
798 {
799     aDoubleClickHdl = rLink;
800 }
801 
SetFactory(const OUString & rFactory)802 void IndexTabPage_Impl::SetFactory( const OUString& rFactory )
803 {
804     OUString sNewFactory( rFactory );
805     DBG_ASSERT( !sNewFactory.isEmpty(), "empty factory" );
806     bool bValid = m_pIdxWin->IsValidFactory( rFactory );
807 
808     if ( sFactory.isEmpty() && !bValid )
809     {
810         sNewFactory = SfxHelp::GetDefaultHelpModule();
811         bValid = true;
812     }
813 
814     if ( sNewFactory != sFactory && bValid )
815     {
816         sFactory = sNewFactory;
817         ClearIndex();
818         if ( bIsActivated )
819             aFactoryIdle.Start();
820     }
821 }
822 
GetSelectedEntry() const823 OUString IndexTabPage_Impl::GetSelectedEntry() const
824 {
825     OUString aRet;
826     IndexEntry_Impl* pEntry = reinterpret_cast<IndexEntry_Impl*>(m_xIndexList->get_id(m_xIndexList->find_text(m_xIndexEntry->get_text())).toInt64());
827     if (pEntry)
828         aRet = pEntry->m_aURL;
829     return aRet;
830 }
831 
SetKeyword(const OUString & rKeyword)832 void IndexTabPage_Impl::SetKeyword( const OUString& rKeyword )
833 {
834     sKeyword = rKeyword;
835 
836     if (m_xIndexList->n_children() > 0)
837         aKeywordTimer.Start();
838     else if ( !bIsActivated )
839         aFactoryIdle.Start();
840 }
841 
842 
HasKeyword() const843 bool IndexTabPage_Impl::HasKeyword() const
844 {
845     bool bRet = false;
846     if ( !sKeyword.isEmpty() )
847     {
848         sal_Int32 nPos = m_xIndexList->find_text( sKeyword );
849         bRet = nPos != -1;
850     }
851 
852     return bRet;
853 }
854 
855 
HasKeywordIgnoreCase()856 bool IndexTabPage_Impl::HasKeywordIgnoreCase()
857 {
858     bool bRet = false;
859     if ( !sKeyword.isEmpty() )
860     {
861         sal_Int32 nEntries = m_xIndexList->n_children();
862         const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetLocaleI18nHelper();
863         for ( sal_Int32 n = 0; n < nEntries; n++)
864         {
865             const OUString sIndexItem {m_xIndexList->get_text(n)};
866             if (rI18nHelper.MatchString( sIndexItem, sKeyword ))
867             {
868                 sKeyword = sIndexItem;
869                 bRet = true;
870             }
871         }
872     }
873 
874     return bRet;
875 }
876 
OpenKeyword()877 void IndexTabPage_Impl::OpenKeyword()
878 {
879     if ( !sKeyword.isEmpty() )
880     {
881         m_xIndexEntry->set_text(sKeyword);
882         aDoubleClickHdl.Call(nullptr);
883         sKeyword.clear();
884     }
885 }
886 
IMPL_LINK_NOARG(SearchTabPage_Impl,ActivateHdl,weld::ComboBox &,bool)887 IMPL_LINK_NOARG(SearchTabPage_Impl, ActivateHdl, weld::ComboBox&, bool)
888 {
889     Search();
890     return true;
891 }
892 
893 // class SearchTabPage_Impl ----------------------------------------------
894 
SearchTabPage_Impl(weld::Widget * pParent,SfxHelpIndexWindow_Impl * pIdxWin)895 SearchTabPage_Impl::SearchTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin)
896     : HelpTabPage_Impl(pParent, pIdxWin, "HelpSearchPage",
897         "sfx/ui/helpsearchpage.ui")
898     , m_xSearchED(m_xBuilder->weld_combo_box("search"))
899     , m_xSearchBtn(m_xBuilder->weld_button("find"))
900     , m_xFullWordsCB(m_xBuilder->weld_check_button("completewords"))
901     , m_xScopeCB(m_xBuilder->weld_check_button("headings"))
902     , m_xResultsLB(m_xBuilder->weld_tree_view("results"))
903     , m_xOpenBtn(m_xBuilder->weld_button("display"))
904     , xBreakIterator(vcl::unohelper::CreateBreakIterator())
905 {
906     m_xResultsLB->set_size_request(m_xResultsLB->get_approximate_digit_width() * 30,
907                                    m_xResultsLB->get_height_rows(15));
908 
909     m_xSearchBtn->connect_clicked(LINK(this, SearchTabPage_Impl, ClickHdl));
910     m_xSearchED->connect_changed(LINK(this, SearchTabPage_Impl, ModifyHdl));
911     m_xSearchED->connect_entry_activate(LINK(this, SearchTabPage_Impl, ActivateHdl));
912     m_xOpenBtn->connect_clicked(LINK(this, SearchTabPage_Impl, OpenHdl));
913     m_xResultsLB->connect_row_activated(LINK(this, SearchTabPage_Impl, DoubleClickHdl));
914 
915     SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE );
916     if ( aViewOpt.Exists() )
917     {
918         OUString aUserData;
919         Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
920         if ( aUserItem >>= aUserData )
921         {
922             sal_Int32 nIdx {0};
923             bool bChecked = aUserData.getToken(0, ';', nIdx).toInt32() == 1;
924             m_xFullWordsCB->set_active(bChecked);
925             bChecked = aUserData.getToken(0, ';', nIdx).toInt32() == 1;
926             m_xScopeCB->set_active(bChecked);
927 
928             while ( nIdx > 0 )
929             {
930                 m_xSearchED->append_text( INetURLObject::decode(
931                     aUserData.getToken(0, ';', nIdx),
932                     INetURLObject::DecodeMechanism::WithCharset ) );
933             }
934         }
935     }
936 
937     ModifyHdl(*m_xSearchED);
938 }
939 
~SearchTabPage_Impl()940 SearchTabPage_Impl::~SearchTabPage_Impl()
941 {
942     SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE );
943     OUStringBuffer aUserData =
944             OUString::number(m_xFullWordsCB->get_active() ? 1 : 0) +
945             ";" +
946             OUString::number(m_xScopeCB->get_active() ? 1 : 0);
947     sal_Int32 nCount = std::min(m_xSearchED->get_count(), 10);  // save only 10 entries
948 
949     for ( sal_Int32 i = 0; i < nCount; ++i )
950     {
951         aUserData.append(";" +
952             INetURLObject::encode(
953                 m_xSearchED->get_text(i),
954                 INetURLObject::PART_UNO_PARAM_VALUE,
955                 INetURLObject::EncodeMechanism::All ));
956     }
957 
958     Any aUserItem = makeAny( aUserData.makeStringAndClear() );
959     aViewOpt.SetUserItem( USERITEM_NAME, aUserItem );
960 
961     m_xSearchED.reset();
962     m_xSearchBtn.reset();
963     m_xFullWordsCB.reset();
964     m_xScopeCB.reset();
965     m_xResultsLB.reset();
966     m_xOpenBtn.reset();
967 }
968 
ClearSearchResults()969 void SearchTabPage_Impl::ClearSearchResults()
970 {
971     m_xResultsLB->clear();
972 }
973 
RememberSearchText(const OUString & rSearchText)974 void SearchTabPage_Impl::RememberSearchText( const OUString& rSearchText )
975 {
976     for (sal_Int32 i = 0, nEntryCount = m_xSearchED->get_count(); i < nEntryCount; ++i)
977     {
978         if (rSearchText == m_xSearchED->get_text(i))
979         {
980             m_xSearchED->remove(i);
981             break;
982         }
983     }
984 
985     m_xSearchED->insert_text(0, rSearchText);
986 }
987 
IMPL_LINK_NOARG(SearchTabPage_Impl,ClickHdl,weld::Button &,void)988 IMPL_LINK_NOARG(SearchTabPage_Impl, ClickHdl, weld::Button&, void)
989 {
990     Search();
991 }
992 
Search()993 void SearchTabPage_Impl::Search()
994 {
995     OUString aSearchText = comphelper::string::strip(m_xSearchED->get_active_text(), ' ');
996     if ( aSearchText.isEmpty() )
997         return;
998 
999     std::unique_ptr<weld::WaitObject> xWaitCursor(new weld::WaitObject(m_pIdxWin->GetFrameWeld()));
1000     ClearSearchResults();
1001     RememberSearchText( aSearchText );
1002     OUStringBuffer aSearchURL(HELP_URL);
1003     aSearchURL.append(aFactory);
1004     aSearchURL.append(HELP_SEARCH_TAG);
1005     if (!m_xFullWordsCB->get_active())
1006         aSearchText = sfx2::PrepareSearchString( aSearchText, xBreakIterator, true );
1007     aSearchURL.append(aSearchText);
1008     AppendConfigToken(aSearchURL, false);
1009     if (m_xScopeCB->get_active())
1010         aSearchURL.append("&Scope=Heading");
1011     std::vector< OUString > aFactories = SfxContentHelper::GetResultSet(aSearchURL.makeStringAndClear());
1012     for (const OUString & rRow : aFactories)
1013     {
1014         sal_Int32 nIdx = 0;
1015         OUString aTitle = rRow.getToken(0, '\t', nIdx);
1016         OUString sURL(rRow.getToken(1, '\t', nIdx));
1017         m_xResultsLB->append(sURL, aTitle);
1018     }
1019     xWaitCursor.reset();
1020 
1021     if ( aFactories.empty() )
1022     {
1023         std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xContainer.get(),
1024                                                                  VclMessageType::Info, VclButtonsType::Ok,
1025                                                                  SfxResId(STR_INFO_NOSEARCHRESULTS)));
1026         xBox->run();
1027     }
1028 }
1029 
IMPL_LINK_NOARG(SearchTabPage_Impl,OpenHdl,weld::Button &,void)1030 IMPL_LINK_NOARG(SearchTabPage_Impl, OpenHdl, weld::Button&, void)
1031 {
1032     aDoubleClickHdl.Call(nullptr);
1033 }
1034 
IMPL_LINK(SearchTabPage_Impl,ModifyHdl,weld::ComboBox &,rComboBox,void)1035 IMPL_LINK(SearchTabPage_Impl, ModifyHdl, weld::ComboBox&, rComboBox, void)
1036 {
1037     OUString aSearchText = comphelper::string::strip(m_xSearchED->get_active_text(), ' ');
1038     m_xSearchBtn->set_sensitive(!aSearchText.isEmpty());
1039 
1040     if (rComboBox.changed_by_direct_pick())
1041         Search();
1042 }
1043 
IMPL_LINK_NOARG(SearchTabPage_Impl,DoubleClickHdl,weld::TreeView &,bool)1044 IMPL_LINK_NOARG(SearchTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
1045 {
1046     aDoubleClickHdl.Call(nullptr);
1047     return true;
1048 }
1049 
SetDoubleClickHdl(const Link<LinkParamNone *,void> & rLink)1050 void SearchTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
1051 {
1052     aDoubleClickHdl = rLink;
1053 }
1054 
GetSelectedEntry() const1055 OUString SearchTabPage_Impl::GetSelectedEntry() const
1056 {
1057     return m_xResultsLB->get_selected_id();
1058 }
1059 
ClearPage()1060 void SearchTabPage_Impl::ClearPage()
1061 {
1062     ClearSearchResults();
1063     m_xSearchED->set_entry_text(OUString());
1064 }
1065 
OpenKeyword(const OUString & rKeyword)1066 bool SearchTabPage_Impl::OpenKeyword( const OUString& rKeyword )
1067 {
1068     bool bRet = false;
1069     m_xSearchED->set_entry_text(rKeyword);
1070     Search();
1071     if (m_xResultsLB->n_children() > 0)
1072     {
1073         // found keyword -> open it
1074         m_xResultsLB->select(0);
1075         OpenHdl(*m_xOpenBtn);
1076         bRet = true;
1077     }
1078     return bRet;
1079 }
1080 
1081 // class BookmarksTabPage_Impl -------------------------------------------
GetBookmarkEntry_Impl(const Sequence<PropertyValue> & aBookmarkEntry,OUString & rTitle,OUString & rURL)1082 static void GetBookmarkEntry_Impl
1083 (
1084     const Sequence< PropertyValue >& aBookmarkEntry,
1085     OUString& rTitle,
1086     OUString& rURL
1087 )
1088 {
1089     for ( const PropertyValue& aValue : aBookmarkEntry )
1090     {
1091         if ( aValue.Name == HISTORY_PROPERTYNAME_URL )
1092             aValue.Value >>= rURL;
1093         else if ( aValue.Name == HISTORY_PROPERTYNAME_TITLE )
1094             aValue.Value >>= rTitle;
1095     }
1096 }
1097 
DoAction(std::string_view rAction)1098 void BookmarksTabPage_Impl::DoAction(std::string_view rAction)
1099 {
1100     if (rAction == "display")
1101         aDoubleClickHdl.Call(nullptr);
1102     else if (rAction == "rename")
1103     {
1104         sal_Int32 nPos = m_xBookmarksBox->get_selected_index();
1105         if (nPos != -1)
1106         {
1107             SfxAddHelpBookmarkDialog_Impl aDlg(m_xBookmarksBox.get(), true);
1108             aDlg.SetTitle(m_xBookmarksBox->get_text(nPos));
1109             if (aDlg.run() == RET_OK)
1110             {
1111                 OUString sURL = m_xBookmarksBox->get_id(nPos);
1112                 m_xBookmarksBox->remove(nPos);
1113                 m_xBookmarksBox->append(sURL, aDlg.GetTitle(), SvFileInformationManager::GetImageId(INetURLObject(IMAGE_URL+INetURLObject(sURL).GetHost())));
1114                 m_xBookmarksBox->select(m_xBookmarksBox->n_children() - 1);
1115             }
1116         }
1117     }
1118     else if (rAction == "delete")
1119     {
1120         sal_Int32 nPos = m_xBookmarksBox->get_selected_index();
1121         if (nPos != -1)
1122         {
1123             m_xBookmarksBox->remove(nPos);
1124             const sal_Int32 nCount = m_xBookmarksBox->n_children();
1125             if (nCount)
1126             {
1127                 if (nPos >= nCount)
1128                     nPos = nCount - 1;
1129                 m_xBookmarksBox->select(nPos);
1130             }
1131         }
1132     }
1133 }
1134 
IMPL_LINK(BookmarksTabPage_Impl,CommandHdl,const CommandEvent &,rCEvt,bool)1135 IMPL_LINK(BookmarksTabPage_Impl, CommandHdl, const CommandEvent&, rCEvt, bool)
1136 {
1137     if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
1138         return false;
1139 
1140     std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xBookmarksBox.get(), "sfx/ui/bookmarkmenu.ui"));
1141     std::unique_ptr<weld::Menu> xMenu = xBuilder->weld_menu("menu");
1142 
1143     OString sIdent = xMenu->popup_at_rect(m_xBookmarksBox.get(), ::tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
1144     if (!sIdent.isEmpty())
1145         DoAction(sIdent);
1146     return true;
1147 }
1148 
IMPL_LINK(BookmarksTabPage_Impl,KeyInputHdl,const KeyEvent &,rKEvt,bool)1149 IMPL_LINK(BookmarksTabPage_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
1150 {
1151     bool bHandled = false;
1152     sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
1153     if (KEY_DELETE == nCode && m_xBookmarksBox->n_children() > 0)
1154     {
1155         DoAction("delete");
1156         bHandled = true;
1157     }
1158     return bHandled;
1159 }
1160 
1161 // class BookmarksTabPage_Impl -------------------------------------------
BookmarksTabPage_Impl(weld::Widget * pParent,SfxHelpIndexWindow_Impl * _pIdxWin)1162 BookmarksTabPage_Impl::BookmarksTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* _pIdxWin)
1163     : HelpTabPage_Impl(pParent, _pIdxWin, "HelpBookmarkPage",
1164         "sfx/ui/helpbookmarkpage.ui")
1165     , m_xBookmarksBox(m_xBuilder->weld_tree_view("bookmarks"))
1166     , m_xBookmarksPB(m_xBuilder->weld_button("display"))
1167 {
1168     m_xBookmarksBox->set_size_request(m_xBookmarksBox->get_approximate_digit_width() * 30,
1169                                       m_xBookmarksBox->get_height_rows(20));
1170 
1171     m_xBookmarksPB->connect_clicked( LINK(this, BookmarksTabPage_Impl, OpenHdl));
1172     m_xBookmarksBox->connect_row_activated(LINK(this, BookmarksTabPage_Impl, DoubleClickHdl));
1173     m_xBookmarksBox->connect_popup_menu(LINK(this, BookmarksTabPage_Impl, CommandHdl));
1174     m_xBookmarksBox->connect_key_press(LINK(this, BookmarksTabPage_Impl, KeyInputHdl));
1175 
1176     // load bookmarks from configuration
1177     const Sequence< Sequence< PropertyValue > > aBookmarkSeq = SvtHistoryOptions().GetList( EHistoryType::HelpBookmarks );
1178 
1179     OUString aTitle;
1180     OUString aURL;
1181 
1182     for ( const auto& rBookmark : aBookmarkSeq )
1183     {
1184         GetBookmarkEntry_Impl( rBookmark, aTitle, aURL );
1185         AddBookmarks( aTitle, aURL );
1186     }
1187 }
1188 
~BookmarksTabPage_Impl()1189 BookmarksTabPage_Impl::~BookmarksTabPage_Impl()
1190 {
1191     // save bookmarks to configuration
1192     SvtHistoryOptions aHistOpt;
1193     aHistOpt.Clear( EHistoryType::HelpBookmarks );
1194     const sal_Int32 nCount = m_xBookmarksBox->n_children();
1195     for (sal_Int32 i = 0; i < nCount; ++i)
1196         aHistOpt.AppendItem(EHistoryType::HelpBookmarks, m_xBookmarksBox->get_id(i), "", m_xBookmarksBox->get_text(i), std::nullopt);
1197 
1198     m_xBookmarksBox.reset();
1199     m_xBookmarksPB.reset();
1200 }
1201 
IMPL_LINK_NOARG(BookmarksTabPage_Impl,OpenHdl,weld::Button &,void)1202 IMPL_LINK_NOARG(BookmarksTabPage_Impl, OpenHdl, weld::Button&, void)
1203 {
1204     aDoubleClickHdl.Call(nullptr);
1205 }
1206 
IMPL_LINK_NOARG(BookmarksTabPage_Impl,DoubleClickHdl,weld::TreeView &,bool)1207 IMPL_LINK_NOARG(BookmarksTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool)
1208 {
1209     aDoubleClickHdl.Call(nullptr);
1210     return true;
1211 }
1212 
SetDoubleClickHdl(const Link<LinkParamNone *,void> & rLink)1213 void BookmarksTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
1214 {
1215     aDoubleClickHdl = rLink;
1216 }
1217 
GetSelectedEntry() const1218 OUString BookmarksTabPage_Impl::GetSelectedEntry() const
1219 {
1220     return m_xBookmarksBox->get_selected_id();
1221 }
1222 
AddBookmarks(const OUString & rTitle,const OUString & rURL)1223 void BookmarksTabPage_Impl::AddBookmarks(const OUString& rTitle, const OUString& rURL)
1224 {
1225     const OUString aImageURL {IMAGE_URL + INetURLObject(rURL).GetHost()};
1226     m_xBookmarksBox->append(rURL, rTitle, SvFileInformationManager::GetImageId(INetURLObject(aImageURL)));
1227 }
1228 
buildHelpURL(std::u16string_view sFactory,std::u16string_view sContent,std::u16string_view sAnchor)1229 OUString SfxHelpWindow_Impl::buildHelpURL(std::u16string_view sFactory        ,
1230                                           std::u16string_view sContent        ,
1231                                           std::u16string_view sAnchor)
1232 {
1233     OUStringBuffer sHelpURL(256);
1234     sHelpURL.append(HELP_URL);
1235     sHelpURL.append(sFactory);
1236     sHelpURL.append(sContent);
1237     AppendConfigToken(sHelpURL, true/*bUseQuestionMark*/);
1238     if (!sAnchor.empty())
1239         sHelpURL.append(sAnchor);
1240     return sHelpURL.makeStringAndClear();
1241 }
1242 
loadHelpContent(const OUString & sHelpURL,bool bAddToHistory)1243 void SfxHelpWindow_Impl::loadHelpContent(const OUString& sHelpURL, bool bAddToHistory)
1244 {
1245     Reference< XComponentLoader > xLoader(getTextFrame(), UNO_QUERY);
1246     if (!xLoader.is())
1247         return;
1248 
1249     // If a print job runs do not open a new page
1250     Reference< XFrame2 >     xTextFrame      = pTextWin->getFrame();
1251     Reference< XController > xTextController ;
1252     if (xTextFrame.is())
1253         xTextController = xTextFrame->getController ();
1254     if ( xTextController.is() && !xTextController->suspend( true ) )
1255     {
1256         xTextController->suspend( false );
1257         return;
1258     }
1259 
1260     // save url to history
1261     if (bAddToHistory)
1262         pHelpInterceptor->addURL(sHelpURL);
1263 
1264     if ( !IsWait() )
1265         EnterWait();
1266     bool bSuccess = false;
1267 // TODO implement locale fallback ... see below    while(true)
1268     {
1269         try
1270         {
1271             Reference< XComponent > xContent = xLoader->loadComponentFromURL(sHelpURL, "_self", 0, Sequence< PropertyValue >());
1272             if (xContent.is())
1273             {
1274                 bSuccess = true;
1275             }
1276         }
1277         catch(const RuntimeException&)
1278             { throw; }
1279         catch(const Exception&)
1280             { /*break;*/ }
1281 
1282         /* TODO try next locale ...
1283                 no further locale available? => break loop and show error page
1284         */
1285     }
1286     openDone(sHelpURL, bSuccess);
1287     if ( IsWait() )
1288         LeaveWait();
1289 }
1290 
IMPL_LINK(SfxHelpIndexWindow_Impl,ActivatePageHdl,const OString &,rPage,void)1291 IMPL_LINK(SfxHelpIndexWindow_Impl, ActivatePageHdl, const OString&, rPage, void)
1292 {
1293     GetPage(rPage)->Activate();
1294 }
1295 
SfxHelpIndexWindow_Impl(SfxHelpWindow_Impl * _pParent,weld::Container * pContainer)1296 SfxHelpIndexWindow_Impl::SfxHelpIndexWindow_Impl(SfxHelpWindow_Impl* _pParent, weld::Container* pContainer)
1297     : m_xBuilder(Application::CreateBuilder(pContainer, "sfx/ui/helpcontrol.ui"))
1298     , m_xContainer(m_xBuilder->weld_container("HelpControl"))
1299     , m_xActiveLB(m_xBuilder->weld_combo_box("active"))
1300     , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol"))
1301     , aIdle("sfx2 appl SfxHelpIndexWindow_Impl")
1302     , aIndexKeywordLink(LINK(this, SfxHelpIndexWindow_Impl, KeywordHdl))
1303     , pParentWin(_pParent)
1304     , bIsInitDone(false)
1305 {
1306     // create the pages
1307     GetContentPage();
1308     GetIndexPage();
1309     GetSearchPage();
1310     GetBookmarksPage();
1311 
1312     OString sPageId("index");
1313     SvtViewOptions aViewOpt( EViewType::TabDialog, CONFIGNAME_INDEXWIN );
1314     if ( aViewOpt.Exists() )
1315         sPageId = aViewOpt.GetPageID();
1316     m_xTabCtrl->set_current_page(sPageId);
1317     ActivatePageHdl(sPageId);
1318     m_xActiveLB->connect_changed(LINK(this, SfxHelpIndexWindow_Impl, SelectHdl));
1319 
1320     m_xTabCtrl->connect_enter_page(LINK(this, SfxHelpIndexWindow_Impl, ActivatePageHdl));
1321 
1322     aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, InitHdl ) );
1323     aIdle.Start();
1324 
1325     m_xContainer->show();
1326 }
1327 
~SfxHelpIndexWindow_Impl()1328 SfxHelpIndexWindow_Impl::~SfxHelpIndexWindow_Impl()
1329 {
1330     SvtViewOptions aViewOpt(EViewType::TabDialog, CONFIGNAME_INDEXWIN);
1331     aViewOpt.SetPageID(m_xTabCtrl->get_current_page_ident());
1332 
1333     xCPage.reset();
1334     xIPage.reset();
1335     xSPage.reset();
1336     xBPage.reset();
1337 }
1338 
Initialize()1339 void SfxHelpIndexWindow_Impl::Initialize()
1340 {
1341     OUStringBuffer aHelpURL(HELP_URL);
1342     AppendConfigToken(aHelpURL, true);
1343     std::vector<OUString> aFactories = SfxContentHelper::GetResultSet(aHelpURL.makeStringAndClear());
1344     for (const OUString & rRow : aFactories)
1345     {
1346         sal_Int32 nIdx = 0;
1347         OUString aTitle = rRow.getToken( 0, '\t', nIdx ); // token 0
1348         OUString aURL = rRow.getToken( 1, '\t', nIdx ); // token 2
1349         OUString aFactory(INetURLObject(aURL).GetHost());
1350         m_xActiveLB->append(aFactory, aTitle);
1351     }
1352 
1353     if (m_xActiveLB->get_active() == -1)
1354         SetActiveFactory();
1355 }
1356 
SetActiveFactory()1357 void SfxHelpIndexWindow_Impl::SetActiveFactory()
1358 {
1359     DBG_ASSERT( xIPage, "index page not initialized" );
1360     if (!bIsInitDone && !m_xActiveLB->get_count())
1361     {
1362         aIdle.Stop();
1363         InitHdl( nullptr );
1364     }
1365 
1366     for (sal_Int32 i = 0, nEntryCount = m_xActiveLB->get_count(); i < nEntryCount; ++i)
1367     {
1368         OUString aFactory = m_xActiveLB->get_id(i);
1369         aFactory = aFactory.toAsciiLowerCase();
1370         if (aFactory == xIPage->GetFactory())
1371         {
1372             if (m_xActiveLB->get_active() != i)
1373             {
1374                 m_xActiveLB->set_active(i);
1375                 aSelectFactoryLink.Call(nullptr);
1376             }
1377             break;
1378         }
1379     }
1380 }
1381 
GetPage(std::string_view rName)1382 HelpTabPage_Impl* SfxHelpIndexWindow_Impl::GetPage(std::string_view  rName)
1383 {
1384     HelpTabPage_Impl* pPage = nullptr;
1385 
1386     if (rName == "contents")
1387         pPage = GetContentPage();
1388     else if (rName == "index")
1389         pPage = GetIndexPage();
1390     else if (rName == "find")
1391         pPage = GetSearchPage();
1392     else if (rName == "bookmarks")
1393         pPage = GetBookmarksPage();
1394 
1395     assert(pPage && "SfxHelpIndexWindow_Impl::GetCurrentPage(): no current page");
1396 
1397     return pPage;
1398 }
1399 
IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl,SelectHdl,weld::ComboBox &,void)1400 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectHdl, weld::ComboBox&, void)
1401 {
1402     aIdle.Start();
1403 }
1404 
IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl,InitHdl,Timer *,void)1405 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, InitHdl, Timer *, void)
1406 {
1407     bIsInitDone = true;
1408     Initialize();
1409 
1410     // now use the timer for selection
1411     aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, SelectFactoryHdl ) );
1412     aIdle.SetPriority( TaskPriority::LOWEST );
1413 }
1414 
IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl,SelectFactoryHdl,Timer *,void)1415 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectFactoryHdl, Timer *, void)
1416 {
1417     OUString aFactory = m_xActiveLB->get_active_id();
1418     if (!aFactory.isEmpty())
1419     {
1420         SetFactory(aFactory.toAsciiLowerCase(), false);
1421         aSelectFactoryLink.Call(this);
1422     }
1423 }
1424 
IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl,KeywordHdl,IndexTabPage_Impl &,void)1425 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, KeywordHdl, IndexTabPage_Impl&, void)
1426 {
1427     // keyword found on index?
1428     bool bIndex = xIPage->HasKeyword();
1429 
1430     if( !bIndex)
1431         bIndex = xIPage->HasKeywordIgnoreCase();
1432     // then set index or search page as current.
1433     OString sPageId = bIndex ? "index" : "find";
1434     if (sPageId != m_xTabCtrl->get_current_page_ident())
1435         m_xTabCtrl->set_current_page(sPageId);
1436 
1437     // at last we open the keyword
1438     if ( bIndex )
1439         xIPage->OpenKeyword();
1440     else if ( !xSPage->OpenKeyword( sKeyword ) )
1441         pParentWin->ShowStartPage();
1442 }
1443 
IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl,IndexTabPageDoubleClickHdl,LinkParamNone *,void)1444 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, IndexTabPageDoubleClickHdl, LinkParamNone*, void)
1445 {
1446     aPageDoubleClickLink.Call(nullptr);
1447 }
1448 
SetDoubleClickHdl(const Link<LinkParamNone *,void> & rLink)1449 void SfxHelpIndexWindow_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink)
1450 {
1451     aPageDoubleClickLink = rLink;
1452 }
1453 
IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl,ContentTabPageDoubleClickHdl,LinkParamNone *,void)1454 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, ContentTabPageDoubleClickHdl, LinkParamNone*, void)
1455 {
1456     aPageDoubleClickLink.Call(nullptr);
1457 }
1458 
IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl,TabPageDoubleClickHdl,LinkParamNone *,void)1459 IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, TabPageDoubleClickHdl, LinkParamNone*, void)
1460 {
1461     aPageDoubleClickLink.Call(nullptr);
1462 }
1463 
SetFactory(const OUString & rFactory,bool bActive)1464 void SfxHelpIndexWindow_Impl::SetFactory( const OUString& rFactory, bool bActive )
1465 {
1466     if ( !rFactory.isEmpty() )
1467     {
1468         GetIndexPage()->SetFactory( rFactory );
1469         // the index page made a check if rFactory is valid,
1470         // so the index page always returns a valid factory
1471         GetSearchPage()->SetFactory( GetIndexPage()->GetFactory() );
1472         if ( bActive )
1473             SetActiveFactory();
1474     }
1475 }
1476 
GetSelectedEntry() const1477 OUString SfxHelpIndexWindow_Impl::GetSelectedEntry() const
1478 {
1479     OUString sRet;
1480 
1481     OString sName(m_xTabCtrl->get_current_page_ident());
1482 
1483     if (sName == "contents")
1484     {
1485         sRet = xCPage->GetSelectedEntry();
1486     }
1487     else if (sName == "index")
1488     {
1489         sRet = xIPage->GetSelectedEntry();
1490     }
1491     else if (sName == "find")
1492     {
1493         sRet = xSPage->GetSelectedEntry();
1494     }
1495     else if (sName == "bookmarks")
1496     {
1497         sRet = xBPage->GetSelectedEntry();
1498     }
1499 
1500     return sRet;
1501 }
1502 
AddBookmarks(const OUString & rTitle,const OUString & rURL)1503 void SfxHelpIndexWindow_Impl::AddBookmarks( const OUString& rTitle, const OUString& rURL )
1504 {
1505     GetBookmarksPage()->AddBookmarks( rTitle, rURL );
1506 }
1507 
IsValidFactory(std::u16string_view _rFactory)1508 bool SfxHelpIndexWindow_Impl::IsValidFactory( std::u16string_view _rFactory )
1509 {
1510     bool bValid = false;
1511     for (sal_Int32 i = 0, nEntryCount = m_xActiveLB->get_count(); i < nEntryCount; ++i)
1512     {
1513         OUString aFactory = m_xActiveLB->get_id(i);
1514         if (aFactory == _rFactory)
1515         {
1516             bValid = true;
1517             break;
1518         }
1519     }
1520     return bValid;
1521 }
1522 
ClearSearchPage()1523 void SfxHelpIndexWindow_Impl::ClearSearchPage()
1524 {
1525     if ( xSPage )
1526         xSPage->ClearPage();
1527 }
1528 
GrabFocusBack()1529 void SfxHelpIndexWindow_Impl::GrabFocusBack()
1530 {
1531     OString sName(m_xTabCtrl->get_current_page_ident());
1532 
1533     if (sName == "contents" && xCPage)
1534         xCPage->SetFocusOnBox();
1535     else if (sName == "index" && xIPage)
1536         xIPage->SetFocusOnBox();
1537     else if (sName == "find" && xSPage)
1538         xSPage->SetFocusOnBox();
1539     else if (sName == "bookmarks" && xBPage)
1540         xBPage->SetFocusOnBox();
1541 }
1542 
HasFocusOnEdit() const1543 bool SfxHelpIndexWindow_Impl::HasFocusOnEdit() const
1544 {
1545     bool bRet = false;
1546     OString sName(m_xTabCtrl->get_current_page_ident());
1547     if (sName == "index" && xIPage)
1548         bRet = xIPage->HasFocusOnEdit();
1549     else if (sName == "find" && xSPage)
1550         bRet = xSPage->HasFocusOnEdit();
1551     return bRet;
1552 }
1553 
GetSearchText() const1554 OUString SfxHelpIndexWindow_Impl::GetSearchText() const
1555 {
1556     OUString sRet;
1557     OString sName(m_xTabCtrl->get_current_page_ident());
1558     if (sName == "find" && xSPage)
1559         sRet = xSPage->GetSearchText();
1560     return sRet;
1561 }
1562 
IsFullWordSearch() const1563 bool SfxHelpIndexWindow_Impl::IsFullWordSearch() const
1564 {
1565     bool bRet = false;
1566     OString sName(m_xTabCtrl->get_current_page_ident());
1567     if (sName == "find" && xSPage)
1568         bRet = xSPage->IsFullWordSearch();
1569     return bRet;
1570 }
1571 
OpenKeyword(const OUString & rKeyword)1572 void SfxHelpIndexWindow_Impl::OpenKeyword( const OUString& rKeyword )
1573 {
1574     sKeyword = rKeyword;
1575     DBG_ASSERT( xIPage, "invalid index page" );
1576     xIPage->SetKeyword( sKeyword );
1577 }
1578 
SelectExecutableEntry()1579 void SfxHelpIndexWindow_Impl::SelectExecutableEntry()
1580 {
1581     OString sName(m_xTabCtrl->get_current_page_ident());
1582     if (sName == "index" && xIPage )
1583         xIPage->SelectExecutableEntry();
1584 }
1585 
GetFrameWeld() const1586 weld::Window* SfxHelpIndexWindow_Impl::GetFrameWeld() const
1587 {
1588     return pParentWin->GetFrameWeld();
1589 }
1590 
1591 // class TextWin_Impl ----------------------------------------------------
TextWin_Impl(vcl::Window * p)1592 TextWin_Impl::TextWin_Impl( vcl::Window* p ) : DockingWindow( p, 0 )
1593 {
1594 }
1595 
EventNotify(NotifyEvent & rNEvt)1596 bool TextWin_Impl::EventNotify( NotifyEvent& rNEvt )
1597 {
1598     if( ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT ) && rNEvt.GetKeyEvent()->GetKeyCode().GetCode() == KEY_TAB )
1599         return GetParent()->EventNotify( rNEvt );
1600     else
1601         return DockingWindow::EventNotify( rNEvt );
1602 }
1603 
1604 
1605 // remove docking area acceptor from layoutmanager, so it will not layout anything further .-)
lcl_disableLayoutOfFrame(const Reference<XFrame2> & xFrame)1606 static void lcl_disableLayoutOfFrame(const Reference< XFrame2 >& xFrame)
1607 {
1608     xFrame->setLayoutManager( Reference< XLayoutManager >() );
1609 }
1610 
1611 // class SfxHelpTextWindow_Impl ------------------------------------------
1612 
SfxHelpTextWindow_Impl(SfxHelpWindow_Impl * pHelpWin,weld::Builder & rBuilder,vcl::Window * pParent)1613 SfxHelpTextWindow_Impl::SfxHelpTextWindow_Impl(SfxHelpWindow_Impl* pHelpWin, weld::Builder& rBuilder, vcl::Window* pParent) :
1614 
1615     Window( pParent, WB_CLIPCHILDREN | WB_TABSTOP | WB_DIALOGCONTROL ),
1616 
1617     xToolBox            ( rBuilder.weld_toolbar("toolbar") ),
1618     xOnStartupCB        ( rBuilder.weld_check_button("checkbutton") ),
1619     xMenu               ( rBuilder.weld_menu("menu") ),
1620     aSelectIdle         ( "sfx2 appl SfxHelpTextWindow_Impl Select" ),
1621     aIndexOnImage       ( BMP_HELP_TOOLBOX_INDEX_ON ),
1622     aIndexOffImage      ( BMP_HELP_TOOLBOX_INDEX_OFF ),
1623     aIndexOnText        ( SfxResId( STR_HELP_BUTTON_INDEX_ON ) ),
1624     aIndexOffText       ( SfxResId( STR_HELP_BUTTON_INDEX_OFF ) ),
1625     aOnStartupText      ( SfxResId( RID_HELP_ONSTARTUP_TEXT ) ),
1626     xHelpWin            ( pHelpWin ),
1627     pTextWin            ( VclPtr<TextWin_Impl>::Create( this ) ),
1628     bIsDebug            ( false ),
1629     bIsIndexOn          ( false ),
1630     bIsInClose          ( false ),
1631     bIsFullWordSearch   ( false )
1632 {
1633     xFrame = Frame::create( ::comphelper::getProcessComponentContext() );
1634     xFrame->initialize( VCLUnoHelper::GetInterface ( pTextWin ) );
1635     xFrame->setName( "OFFICE_HELP" );
1636     lcl_disableLayoutOfFrame(xFrame);
1637 
1638     xToolBox->set_help_id(HID_HELP_TOOLBOX);
1639 
1640     xToolBox->set_item_tooltip_text("index", aIndexOffText );
1641     xToolBox->set_item_help_id("index", HID_HELP_TOOLBOXITEM_INDEX);
1642     xToolBox->set_item_help_id("backward", HID_HELP_TOOLBOXITEM_BACKWARD);
1643     xToolBox->set_item_help_id("forward", HID_HELP_TOOLBOXITEM_FORWARD);
1644     xToolBox->set_item_help_id("start", HID_HELP_TOOLBOXITEM_START);
1645     xToolBox->set_item_help_id("print", HID_HELP_TOOLBOXITEM_PRINT);
1646     xToolBox->set_item_help_id("bookmarks", HID_HELP_TOOLBOXITEM_BOOKMARKS );
1647     xToolBox->set_item_help_id("searchdialog", HID_HELP_TOOLBOXITEM_SEARCHDIALOG);
1648 
1649     InitToolBoxImages();
1650     InitOnStartupBox();
1651     xOnStartupCB->connect_toggled(LINK(this, SfxHelpTextWindow_Impl, CheckHdl));
1652 
1653     aSelectIdle.SetInvokeHandler( LINK( this, SfxHelpTextWindow_Impl, SelectHdl ) );
1654     aSelectIdle.SetPriority( TaskPriority::LOWEST );
1655 
1656     char* pEnv = getenv( "help_debug" );
1657     if ( pEnv )
1658         bIsDebug = true;
1659 
1660     SvtMiscOptions().AddListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) );
1661 }
1662 
~SfxHelpTextWindow_Impl()1663 SfxHelpTextWindow_Impl::~SfxHelpTextWindow_Impl()
1664 {
1665     disposeOnce();
1666 }
1667 
dispose()1668 void SfxHelpTextWindow_Impl::dispose()
1669 {
1670     bIsInClose = true;
1671     SvtMiscOptions().RemoveListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) );
1672     m_xSrchDlg.reset();
1673     xToolBox.reset();
1674     xOnStartupCB.reset();
1675     xHelpWin.clear();
1676     pTextWin.disposeAndClear();
1677     vcl::Window::dispose();
1678 }
1679 
HasSelection() const1680 bool SfxHelpTextWindow_Impl::HasSelection() const
1681 {
1682     // is there any selection in the text and not only a cursor?
1683     bool bRet = false;
1684     Reference < XTextRange > xRange = getCursor();
1685     if ( xRange.is() )
1686     {
1687         Reference < XText > xText = xRange->getText();
1688         Reference < XTextCursor > xCursor = xText->createTextCursorByRange( xRange );
1689         bRet = !xCursor->isCollapsed();
1690     }
1691 
1692     return bRet;
1693 }
1694 
InitToolBoxImages()1695 void SfxHelpTextWindow_Impl::InitToolBoxImages()
1696 {
1697     xToolBox->set_item_icon_name("index", bIsIndexOn ? aIndexOffImage : aIndexOnImage);
1698 }
1699 
InitOnStartupBox()1700 void SfxHelpTextWindow_Impl::InitOnStartupBox()
1701 {
1702     sCurrentFactory = SfxHelp::GetCurrentModuleIdentifier();
1703 
1704     Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
1705     const OUString sPath { PATH_OFFICE_FACTORIES + sCurrentFactory };
1706 
1707     // Attention: This check boy knows two states:
1708     // 1) Reading of the config key fails with an exception or by getting an empty Any (!) => check box must be hidden
1709     // 2) We read sal_True/sal_False => check box must be shown and enabled/disabled
1710 
1711     bool bHideBox = true;
1712     bool bHelpAtStartup = false;
1713     try
1714     {
1715         xConfiguration = ConfigurationHelper::openConfig(
1716             xContext, PACKAGE_SETUP, EConfigurationModes::Standard );
1717         if ( xConfiguration.is() )
1718         {
1719             Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, KEY_HELP_ON_OPEN );
1720             if (aAny >>= bHelpAtStartup)
1721                 bHideBox = false;
1722         }
1723     }
1724     catch( Exception& )
1725     {
1726         bHideBox = true;
1727     }
1728 
1729     if ( bHideBox )
1730         xOnStartupCB->hide();
1731     else
1732     {
1733         // detect module name
1734         OUString sModuleName;
1735 
1736         if ( xConfiguration.is() )
1737         {
1738             OUString sTemp;
1739             try
1740             {
1741                 Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, KEY_UI_NAME );
1742                 aAny >>= sTemp;
1743             }
1744             catch( Exception const & )
1745             {
1746                 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::InitOnStartupBox()" );
1747             }
1748             sModuleName = sTemp;
1749         }
1750 
1751         if ( !sModuleName.isEmpty() )
1752         {
1753             // set module name in checkbox text
1754             xOnStartupCB->set_label(aOnStartupText.replaceFirst("%MODULENAME", sModuleName));
1755             // and show it
1756             xOnStartupCB->show();
1757             // set check state
1758             xOnStartupCB->set_active(bHelpAtStartup);
1759             xOnStartupCB->save_state();
1760         }
1761     }
1762 }
1763 
GetBreakIterator()1764 Reference< XBreakIterator > const & SfxHelpTextWindow_Impl::GetBreakIterator()
1765 {
1766     if ( !xBreakIterator.is() )
1767         xBreakIterator = vcl::unohelper::CreateBreakIterator();
1768     DBG_ASSERT( xBreakIterator.is(), "Could not create BreakIterator" );
1769     return xBreakIterator;
1770 }
1771 
getCursor() const1772 Reference< XTextRange > SfxHelpTextWindow_Impl::getCursor() const
1773 {
1774     // return the current cursor
1775     Reference< XTextRange > xCursor;
1776 
1777     try
1778     {
1779         Reference < XSelectionSupplier > xSelSup( xFrame->getController(), UNO_QUERY );
1780         if ( xSelSup.is() )
1781         {
1782             Any aAny = xSelSup->getSelection();
1783             Reference < XIndexAccess > xSelection;
1784             if ( aAny >>= xSelection )
1785             {
1786                 if ( xSelection->getCount() == 1 )
1787                 {
1788                     aAny = xSelection->getByIndex(0);
1789                     aAny >>= xCursor;
1790                 }
1791             }
1792         }
1793     }
1794     catch( Exception& )
1795     {
1796         TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::getCursor(): unexpected exception" );
1797     }
1798 
1799     return xCursor;
1800 }
1801 
1802 
isHandledKey(const vcl::KeyCode & _rKeyCode)1803 bool SfxHelpTextWindow_Impl::isHandledKey( const vcl::KeyCode& _rKeyCode )
1804 {
1805     bool bRet = false;
1806     sal_uInt16 nCode = _rKeyCode.GetCode();
1807 
1808     // the keys <CTRL><A> (select all), <CTRL><C> (copy),
1809     //          <CTRL><F> (find), <CTRL><P> (print) and <CTRL><W> (close window)
1810     // were handled in help
1811     if ( _rKeyCode.IsMod1() &&
1812          ( KEY_A == nCode || KEY_C == nCode || KEY_F == nCode || KEY_P == nCode || KEY_W == nCode ) )
1813     {
1814         if ( KEY_F == nCode )
1815             DoSearch();
1816         else
1817             bRet = true;
1818     }
1819 
1820     return bRet;
1821 }
1822 
1823 
IMPL_LINK_NOARG(SfxHelpTextWindow_Impl,SelectHdl,Timer *,void)1824 IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, SelectHdl, Timer *, void)
1825 {
1826     try
1827     {
1828         // select the words, which are equal to the search text of the search page
1829         Reference < XController > xController = xFrame->getController();
1830         if ( xController.is() )
1831         {
1832             // get document
1833             Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY );
1834             if ( xSearchable.is() )
1835             {
1836                 // create descriptor, set string and find all words
1837                 Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor();
1838                 xSrchDesc->setPropertyValue( "SearchRegularExpression", makeAny( true ) );
1839                 if ( bIsFullWordSearch )
1840                     xSrchDesc->setPropertyValue( "SearchWords", makeAny( true ) );
1841 
1842                 xSrchDesc->setSearchString( sfx2::PrepareSearchString( aSearchText, GetBreakIterator(), false ) );
1843                 Reference< XIndexAccess > xSelection = xSearchable->findAll( xSrchDesc );
1844 
1845                 // then select all found words
1846                 Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY );
1847                 if ( xSelectionSup.is() )
1848                 {
1849                     xSelectionSup->select( Any(xSelection) );
1850                 }
1851             }
1852         }
1853     }
1854     catch( Exception& )
1855     {
1856         TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" );
1857     }
1858 }
1859 
1860 
IMPL_LINK_NOARG(SfxHelpTextWindow_Impl,NotifyHdl,LinkParamNone *,void)1861 IMPL_LINK_NOARG( SfxHelpTextWindow_Impl, NotifyHdl, LinkParamNone*, void )
1862 {
1863     InitToolBoxImages();
1864     Resize();
1865 }
1866 
IMPL_LINK(SfxHelpTextWindow_Impl,FindHdl,sfx2::SearchDialog &,rDlg,void)1867 IMPL_LINK( SfxHelpTextWindow_Impl, FindHdl, sfx2::SearchDialog&, rDlg, void )
1868 {
1869     FindHdl(&rDlg);
1870 }
FindHdl(sfx2::SearchDialog * pDlg)1871 void SfxHelpTextWindow_Impl::FindHdl(sfx2::SearchDialog* pDlg)
1872 {
1873     bool bWrapAround = ( nullptr == pDlg );
1874     if ( bWrapAround )
1875         pDlg = m_xSrchDlg.get();
1876     DBG_ASSERT( pDlg, "invalid search dialog" );
1877     try
1878     {
1879         // select the words, which are equal to the search text of the search page
1880         Reference < XController > xController = xFrame->getController();
1881         if ( xController.is() )
1882         {
1883             // get document
1884             Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY );
1885             if ( xSearchable.is() )
1886             {
1887                 // create descriptor, set string and find all words
1888                 Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor();
1889                 xSrchDesc->setPropertyValue( "SearchWords", makeAny(pDlg->IsOnlyWholeWords()) );
1890                 xSrchDesc->setPropertyValue( "SearchCaseSensitive", makeAny(pDlg->IsMarchCase()) );
1891                 xSrchDesc->setPropertyValue( "SearchBackwards", makeAny(pDlg->IsSearchBackwards()) );
1892                 xSrchDesc->setSearchString( pDlg->GetSearchText() );
1893                 Reference< XInterface > xSelection;
1894                 Reference< XTextRange > xCursor = getCursor();
1895 
1896                 if ( xCursor.is() )
1897                 {
1898                     if ( pDlg->IsSearchBackwards() )
1899                         xCursor = xCursor->getStart();
1900                     xSelection = xSearchable->findNext( xCursor, xSrchDesc );
1901                 }
1902                 else
1903                     xSelection = xSearchable->findFirst( xSrchDesc );
1904 
1905                 // then select the found word
1906                 if ( xSelection.is() )
1907                 {
1908                     Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY );
1909                     if ( xSelectionSup.is() )
1910                     {
1911                         xSelectionSup->select( Any(xSelection) );
1912                     }
1913                 }
1914                 else if ( pDlg->IsWrapAround() && !bWrapAround )
1915                 {
1916                     Reference < text::XTextViewCursorSupplier > xCrsrSupp( xController, uno::UNO_QUERY );
1917                     Reference < text::XTextViewCursor > xTVCrsr = xCrsrSupp->getViewCursor();
1918                     if ( xTVCrsr.is() )
1919                     {
1920                         Reference < text::XTextDocument > xDoc( xController->getModel(), uno::UNO_QUERY );
1921                         Reference < text::XText > xText = xDoc->getText();
1922                         if ( xText.is() )
1923                         {
1924                             if ( pDlg->IsSearchBackwards() )
1925                                 xTVCrsr->gotoRange( xText->getEnd(), false );
1926                             else
1927                                 xTVCrsr->gotoRange( xText->getStart(), false );
1928                             FindHdl( nullptr );
1929                         }
1930                     }
1931                 }
1932                 else
1933                 {
1934                     assert(m_xSrchDlg && "no search dialog");
1935                     std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xSrchDlg->getDialog(),
1936                                                               VclMessageType::Info, VclButtonsType::Ok, SfxResId(STR_INFO_NOSEARCHTEXTFOUND)));
1937                     xBox->run();
1938                     m_xSrchDlg->SetFocusOnEdit();
1939                 }
1940             }
1941         }
1942     }
1943     catch( Exception& )
1944     {
1945         TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" );
1946     }
1947 }
1948 
IMPL_LINK_NOARG(SfxHelpTextWindow_Impl,CloseHdl,LinkParamNone *,void)1949 IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, CloseHdl, LinkParamNone*, void)
1950 {
1951     m_xSrchDlg.reset();
1952 }
1953 
IMPL_LINK_NOARG(SfxHelpTextWindow_Impl,CheckHdl,weld::Toggleable &,void)1954 IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, CheckHdl, weld::Toggleable&, void)
1955 {
1956     if ( !xConfiguration.is() )
1957         return;
1958 
1959     bool bChecked = xOnStartupCB->get_active();
1960     try
1961     {
1962         ConfigurationHelper::writeRelativeKey(
1963             xConfiguration, PATH_OFFICE_FACTORIES + sCurrentFactory, KEY_HELP_ON_OPEN, makeAny( bChecked ) );
1964         ConfigurationHelper::flush( xConfiguration );
1965     }
1966     catch( Exception const & )
1967     {
1968         TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::CheckHdl()" );
1969     }
1970 }
1971 
Resize()1972 void SfxHelpTextWindow_Impl::Resize()
1973 {
1974     Size aSize = GetOutputSizePixel();
1975     pTextWin->SetPosSizePixel( Point(0, 0), aSize );
1976 }
1977 
PreNotify(NotifyEvent & rNEvt)1978 bool SfxHelpTextWindow_Impl::PreNotify( NotifyEvent& rNEvt )
1979 {
1980     bool bDone = false;
1981     MouseNotifyEvent nType = rNEvt.GetType();
1982     if ( MouseNotifyEvent::COMMAND == nType && rNEvt.GetCommandEvent() )
1983     {
1984         const CommandEvent* pCmdEvt = rNEvt.GetCommandEvent();
1985         vcl::Window* pCmdWin = rNEvt.GetWindow();
1986 
1987         if ( pCmdEvt->GetCommand() == CommandEventId::ContextMenu && pCmdWin != this )
1988         {
1989             Point aPos;
1990             if ( pCmdEvt->IsMouseEvent() )
1991                 aPos = pCmdEvt->GetMousePosPixel();
1992             else
1993                 aPos = Point( pTextWin->GetPosPixel().X() + 20, 20 );
1994 
1995             xMenu->clear();
1996 
1997             if (bIsIndexOn)
1998                 xMenu->append("index", aIndexOffText, BMP_HELP_TOOLBOX_INDEX_OFF);
1999             else
2000                 xMenu->append("index", aIndexOnText, BMP_HELP_TOOLBOX_INDEX_ON);
2001 
2002             xMenu->append_separator("separator1");
2003             xMenu->append("backward", SfxResId(STR_HELP_BUTTON_PREV), BMP_HELP_TOOLBOX_PREV);
2004             xMenu->set_sensitive("backward", xHelpWin->HasHistoryPredecessor());
2005             xMenu->append("forward", SfxResId(STR_HELP_BUTTON_NEXT), BMP_HELP_TOOLBOX_NEXT);
2006             xMenu->set_sensitive("forward", xHelpWin->HasHistorySuccessor());
2007             xMenu->append("start", SfxResId(STR_HELP_BUTTON_START), BMP_HELP_TOOLBOX_START);
2008             xMenu->append_separator("separator2");
2009             xMenu->append("print", SfxResId(STR_HELP_BUTTON_PRINT), BMP_HELP_TOOLBOX_PRINT);
2010             xMenu->append("bookmarks", SfxResId(STR_HELP_BUTTON_ADDBOOKMARK), BMP_HELP_TOOLBOX_BOOKMARKS);
2011             xMenu->append("searchdialog", SfxResId(STR_HELP_BUTTON_SEARCHDIALOG), BMP_HELP_TOOLBOX_SEARCHDIALOG);
2012             xMenu->append_separator("separator3");
2013             xMenu->append_check("selectionmode", SfxResId(STR_HELP_MENU_TEXT_SELECTION_MODE));
2014             URL aURL;
2015             aURL.Complete = ".uno:SelectTextMode";
2016             Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
2017             xTrans->parseStrict(aURL);
2018             Reference < XDispatch > xDisp = xFrame->queryDispatch( aURL, OUString(), 0 );
2019             if(xDisp.is())
2020             {
2021                 rtl::Reference<HelpStatusListener_Impl> pStateListener =
2022                                         new HelpStatusListener_Impl(xDisp, aURL );
2023                 FeatureStateEvent rEvent = pStateListener->GetStateEvent();
2024                 bool bCheck = false;
2025                 rEvent.State >>= bCheck;
2026                 xMenu->set_active("selectionmode", bCheck);
2027             }
2028             xMenu->append_separator("separator4");
2029             xMenu->append("copy", SfxResId(STR_HELP_MENU_TEXT_COPY), BMP_HELP_TOOLBOX_COPY);
2030             xMenu->set_sensitive("copy", HasSelection());
2031 
2032             if ( bIsDebug )
2033             {
2034                 xMenu->append_separator("separator5");
2035                 xMenu->append("sourceview", SfxResId(STR_HELP_BUTTON_SOURCEVIEW));
2036             }
2037 
2038             int x, y, width, height;
2039             weld::Window* pTopLevel = GetFrameWeld();
2040             xHelpWin->GetContainer()->get_extents_relative_to(*pTopLevel, x, y, width, height);
2041             aPos.AdjustX(x);
2042             aPos.AdjustY(y);
2043 
2044             xHelpWin->DoAction(xMenu->popup_at_rect(pTopLevel, tools::Rectangle(aPos, Size(1,1))));
2045             bDone = true;
2046         }
2047     }
2048     else if ( MouseNotifyEvent::KEYINPUT == nType && rNEvt.GetKeyEvent() )
2049     {
2050         const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
2051         const vcl::KeyCode& rKeyCode = pKEvt->GetKeyCode();
2052         sal_uInt16 nKeyGroup = rKeyCode.GetGroup();
2053         sal_uInt16 nKey = rKeyCode.GetCode();
2054         if ( KEYGROUP_ALPHA == nKeyGroup &&  !isHandledKey( rKeyCode ) )
2055         {
2056             // do nothing disables the writer accelerators
2057             bDone = true;
2058          }
2059         else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) )
2060         {
2061             // <CTRL><F4> or <CTRL><W> -> close top frame
2062             xHelpWin->CloseWindow();
2063             bDone = true;
2064         }
2065         else if ( KEY_TAB == nKey && xOnStartupCB->has_focus() )
2066         {
2067             xToolBox->grab_focus();
2068             bDone = true;
2069         }
2070     }
2071 
2072     return bDone || Window::PreNotify( rNEvt );
2073 }
2074 
2075 
GetFocus()2076 void SfxHelpTextWindow_Impl::GetFocus()
2077 {
2078     if ( bIsInClose )
2079         return;
2080 
2081     try
2082     {
2083         if( xFrame.is() )
2084         {
2085             Reference< css::awt::XWindow > xWindow = xFrame->getComponentWindow();
2086             if( xWindow.is() )
2087                 xWindow->setFocus();
2088         }
2089     }
2090     catch( Exception const & )
2091     {
2092         TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::GetFocus()" );
2093     }
2094 }
2095 
2096 
DataChanged(const DataChangedEvent & rDCEvt)2097 void SfxHelpTextWindow_Impl::DataChanged( const DataChangedEvent& rDCEvt )
2098 {
2099     Window::DataChanged( rDCEvt );
2100 
2101     if ( ( ( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) ||
2102            ( rDCEvt.GetType() == DataChangedEventType::DISPLAY ) ) &&
2103          ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) )
2104     {
2105         SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFaceColor() ) );
2106         InitToolBoxImages();
2107     }
2108 }
2109 
ToggleIndex(bool bOn)2110 void SfxHelpTextWindow_Impl::ToggleIndex( bool bOn )
2111 {
2112     bIsIndexOn = bOn;
2113     if ( bIsIndexOn )
2114     {
2115         xToolBox->set_item_icon_name("index", aIndexOffImage);
2116         xToolBox->set_item_tooltip_text("index", aIndexOffText);
2117     }
2118     else
2119     {
2120         xToolBox->set_item_icon_name("index", aIndexOnImage);
2121         xToolBox->set_item_tooltip_text("index", aIndexOnText);
2122     }
2123 }
2124 
SelectSearchText(const OUString & rSearchText,bool _bIsFullWordSearch)2125 void SfxHelpTextWindow_Impl::SelectSearchText( const OUString& rSearchText, bool _bIsFullWordSearch )
2126 {
2127     aSearchText = rSearchText;
2128     bIsFullWordSearch = _bIsFullWordSearch;
2129     aSelectIdle.Start();
2130 }
2131 
2132 
SetPageStyleHeaderOff() const2133 void SfxHelpTextWindow_Impl::SetPageStyleHeaderOff() const
2134 {
2135     bool bSetOff = false;
2136     // set off the pagestyle header to prevent print output of the help URL
2137     try
2138     {
2139         Reference < XController > xController = xFrame->getController();
2140         Reference < XSelectionSupplier > xSelSup( xController, UNO_QUERY );
2141         if ( xSelSup.is() )
2142         {
2143             Reference < XIndexAccess > xSelection;
2144             if ( xSelSup->getSelection() >>= xSelection )
2145             {
2146                 Reference < XTextRange > xRange;
2147                 if ( xSelection->getByIndex(0) >>= xRange )
2148                 {
2149                     Reference < XText > xText = xRange->getText();
2150                     Reference < XPropertySet > xProps( xText->createTextCursorByRange( xRange ), UNO_QUERY );
2151                     OUString sStyleName;
2152                     if ( xProps->getPropertyValue( "PageStyleName" ) >>= sStyleName )
2153                     {
2154                         Reference < XStyleFamiliesSupplier > xStyles( xController->getModel(), UNO_QUERY );
2155                         Reference < XNameContainer > xContainer;
2156                         if ( xStyles->getStyleFamilies()->getByName( "PageStyles" )
2157                              >>= xContainer )
2158                         {
2159                             Reference < XStyle > xStyle;
2160                             if ( xContainer->getByName( sStyleName ) >>= xStyle )
2161                             {
2162                                 Reference < XPropertySet > xPropSet( xStyle, UNO_QUERY );
2163                                 xPropSet->setPropertyValue( "HeaderIsOn",  makeAny( false ) );
2164 
2165                                 Reference< XModifiable > xReset(xStyles, UNO_QUERY);
2166                                 xReset->setModified(false);
2167                                 bSetOff = true;
2168                             }
2169                         }
2170                     }
2171                 }
2172             }
2173         }
2174     }
2175     catch( Exception const & )
2176     {
2177         TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff()" );
2178     }
2179 
2180     SAL_WARN_IF( !bSetOff, "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff(): set off failed" );
2181 }
2182 
2183 
CloseFrame()2184 void SfxHelpTextWindow_Impl::CloseFrame()
2185 {
2186     bIsInClose = true;
2187     try
2188     {
2189         css::uno::Reference< css::util::XCloseable > xCloseable  ( xFrame, css::uno::UNO_QUERY );
2190         if (xCloseable.is())
2191             xCloseable->close(true);
2192     }
2193     catch( css::util::CloseVetoException& )
2194     {
2195     }
2196 }
2197 
2198 
DoSearch()2199 void SfxHelpTextWindow_Impl::DoSearch()
2200 {
2201     if (m_xSrchDlg)
2202         return;
2203 
2204     // create the search dialog
2205     m_xSrchDlg = std::make_shared<sfx2::SearchDialog>(pTextWin->GetFrameWeld(), "HelpSearchDialog");
2206     // set handler
2207     m_xSrchDlg->SetFindHdl( LINK( this, SfxHelpTextWindow_Impl, FindHdl ) );
2208     m_xSrchDlg->SetCloseHdl( LINK( this, SfxHelpTextWindow_Impl, CloseHdl ) );
2209     // get selected text of the help page to set it as the search text
2210     Reference< XTextRange > xCursor = getCursor();
2211     if ( xCursor.is() )
2212     {
2213         OUString sText = xCursor->getString();
2214         if ( !sText.isEmpty() )
2215             m_xSrchDlg->SetSearchText( sText );
2216     }
2217     sfx2::SearchDialog::runAsync(m_xSrchDlg);
2218 }
2219 
GetFocus()2220 void SfxHelpWindow_Impl::GetFocus()
2221 {
2222     if (pTextWin)
2223         pTextWin->GrabFocus();
2224     else
2225         ResizableDockingWindow::GetFocus();
2226 }
2227 
MakeLayout()2228 void SfxHelpWindow_Impl::MakeLayout()
2229 {
2230     Split();
2231 
2232     m_xHelpPaneWindow->set_visible(bIndex);
2233 }
2234 
IMPL_LINK(SfxHelpWindow_Impl,ResizeHdl,const Size &,rSize,void)2235 IMPL_LINK(SfxHelpWindow_Impl, ResizeHdl, const Size&, rSize, void)
2236 {
2237     int nNewWidth = rSize.Width();
2238     if (!nNewWidth)
2239         return;
2240     if (bSplit)
2241         nIndexSize = round(m_xContainer->get_position() * 100.0 / nNewWidth);
2242     nWidth = nNewWidth;
2243     Split();
2244     nIndexSize = round(m_xContainer->get_position() * 100.0 / nWidth);
2245 }
2246 
Split()2247 void SfxHelpWindow_Impl::Split()
2248 {
2249     if (!nWidth)
2250         return;
2251     m_xContainer->set_position(nWidth * nIndexSize / 100);
2252     bSplit = true;
2253 }
2254 
LoadConfig()2255 void SfxHelpWindow_Impl::LoadConfig()
2256 {
2257     SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN );
2258     if ( !aViewOpt.Exists() )
2259         return;
2260     bIndex = aViewOpt.IsVisible();
2261 
2262     Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
2263     OUString aUserData;
2264     if ( aUserItem >>= aUserData )
2265     {
2266         DBG_ASSERT( comphelper::string::getTokenCount(aUserData, ';') == 6, "invalid user data" );
2267         sal_Int32 nIdx = 0;
2268         nIndexSize = aUserData.getToken( 0, ';', nIdx ).toInt32();
2269         aUserData.getToken(0, ';', nIdx); // ignore nTextSize
2270         sal_Int32 nOldWidth = aUserData.getToken( 0, ';', nIdx ).toInt32();
2271         sal_Int32 nOldHeight = aUserData.getToken( 0, ';', nIdx ).toInt32();
2272         aWinSize = Size(nOldWidth, nOldHeight);
2273         aWinPos.setX( aUserData.getToken( 0, ';', nIdx ).toInt32() );
2274         aWinPos.setY( aUserData.getToken( 0, ';', nIdx ).toInt32() );
2275     }
2276 
2277     pTextWin->ToggleIndex( bIndex );
2278 }
2279 
SaveConfig()2280 void SfxHelpWindow_Impl::SaveConfig()
2281 {
2282     SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN );
2283     sal_Int32 nW = 0, nH = 0;
2284 
2285     if ( xWindow.is() )
2286     {
2287         css::awt::Rectangle aRect = xWindow->getPosSize();
2288         nW = aRect.Width;
2289         nH = aRect.Height;
2290     }
2291 
2292     aViewOpt.SetVisible( bIndex );
2293     VclPtr<vcl::Window> pScreenWin = VCLUnoHelper::GetWindow( xWindow );
2294     aWinPos = pScreenWin->GetWindowExtentsRelative( nullptr ).TopLeft();
2295     if (bSplit)
2296         nIndexSize = round(m_xContainer->get_position() * 100.0 / nWidth);
2297     const OUString aUserData = OUString::number( nIndexSize )
2298         + ";" + OUString::number( 100 - nIndexSize )
2299         + ";" + OUString::number( nW )
2300         + ";" + OUString::number( nH )
2301         + ";" + OUString::number( aWinPos.X() )
2302         + ";" + OUString::number( aWinPos.Y() );
2303 
2304     aViewOpt.SetUserItem( USERITEM_NAME, makeAny( aUserData ) );
2305 }
2306 
ShowStartPage()2307 void SfxHelpWindow_Impl::ShowStartPage()
2308 {
2309     loadHelpContent(SfxHelpWindow_Impl::buildHelpURL(xIndexWin->GetFactory(), u"/start", u""));
2310 }
2311 
IMPL_LINK(SfxHelpWindow_Impl,SelectHdl,const OString &,rCurItem,void)2312 IMPL_LINK(SfxHelpWindow_Impl, SelectHdl, const OString&, rCurItem, void)
2313 {
2314     bGrabFocusToToolBox = pTextWin->GetToolBox().has_focus();
2315     DoAction(rCurItem);
2316 }
2317 
IMPL_LINK_NOARG(SfxHelpWindow_Impl,OpenHdl,LinkParamNone *,void)2318 IMPL_LINK_NOARG(SfxHelpWindow_Impl, OpenHdl, LinkParamNone*, void)
2319 {
2320     xIndexWin->SelectExecutableEntry();
2321     OUString aEntry = xIndexWin->GetSelectedEntry();
2322 
2323     if ( aEntry.isEmpty() )
2324         return;
2325 
2326     OUString sHelpURL;
2327 
2328     bool bComplete = aEntry.toAsciiLowerCase().match("vnd.sun.star.help");
2329 
2330     if (bComplete)
2331         sHelpURL = aEntry;
2332     else
2333     {
2334         OUString aId;
2335         OUString aAnchor('#');
2336         if ( comphelper::string::getTokenCount(aEntry, '#') == 2 )
2337         {
2338             sal_Int32 nIdx{ 0 };
2339             aId = aEntry.getToken( 0, '#', nIdx );
2340             aAnchor += aEntry.getToken( 0, '#', nIdx );
2341         }
2342         else
2343             aId = aEntry;
2344 
2345         sHelpURL = SfxHelpWindow_Impl::buildHelpURL(xIndexWin->GetFactory(), OUString("/" + aId), aAnchor);
2346     }
2347 
2348     loadHelpContent(sHelpURL);
2349 }
2350 
IMPL_LINK(SfxHelpWindow_Impl,SelectFactoryHdl,SfxHelpIndexWindow_Impl *,pWin,void)2351 IMPL_LINK( SfxHelpWindow_Impl, SelectFactoryHdl, SfxHelpIndexWindow_Impl* , pWin, void )
2352 {
2353     if ( sTitle.isEmpty() )
2354         sTitle = GetParent()->GetText();
2355 
2356     Reference< XTitle > xTitle(xFrame, UNO_QUERY);
2357     if (xTitle.is ())
2358         xTitle->setTitle(sTitle + " - " + xIndexWin->GetActiveFactoryTitle());
2359 
2360     if ( pWin )
2361         ShowStartPage();
2362     xIndexWin->ClearSearchPage();
2363 }
2364 
2365 
IMPL_LINK(SfxHelpWindow_Impl,ChangeHdl,HelpListener_Impl &,rListener,void)2366 IMPL_LINK( SfxHelpWindow_Impl, ChangeHdl, HelpListener_Impl&, rListener, void )
2367 {
2368     SetFactory( rListener.GetFactory() );
2369 }
2370 
2371 
openDone(const OUString & sURL,bool bSuccess)2372 void SfxHelpWindow_Impl::openDone(const OUString& sURL    ,
2373                                         bool         bSuccess)
2374 {
2375     INetURLObject aObj( sURL );
2376     if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp )
2377         SetFactory( aObj.GetHost() );
2378     if ( IsWait() )
2379         LeaveWait();
2380     if ( bGrabFocusToToolBox )
2381     {
2382         pTextWin->GetToolBox().grab_focus();
2383         bGrabFocusToToolBox = false;
2384     }
2385     else
2386         xIndexWin->GrabFocusBack();
2387     if ( !bSuccess )
2388         return;
2389 
2390     // set some view settings: "prevent help tips" and "helpid == 68245"
2391     try
2392     {
2393         Reference < XController > xController = pTextWin->getFrame()->getController();
2394         if ( xController.is() )
2395         {
2396             Reference < XViewSettingsSupplier > xSettings( xController, UNO_QUERY );
2397             Reference < XPropertySet > xViewProps = xSettings->getViewSettings();
2398             Reference< XPropertySetInfo > xInfo = xViewProps->getPropertySetInfo();
2399             xViewProps->setPropertyValue( "ShowContentTips", makeAny( false ) );
2400             xViewProps->setPropertyValue( "ShowGraphics", makeAny( true ) );
2401             xViewProps->setPropertyValue( "ShowTables", makeAny( true ) );
2402             xViewProps->setPropertyValue( "HelpURL", makeAny( OUString("HID:SFX2_HID_HELP_ONHELP") ) );
2403             OUString sProperty( "IsExecuteHyperlinks" );
2404             if ( xInfo->hasPropertyByName( sProperty ) )
2405                 xViewProps->setPropertyValue( sProperty, makeAny( true ) );
2406             xController->restoreViewData(Any());
2407         }
2408     }
2409     catch( Exception& )
2410     {
2411         TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::OpenDoneHdl(): unexpected exception" );
2412     }
2413 
2414     // When the SearchPage opens the help doc, then select all words, which are equal to its text
2415     OUString sSearchText = comphelper::string::strip(xIndexWin->GetSearchText(), ' ');
2416     if ( !sSearchText.isEmpty() )
2417         pTextWin->SelectSearchText( sSearchText, xIndexWin->IsFullWordSearch() );
2418 
2419     // no page style header -> this prevents a print output of the URL
2420     pTextWin->SetPageStyleHeaderOff();
2421 }
2422 
2423 
SfxHelpWindow_Impl(const css::uno::Reference<css::frame::XFrame2> & rFrame,vcl::Window * pParent)2424 SfxHelpWindow_Impl::SfxHelpWindow_Impl(
2425     const css::uno::Reference < css::frame::XFrame2 >& rFrame,
2426     vcl::Window* pParent ) :
2427 
2428     ResizableDockingWindow(pParent),
2429 
2430     xFrame              ( rFrame ),
2431     pTextWin            ( nullptr ),
2432     pHelpInterceptor    ( new HelpInterceptor_Impl() ),
2433     pHelpListener       ( new HelpListener_Impl( pHelpInterceptor ) ),
2434     bIndex              ( true ),
2435     bGrabFocusToToolBox ( false ),
2436     bSplit              ( false ),
2437     nWidth              ( 0 ),
2438     nIndexSize          ( 40 ), // % of width
2439     aWinPos             ( 0, 0 ),
2440     aWinSize            ( 0, 0 ),
2441     sTitle              ( pParent->GetText() )
2442 {
2443     SetStyle(GetStyle() & ~WB_DOCKABLE);
2444 
2445     SetHelpId( HID_HELP_WINDOW );
2446 
2447     m_xBuilder.reset(Application::CreateInterimBuilder(m_xBox.get(), "sfx/ui/helpwindow.ui", false));
2448     m_xContainer = m_xBuilder->weld_paned("HelpWindow");
2449     m_xContainer->connect_size_allocate(LINK(this, SfxHelpWindow_Impl, ResizeHdl));
2450     m_xHelpPaneWindow = m_xBuilder->weld_container("helppanewindow");
2451     m_xHelpTextWindow = m_xBuilder->weld_container("helptextwindow");
2452     m_xHelpTextXWindow = m_xHelpTextWindow->CreateChildFrame();
2453 
2454     pHelpInterceptor->InitWaiter( this );
2455     xIndexWin.reset(new SfxHelpIndexWindow_Impl(this, m_xHelpPaneWindow.get()));
2456     xIndexWin->SetDoubleClickHdl( LINK( this, SfxHelpWindow_Impl, OpenHdl ) );
2457     xIndexWin->SetSelectFactoryHdl( LINK( this, SfxHelpWindow_Impl, SelectFactoryHdl ) );
2458 
2459     pTextWin = VclPtr<SfxHelpTextWindow_Impl>::Create(this, *m_xBuilder, VCLUnoHelper::GetWindow(m_xHelpTextXWindow));
2460     Reference < XFrames > xFrames = rFrame->getFrames();
2461     xFrames->append( Reference<XFrame>(pTextWin->getFrame(), UNO_QUERY_THROW) );
2462     pTextWin->SetSelectHdl( LINK( this, SfxHelpWindow_Impl, SelectHdl ) );
2463     pTextWin->Show();
2464     pHelpInterceptor->setInterception( Reference<XFrame>(pTextWin->getFrame(), UNO_QUERY_THROW) );
2465     pHelpListener->SetChangeHdl( LINK( this, SfxHelpWindow_Impl, ChangeHdl ) );
2466     LoadConfig();
2467 }
2468 
~SfxHelpWindow_Impl()2469 SfxHelpWindow_Impl::~SfxHelpWindow_Impl()
2470 {
2471     disposeOnce();
2472 }
2473 
dispose()2474 void SfxHelpWindow_Impl::dispose()
2475 {
2476     SaveConfig();
2477     xIndexWin.reset();
2478     pTextWin->CloseFrame();
2479     pTextWin.disposeAndClear();
2480 
2481     m_xHelpTextXWindow->dispose();
2482     m_xHelpTextXWindow.clear();
2483     m_xHelpTextWindow.reset();
2484     m_xHelpPaneWindow.reset();
2485     m_xContainer.reset();
2486     m_xBuilder.reset();
2487 
2488     ResizableDockingWindow::dispose();
2489 }
2490 
PreNotify(NotifyEvent & rNEvt)2491 bool SfxHelpWindow_Impl::PreNotify( NotifyEvent& rNEvt )
2492 {
2493     bool bHandled = false;
2494     if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
2495     {
2496         // Backward == <ALT><LEFT> or <BACKSPACE> Forward == <ALT><RIGHT>
2497         const vcl::KeyCode& rKeyCode = rNEvt.GetKeyEvent()->GetKeyCode();
2498         sal_uInt16 nKey = rKeyCode.GetCode();
2499         if ( ( rKeyCode.IsMod2() && ( KEY_LEFT == nKey || KEY_RIGHT == nKey ) ) ||
2500              ( !rKeyCode.GetModifier() && KEY_BACKSPACE == nKey && !xIndexWin->HasFocusOnEdit() ) )
2501         {
2502             DoAction( rKeyCode.GetCode() == KEY_RIGHT ? "forward" : "backward" );
2503             bHandled = true;
2504         }
2505         else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) )
2506         {
2507             // <CTRL><F4> or <CTRL><W> -> close top frame
2508             CloseWindow();
2509             bHandled = true;
2510         }
2511     }
2512     return bHandled || Window::PreNotify( rNEvt );
2513 }
2514 
setContainerWindow(const Reference<css::awt::XWindow> & xWin)2515 void SfxHelpWindow_Impl::setContainerWindow( const Reference < css::awt::XWindow >& xWin )
2516 {
2517     xWindow = xWin;
2518     MakeLayout();
2519     if (xWindow.is())
2520     {
2521         VclPtr<vcl::Window> pScreenWin = VCLUnoHelper::GetWindow(xWindow);
2522         if (aWinSize.Width() && aWinSize.Height())
2523             pScreenWin->SetPosSizePixel(aWinPos, aWinSize);
2524         else
2525             pScreenWin->SetPosPixel(aWinPos);
2526     }
2527 }
2528 
SetFactory(const OUString & rFactory)2529 void SfxHelpWindow_Impl::SetFactory( const OUString& rFactory )
2530 {
2531     xIndexWin->SetFactory( rFactory, true );
2532 }
2533 
SetHelpURL(const OUString & rURL)2534 void SfxHelpWindow_Impl::SetHelpURL( const OUString& rURL )
2535 {
2536     INetURLObject aObj( rURL );
2537     if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp )
2538         SetFactory( aObj.GetHost() );
2539 }
2540 
DoAction(std::string_view rActionId)2541 void SfxHelpWindow_Impl::DoAction(std::string_view  rActionId)
2542 {
2543     if (rActionId == "index")
2544     {
2545         bIndex = !bIndex;
2546         MakeLayout();
2547         pTextWin->ToggleIndex( bIndex );
2548     }
2549     else if (rActionId == "start")
2550     {
2551         ShowStartPage();
2552     }
2553     else if (rActionId == "backward" || rActionId == "forward")
2554     {
2555         URL aURL;
2556         aURL.Complete = ".uno:Backward";
2557         if (rActionId == "forward")
2558             aURL.Complete = ".uno:Forward";
2559         Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
2560         xTrans->parseStrict(aURL);
2561         pHelpInterceptor->dispatch( aURL, Sequence < PropertyValue >() );
2562     }
2563     else if (rActionId == "searchdialog")
2564     {
2565         pTextWin->DoSearch();
2566     }
2567     else if (rActionId == "print" || rActionId == "sourceview" || rActionId == "copy" || rActionId == "selectionmode")
2568     {
2569         Reference < XDispatchProvider > xProv = pTextWin->getFrame();
2570         if ( xProv.is() )
2571         {
2572             URL aURL;
2573             if (rActionId == "print")
2574                 aURL.Complete = ".uno:Print";
2575             else if (rActionId == "sourceview")
2576                 aURL.Complete = ".uno:SourceView";
2577             else if (rActionId == "copy")
2578                 aURL.Complete = ".uno:Copy";
2579             else // rActionId == "selectionmode"
2580                 aURL.Complete = ".uno:SelectTextMode";
2581             Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
2582             xTrans->parseStrict(aURL);
2583             Reference < XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
2584             if ( xDisp.is() )
2585                 xDisp->dispatch( aURL, Sequence < PropertyValue >() );
2586         }
2587     }
2588     else if (rActionId == "bookmarks")
2589     {
2590         OUString aURL = pHelpInterceptor->GetCurrentURL();
2591         if ( !aURL.isEmpty() )
2592         {
2593             try
2594             {
2595                 Content aCnt( aURL, Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
2596                 css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties();
2597                 if ( xInfo->hasPropertyByName( PROPERTY_TITLE ) )
2598                 {
2599                     css::uno::Any aAny = aCnt.getPropertyValue( PROPERTY_TITLE );
2600                     OUString aValue;
2601                     if ( aAny >>= aValue )
2602                     {
2603                         SfxAddHelpBookmarkDialog_Impl aDlg(GetFrameWeld(), false);
2604                         aDlg.SetTitle(aValue);
2605                         if (aDlg.run() == RET_OK )
2606                         {
2607                             xIndexWin->AddBookmarks( aDlg.GetTitle(), aURL );
2608                         }
2609                     }
2610                 }
2611             }
2612             catch( Exception& )
2613             {
2614                 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::DoAction(): unexpected exception" );
2615             }
2616         }
2617     }
2618 }
2619 
CloseWindow()2620 void SfxHelpWindow_Impl::CloseWindow()
2621 {
2622     try
2623     {
2624         // search for top frame
2625         Reference< XFramesSupplier > xCreator = getTextFrame()->getCreator();
2626         while ( xCreator.is() && !xCreator->isTop() )
2627         {
2628             xCreator = xCreator->getCreator();
2629         }
2630 
2631         // when found, close it
2632         if ( xCreator.is() && xCreator->isTop() )
2633         {
2634             Reference < XCloseable > xCloser( xCreator, UNO_QUERY );
2635             if ( xCloser.is() )
2636                 xCloser->close( false );
2637         }
2638     }
2639     catch( Exception const & )
2640     {
2641         TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::CloseWindow()" );
2642     }
2643 }
2644 
2645 
UpdateToolbox()2646 void SfxHelpWindow_Impl::UpdateToolbox()
2647 {
2648     pTextWin->GetToolBox().set_item_sensitive("backward", pHelpInterceptor->HasHistoryPred());
2649     pTextWin->GetToolBox().set_item_sensitive("forward", pHelpInterceptor->HasHistorySucc());
2650 }
2651 
2652 
HasHistoryPredecessor() const2653 bool SfxHelpWindow_Impl::HasHistoryPredecessor() const
2654 {
2655     return pHelpInterceptor->HasHistoryPred();
2656 }
2657 
2658 
HasHistorySuccessor() const2659 bool SfxHelpWindow_Impl::HasHistorySuccessor() const
2660 {
2661     return pHelpInterceptor->HasHistorySucc();
2662 }
2663 
2664 // class SfxAddHelpBookmarkDialog_Impl -----------------------------------
2665 
SfxAddHelpBookmarkDialog_Impl(weld::Widget * pParent,bool bRename)2666 SfxAddHelpBookmarkDialog_Impl::SfxAddHelpBookmarkDialog_Impl(weld::Widget* pParent, bool bRename)
2667     : GenericDialogController(pParent, "sfx/ui/bookmarkdialog.ui", "BookmarkDialog")
2668     , m_xTitleED(m_xBuilder->weld_entry("entry"))
2669     , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
2670 {
2671     if (bRename)
2672         m_xDialog->set_title(m_xAltTitle->get_label());
2673 }
2674 
SetTitle(const OUString & rTitle)2675 void SfxAddHelpBookmarkDialog_Impl::SetTitle(const OUString& rTitle)
2676 {
2677     m_xTitleED->set_text(rTitle);
2678     m_xTitleED->select_region(0, -1);
2679 }
2680 
2681 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2682