LibreOffice Module sfx2 (master) 1
sfxhelp.cxx
Go to the documentation of this file.
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#include <config_folders.h>
21#include <sfx2/sfxhelp.hxx>
22
23#include <string_view>
24#include <algorithm>
25#include <cassert>
26#include <cstddef>
27#ifdef MACOSX
28#include <premac.h>
29#include <Foundation/NSString.h>
30#include <CoreFoundation/CFURL.h>
31#include <CoreServices/CoreServices.h>
32#include <postmac.h>
33#endif
34
35#include <sal/log.hxx>
36#include <com/sun/star/uno/Reference.h>
37#include <com/sun/star/frame/Desktop.hpp>
38#include <com/sun/star/frame/UnknownModuleException.hpp>
39#include <com/sun/star/frame/XFrame2.hpp>
41#include <com/sun/star/awt/XWindow.hpp>
42#include <com/sun/star/awt/XTopWindow.hpp>
43#include <com/sun/star/beans/XPropertySet.hpp>
44#include <com/sun/star/frame/FrameSearchFlag.hpp>
46#include <com/sun/star/frame/ModuleManager.hpp>
49#include <tools/urlobj.hxx>
50#include <ucbhelper/content.hxx>
52#include <rtl/byteseq.hxx>
53#include <rtl/ustring.hxx>
54#include <o3tl/string_view.hxx>
55#include <officecfg/Office/Common.hxx>
56#include <osl/process.h>
57#include <osl/file.hxx>
58#include <unotools/tempfile.hxx>
60#include <rtl/uri.hxx>
62#include <vcl/keycod.hxx>
63#include <vcl/settings.hxx>
64#include <vcl/locktoplevels.hxx>
65#include <vcl/weld.hxx>
66#include <openuriexternally.hxx>
67
68#include <comphelper/lok.hxx>
69#include <LibreOfficeKit/LibreOfficeKitEnums.h>
70#include <sfx2/viewsh.hxx>
71
72#include "newhelp.hxx"
73#include <sfx2/flatpak.hxx>
74#include <sfx2/sfxresid.hxx>
75#include <helper.hxx>
76#include <sfx2/strings.hrc>
77#include <vcl/svapp.hxx>
78#include <rtl/string.hxx>
79#include <svtools/langtab.hxx>
81
82using namespace ::com::sun::star::beans;
83using namespace ::com::sun::star::frame;
84using namespace ::com::sun::star::uno;
85using namespace ::com::sun::star::util;
86using namespace ::com::sun::star::lang;
87
88namespace {
89
90class NoHelpErrorBox
91{
92private:
93 std::unique_ptr<weld::MessageDialog> m_xErrBox;
94public:
95 DECL_STATIC_LINK(NoHelpErrorBox, HelpRequestHdl, weld::Widget&, bool);
96public:
97 explicit NoHelpErrorBox(weld::Widget* pParent)
98 : m_xErrBox(Application::CreateMessageDialog(pParent, VclMessageType::Error, VclButtonsType::Ok,
99 SfxResId(RID_STR_HLPFILENOTEXIST)))
100 {
101 // Error message: "No help available"
102 m_xErrBox->connect_help(LINK(nullptr, NoHelpErrorBox, HelpRequestHdl));
103 }
104 void run()
105 {
106 m_xErrBox->run();
107 }
108};
109
110}
111
112IMPL_STATIC_LINK_NOARG(NoHelpErrorBox, HelpRequestHdl, weld::Widget&, bool)
113{
114 // do nothing, because no help available
115 return false;
116}
117
118static OUString const & HelpLocaleString();
119
120namespace {
121
123OUString const & getHelpRootURL()
124{
125 static OUString const s_instURL = []()
126 {
127 OUString tmp = officecfg::Office::Common::Path::Current::Help::get();
128 if (tmp.isEmpty())
129 {
130 // try to determine path from default
131 tmp = "$(instpath)/" LIBO_SHARE_HELP_FOLDER;
132 }
133
134 // replace anything like $(instpath);
135 SvtPathOptions aOptions;
136 tmp = aOptions.SubstituteVariable(tmp);
137
138 OUString url;
139 if (osl::FileBase::getFileURLFromSystemPath(tmp, url) == osl::FileBase::E_None)
140 tmp = url;
141 return tmp;
142 }();
143 return s_instURL;
144}
145
146bool impl_checkHelpLocalePath(OUString const & rpPath)
147{
148 osl::DirectoryItem directoryItem;
149 bool bOK = false;
150
151 osl::FileStatus fileStatus(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL | osl_FileStatus_Mask_FileName);
152 if (osl::DirectoryItem::get(rpPath, directoryItem) == osl::FileBase::E_None &&
153 directoryItem.getFileStatus(fileStatus) == osl::FileBase::E_None &&
154 fileStatus.isDirectory())
155 {
156 bOK = true;
157 }
158 return bOK;
159}
160
163bool impl_hasHelpInstalled()
164{
166 return false;
167
168 // detect installed locale
169 static OUString const aLocaleStr = HelpLocaleString();
170
171 OUString helpRootURL = getHelpRootURL() + "/" + aLocaleStr + "/err.html";
172 bool bOK = false;
173 osl::DirectoryItem directoryItem;
174 if(osl::DirectoryItem::get(helpRootURL, directoryItem) == osl::FileBase::E_None){
175 bOK=true;
176 }
177
178 SAL_INFO( "sfx.appl", "Checking old help installed " << bOK);
179 return bOK;
180}
181
184bool impl_hasHTMLHelpInstalled()
185{
187 return false;
188
189 // detect installed locale
190 static OUString const aLocaleStr = HelpLocaleString();
191
192 OUString helpRootURL = getHelpRootURL() + "/" + aLocaleStr + "/text";
193 bool bOK = impl_checkHelpLocalePath( helpRootURL );
194 SAL_INFO( "sfx.appl", "Checking new help (html) installed " << bOK);
195 return bOK;
196}
197
198} // namespace
199
201static OUString const & HelpLocaleString()
202{
205
206 static OUString aLocaleStr;
207 if (!aLocaleStr.isEmpty())
208 return aLocaleStr;
209
210 static constexpr OUStringLiteral aEnglish(u"en-US");
211 // detect installed locale
212 aLocaleStr = utl::ConfigManager::getUILocale();
213
214 if ( aLocaleStr.isEmpty() )
215 {
216 aLocaleStr = aEnglish;
217 return aLocaleStr;
218 }
219
220 // get fall-back language (country)
221 OUString sLang = aLocaleStr;
222 sal_Int32 nSepPos = sLang.indexOf( '-' );
223 if (nSepPos != -1)
224 {
225 sLang = sLang.copy( 0, nSepPos );
226 }
227 OUString sHelpPath("");
228 sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + aLocaleStr;
229 if (impl_checkHelpLocalePath(sHelpPath))
230 {
231 return aLocaleStr;
232 }
233 sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + sLang;
234 if (impl_checkHelpLocalePath(sHelpPath))
235 {
236 aLocaleStr = sLang;
237 return aLocaleStr;
238 }
239 sHelpPath = getHelpRootURL() + "/" + aLocaleStr;
240 if (impl_checkHelpLocalePath(sHelpPath))
241 {
242 return aLocaleStr;
243 }
244 sHelpPath = getHelpRootURL() + "/" + sLang;
245 if (impl_checkHelpLocalePath(sHelpPath))
246 {
247 aLocaleStr = sLang;
248 return aLocaleStr;
249 }
250 sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + aEnglish;
251 if (impl_checkHelpLocalePath(sHelpPath))
252 {
253 aLocaleStr = aEnglish;
254 return aLocaleStr;
255 }
256 sHelpPath = getHelpRootURL() + "/" + aEnglish;
257 if (impl_checkHelpLocalePath(sHelpPath))
258 {
259 aLocaleStr = aEnglish;
260 return aLocaleStr;
261 }
262 return aLocaleStr;
263}
264
265
266
267void AppendConfigToken( OUStringBuffer& rURL, bool bQuestionMark )
268{
269 OUString aLocaleStr = HelpLocaleString();
270
271 // query part exists?
272 if ( bQuestionMark )
273 // no, so start with '?'
274 rURL.append('?');
275 else
276 // yes, so only append with '&'
277 rURL.append('&');
278
279 // set parameters
280 rURL.append("Language=");
281 rURL.append(aLocaleStr);
282 rURL.append("&System=");
283 rURL.append(officecfg::Office::Common::Help::System::get());
284 rURL.append("&Version=");
286}
287
288static bool GetHelpAnchor_Impl( std::u16string_view _rURL, OUString& _rAnchor )
289{
290 bool bRet = false;
291
292 try
293 {
295 Reference< css::ucb::XCommandEnvironment >(),
297 OUString sAnchor;
298 if ( aCnt.getPropertyValue("AnchorName") >>= sAnchor )
299 {
300
301 if ( !sAnchor.isEmpty() )
302 {
303 _rAnchor = sAnchor;
304 bRet = true;
305 }
306 }
307 else
308 {
309 SAL_WARN( "sfx.appl", "Property 'AnchorName' is missing" );
310 }
311 }
312 catch (const css::uno::Exception&)
313 {
314 }
315
316 return bRet;
317}
318
319namespace {
320
321class SfxHelp_Impl
322{
323public:
324 static OUString GetHelpText( const OUString& aCommandURL, const OUString& rModule );
325};
326
327}
328
329OUString SfxHelp_Impl::GetHelpText( const OUString& aCommandURL, const OUString& rModule )
330{
331 // create help url
332 OUStringBuffer aHelpURL( SfxHelp::CreateHelpURL( aCommandURL, rModule ) );
333 // added 'active' parameter
334 sal_Int32 nIndex = aHelpURL.lastIndexOf( '#' );
335 if ( nIndex < 0 )
336 nIndex = aHelpURL.getLength();
337 aHelpURL.insert( nIndex, "&Active=true" );
338 // load help string
339 return SfxContentHelper::GetActiveHelpString( aHelpURL.makeStringAndClear() );
340}
341
343 : bIsDebug(false)
344 , bLaunchingHelp(false)
345{
346 // read the environment variable "HELP_DEBUG"
347 // if it's set, you will see debug output on active help
348 OUString sHelpDebug;
349 OUString sEnvVarName( "HELP_DEBUG" );
350 osl_getEnvironment( sEnvVarName.pData, &sHelpDebug.pData );
351 bIsDebug = !sHelpDebug.isEmpty();
352}
353
355{
356}
357
358static OUString getDefaultModule_Impl()
359{
360 OUString sDefaultModule;
361 SvtModuleOptions aModOpt;
363 sDefaultModule = "swriter";
365 sDefaultModule = "scalc";
367 sDefaultModule = "simpress";
369 sDefaultModule = "sdraw";
371 sDefaultModule = "smath";
373 sDefaultModule = "schart";
375 sDefaultModule = "sbasic";
377 sDefaultModule = "sdatabase";
378 else
379 {
380 SAL_WARN( "sfx.appl", "getDefaultModule_Impl(): no module installed" );
381 }
382 return sDefaultModule;
383}
384
386{
387 OUString sIdentifier;
388 Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext();
389 Reference < XModuleManager2 > xModuleManager = ModuleManager::create(xContext);
390 Reference < XDesktop2 > xDesktop = Desktop::create(xContext);
391 Reference < XFrame > xCurrentFrame = xDesktop->getCurrentFrame();
392
393 if ( xCurrentFrame.is() )
394 {
395 try
396 {
397 sIdentifier = xModuleManager->identify( xCurrentFrame );
398 }
399 catch (const css::frame::UnknownModuleException&)
400 {
401 SAL_INFO( "sfx.appl", "SfxHelp::getCurrentModuleIdentifier_Impl(): unknown module (help in help?)" );
402 }
403 catch (const Exception&)
404 {
405 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelp::getCurrentModuleIdentifier_Impl(): exception of XModuleManager::identify()" );
406 }
407 }
408
409 return sIdentifier;
410}
411
412namespace
413{
414 OUString MapModuleIdentifier(const OUString &rFactoryShortName)
415 {
416 OUString aFactoryShortName(rFactoryShortName);
417
418 // Map some module identifiers to their "real" help module string.
419 if ( aFactoryShortName == "chart2" )
420 aFactoryShortName = "schart" ;
421 else if ( aFactoryShortName == "BasicIDE" )
422 aFactoryShortName = "sbasic";
423 else if ( aFactoryShortName == "sweb"
424 || aFactoryShortName == "sglobal"
425 || aFactoryShortName == "swxform" )
426 aFactoryShortName = "swriter" ;
427 else if ( aFactoryShortName == "dbquery"
428 || aFactoryShortName == "dbbrowser"
429 || aFactoryShortName == "dbrelation"
430 || aFactoryShortName == "dbtable"
431 || aFactoryShortName == "dbapp"
432 || aFactoryShortName == "dbreport"
433 || aFactoryShortName == "dbtdata"
434 || aFactoryShortName == "swreport"
435 || aFactoryShortName == "swform" )
436 aFactoryShortName = "sdatabase";
437 else if ( aFactoryShortName == "sbibliography"
438 || aFactoryShortName == "sabpilot"
439 || aFactoryShortName == "scanner"
440 || aFactoryShortName == "spropctrlr"
441 || aFactoryShortName == "StartModule" )
442 aFactoryShortName.clear();
443
444 return aFactoryShortName;
445 }
446}
447
448OUString SfxHelp::GetHelpModuleName_Impl(std::u16string_view rHelpID)
449{
450 OUString aFactoryShortName;
451
452 //rhbz#1438876 detect preferred module for this help id, e.g. csv dialog
453 //for calc import before any toplevel is created and so context is
454 //otherwise unknown. Cosmetic, same help is shown in any case because its
455 //in the shared section, but title bar would state "Writer" when context is
456 //expected to be "Calc"
457 std::u16string_view sRemainder;
458 if (o3tl::starts_with(rHelpID, u"modules/", &sRemainder))
459 {
460 std::size_t nEndModule = sRemainder.find(u'/');
461 aFactoryShortName = nEndModule != std::u16string_view::npos
462 ? sRemainder.substr(0, nEndModule) : sRemainder;
463 }
464
465 if (aFactoryShortName.isEmpty())
466 {
467 OUString aModuleIdentifier = getCurrentModuleIdentifier_Impl();
468 if (!aModuleIdentifier.isEmpty())
469 {
470 try
471 {
472 Reference < XModuleManager2 > xModuleManager(
473 ModuleManager::create(::comphelper::getProcessComponentContext()) );
474 Sequence< PropertyValue > lProps;
475 xModuleManager->getByName( aModuleIdentifier ) >>= lProps;
476 auto pProp = std::find_if(std::cbegin(lProps), std::cend(lProps),
477 [](const PropertyValue& rProp) { return rProp.Name == "ooSetupFactoryShortName"; });
478 if (pProp != std::cend(lProps))
479 pProp->Value >>= aFactoryShortName;
480 }
481 catch (const Exception&)
482 {
483 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelp::GetHelpModuleName_Impl()" );
484 }
485 }
486 }
487
488 if (!aFactoryShortName.isEmpty())
489 aFactoryShortName = MapModuleIdentifier(aFactoryShortName);
490 if (aFactoryShortName.isEmpty())
491 aFactoryShortName = getDefaultModule_Impl();
492
493 return aFactoryShortName;
494}
495
496OUString SfxHelp::CreateHelpURL_Impl( const OUString& aCommandURL, const OUString& rModuleName )
497{
498 // build up the help URL
499 OUStringBuffer aHelpURL("vnd.sun.star.help://");
500 bool bHasAnchor = false;
501 OUString aAnchor;
502
503 OUString aModuleName( rModuleName );
504 if (aModuleName.isEmpty())
505 aModuleName = getDefaultModule_Impl();
506
507 aHelpURL.append(aModuleName);
508
509 if ( aCommandURL.isEmpty() )
510 aHelpURL.append("/start");
511 else
512 {
513 aHelpURL.append("/" +
514 rtl::Uri::encode(aCommandURL,
515 rtl_UriCharClassRelSegment,
516 rtl_UriEncodeKeepEscapes,
517 RTL_TEXTENCODING_UTF8));
518
519 OUStringBuffer aTempURL = aHelpURL;
520 AppendConfigToken( aTempURL, true );
521 bHasAnchor = GetHelpAnchor_Impl(aTempURL, aAnchor);
522 }
523
524 AppendConfigToken( aHelpURL, true );
525
526 if ( bHasAnchor )
527 aHelpURL.append("#" + aAnchor);
528
529 return aHelpURL.makeStringAndClear();
530}
531
532static SfxHelpWindow_Impl* impl_createHelp(Reference< XFrame2 >& rHelpTask ,
533 Reference< XFrame >& rHelpContent)
534{
535 Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
536
537 // otherwise - create new help task
538 Reference< XFrame2 > xHelpTask(
539 xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::TASKS | FrameSearchFlag::CREATE),
540 UNO_QUERY);
541 if (!xHelpTask.is())
542 return nullptr;
543
544 // create all internal windows and sub frames ...
545 Reference< css::awt::XWindow > xParentWindow = xHelpTask->getContainerWindow();
546 VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow( xParentWindow );
547 VclPtrInstance<SfxHelpWindow_Impl> pHelpWindow( xHelpTask, pParentWindow );
548 Reference< css::awt::XWindow > xHelpWindow = VCLUnoHelper::GetInterface( pHelpWindow );
549
550 Reference< XFrame > xHelpContent;
551 if (xHelpTask->setComponent( xHelpWindow, Reference< XController >() ))
552 {
553 // Customize UI ...
554 xHelpTask->setName("OFFICE_HELP_TASK");
555
556 Reference< XPropertySet > xProps(xHelpTask, UNO_QUERY);
557 if (xProps.is())
558 xProps->setPropertyValue(
559 "Title",
560 Any(SfxResId(STR_HELP_WINDOW_TITLE)));
561
562 pHelpWindow->setContainerWindow( xParentWindow );
563 xParentWindow->setVisible(true);
564 xHelpWindow->setVisible(true);
565
566 // This sub frame is created internally (if we called new SfxHelpWindow_Impl() ...)
567 // It should exist :-)
568 xHelpContent = xHelpTask->findFrame("OFFICE_HELP", FrameSearchFlag::CHILDREN);
569 }
570
571 if (!xHelpContent.is())
572 {
573 pHelpWindow.disposeAndClear();
574 return nullptr;
575 }
576
577 xHelpContent->setName("OFFICE_HELP");
578
579 rHelpTask = xHelpTask;
580 rHelpContent = xHelpContent;
581 return pHelpWindow;
582}
583
584OUString SfxHelp::GetHelpText( const OUString& aCommandURL, const vcl::Window* pWindow )
585{
586 OUString sModuleName = GetHelpModuleName_Impl(aCommandURL);
589 OUString sHelpText = SfxHelp_Impl::GetHelpText( sRealCommand.isEmpty() ? aCommandURL : sRealCommand, sModuleName );
590
591 OUString aNewHelpId;
592
593 if (pWindow && sHelpText.isEmpty())
594 {
595 // no help text found -> try with parent help id.
596 vcl::Window* pParent = pWindow->GetParent();
597 while ( pParent )
598 {
599 aNewHelpId = pParent->GetHelpId();
600 sHelpText = SfxHelp_Impl::GetHelpText( aNewHelpId, sModuleName );
601 if (!sHelpText.isEmpty())
602 pParent = nullptr;
603 else
604 pParent = pParent->GetParent();
605 }
606
607 if (bIsDebug && sHelpText.isEmpty())
608 aNewHelpId.clear();
609 }
610
611 // add some debug information?
612 if ( bIsDebug )
613 {
614 sHelpText += "\n-------------\n" +
615 sModuleName + ": " + aCommandURL;
616 if ( !aNewHelpId.isEmpty() )
617 {
618 sHelpText += " - " + aNewHelpId;
619 }
620 }
621
622 return sHelpText;
623}
624
625OUString SfxHelp::GetHelpText(const OUString& aCommandURL, const weld::Widget* pWidget)
626{
627 OUString sModuleName = GetHelpModuleName_Impl(aCommandURL);
630 OUString sHelpText = SfxHelp_Impl::GetHelpText( sRealCommand.isEmpty() ? aCommandURL : sRealCommand, sModuleName );
631
632 OUString aNewHelpId;
633
634 if (pWidget && sHelpText.isEmpty())
635 {
636 // no help text found -> try with parent help id.
637 std::unique_ptr<weld::Widget> xParent(pWidget->weld_parent());
638 while (xParent)
639 {
640 aNewHelpId = xParent->get_help_id();
641 sHelpText = SfxHelp_Impl::GetHelpText( aNewHelpId, sModuleName );
642 if (!sHelpText.isEmpty())
643 xParent.reset();
644 else
645 xParent = xParent->weld_parent();
646 }
647
648 if (bIsDebug && sHelpText.isEmpty())
649 aNewHelpId.clear();
650 }
651
652 // add some debug information?
653 if ( bIsDebug )
654 {
655 sHelpText += "\n-------------\n" +
656 sModuleName + ": " + aCommandURL;
657 if ( !aNewHelpId.isEmpty() )
658 {
659 sHelpText += " - " + aNewHelpId;
660 }
661 }
662
663 return sHelpText;
664}
665
666OUString SfxHelp::GetURLHelpText(std::u16string_view aURL)
667{
669
670 // "ctrl-click to follow link:" for not MacOS
671 // "⌘-click to follow link:" for MacOs
672 vcl::KeyCode aCode(KEY_SPACE);
673 vcl::KeyCode aModifiedCode(KEY_SPACE, KEY_MOD1);
674 OUString aModStr(aModifiedCode.GetName());
675 aModStr = aModStr.replaceFirst(aCode.GetName(), "");
676 aModStr = aModStr.replaceAll("+", "");
677 OUString aHelpStr
678 = bCtrlClickHlink ? SfxResId(STR_CTRLCLICKHYPERLINK) : SfxResId(STR_CLICKHYPERLINK);
679 aHelpStr = aHelpStr.replaceFirst("%{key}", aModStr);
680 aHelpStr = aHelpStr.replaceFirst("%{link}", aURL);
681 return aHelpStr;
682}
683
684void SfxHelp::SearchKeyword( const OUString& rKeyword )
685{
686 Start_Impl(OUString(), static_cast<weld::Widget*>(nullptr), rKeyword);
687}
688
689bool SfxHelp::Start( const OUString& rURL, const vcl::Window* pWindow )
690{
691 if (bLaunchingHelp)
692 return true;
693 bLaunchingHelp = true;
694 bool bRet = Start_Impl( rURL, pWindow );
695 bLaunchingHelp = false;
696 return bRet;
697}
698
699bool SfxHelp::Start(const OUString& rURL, weld::Widget* pWidget)
700{
701 if (bLaunchingHelp)
702 return true;
703 bLaunchingHelp = true;
704 bool bRet = Start_Impl(rURL, pWidget, OUString());
705 bLaunchingHelp = false;
706 return bRet;
707}
708
710static bool impl_showOnlineHelp(const OUString& rURL, weld::Widget* pDialogParent)
711{
712 static constexpr OUStringLiteral aInternal(u"vnd.sun.star.help://");
713 if ( rURL.getLength() <= aInternal.getLength() || !rURL.startsWith(aInternal) )
714 return false;
715
716 OUString aHelpLink = officecfg::Office::Common::Help::HelpRootURL::get();
717 OUString aTarget = OUString::Concat("Target=") + rURL.subView(aInternal.getLength());
718 aTarget = aTarget.replaceAll("%2F", "/").replaceAll("?", "&");
719 aHelpLink += aTarget;
720
722 {
723 if(SfxViewShell* pViewShell = SfxViewShell::Current())
724 {
725 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED,
726 aHelpLink.toUtf8());
727 return true;
728 }
729 else if (GetpApp())
730 {
731 GetpApp()->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED,
732 aHelpLink.toUtf8());
733 return true;
734 }
735
736 return false;
737 }
738
739 try
740 {
741#ifdef MACOSX
742 LSOpenCFURLRef(CFURLCreateWithString(kCFAllocatorDefault,
743 CFStringCreateWithCString(kCFAllocatorDefault,
744 aHelpLink.toUtf8().getStr(),
745 kCFStringEncodingUTF8),
746 nullptr),
747 nullptr);
748 (void)pDialogParent;
749#else
750 sfx2::openUriExternally(aHelpLink, false, pDialogParent);
751#endif
752 return true;
753 }
754 catch (const Exception&)
755 {
756 }
757 return false;
758}
759
760namespace {
761
762bool rewriteFlatpakHelpRootUrl(OUString * helpRootUrl) {
763 assert(helpRootUrl != nullptr);
764 //TODO: this function for now assumes that the passed-in *helpRootUrl references
765 // /app/libreoffice/help (which belongs to the org.libreoffice.LibreOffice.Help
766 // extension); it replaces it with the corresponding file URL as seen outside the flatpak
767 // sandbox:
768 struct Failure: public std::exception {};
769 try {
770 static auto const url = [] {
771 // From /.flatpak-info [Instance] section, read
772 // app-path=<path>
773 // app-extensions=...;org.libreoffice.LibreOffice.Help=<sha>;...
774 // lines:
775 osl::File ini("file:///.flatpak-info");
776 auto err = ini.open(osl_File_OpenFlag_Read);
777 if (err != osl::FileBase::E_None) {
778 SAL_WARN("sfx.appl", "LIBO_FLATPAK mode failure opening /.flatpak-info: " << err);
779 throw Failure();
780 }
781 OUString path;
782 OUString extensions;
783 bool havePath = false;
784 bool haveExtensions = false;
785 for (bool instance = false; !(havePath && haveExtensions);) {
786 rtl::ByteSequence bytes;
787 err = ini.readLine(bytes);
788 if (err != osl::FileBase::E_None) {
789 SAL_WARN(
790 "sfx.appl",
791 "LIBO_FLATPAK mode reading /.flatpak-info fails with " << err
792 << " before [Instance] app-path");
793 throw Failure();
794 }
795 std::string_view const line(
796 reinterpret_cast<char const *>(bytes.getConstArray()), bytes.getLength());
797 if (instance) {
798 static constexpr auto keyPath = std::string_view("app-path=");
799 static constexpr auto keyExtensions = std::string_view("app-extensions=");
800 if (!havePath && line.length() >= keyPath.size()
801 && line.substr(0, keyPath.size()) == keyPath.data())
802 {
803 auto const value = line.substr(keyPath.size());
804 if (!rtl_convertStringToUString(
805 &path.pData, value.data(), value.length(),
806 osl_getThreadTextEncoding(),
807 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
808 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
809 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
810 {
811 SAL_WARN(
812 "sfx.appl",
813 "LIBO_FLATPAK mode failure converting app-path \"" << value
814 << "\" encoding");
815 throw Failure();
816 }
817 havePath = true;
818 } else if (!haveExtensions && line.length() >= keyExtensions.size()
819 && line.substr(0, keyExtensions.size()) == keyExtensions.data())
820 {
821 auto const value = line.substr(keyExtensions.size());
822 if (!rtl_convertStringToUString(
823 &extensions.pData, value.data(), value.length(),
824 osl_getThreadTextEncoding(),
825 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
826 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
827 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
828 {
829 SAL_WARN(
830 "sfx.appl",
831 "LIBO_FLATPAK mode failure converting app-extensions \"" << value
832 << "\" encoding");
833 throw Failure();
834 }
835 haveExtensions = true;
836 } else if (line.length() > 0 && line[0] == '[') {
837 SAL_WARN(
838 "sfx.appl",
839 "LIBO_FLATPAK mode /.flatpak-info lacks [Instance] app-path and"
840 " app-extensions");
841 throw Failure();
842 }
843 } else if (line == "[Instance]") {
844 instance = true;
845 }
846 }
847 ini.close();
848 // Extract <sha> from ...;org.libreoffice.LibreOffice.Help=<sha>;...:
849 OUString sha;
850 for (sal_Int32 i = 0;;) {
851 OUString elem = extensions.getToken(0, ';', i);
852 if (elem.startsWith("org.libreoffice.LibreOffice.Help=", &sha)) {
853 break;
854 }
855 if (i == -1) {
856 SAL_WARN(
857 "sfx.appl",
858 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-extensions \""
859 << extensions << "\" org.libreoffice.LibreOffice.Help");
860 throw Failure();
861 }
862 }
863 // Assuming that <path> is of the form
864 // /.../app/org.libreoffice.LibreOffice/<arch>/<branch>/<sha'>/files
865 // rewrite it as
866 // /.../runtime/org.libreoffice.LibreOffice.Help/<arch>/<branch>/<sha>/files
867 // because the extension's files are stored at a different place than the app's files,
868 // so use this hack until flatpak itself provides a better solution:
869 static constexpr OUStringLiteral segments = u"/app/org.libreoffice.LibreOffice/";
870 auto const i1 = path.lastIndexOf(segments);
871 // use lastIndexOf instead of indexOf, in case the user-controlled prefix /.../
872 // happens to contain such segments
873 if (i1 == -1) {
874 SAL_WARN(
875 "sfx.appl",
876 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
877 << "\" doesn't contain /app/org.libreoffice.LibreOffice/");
878 throw Failure();
879 }
880 auto const i2 = i1 + segments.getLength();
881 auto i3 = path.indexOf('/', i2);
882 if (i3 == -1) {
883 SAL_WARN(
884 "sfx.appl",
885 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
886 << "\" doesn't contain branch segment");
887 throw Failure();
888 }
889 i3 = path.indexOf('/', i3 + 1);
890 if (i3 == -1) {
891 SAL_WARN(
892 "sfx.appl",
893 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
894 << "\" doesn't contain sha segment");
895 throw Failure();
896 }
897 ++i3;
898 auto const i4 = path.indexOf('/', i3);
899 if (i4 == -1) {
900 SAL_WARN(
901 "sfx.appl",
902 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
903 << "\" doesn't contain files segment");
904 throw Failure();
905 }
906 path = path.subView(0, i1) + OUString::Concat("/runtime/org.libreoffice.LibreOffice.Help/")
907 + path.subView(i2, i3 - i2) + sha + path.subView(i4);
908 // Turn <path> into a file URL:
909 OUString url_;
910 err = osl::FileBase::getFileURLFromSystemPath(path, url_);
911 if (err != osl::FileBase::E_None) {
912 SAL_WARN(
913 "sfx.appl",
914 "LIBO_FLATPAK mode failure converting app-path \"" << path << "\" to URL: "
915 << err);
916 throw Failure();
917 }
918 return url_;
919 }();
920 *helpRootUrl = url;
921 return true;
922 } catch (Failure &) {
923 return false;
924 }
925}
926
927}
928
929// add <noscript> meta for browsers without javascript
930
931constexpr OUStringLiteral SHTML1 = u"<!DOCTYPE HTML><html lang=\"en-US\"><head><meta charset=\"UTF-8\">";
932constexpr OUStringLiteral SHTML2 = u"<noscript><meta http-equiv=\"refresh\" content=\"0; url='";
933constexpr OUStringLiteral SHTML3 = u"/noscript.html'\"></noscript><meta http-equiv=\"refresh\" content=\"1; url='";
934constexpr OUStringLiteral SHTML4 = u"'\"><script type=\"text/javascript\"> window.location.href = \"";
935constexpr OUStringLiteral SHTML5 = u"\";</script><title>Help Page Redirection</title></head><body></body></html>";
936
937// use a tempfile since e.g. xdg-open doesn't support URL-parameters with file:// URLs
938static bool impl_showOfflineHelp(const OUString& rURL, weld::Widget* pDialogParent)
939{
940 OUString aBaseInstallPath = getHelpRootURL();
941 // For the flatpak case, find the pathname outside the flatpak sandbox that corresponds to
942 // aBaseInstallPath, because that is what needs to be stored in aTempFile below:
943 if (flatpak::isFlatpak() && !rewriteFlatpakHelpRootUrl(&aBaseInstallPath)) {
944 return false;
945 }
946
947 OUString aHelpLink( aBaseInstallPath + "/index.html?" );
948 OUString aTarget = OUString::Concat("Target=") + rURL.subView(RTL_CONSTASCII_LENGTH("vnd.sun.star.help://"));
949 aTarget = aTarget.replaceAll("%2F","/").replaceAll("?","&");
950 aHelpLink += aTarget;
951
952 // Get a html tempfile (for the flatpak case, create it in XDG_CACHE_HOME instead of /tmp for
953 // technical reasons, so that it can be accessed by the browser running outside the sandbox):
954 static constexpr OUStringLiteral aExtension(u".html");
955 OUString * parent = nullptr;
957 return false;
958 }
959 ::utl::TempFileNamed aTempFile(u"NewHelp", true, aExtension, parent, false );
960
961 SvStream* pStream = aTempFile.GetStream(StreamMode::WRITE);
962 pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8);
963
964 OUString aTempStr = SHTML1 + SHTML2 +
965 aBaseInstallPath + "/" + HelpLocaleString() + SHTML3 +
966 aHelpLink + SHTML4 +
967 aHelpLink + SHTML5;
968
969 pStream->WriteUnicodeOrByteText(aTempStr);
970
971 aTempFile.CloseStream();
972 try
973 {
974#ifdef MACOSX
975 LSOpenCFURLRef(CFURLCreateWithString(kCFAllocatorDefault,
976 CFStringCreateWithCString(kCFAllocatorDefault,
977 aTempFile.GetURL().toUtf8().getStr(),
978 kCFStringEncodingUTF8),
979 nullptr),
980 nullptr);
981 (void)pDialogParent;
982#else
983 sfx2::openUriExternally(aTempFile.GetURL(), false, pDialogParent);
984#endif
985 return true;
986 }
987 catch (const Exception&)
988 {
989 }
990 aTempFile.EnableKillingFile();
991 return false;
992}
993
994namespace
995{
996 // tdf#119579 skip floating windows as potential parent for missing help dialog
997 const vcl::Window* GetBestParent(const vcl::Window* pWindow)
998 {
999 while (pWindow)
1000 {
1001 if (pWindow->IsSystemWindow() && pWindow->GetType() != WindowType::FLOATINGWINDOW)
1002 break;
1003 pWindow = pWindow->GetParent();
1004 }
1005 return pWindow;
1006 }
1007}
1008
1009namespace {
1010
1011class HelpManualMessage : public weld::MessageDialogController
1012{
1013private:
1014 std::unique_ptr<weld::CheckButton> m_xHideOfflineHelpCB;
1015
1016public:
1017 HelpManualMessage(weld::Widget* pParent)
1018 : MessageDialogController(pParent, "sfx/ui/helpmanual.ui", "onlinehelpmanual", "hidedialog")
1019 , m_xHideOfflineHelpCB(m_xBuilder->weld_check_button("hidedialog"))
1020 {
1022 OUString sLocaleString = SvtLanguageTable::GetLanguageString(aLangType);
1023 OUString sPrimText = get_primary_text();
1024 set_primary_text(sPrimText.replaceAll("$UILOCALE", sLocaleString));
1025 }
1026
1027 bool GetOfflineHelpPopUp() const { return !m_xHideOfflineHelpCB->get_active(); }
1028};
1029
1030}
1031
1032bool SfxHelp::Start_Impl(const OUString& rURL, const vcl::Window* pWindow)
1033{
1034 OUStringBuffer aHelpRootURL("vnd.sun.star.help://");
1035 AppendConfigToken(aHelpRootURL, true);
1036 SfxContentHelper::GetResultSet(aHelpRootURL.makeStringAndClear());
1037
1038 /* rURL may be
1039 * - a "real" URL
1040 * - a HelpID (formerly a long, now a string)
1041 * If rURL is a URL, CreateHelpURL should be called for this URL
1042 * If rURL is an arbitrary string, the same should happen, but the URL should be tried out
1043 * if it delivers real help content. In case only the Help Error Document is returned, the
1044 * parent of the window for that help was called, is asked for its HelpID.
1045 * For compatibility reasons this upward search is not implemented for "real" URLs.
1046 * Help keyword search now is implemented as own method; in former versions it
1047 * was done via Help::Start, but this implementation conflicted with the upward search.
1048 */
1049 OUString aHelpURL;
1050 INetURLObject aParser( rURL );
1051 INetProtocol nProtocol = aParser.GetProtocol();
1052
1053 switch ( nProtocol )
1054 {
1055 case INetProtocol::VndSunStarHelp:
1056 // already a vnd.sun.star.help URL -> nothing to do
1057 aHelpURL = rURL;
1058 break;
1059 default:
1060 {
1061 OUString aHelpModuleName(GetHelpModuleName_Impl(rURL));
1062 OUString aRealCommand;
1063
1064 if ( nProtocol == INetProtocol::Uno )
1065 {
1066 // Command can be just an alias to another command.
1069 }
1070
1071 // no URL, just a HelpID (maybe empty in case of keyword search)
1072 aHelpURL = CreateHelpURL_Impl( aRealCommand.isEmpty() ? rURL : aRealCommand, aHelpModuleName );
1073
1074 if ( impl_hasHelpInstalled() && pWindow && SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
1075 {
1076 // no help found -> try with parent help id.
1077 vcl::Window* pParent = pWindow->GetParent();
1078 while ( pParent )
1079 {
1080 OUString aHelpId = pParent->GetHelpId();
1081 aHelpURL = CreateHelpURL( aHelpId, aHelpModuleName );
1082
1083 if ( !SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
1084 {
1085 break;
1086 }
1087 else
1088 {
1089 pParent = pParent->GetParent();
1090 if (!pParent)
1091 {
1092 // create help url of start page ( helpid == 0 -> start page)
1093 aHelpURL = CreateHelpURL( OUString(), aHelpModuleName );
1094 }
1095 }
1096 }
1097 }
1098 break;
1099 }
1100 }
1101
1102 pWindow = GetBestParent(pWindow);
1103 weld::Window* pWeldWindow = pWindow ? pWindow->GetFrameWeld() : nullptr;
1104
1106 {
1107 impl_showOnlineHelp(aHelpURL, pWeldWindow);
1108 return true;
1109 }
1110#ifdef MACOSX
1111 if (@available(macOS 10.14, *)) {
1112 // Workaround: Safari sandboxing prevents it from accessing files in the LibreOffice.app folder
1113 // force online-help instead if Safari is default browser.
1114 CFURLRef pBrowser = LSCopyDefaultApplicationURLForURL(
1115 CFURLCreateWithString(
1116 kCFAllocatorDefault,
1117 static_cast<CFStringRef>(@"https://www.libreoffice.org"),
1118 nullptr),
1119 kLSRolesAll, nullptr);
1120 if([static_cast<NSString*>(CFURLGetString(pBrowser)) hasSuffix:@"/Applications/Safari.app/"]) {
1121 impl_showOnlineHelp(aHelpURL, pWeldWindow);
1122 return true;
1123 }
1124 }
1125#endif
1126
1127 // If the HTML or no help is installed, but aHelpURL nevertheless references valid help content,
1128 // that implies that this help content belongs to an extension (and thus would not be available
1129 // in neither the offline nor online HTML help); in that case, fall through to the "old-help to
1130 // display" code below:
1132 {
1133 if ( impl_hasHTMLHelpInstalled() && impl_showOfflineHelp(aHelpURL, pWeldWindow) )
1134 {
1135 return true;
1136 }
1137
1138 if ( !impl_hasHelpInstalled() )
1139 {
1140 bool bShowOfflineHelpPopUp = officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::get();
1141
1143
1144 if(bShowOfflineHelpPopUp)
1145 {
1146 aBusy.incBusy(pWeldWindow);
1147 HelpManualMessage aQueryBox(pWeldWindow);
1148 short OnlineHelpBox = aQueryBox.run();
1149 bShowOfflineHelpPopUp = OnlineHelpBox != RET_OK;
1151 officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::set(aQueryBox.GetOfflineHelpPopUp(), xChanges);
1152 xChanges->commit();
1153 aBusy.decBusy();
1154 }
1155 if(!bShowOfflineHelpPopUp)
1156 {
1157 if ( impl_showOnlineHelp(aHelpURL, pWeldWindow) )
1158 return true;
1159 else
1160 {
1161 aBusy.incBusy(pWeldWindow);
1162 NoHelpErrorBox aErrBox(pWeldWindow);
1163 aErrBox.run();
1164 aBusy.decBusy();
1165 return false;
1166 }
1167 }
1168 else
1169 {
1170 return false;
1171 }
1172 }
1173 }
1174
1175 // old-help to display
1176 Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
1177
1178 // check if help window is still open
1179 // If not, create a new one and return access directly to the internal sub frame showing the help content
1180 // search must be done here; search one desktop level could return an arbitrary frame
1181 Reference< XFrame2 > xHelp(
1182 xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::CHILDREN),
1183 UNO_QUERY);
1184 Reference< XFrame > xHelpContent = xDesktop->findFrame(
1185 "OFFICE_HELP",
1186 FrameSearchFlag::CHILDREN);
1187
1188 SfxHelpWindow_Impl* pHelpWindow = nullptr;
1189 if (!xHelp.is())
1190 pHelpWindow = impl_createHelp(xHelp, xHelpContent);
1191 else
1192 pHelpWindow = static_cast<SfxHelpWindow_Impl*>(VCLUnoHelper::GetWindow(xHelp->getComponentWindow()));
1193 if (!xHelp.is() || !xHelpContent.is() || !pHelpWindow)
1194 return false;
1195
1196 SAL_INFO("sfx.appl", "HelpId = " << aHelpURL);
1197
1198 pHelpWindow->SetHelpURL( aHelpURL );
1199 pHelpWindow->loadHelpContent(aHelpURL);
1200
1201 Reference < css::awt::XTopWindow > xTopWindow( xHelp->getContainerWindow(), UNO_QUERY );
1202 if ( xTopWindow.is() )
1203 xTopWindow->toFront();
1204
1205 return true;
1206}
1207
1208bool SfxHelp::Start_Impl(const OUString& rURL, weld::Widget* pWidget, const OUString& rKeyword)
1209{
1210 OUStringBuffer aHelpRootURL("vnd.sun.star.help://");
1211 AppendConfigToken(aHelpRootURL, true);
1212 SfxContentHelper::GetResultSet(aHelpRootURL.makeStringAndClear());
1213
1214 /* rURL may be
1215 * - a "real" URL
1216 * - a HelpID (formerly a long, now a string)
1217 * If rURL is a URL, CreateHelpURL should be called for this URL
1218 * If rURL is an arbitrary string, the same should happen, but the URL should be tried out
1219 * if it delivers real help content. In case only the Help Error Document is returned, the
1220 * parent of the window for that help was called, is asked for its HelpID.
1221 * For compatibility reasons this upward search is not implemented for "real" URLs.
1222 * Help keyword search now is implemented as own method; in former versions it
1223 * was done via Help::Start, but this implementation conflicted with the upward search.
1224 */
1225 OUString aHelpURL;
1226 INetURLObject aParser( rURL );
1227 INetProtocol nProtocol = aParser.GetProtocol();
1228
1229 switch ( nProtocol )
1230 {
1231 case INetProtocol::VndSunStarHelp:
1232 // already a vnd.sun.star.help URL -> nothing to do
1233 aHelpURL = rURL;
1234 break;
1235 default:
1236 {
1237 OUString aHelpModuleName(GetHelpModuleName_Impl(rURL));
1238 OUString aRealCommand;
1239
1240 if ( nProtocol == INetProtocol::Uno )
1241 {
1242 // Command can be just an alias to another command.
1245 }
1246
1247 // no URL, just a HelpID (maybe empty in case of keyword search)
1248 aHelpURL = CreateHelpURL_Impl( aRealCommand.isEmpty() ? rURL : aRealCommand, aHelpModuleName );
1249
1250 if ( impl_hasHelpInstalled() && pWidget && SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
1251 {
1252 bool bUseFinalFallback = true;
1253 // no help found -> try ids of parents.
1254 pWidget->help_hierarchy_foreach([&aHelpModuleName, &aHelpURL, &bUseFinalFallback](const OUString& rHelpId){
1255 if (rHelpId.isEmpty())
1256 return false;
1257 aHelpURL = CreateHelpURL(rHelpId, aHelpModuleName);
1258 bool bFinished = !SfxContentHelper::IsHelpErrorDocument(aHelpURL);
1259 if (bFinished)
1260 bUseFinalFallback = false;
1261 return bFinished;
1262 });
1263
1264 if (bUseFinalFallback)
1265 {
1266 // create help url of start page ( helpid == 0 -> start page)
1267 aHelpURL = CreateHelpURL( OUString(), aHelpModuleName );
1268 }
1269 }
1270 break;
1271 }
1272 }
1273
1275 {
1276 impl_showOnlineHelp(aHelpURL, pWidget);
1277 return true;
1278 }
1279#ifdef MACOSX
1280 if (@available(macOS 10.14, *)) {
1281 // Workaround: Safari sandboxing prevents it from accessing files in the LibreOffice.app folder
1282 // force online-help instead if Safari is default browser.
1283 CFURLRef pBrowser = LSCopyDefaultApplicationURLForURL(
1284 CFURLCreateWithString(
1285 kCFAllocatorDefault,
1286 static_cast<CFStringRef>(@"https://www.libreoffice.org"),
1287 nullptr),
1288 kLSRolesAll, nullptr);
1289 if([static_cast<NSString*>(CFURLGetString(pBrowser)) hasSuffix:@"/Applications/Safari.app/"]) {
1290 impl_showOnlineHelp(aHelpURL, pWidget);
1291 return true;
1292 }
1293 }
1294#endif
1295
1296 // If the HTML or no help is installed, but aHelpURL nevertheless references valid help content,
1297 // that implies that help content belongs to an extension (and thus would not be available
1298 // in neither the offline nor online HTML help); in that case, fall through to the "old-help to
1299 // display" code below:
1301 {
1302 if ( impl_hasHTMLHelpInstalled() && impl_showOfflineHelp(aHelpURL, pWidget) )
1303 {
1304 return true;
1305 }
1306
1307 if ( !impl_hasHelpInstalled() )
1308 {
1309 bool bShowOfflineHelpPopUp = officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::get();
1310
1312
1313 if(bShowOfflineHelpPopUp)
1314 {
1315 aBusy.incBusy(pWidget);
1316 HelpManualMessage aQueryBox(pWidget);
1317 short OnlineHelpBox = aQueryBox.run();
1318 bShowOfflineHelpPopUp = OnlineHelpBox != RET_OK;
1320 officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::set(aQueryBox.GetOfflineHelpPopUp(), xChanges);
1321 xChanges->commit();
1322 aBusy.decBusy();
1323 }
1324 if(!bShowOfflineHelpPopUp)
1325 {
1326 if ( impl_showOnlineHelp(aHelpURL, pWidget) )
1327 return true;
1328 else
1329 {
1330 aBusy.incBusy(pWidget);
1331 NoHelpErrorBox aErrBox(pWidget);
1332 aErrBox.run();
1333 aBusy.decBusy();
1334 return false;
1335 }
1336 }
1337 else
1338 {
1339 return false;
1340 }
1341
1342 }
1343 }
1344
1345 // old-help to display
1346 Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
1347
1348 // check if help window is still open
1349 // If not, create a new one and return access directly to the internal sub frame showing the help content
1350 // search must be done here; search one desktop level could return an arbitrary frame
1351 Reference< XFrame2 > xHelp(
1352 xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::CHILDREN),
1353 UNO_QUERY);
1354 Reference< XFrame > xHelpContent = xDesktop->findFrame(
1355 "OFFICE_HELP",
1356 FrameSearchFlag::CHILDREN);
1357
1358 SfxHelpWindow_Impl* pHelpWindow = nullptr;
1359 if (!xHelp.is())
1360 pHelpWindow = impl_createHelp(xHelp, xHelpContent);
1361 else
1362 pHelpWindow = static_cast<SfxHelpWindow_Impl*>(VCLUnoHelper::GetWindow(xHelp->getComponentWindow()));
1363 if (!xHelp.is() || !xHelpContent.is() || !pHelpWindow)
1364 return false;
1365
1366 SAL_INFO("sfx.appl", "HelpId = " << aHelpURL);
1367
1368 pHelpWindow->SetHelpURL( aHelpURL );
1369 pHelpWindow->loadHelpContent(aHelpURL);
1370 if (!rKeyword.isEmpty())
1371 pHelpWindow->OpenKeyword( rKeyword );
1372
1373 Reference < css::awt::XTopWindow > xTopWindow( xHelp->getContainerWindow(), UNO_QUERY );
1374 if ( xTopWindow.is() )
1375 xTopWindow->toFront();
1376
1377 return true;
1378}
1379
1380OUString SfxHelp::CreateHelpURL(const OUString& aCommandURL, const OUString& rModuleName)
1381{
1382 SfxHelp* pHelp = static_cast< SfxHelp* >(Application::GetHelp());
1383 return pHelp ? SfxHelp::CreateHelpURL_Impl( aCommandURL, rModuleName ) : OUString();
1384}
1385
1387{
1388 return getDefaultModule_Impl();
1389}
1390
1392{
1394}
1395
1397{
1398 return impl_hasHelpInstalled();
1399}
1400
1401/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
PropertiesInfo aProperties
const LanguageTag & GetUILanguageTag() const
virtual void libreOfficeKitViewCallback(int nType, const OString &pPayload) const override
static const AllSettings & GetSettings()
static Help * GetHelp()
INetProtocol GetProtocol() const
LanguageType getLanguageType(bool bResolveSystem=true) const
const OUString & getBcp47(bool bResolveSystem=true) const
static OUString GetActiveHelpString(const OUString &rURL)
Definition: helper.cxx:160
static std::vector< OUString > GetResultSet(const OUString &rURL)
Definition: helper.cxx:47
static bool IsHelpErrorDocument(std::u16string_view rURL)
Definition: helper.cxx:191
void SetHelpURL(std::u16string_view rURL)
Definition: newhelp.cxx:2519
void OpenKeyword(const OUString &rKeyword)
Definition: newhelp.hxx:478
void loadHelpContent(const OUString &sHelpURL, bool bAddToHistory=true)
Definition: newhelp.cxx:1224
static SAL_DLLPRIVATE OUString CreateHelpURL_Impl(const OUString &aCommandURL, const OUString &rModuleName)
Definition: sfxhelp.cxx:496
virtual SAL_DLLPRIVATE void SearchKeyword(const OUString &rKeyWord) override
Definition: sfxhelp.cxx:684
static SAL_DLLPRIVATE bool Start_Impl(const OUString &rURL, weld::Widget *pWidget, const OUString &rKeyword)
Definition: sfxhelp.cxx:1208
static OUString CreateHelpURL(const OUString &aCommandURL, const OUString &rModuleName)
Definition: sfxhelp.cxx:1380
bool bLaunchingHelp
Definition: sfxhelp.hxx:34
virtual SAL_DLLPRIVATE bool Start(const OUString &rURL, weld::Widget *pWidget=nullptr) override
Definition: sfxhelp.cxx:699
SfxHelp()
Definition: sfxhelp.cxx:342
virtual ~SfxHelp() override
Definition: sfxhelp.cxx:354
static bool IsHelpInstalled()
Definition: sfxhelp.cxx:1396
static OUString GetURLHelpText(std::u16string_view)
Definition: sfxhelp.cxx:666
bool bIsDebug
Definition: sfxhelp.hxx:33
static SAL_DLLPRIVATE OUString GetHelpModuleName_Impl(std::u16string_view rHelpId)
Definition: sfxhelp.cxx:448
static OUString GetCurrentModuleIdentifier()
Definition: sfxhelp.cxx:1391
static OUString GetDefaultHelpModule()
Definition: sfxhelp.cxx:1386
virtual OUString GetHelpText(const OUString &, const vcl::Window *pWindow) override
Definition: sfxhelp.cxx:584
One SfxViewShell more or less represents one edit window for a document, there can be multiple ones f...
Definition: viewsh.hxx:165
static SAL_WARN_UNUSED_RESULT SfxViewShell * Current()
Definition: viewsh.cxx:1848
bool WriteUnicodeOrByteText(std::u16string_view rStr, rtl_TextEncoding eDestCharSet)
void SetStreamCharSet(rtl_TextEncoding eCharSet)
static OUString GetLanguageString(const LanguageType eType)
bool IsModuleInstalled(EModule eModule) const
OUString SubstituteVariable(const OUString &rVar) const
void incBusy(const weld::Widget *pIgnore)
static css::uno::Reference< css::awt::XWindow > GetInterface(vcl::Window *pWindow)
static vcl::Window * GetWindow(const css::uno::Reference< css::awt::XWindow > &rxWindow)
void disposeAndClear()
static std::shared_ptr< ConfigurationChanges > create()
css::uno::Any getPropertyValue(const OUString &rPropertyName)
static OUString getProductVersion()
static OUString getUILocale()
void EnableKillingFile(bool bEnable=true)
SvStream * GetStream(StreamMode eMode)
OUString const & GetURL() const
OUString GetName() const
vcl::Window * GetParent() const
WindowType GetType() const
const OUString & GetHelpId() const
bool IsSystemWindow() const
weld::Window * GetFrameWeld() const
OUString get_primary_text() const
void set_primary_text(const OUString &rText)
virtual std::unique_ptr< Container > weld_parent() const=0
virtual void help_hierarchy_foreach(const std::function< bool(const OUString &)> &func)=0
Any value
OUString url_
#define TOOLS_WARN_EXCEPTION(area, stream)
URL aURL
float u
sal_Int32 nIndex
constexpr sal_uInt16 KEY_MOD1
constexpr sal_uInt16 KEY_SPACE
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
def run(arg=None, arg2=-1)
bool IsOptionSet(EOption eOption)
err
@ Exception
const LanguageTag & getLanguageTag()
Reference< XComponentContext > getProcessComponentContext()
Error
bool createTemporaryHtmlDirectory(OUString **url)
Definition: flatpak.cxx:42
bool isFlatpak()
Definition: flatpak.cxx:27
line
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
void openUriExternally(const OUString &sURI, bool bHandleSystemShellExecuteException, weld::Widget *pDialogParent)
Open a URI via com.sun.star.system.SystemShellExecute.
Sequence< beans::PropertyValue > GetCommandProperties(const OUString &rsCommandName, const OUString &rsModuleName)
OUString GetRealCommandForCommand(const css::uno::Sequence< css::beans::PropertyValue > &rProperties)
std::vector< sal_uInt8 > bytes
constexpr OUStringLiteral SHTML2
Definition: sfxhelp.cxx:932
static OUString getCurrentModuleIdentifier_Impl()
Definition: sfxhelp.cxx:385
static SfxHelpWindow_Impl * impl_createHelp(Reference< XFrame2 > &rHelpTask, Reference< XFrame > &rHelpContent)
Definition: sfxhelp.cxx:532
constexpr OUStringLiteral SHTML3
Definition: sfxhelp.cxx:933
constexpr OUStringLiteral SHTML4
Definition: sfxhelp.cxx:934
constexpr OUStringLiteral SHTML5
Definition: sfxhelp.cxx:935
constexpr OUStringLiteral SHTML1
Definition: sfxhelp.cxx:931
static OUString const & HelpLocaleString()
Return the locale we prefer for displaying help.
Definition: sfxhelp.cxx:201
static OUString getDefaultModule_Impl()
Definition: sfxhelp.cxx:358
IMPL_STATIC_LINK_NOARG(NoHelpErrorBox, HelpRequestHdl, weld::Widget &, bool)
Definition: sfxhelp.cxx:112
static bool impl_showOnlineHelp(const OUString &rURL, weld::Widget *pDialogParent)
Redirect the vnd.sun.star.help:// urls to http://help.libreoffice.org.
Definition: sfxhelp.cxx:710
void AppendConfigToken(OUStringBuffer &rURL, bool bQuestionMark)
Appends ?Language=xy&System=abc to the help URL in rURL.
Definition: sfxhelp.cxx:267
static bool impl_showOfflineHelp(const OUString &rURL, weld::Widget *pDialogParent)
Definition: sfxhelp.cxx:938
static bool GetHelpAnchor_Impl(std::u16string_view _rURL, OUString &_rAnchor)
Definition: sfxhelp.cxx:288
OUString SfxResId(TranslateId aId)
Definition: sfxresid.cxx:22
VCL_DLLPUBLIC Application * GetpApp()
INetProtocol
RET_OK
VclMessageType
VclButtonsType