1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_folders.h>
21 #include <sfx2/sfxhelp.hxx>
23 #include <string_view>
29 #include <Foundation/NSString.h>
30 #include <CoreFoundation/CFURL.h>
31 #include <CoreServices/CoreServices.h>
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>
40 #include <comphelper/processfactory.hxx>
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>
45 #include <toolkit/helper/vclunohelper.hxx>
46 #include <com/sun/star/frame/ModuleManager.hpp>
47 #include <unotools/configmgr.hxx>
48 #include <unotools/moduleoptions.hxx>
49 #include <tools/urlobj.hxx>
50 #include <ucbhelper/content.hxx>
51 #include <unotools/pathoptions.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>
59 #include <unotools/securityoptions.hxx>
60 #include <rtl/uri.hxx>
61 #include <vcl/commandinfoprovider.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>
68 #include <comphelper/lok.hxx>
69 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
70 #include <sfx2/viewsh.hxx>
72 #include "newhelp.hxx"
73 #include <sfx2/flatpak.hxx>
74 #include <sfx2/sfxresid.hxx>
76 #include <sfx2/strings.hrc>
77 #include <vcl/svapp.hxx>
78 #include <rtl/string.hxx>
79 #include <svtools/langtab.hxx>
80 #include <tools/diagnose_ex.h>
82 using namespace ::com::sun::star::beans
;
83 using namespace ::com::sun::star::frame
;
84 using namespace ::com::sun::star::uno
;
85 using namespace ::com::sun::star::util
;
86 using namespace ::com::sun::star::lang
;
93 std::unique_ptr
<weld::MessageDialog
> m_xErrBox
;
95 DECL_STATIC_LINK(NoHelpErrorBox
, HelpRequestHdl
, weld::Widget
&, bool);
97 explicit NoHelpErrorBox(weld::Widget
* pParent
)
98 : m_xErrBox(Application::CreateMessageDialog(pParent
, VclMessageType::Error
, VclButtonsType::Ok
,
99 SfxResId(RID_STR_HLPFILENOTEXIST
)))
101 // Error message: "No help available"
102 m_xErrBox
->connect_help(LINK(nullptr, NoHelpErrorBox
, HelpRequestHdl
));
112 IMPL_STATIC_LINK_NOARG(NoHelpErrorBox
, HelpRequestHdl
, weld::Widget
&, bool)
114 // do nothing, because no help available
118 static OUString
const & HelpLocaleString();
122 /// Root path of the help.
123 OUString
const & getHelpRootURL()
125 static OUString
const s_instURL
= []()
127 OUString tmp
= officecfg::Office::Common::Path::Current::Help::get(comphelper::getProcessComponentContext());
130 // try to determine path from default
131 tmp
= "$(instpath)/" LIBO_SHARE_HELP_FOLDER
;
134 // replace anything like $(instpath);
135 SvtPathOptions aOptions
;
136 tmp
= aOptions
.SubstituteVariable(tmp
);
139 if (osl::FileBase::getFileURLFromSystemPath(tmp
, url
) == osl::FileBase::E_None
)
146 bool impl_checkHelpLocalePath(OUString
const & rpPath
)
148 osl::DirectoryItem directoryItem
;
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())
161 /// Check for built-in help
162 /// Check if help/<lang>/err.html file exist
163 bool impl_hasHelpInstalled()
165 if (comphelper::LibreOfficeKit::isActive())
168 // detect installed locale
169 static OUString
const aLocaleStr
= HelpLocaleString();
171 OUString helpRootURL
= getHelpRootURL() + "/" + aLocaleStr
+ "/err.html";
173 osl::DirectoryItem directoryItem
;
174 if(osl::DirectoryItem::get(helpRootURL
, directoryItem
) == osl::FileBase::E_None
){
178 SAL_INFO( "sfx.appl", "Checking old help installed " << bOK
);
182 /// Check for html built-in help
183 /// Check if help/lang/text folder exist. Only html has it.
184 bool impl_hasHTMLHelpInstalled()
186 if (comphelper::LibreOfficeKit::isActive())
189 // detect installed locale
190 static OUString
const aLocaleStr
= HelpLocaleString();
192 OUString helpRootURL
= getHelpRootURL() + "/" + aLocaleStr
+ "/text";
193 bool bOK
= impl_checkHelpLocalePath( helpRootURL
);
194 SAL_INFO( "sfx.appl", "Checking new help (html) installed " << bOK
);
200 /// Return the locale we prefer for displaying help
201 static OUString
const & HelpLocaleString()
203 if (comphelper::LibreOfficeKit::isActive())
204 return comphelper::LibreOfficeKit::getLanguageTag().getBcp47();
206 static OUString aLocaleStr
;
207 if (!aLocaleStr
.isEmpty())
210 static const OUStringLiteral
aEnglish(u
"en-US");
211 // detect installed locale
212 aLocaleStr
= utl::ConfigManager::getUILocale();
214 if ( aLocaleStr
.isEmpty() )
216 aLocaleStr
= aEnglish
;
220 // get fall-back language (country)
221 OUString sLang
= aLocaleStr
;
222 sal_Int32 nSepPos
= sLang
.indexOf( '-' );
225 sLang
= sLang
.copy( 0, nSepPos
);
227 OUString
sHelpPath("");
228 sHelpPath
= getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + aLocaleStr
;
229 if (impl_checkHelpLocalePath(sHelpPath
))
233 sHelpPath
= getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + sLang
;
234 if (impl_checkHelpLocalePath(sHelpPath
))
239 sHelpPath
= getHelpRootURL() + "/" + aLocaleStr
;
240 if (impl_checkHelpLocalePath(sHelpPath
))
244 sHelpPath
= getHelpRootURL() + "/" + sLang
;
245 if (impl_checkHelpLocalePath(sHelpPath
))
250 sHelpPath
= getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + aEnglish
;
251 if (impl_checkHelpLocalePath(sHelpPath
))
253 aLocaleStr
= aEnglish
;
256 sHelpPath
= getHelpRootURL() + "/" + aEnglish
;
257 if (impl_checkHelpLocalePath(sHelpPath
))
259 aLocaleStr
= aEnglish
;
267 void AppendConfigToken( OUStringBuffer
& rURL
, bool bQuestionMark
)
269 OUString aLocaleStr
= HelpLocaleString();
271 // query part exists?
273 // no, so start with '?'
276 // yes, so only append with '&'
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=");
285 rURL
.append(utl::ConfigManager::getProductVersion());
288 static bool GetHelpAnchor_Impl( const OUString
& _rURL
, OUString
& _rAnchor
)
294 ::ucbhelper::Content
aCnt( INetURLObject( _rURL
).GetMainURL( INetURLObject::DecodeMechanism::NONE
),
295 Reference
< css::ucb::XCommandEnvironment
>(),
296 comphelper::getProcessComponentContext() );
298 if ( aCnt
.getPropertyValue("AnchorName") >>= sAnchor
)
301 if ( !sAnchor
.isEmpty() )
309 SAL_WARN( "sfx.appl", "Property 'AnchorName' is missing" );
312 catch (const css::uno::Exception
&)
324 static OUString
GetHelpText( const OUString
& aCommandURL
, const OUString
& rModule
);
329 OUString
SfxHelp_Impl::GetHelpText( const OUString
& aCommandURL
, const OUString
& rModule
)
332 OUStringBuffer
aHelpURL( SfxHelp::CreateHelpURL( aCommandURL
, rModule
) );
333 // added 'active' parameter
334 sal_Int32 nIndex
= aHelpURL
.lastIndexOf( '#' );
336 nIndex
= aHelpURL
.getLength();
337 aHelpURL
.insert( nIndex
, "&Active=true" );
339 return SfxContentHelper::GetActiveHelpString( aHelpURL
.makeStringAndClear() );
344 , bLaunchingHelp(false)
346 // read the environment variable "HELP_DEBUG"
347 // if it's set, you will see debug output on active help
349 OUString
sEnvVarName( "HELP_DEBUG" );
350 osl_getEnvironment( sEnvVarName
.pData
, &sHelpDebug
.pData
);
351 bIsDebug
= !sHelpDebug
.isEmpty();
358 static OUString
getDefaultModule_Impl()
360 OUString sDefaultModule
;
361 SvtModuleOptions aModOpt
;
362 if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::EModule::WRITER
) )
363 sDefaultModule
= "swriter";
364 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::EModule::CALC
) )
365 sDefaultModule
= "scalc";
366 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS
) )
367 sDefaultModule
= "simpress";
368 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::EModule::DRAW
) )
369 sDefaultModule
= "sdraw";
370 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::EModule::MATH
) )
371 sDefaultModule
= "smath";
372 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::EModule::CHART
) )
373 sDefaultModule
= "schart";
374 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::EModule::BASIC
) )
375 sDefaultModule
= "sbasic";
376 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE
) )
377 sDefaultModule
= "sdatabase";
380 SAL_WARN( "sfx.appl", "getDefaultModule_Impl(): no module installed" );
382 return sDefaultModule
;
385 static OUString
getCurrentModuleIdentifier_Impl()
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();
393 if ( xCurrentFrame
.is() )
397 sIdentifier
= xModuleManager
->identify( xCurrentFrame
);
399 catch (const css::frame::UnknownModuleException
&)
401 SAL_INFO( "sfx.appl", "SfxHelp::getCurrentModuleIdentifier_Impl(): unknown module (help in help?)" );
403 catch (const Exception
&)
405 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelp::getCurrentModuleIdentifier_Impl(): exception of XModuleManager::identify()" );
414 OUString
MapModuleIdentifier(const OUString
&rFactoryShortName
)
416 OUString
aFactoryShortName(rFactoryShortName
);
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();
444 return aFactoryShortName
;
448 OUString
SfxHelp::GetHelpModuleName_Impl(std::u16string_view rHelpID
)
450 OUString aFactoryShortName
;
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
))
460 std::size_t nEndModule
= sRemainder
.find(u
'/');
461 aFactoryShortName
= nEndModule
!= std::u16string_view::npos
462 ? sRemainder
.substr(0, nEndModule
) : sRemainder
;
465 if (aFactoryShortName
.isEmpty())
467 OUString aModuleIdentifier
= getCurrentModuleIdentifier_Impl();
468 if (!aModuleIdentifier
.isEmpty())
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
;
481 catch (const Exception
&)
483 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelp::GetHelpModuleName_Impl()" );
488 if (!aFactoryShortName
.isEmpty())
489 aFactoryShortName
= MapModuleIdentifier(aFactoryShortName
);
490 if (aFactoryShortName
.isEmpty())
491 aFactoryShortName
= getDefaultModule_Impl();
493 return aFactoryShortName
;
496 OUString
SfxHelp::CreateHelpURL_Impl( const OUString
& aCommandURL
, const OUString
& rModuleName
)
498 // build up the help URL
499 OUStringBuffer
aHelpURL("vnd.sun.star.help://");
500 bool bHasAnchor
= false;
503 OUString
aModuleName( rModuleName
);
504 if (aModuleName
.isEmpty())
505 aModuleName
= getDefaultModule_Impl();
507 aHelpURL
.append(aModuleName
);
509 if ( aCommandURL
.isEmpty() )
510 aHelpURL
.append("/start");
513 aHelpURL
.append('/');
514 aHelpURL
.append(rtl::Uri::encode(aCommandURL
,
515 rtl_UriCharClassRelSegment
,
516 rtl_UriEncodeKeepEscapes
,
517 RTL_TEXTENCODING_UTF8
));
519 OUStringBuffer aTempURL
= aHelpURL
;
520 AppendConfigToken( aTempURL
, true );
521 bHasAnchor
= GetHelpAnchor_Impl(aTempURL
.makeStringAndClear(), aAnchor
);
524 AppendConfigToken( aHelpURL
, true );
528 aHelpURL
.append('#');
529 aHelpURL
.append(aAnchor
);
532 return aHelpURL
.makeStringAndClear();
535 static SfxHelpWindow_Impl
* impl_createHelp(Reference
< XFrame2
>& rHelpTask
,
536 Reference
< XFrame
>& rHelpContent
)
538 Reference
< XDesktop2
> xDesktop
= Desktop::create( ::comphelper::getProcessComponentContext() );
540 // otherwise - create new help task
541 Reference
< XFrame2
> xHelpTask(
542 xDesktop
->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::TASKS
| FrameSearchFlag::CREATE
),
547 // create all internal windows and sub frames ...
548 Reference
< css::awt::XWindow
> xParentWindow
= xHelpTask
->getContainerWindow();
549 VclPtr
<vcl::Window
> pParentWindow
= VCLUnoHelper::GetWindow( xParentWindow
);
550 VclPtrInstance
<SfxHelpWindow_Impl
> pHelpWindow( xHelpTask
, pParentWindow
);
551 Reference
< css::awt::XWindow
> xHelpWindow
= VCLUnoHelper::GetInterface( pHelpWindow
);
553 Reference
< XFrame
> xHelpContent
;
554 if (xHelpTask
->setComponent( xHelpWindow
, Reference
< XController
>() ))
557 xHelpTask
->setName("OFFICE_HELP_TASK");
559 Reference
< XPropertySet
> xProps(xHelpTask
, UNO_QUERY
);
561 xProps
->setPropertyValue(
563 makeAny(SfxResId(STR_HELP_WINDOW_TITLE
)));
565 pHelpWindow
->setContainerWindow( xParentWindow
);
566 xParentWindow
->setVisible(true);
567 xHelpWindow
->setVisible(true);
569 // This sub frame is created internally (if we called new SfxHelpWindow_Impl() ...)
570 // It should exist :-)
571 xHelpContent
= xHelpTask
->findFrame("OFFICE_HELP", FrameSearchFlag::CHILDREN
);
574 if (!xHelpContent
.is())
576 pHelpWindow
.disposeAndClear();
580 xHelpContent
->setName("OFFICE_HELP");
582 rHelpTask
= xHelpTask
;
583 rHelpContent
= xHelpContent
;
587 OUString
SfxHelp::GetHelpText( const OUString
& aCommandURL
, const vcl::Window
* pWindow
)
589 OUString sModuleName
= GetHelpModuleName_Impl(aCommandURL
);
590 auto aProperties
= vcl::CommandInfoProvider::GetCommandProperties(aCommandURL
, getCurrentModuleIdentifier_Impl());
591 OUString sRealCommand
= vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties
);
592 OUString sHelpText
= SfxHelp_Impl::GetHelpText( sRealCommand
.isEmpty() ? aCommandURL
: sRealCommand
, sModuleName
);
596 if (pWindow
&& sHelpText
.isEmpty())
598 // no help text found -> try with parent help id.
599 vcl::Window
* pParent
= pWindow
->GetParent();
602 aNewHelpId
= pParent
->GetHelpId();
603 sHelpText
= SfxHelp_Impl::GetHelpText( OStringToOUString(aNewHelpId
, RTL_TEXTENCODING_UTF8
), sModuleName
);
604 if (!sHelpText
.isEmpty())
607 pParent
= pParent
->GetParent();
610 if (bIsDebug
&& sHelpText
.isEmpty())
614 // add some debug information?
617 sHelpText
+= "\n-------------\n" +
618 sModuleName
+ ": " + aCommandURL
;
619 if ( !aNewHelpId
.isEmpty() )
622 OStringToOUString(aNewHelpId
, RTL_TEXTENCODING_UTF8
);
629 OUString
SfxHelp::GetHelpText(const OUString
& aCommandURL
, const weld::Widget
* pWidget
)
631 OUString sModuleName
= GetHelpModuleName_Impl(aCommandURL
);
632 auto aProperties
= vcl::CommandInfoProvider::GetCommandProperties(aCommandURL
, getCurrentModuleIdentifier_Impl());
633 OUString sRealCommand
= vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties
);
634 OUString sHelpText
= SfxHelp_Impl::GetHelpText( sRealCommand
.isEmpty() ? aCommandURL
: sRealCommand
, sModuleName
);
638 if (pWidget
&& sHelpText
.isEmpty())
640 // no help text found -> try with parent help id.
641 std::unique_ptr
<weld::Widget
> xParent(pWidget
->weld_parent());
644 aNewHelpId
= xParent
->get_help_id();
645 sHelpText
= SfxHelp_Impl::GetHelpText( OStringToOUString(aNewHelpId
, RTL_TEXTENCODING_UTF8
), sModuleName
);
646 if (!sHelpText
.isEmpty())
649 xParent
= xParent
->weld_parent();
652 if (bIsDebug
&& sHelpText
.isEmpty())
656 // add some debug information?
659 sHelpText
+= "\n-------------\n" +
660 sModuleName
+ ": " + aCommandURL
;
661 if ( !aNewHelpId
.isEmpty() )
664 OStringToOUString(aNewHelpId
, RTL_TEXTENCODING_UTF8
);
671 OUString
SfxHelp::GetURLHelpText(std::u16string_view aURL
)
673 bool bCtrlClickHlink
= SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::CtrlClickHyperlink
);
675 // "ctrl-click to follow link:" for not MacOS
676 // "⌘-click to follow link:" for MacOs
677 vcl::KeyCode
aCode(KEY_SPACE
);
678 vcl::KeyCode
aModifiedCode(KEY_SPACE
, KEY_MOD1
);
679 OUString
aModStr(aModifiedCode
.GetName());
680 aModStr
= aModStr
.replaceFirst(aCode
.GetName(), "");
681 aModStr
= aModStr
.replaceAll("+", "");
683 = bCtrlClickHlink
? SfxResId(STR_CTRLCLICKHYPERLINK
) : SfxResId(STR_CLICKHYPERLINK
);
684 aHelpStr
= aHelpStr
.replaceFirst("%{key}", aModStr
);
685 aHelpStr
= aHelpStr
.replaceFirst("%{link}", aURL
);
689 void SfxHelp::SearchKeyword( const OUString
& rKeyword
)
691 Start_Impl(OUString(), static_cast<weld::Widget
*>(nullptr), rKeyword
);
694 bool SfxHelp::Start( const OUString
& rURL
, const vcl::Window
* pWindow
)
698 bLaunchingHelp
= true;
699 bool bRet
= Start_Impl( rURL
, pWindow
);
700 bLaunchingHelp
= false;
704 bool SfxHelp::Start(const OUString
& rURL
, weld::Widget
* pWidget
)
708 bLaunchingHelp
= true;
709 bool bRet
= Start_Impl(rURL
, pWidget
, OUString());
710 bLaunchingHelp
= false;
714 /// Redirect the vnd.sun.star.help:// urls to http://help.libreoffice.org
715 static bool impl_showOnlineHelp(const OUString
& rURL
, weld::Widget
* pDialogParent
)
717 static constexpr OUStringLiteral
aInternal(u
"vnd.sun.star.help://");
718 if ( rURL
.getLength() <= aInternal
.getLength() || !rURL
.startsWith(aInternal
) )
721 OUString aHelpLink
= officecfg::Office::Common::Help::HelpRootURL::get();
722 OUString aTarget
= OUString::Concat("Target=") + rURL
.subView(aInternal
.getLength());
723 aTarget
= aTarget
.replaceAll("%2F", "/").replaceAll("?", "&");
724 aHelpLink
+= aTarget
;
726 if (comphelper::LibreOfficeKit::isActive())
728 if(SfxViewShell
* pViewShell
= SfxViewShell::Current())
730 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED
,
731 aHelpLink
.toUtf8().getStr());
736 GetpApp()->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED
,
737 aHelpLink
.toUtf8().getStr());
747 LSOpenCFURLRef(CFURLCreateWithString(kCFAllocatorDefault
,
748 CFStringCreateWithCString(kCFAllocatorDefault
,
749 aHelpLink
.toUtf8().getStr(),
750 kCFStringEncodingUTF8
),
755 sfx2::openUriExternally(aHelpLink
, false, pDialogParent
);
759 catch (const Exception
&)
767 bool rewriteFlatpakHelpRootUrl(OUString
* helpRootUrl
) {
768 assert(helpRootUrl
!= nullptr);
769 //TODO: this function for now assumes that the passed-in *helpRootUrl references
770 // /app/libreoffice/help (which belongs to the org.libreoffice.LibreOffice.Help
771 // extension); it replaces it with the corresponding file URL as seen outside the flatpak
773 struct Failure
: public std::exception
{};
775 static auto const url
= [] {
776 // From /.flatpak-info [Instance] section, read
778 // app-extensions=...;org.libreoffice.LibreOffice.Help=<sha>;...
780 osl::File
ini("file:///.flatpak-info");
781 auto err
= ini
.open(osl_File_OpenFlag_Read
);
782 if (err
!= osl::FileBase::E_None
) {
783 SAL_WARN("sfx.appl", "LIBO_FLATPAK mode failure opening /.flatpak-info: " << err
);
788 bool havePath
= false;
789 bool haveExtensions
= false;
790 for (bool instance
= false; !(havePath
&& haveExtensions
);) {
791 rtl::ByteSequence bytes
;
792 err
= ini
.readLine(bytes
);
793 if (err
!= osl::FileBase::E_None
) {
796 "LIBO_FLATPAK mode reading /.flatpak-info fails with " << err
797 << " before [Instance] app-path");
800 std::string_view
const line(
801 reinterpret_cast<char const *>(bytes
.getConstArray()), bytes
.getLength());
803 static constexpr auto keyPath
= std::string_view("app-path=");
804 static constexpr auto keyExtensions
= std::string_view("app-extensions=");
805 if (!havePath
&& line
.length() >= keyPath
.size()
806 && line
.substr(0, keyPath
.size()) == keyPath
.data())
808 auto const value
= line
.substr(keyPath
.size());
809 if (!rtl_convertStringToUString(
810 &path
.pData
, value
.data(), value
.length(),
811 osl_getThreadTextEncoding(),
812 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
813 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
814 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR
)))
818 "LIBO_FLATPAK mode failure converting app-path \"" << value
823 } else if (!haveExtensions
&& line
.length() >= keyExtensions
.size()
824 && line
.substr(0, keyExtensions
.size()) == keyExtensions
.data())
826 auto const value
= line
.substr(keyExtensions
.size());
827 if (!rtl_convertStringToUString(
828 &extensions
.pData
, value
.data(), value
.length(),
829 osl_getThreadTextEncoding(),
830 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
831 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
832 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR
)))
836 "LIBO_FLATPAK mode failure converting app-extensions \"" << value
840 haveExtensions
= true;
841 } else if (line
.length() > 0 && line
[0] == '[') {
844 "LIBO_FLATPAK mode /.flatpak-info lacks [Instance] app-path and"
848 } else if (line
== "[Instance]") {
853 // Extract <sha> from ...;org.libreoffice.LibreOffice.Help=<sha>;...:
855 for (sal_Int32 i
= 0;;) {
856 OUString elem
= extensions
.getToken(0, ';', i
);
857 if (elem
.startsWith("org.libreoffice.LibreOffice.Help=", &sha
)) {
863 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-extensions \""
864 << extensions
<< "\" org.libreoffice.LibreOffice.Help");
868 // Assuming that <path> is of the form
869 // /.../app/org.libreoffice.LibreOffice/<arch>/<branch>/<sha'>/files
871 // /.../runtime/org.libreoffice.LibreOffice.Help/<arch>/<branch>/<sha>/files
872 // because the extension's files are stored at a different place than the app's files,
873 // so use this hack until flatpak itself provides a better solution:
874 static constexpr OUStringLiteral segments
= u
"/app/org.libreoffice.LibreOffice/";
875 auto const i1
= path
.lastIndexOf(segments
);
876 // use lastIndexOf instead of indexOf, in case the user-controlled prefix /.../
877 // happens to contain such segments
881 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
882 << "\" doesn't contain /app/org.libreoffice.LibreOffice/");
885 auto const i2
= i1
+ segments
.getLength();
886 auto i3
= path
.indexOf('/', i2
);
890 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
891 << "\" doesn't contain branch segment");
894 i3
= path
.indexOf('/', i3
+ 1);
898 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
899 << "\" doesn't contain sha segment");
903 auto const i4
= path
.indexOf('/', i3
);
907 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
908 << "\" doesn't contain files segment");
911 path
= path
.subView(0, i1
) + OUString::Concat("/runtime/org.libreoffice.LibreOffice.Help/")
912 + path
.subView(i2
, i3
- i2
) + sha
+ path
.subView(i4
);
913 // Turn <path> into a file URL:
915 err
= osl::FileBase::getFileURLFromSystemPath(path
, url_
);
916 if (err
!= osl::FileBase::E_None
) {
919 "LIBO_FLATPAK mode failure converting app-path \"" << path
<< "\" to URL: "
927 } catch (Failure
&) {
934 // add <noscript> meta for browsers without javascript
936 constexpr OUStringLiteral SHTML1
= u
"<!DOCTYPE HTML><html lang=\"en-US\"><head><meta charset=\"UTF-8\">";
937 constexpr OUStringLiteral SHTML2
= u
"<noscript><meta http-equiv=\"refresh\" content=\"0; url='";
938 constexpr OUStringLiteral SHTML3
= u
"/noscript.html'\"></noscript><meta http-equiv=\"refresh\" content=\"1; url='";
939 constexpr OUStringLiteral SHTML4
= u
"'\"><script type=\"text/javascript\"> window.location.href = \"";
940 constexpr OUStringLiteral SHTML5
= u
"\";</script><title>Help Page Redirection</title></head><body></body></html>";
942 // use a tempfile since e.g. xdg-open doesn't support URL-parameters with file:// URLs
943 static bool impl_showOfflineHelp(const OUString
& rURL
, weld::Widget
* pDialogParent
)
945 OUString aBaseInstallPath
= getHelpRootURL();
946 // For the flatpak case, find the pathname outside the flatpak sandbox that corresponds to
947 // aBaseInstallPath, because that is what needs to be stored in aTempFile below:
948 if (flatpak::isFlatpak() && !rewriteFlatpakHelpRootUrl(&aBaseInstallPath
)) {
952 OUString
aHelpLink( aBaseInstallPath
+ "/index.html?" );
953 OUString aTarget
= OUString::Concat("Target=") + rURL
.subView(RTL_CONSTASCII_LENGTH("vnd.sun.star.help://"));
954 aTarget
= aTarget
.replaceAll("%2F","/").replaceAll("?","&");
955 aHelpLink
+= aTarget
;
957 // Get a html tempfile (for the flatpak case, create it in XDG_CACHE_HOME instead of /tmp for
958 // technical reasons, so that it can be accessed by the browser running outside the sandbox):
959 OUString
const aExtension(".html");
960 OUString
* parent
= nullptr;
961 if (flatpak::isFlatpak() && !flatpak::createTemporaryHtmlDirectory(&parent
)) {
964 ::utl::TempFile
aTempFile("NewHelp", true, &aExtension
, parent
, false );
966 SvStream
* pStream
= aTempFile
.GetStream(StreamMode::WRITE
);
967 pStream
->SetStreamCharSet(RTL_TEXTENCODING_UTF8
);
969 OUString aTempStr
= SHTML1
+ SHTML2
+
970 aBaseInstallPath
+ "/" + HelpLocaleString() + SHTML3
+
974 pStream
->WriteUnicodeOrByteText(aTempStr
);
976 aTempFile
.CloseStream();
980 LSOpenCFURLRef(CFURLCreateWithString(kCFAllocatorDefault
,
981 CFStringCreateWithCString(kCFAllocatorDefault
,
982 aTempFile
.GetURL().toUtf8().getStr(),
983 kCFStringEncodingUTF8
),
988 sfx2::openUriExternally(aTempFile
.GetURL(), false, pDialogParent
);
992 catch (const Exception
&)
995 aTempFile
.EnableKillingFile();
1001 // tdf#119579 skip floating windows as potential parent for missing help dialog
1002 const vcl::Window
* GetBestParent(const vcl::Window
* pWindow
)
1006 if (pWindow
->IsSystemWindow() && pWindow
->GetType() != WindowType::FLOATINGWINDOW
)
1008 pWindow
= pWindow
->GetParent();
1016 class HelpManualMessage
: public weld::MessageDialogController
1019 std::unique_ptr
<weld::CheckButton
> m_xHideOfflineHelpCB
;
1022 HelpManualMessage(weld::Widget
* pParent
)
1023 : MessageDialogController(pParent
, "sfx/ui/helpmanual.ui", "onlinehelpmanual", "hidedialog")
1024 , m_xHideOfflineHelpCB(m_xBuilder
->weld_check_button("hidedialog"))
1026 LanguageType aLangType
= Application::GetSettings().GetUILanguageTag().getLanguageType();
1027 OUString sLocaleString
= SvtLanguageTable::GetLanguageString(aLangType
);
1028 OUString sPrimText
= get_primary_text();
1029 set_primary_text(sPrimText
.replaceAll("$UILOCALE", sLocaleString
));
1032 bool GetOfflineHelpPopUp() const { return !m_xHideOfflineHelpCB
->get_active(); }
1037 bool SfxHelp::Start_Impl(const OUString
& rURL
, const vcl::Window
* pWindow
)
1039 OUStringBuffer
aHelpRootURL("vnd.sun.star.help://");
1040 AppendConfigToken(aHelpRootURL
, true);
1041 SfxContentHelper::GetResultSet(aHelpRootURL
.makeStringAndClear());
1045 * - a HelpID (formerly a long, now a string)
1046 * If rURL is a URL, CreateHelpURL should be called for this URL
1047 * If rURL is an arbitrary string, the same should happen, but the URL should be tried out
1048 * if it delivers real help content. In case only the Help Error Document is returned, the
1049 * parent of the window for that help was called, is asked for its HelpID.
1050 * For compatibility reasons this upward search is not implemented for "real" URLs.
1051 * Help keyword search now is implemented as own method; in former versions it
1052 * was done via Help::Start, but this implementation conflicted with the upward search.
1055 INetURLObject
aParser( rURL
);
1056 INetProtocol nProtocol
= aParser
.GetProtocol();
1058 switch ( nProtocol
)
1060 case INetProtocol::VndSunStarHelp
:
1061 // already a vnd.sun.star.help URL -> nothing to do
1066 OUString
aHelpModuleName(GetHelpModuleName_Impl(rURL
));
1067 OUString aRealCommand
;
1069 if ( nProtocol
== INetProtocol::Uno
)
1071 // Command can be just an alias to another command.
1072 auto aProperties
= vcl::CommandInfoProvider::GetCommandProperties(rURL
, getCurrentModuleIdentifier_Impl());
1073 aRealCommand
= vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties
);
1076 // no URL, just a HelpID (maybe empty in case of keyword search)
1077 aHelpURL
= CreateHelpURL_Impl( aRealCommand
.isEmpty() ? rURL
: aRealCommand
, aHelpModuleName
);
1079 if ( impl_hasHelpInstalled() && pWindow
&& SfxContentHelper::IsHelpErrorDocument( aHelpURL
) )
1081 // no help found -> try with parent help id.
1082 vcl::Window
* pParent
= pWindow
->GetParent();
1085 OString aHelpId
= pParent
->GetHelpId();
1086 aHelpURL
= CreateHelpURL( OStringToOUString(aHelpId
, RTL_TEXTENCODING_UTF8
), aHelpModuleName
);
1088 if ( !SfxContentHelper::IsHelpErrorDocument( aHelpURL
) )
1094 pParent
= pParent
->GetParent();
1097 // create help url of start page ( helpid == 0 -> start page)
1098 aHelpURL
= CreateHelpURL( OUString(), aHelpModuleName
);
1107 pWindow
= GetBestParent(pWindow
);
1108 weld::Window
* pWeldWindow
= pWindow
? pWindow
->GetFrameWeld() : nullptr;
1110 if ( comphelper::LibreOfficeKit::isActive() )
1112 impl_showOnlineHelp(aHelpURL
, pWeldWindow
);
1116 if (@
available(macOS
10.14, *)) {
1117 // Workaround: Safari sandboxing prevents it from accessing files in the LibreOffice.app folder
1118 // force online-help instead if Safari is default browser.
1119 CFURLRef pBrowser
= LSCopyDefaultApplicationURLForURL(
1120 CFURLCreateWithString(
1121 kCFAllocatorDefault
,
1122 static_cast<CFStringRef
>(@
"https://www.libreoffice.org"),
1124 kLSRolesAll
, nullptr);
1125 if([static_cast<NSString
*>(CFURLGetString(pBrowser
)) isEqualToString
:@
"file:///Applications/Safari.app/"]) {
1126 impl_showOnlineHelp(aHelpURL
, pWeldWindow
);
1132 // If the HTML or no help is installed, but aHelpURL nevertheless references valid help content,
1133 // that implies that this help content belongs to an extension (and thus would not be available
1134 // in neither the offline nor online HTML help); in that case, fall through to the "old-help to
1135 // display" code below:
1136 if (SfxContentHelper::IsHelpErrorDocument(aHelpURL
))
1138 if ( impl_hasHTMLHelpInstalled() && impl_showOfflineHelp(aHelpURL
, pWeldWindow
) )
1143 if ( !impl_hasHelpInstalled() )
1145 bool bShowOfflineHelpPopUp
= officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::get();
1147 TopLevelWindowLocker aBusy
;
1149 if(bShowOfflineHelpPopUp
)
1151 aBusy
.incBusy(pWeldWindow
);
1152 HelpManualMessage
aQueryBox(pWeldWindow
);
1153 short OnlineHelpBox
= aQueryBox
.run();
1154 bShowOfflineHelpPopUp
= OnlineHelpBox
!= RET_OK
;
1155 auto xChanges
= comphelper::ConfigurationChanges::create();
1156 officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::set(aQueryBox
.GetOfflineHelpPopUp(), xChanges
);
1160 if(!bShowOfflineHelpPopUp
)
1162 if ( impl_showOnlineHelp(aHelpURL
, pWeldWindow
) )
1166 aBusy
.incBusy(pWeldWindow
);
1167 NoHelpErrorBox
aErrBox(pWeldWindow
);
1180 // old-help to display
1181 Reference
< XDesktop2
> xDesktop
= Desktop::create( ::comphelper::getProcessComponentContext() );
1183 // check if help window is still open
1184 // If not, create a new one and return access directly to the internal sub frame showing the help content
1185 // search must be done here; search one desktop level could return an arbitrary frame
1186 Reference
< XFrame2
> xHelp(
1187 xDesktop
->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::CHILDREN
),
1189 Reference
< XFrame
> xHelpContent
= xDesktop
->findFrame(
1191 FrameSearchFlag::CHILDREN
);
1193 SfxHelpWindow_Impl
* pHelpWindow
= nullptr;
1195 pHelpWindow
= impl_createHelp(xHelp
, xHelpContent
);
1197 pHelpWindow
= static_cast<SfxHelpWindow_Impl
*>(VCLUnoHelper::GetWindow(xHelp
->getComponentWindow()));
1198 if (!xHelp
.is() || !xHelpContent
.is() || !pHelpWindow
)
1201 SAL_INFO("sfx.appl", "HelpId = " << aHelpURL
);
1203 pHelpWindow
->SetHelpURL( aHelpURL
);
1204 pHelpWindow
->loadHelpContent(aHelpURL
);
1206 Reference
< css::awt::XTopWindow
> xTopWindow( xHelp
->getContainerWindow(), UNO_QUERY
);
1207 if ( xTopWindow
.is() )
1208 xTopWindow
->toFront();
1213 bool SfxHelp::Start_Impl(const OUString
& rURL
, weld::Widget
* pWidget
, const OUString
& rKeyword
)
1215 OUStringBuffer
aHelpRootURL("vnd.sun.star.help://");
1216 AppendConfigToken(aHelpRootURL
, true);
1217 SfxContentHelper::GetResultSet(aHelpRootURL
.makeStringAndClear());
1221 * - a HelpID (formerly a long, now a string)
1222 * If rURL is a URL, CreateHelpURL should be called for this URL
1223 * If rURL is an arbitrary string, the same should happen, but the URL should be tried out
1224 * if it delivers real help content. In case only the Help Error Document is returned, the
1225 * parent of the window for that help was called, is asked for its HelpID.
1226 * For compatibility reasons this upward search is not implemented for "real" URLs.
1227 * Help keyword search now is implemented as own method; in former versions it
1228 * was done via Help::Start, but this implementation conflicted with the upward search.
1231 INetURLObject
aParser( rURL
);
1232 INetProtocol nProtocol
= aParser
.GetProtocol();
1234 switch ( nProtocol
)
1236 case INetProtocol::VndSunStarHelp
:
1237 // already a vnd.sun.star.help URL -> nothing to do
1242 OUString
aHelpModuleName(GetHelpModuleName_Impl(rURL
));
1243 OUString aRealCommand
;
1245 if ( nProtocol
== INetProtocol::Uno
)
1247 // Command can be just an alias to another command.
1248 auto aProperties
= vcl::CommandInfoProvider::GetCommandProperties(rURL
, getCurrentModuleIdentifier_Impl());
1249 aRealCommand
= vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties
);
1252 // no URL, just a HelpID (maybe empty in case of keyword search)
1253 aHelpURL
= CreateHelpURL_Impl( aRealCommand
.isEmpty() ? rURL
: aRealCommand
, aHelpModuleName
);
1255 if ( impl_hasHelpInstalled() && pWidget
&& SfxContentHelper::IsHelpErrorDocument( aHelpURL
) )
1257 bool bUseFinalFallback
= true;
1258 // no help found -> try ids of parents.
1259 pWidget
->help_hierarchy_foreach([&aHelpModuleName
, &aHelpURL
, &bUseFinalFallback
](const OString
& rHelpId
){
1260 if (rHelpId
.isEmpty())
1262 aHelpURL
= CreateHelpURL( OStringToOUString(rHelpId
, RTL_TEXTENCODING_UTF8
), aHelpModuleName
);
1263 bool bFinished
= !SfxContentHelper::IsHelpErrorDocument(aHelpURL
);
1265 bUseFinalFallback
= false;
1269 if (bUseFinalFallback
)
1271 // create help url of start page ( helpid == 0 -> start page)
1272 aHelpURL
= CreateHelpURL( OUString(), aHelpModuleName
);
1279 if ( comphelper::LibreOfficeKit::isActive() )
1281 impl_showOnlineHelp(aHelpURL
, pWidget
);
1285 if (@
available(macOS
10.14, *)) {
1286 // Workaround: Safari sandboxing prevents it from accessing files in the LibreOffice.app folder
1287 // force online-help instead if Safari is default browser.
1288 CFURLRef pBrowser
= LSCopyDefaultApplicationURLForURL(
1289 CFURLCreateWithString(
1290 kCFAllocatorDefault
,
1291 static_cast<CFStringRef
>(@
"https://www.libreoffice.org"),
1293 kLSRolesAll
, nullptr);
1294 if([static_cast<NSString
*>(CFURLGetString(pBrowser
)) isEqualToString
:@
"file:///Applications/Safari.app/"]) {
1295 impl_showOnlineHelp(aHelpURL
, pWidget
);
1301 // If the HTML or no help is installed, but aHelpURL nevertheless references valid help content,
1302 // that implies that help content belongs to an extension (and thus would not be available
1303 // in neither the offline nor online HTML help); in that case, fall through to the "old-help to
1304 // display" code below:
1305 if (SfxContentHelper::IsHelpErrorDocument(aHelpURL
))
1307 if ( impl_hasHTMLHelpInstalled() && impl_showOfflineHelp(aHelpURL
, pWidget
) )
1312 if ( !impl_hasHelpInstalled() )
1314 bool bShowOfflineHelpPopUp
= officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::get();
1316 TopLevelWindowLocker aBusy
;
1318 if(bShowOfflineHelpPopUp
)
1320 aBusy
.incBusy(pWidget
);
1321 HelpManualMessage
aQueryBox(pWidget
);
1322 short OnlineHelpBox
= aQueryBox
.run();
1323 bShowOfflineHelpPopUp
= OnlineHelpBox
!= RET_OK
;
1324 auto xChanges
= comphelper::ConfigurationChanges::create();
1325 officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::set(aQueryBox
.GetOfflineHelpPopUp(), xChanges
);
1329 if(!bShowOfflineHelpPopUp
)
1331 if ( impl_showOnlineHelp(aHelpURL
, pWidget
) )
1335 aBusy
.incBusy(pWidget
);
1336 NoHelpErrorBox
aErrBox(pWidget
);
1350 // old-help to display
1351 Reference
< XDesktop2
> xDesktop
= Desktop::create( ::comphelper::getProcessComponentContext() );
1353 // check if help window is still open
1354 // If not, create a new one and return access directly to the internal sub frame showing the help content
1355 // search must be done here; search one desktop level could return an arbitrary frame
1356 Reference
< XFrame2
> xHelp(
1357 xDesktop
->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::CHILDREN
),
1359 Reference
< XFrame
> xHelpContent
= xDesktop
->findFrame(
1361 FrameSearchFlag::CHILDREN
);
1363 SfxHelpWindow_Impl
* pHelpWindow
= nullptr;
1365 pHelpWindow
= impl_createHelp(xHelp
, xHelpContent
);
1367 pHelpWindow
= static_cast<SfxHelpWindow_Impl
*>(VCLUnoHelper::GetWindow(xHelp
->getComponentWindow()));
1368 if (!xHelp
.is() || !xHelpContent
.is() || !pHelpWindow
)
1371 SAL_INFO("sfx.appl", "HelpId = " << aHelpURL
);
1373 pHelpWindow
->SetHelpURL( aHelpURL
);
1374 pHelpWindow
->loadHelpContent(aHelpURL
);
1375 if (!rKeyword
.isEmpty())
1376 pHelpWindow
->OpenKeyword( rKeyword
);
1378 Reference
< css::awt::XTopWindow
> xTopWindow( xHelp
->getContainerWindow(), UNO_QUERY
);
1379 if ( xTopWindow
.is() )
1380 xTopWindow
->toFront();
1385 OUString
SfxHelp::CreateHelpURL(const OUString
& aCommandURL
, const OUString
& rModuleName
)
1387 SfxHelp
* pHelp
= static_cast< SfxHelp
* >(Application::GetHelp());
1388 return pHelp
? SfxHelp::CreateHelpURL_Impl( aCommandURL
, rModuleName
) : OUString();
1391 OUString
SfxHelp::GetDefaultHelpModule()
1393 return getDefaultModule_Impl();
1396 OUString
SfxHelp::GetCurrentModuleIdentifier()
1398 return getCurrentModuleIdentifier_Impl();
1401 bool SfxHelp::IsHelpInstalled()
1403 return impl_hasHelpInstalled();
1406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */