Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / appl / sfxhelp.cxx
blob7d81085fa65127544208c5a95bd832e7a1f68b3e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_folders.h>
21 #include <sfx2/sfxhelp.hxx>
23 #include <string_view>
24 #include <algorithm>
25 #include <cassert>
26 #ifdef MACOSX
27 #include <premac.h>
28 #include <Foundation/NSString.h>
29 #include <CoreFoundation/CFURL.h>
30 #include <CoreServices/CoreServices.h>
31 #include <postmac.h>
32 #endif
34 #include <sal/log.hxx>
35 #include <com/sun/star/uno/Reference.h>
36 #include <com/sun/star/frame/Desktop.hpp>
37 #include <com/sun/star/frame/UnknownModuleException.hpp>
38 #include <com/sun/star/frame/XFrame2.hpp>
39 #include <comphelper/processfactory.hxx>
40 #include <com/sun/star/awt/XWindow.hpp>
41 #include <com/sun/star/awt/XTopWindow.hpp>
42 #include <com/sun/star/beans/XPropertySet.hpp>
43 #include <com/sun/star/frame/FrameSearchFlag.hpp>
44 #include <toolkit/helper/vclunohelper.hxx>
45 #include <com/sun/star/frame/ModuleManager.hpp>
46 #include <unotools/configmgr.hxx>
47 #include <svtools/helpopt.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 <officecfg/Office/Common.hxx>
55 #include <osl/process.h>
56 #include <osl/file.hxx>
57 #include <unotools/tempfile.hxx>
58 #include <unotools/securityoptions.hxx>
59 #include <rtl/uri.hxx>
60 #include <vcl/commandinfoprovider.hxx>
61 #include <vcl/keycod.hxx>
62 #include <vcl/settings.hxx>
63 #include <vcl/waitobj.hxx>
64 #include <vcl/weld.hxx>
65 #include <openuriexternally.hxx>
67 #include <comphelper/lok.hxx>
68 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
69 #include <sfx2/viewsh.hxx>
71 #include "newhelp.hxx"
72 #include <sfx2/flatpak.hxx>
73 #include <sfx2/sfxresid.hxx>
74 #include <helper.hxx>
75 #include <sfx2/strings.hrc>
76 #include <vcl/svapp.hxx>
77 #include <rtl/string.hxx>
78 #include <svtools/langtab.hxx>
79 #include <tools/diagnose_ex.h>
81 using namespace ::com::sun::star::beans;
82 using namespace ::com::sun::star::frame;
83 using namespace ::com::sun::star::uno;
84 using namespace ::com::sun::star::util;
85 using namespace ::com::sun::star::lang;
87 namespace {
89 class NoHelpErrorBox
91 private:
92 std::unique_ptr<weld::MessageDialog> m_xErrBox;
93 public:
94 DECL_STATIC_LINK(NoHelpErrorBox, HelpRequestHdl, weld::Widget&, bool);
95 public:
96 explicit NoHelpErrorBox(weld::Widget* pParent)
97 : m_xErrBox(Application::CreateMessageDialog(pParent, VclMessageType::Error, VclButtonsType::Ok,
98 SfxResId(RID_STR_HLPFILENOTEXIST)))
100 // Error message: "No help available"
101 m_xErrBox->connect_help(LINK(nullptr, NoHelpErrorBox, HelpRequestHdl));
103 void run()
105 m_xErrBox->run();
111 IMPL_STATIC_LINK_NOARG(NoHelpErrorBox, HelpRequestHdl, weld::Widget&, bool)
113 // do nothing, because no help available
114 return false;
117 static OUString const & HelpLocaleString();
119 namespace {
121 /// Root path of the help.
122 OUString const & getHelpRootURL()
124 static OUString const s_instURL = [&]()
126 OUString tmp = officecfg::Office::Common::Path::Current::Help::get(comphelper::getProcessComponentContext());
127 if (tmp.isEmpty())
129 // try to determine path from default
130 tmp = "$(instpath)/" LIBO_SHARE_HELP_FOLDER;
133 // replace anything like $(instpath);
134 SvtPathOptions aOptions;
135 tmp = aOptions.SubstituteVariable(tmp);
137 OUString url;
138 if (osl::FileBase::getFileURLFromSystemPath(tmp, url) == osl::FileBase::E_None)
139 tmp = url;
140 return tmp;
141 }();
142 return s_instURL;
145 bool impl_checkHelpLocalePath(OUString const & rpPath)
147 osl::DirectoryItem directoryItem;
148 bool bOK = false;
150 osl::FileStatus fileStatus(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL | osl_FileStatus_Mask_FileName);
151 if (osl::DirectoryItem::get(rpPath, directoryItem) == osl::FileBase::E_None &&
152 directoryItem.getFileStatus(fileStatus) == osl::FileBase::E_None &&
153 fileStatus.isDirectory())
155 bOK = true;
157 return bOK;
160 /// Check for built-in help
161 /// Check if help/<lang>/err.html file exist
162 bool impl_hasHelpInstalled()
164 if (comphelper::LibreOfficeKit::isActive())
165 return false;
167 // detect installed locale
168 static OUString const aLocaleStr = HelpLocaleString();
170 OUString helpRootURL = getHelpRootURL() + "/" + aLocaleStr + "/err.html";
171 bool bOK = false;
172 osl::DirectoryItem directoryItem;
173 if(osl::DirectoryItem::get(helpRootURL, directoryItem) == osl::FileBase::E_None){
174 bOK=true;
177 SAL_INFO( "sfx.appl", "Checking old help installed " << bOK);
178 return bOK;
181 /// Check for html built-in help
182 /// Check if help/lang/text folder exist. Only html has it.
183 bool impl_hasHTMLHelpInstalled()
185 if (comphelper::LibreOfficeKit::isActive())
186 return false;
188 // detect installed locale
189 static OUString const aLocaleStr = HelpLocaleString();
191 OUString helpRootURL = getHelpRootURL() + "/" + aLocaleStr + "/text";
192 bool bOK = impl_checkHelpLocalePath( helpRootURL );
193 SAL_INFO( "sfx.appl", "Checking new help (html) installed " << bOK);
194 return bOK;
197 } // namespace
199 /// Return the locale we prefer for displaying help
200 static OUString const & HelpLocaleString()
202 if (comphelper::LibreOfficeKit::isActive())
203 return comphelper::LibreOfficeKit::getLanguageTag().getBcp47();
205 static OUString aLocaleStr;
206 if (!aLocaleStr.isEmpty())
207 return aLocaleStr;
209 const OUString aEnglish("en-US");
210 // detect installed locale
211 aLocaleStr = utl::ConfigManager::getUILocale();
213 if ( aLocaleStr.isEmpty() )
215 aLocaleStr = aEnglish;
216 return aLocaleStr;
219 // get fall-back language (country)
220 OUString sLang = aLocaleStr;
221 sal_Int32 nSepPos = sLang.indexOf( '-' );
222 if (nSepPos != -1)
224 sLang = sLang.copy( 0, nSepPos );
226 OUString sHelpPath("");
227 sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + aLocaleStr;
228 if (impl_checkHelpLocalePath(sHelpPath))
230 return aLocaleStr;
232 sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + sLang;
233 if (impl_checkHelpLocalePath(sHelpPath))
235 aLocaleStr = sLang;
236 return aLocaleStr;
238 sHelpPath = getHelpRootURL() + "/" + aLocaleStr;
239 if (impl_checkHelpLocalePath(sHelpPath))
241 return aLocaleStr;
243 sHelpPath = getHelpRootURL() + "/" + sLang;
244 if (impl_checkHelpLocalePath(sHelpPath))
246 aLocaleStr = sLang;
247 return aLocaleStr;
249 sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + aEnglish;
250 if (impl_checkHelpLocalePath(sHelpPath))
252 aLocaleStr = aEnglish;
253 return aLocaleStr;
255 sHelpPath = getHelpRootURL() + "/" + aEnglish;
256 if (impl_checkHelpLocalePath(sHelpPath))
258 aLocaleStr = aEnglish;
259 return aLocaleStr;
261 return aLocaleStr;
266 void AppendConfigToken( OUStringBuffer& rURL, bool bQuestionMark )
268 OUString aLocaleStr = HelpLocaleString();
270 // query part exists?
271 if ( bQuestionMark )
272 // no, so start with '?'
273 rURL.append('?');
274 else
275 // yes, so only append with '&'
276 rURL.append('&');
278 // set parameters
279 rURL.append("Language=");
280 rURL.append(aLocaleStr);
281 rURL.append("&System=");
282 rURL.append(SvtHelpOptions().GetSystem());
283 rURL.append("&Version=");
284 rURL.append(utl::ConfigManager::getProductVersion());
287 static bool GetHelpAnchor_Impl( const OUString& _rURL, OUString& _rAnchor )
289 bool bRet = false;
293 ::ucbhelper::Content aCnt( INetURLObject( _rURL ).GetMainURL( INetURLObject::DecodeMechanism::NONE ),
294 Reference< css::ucb::XCommandEnvironment >(),
295 comphelper::getProcessComponentContext() );
296 OUString sAnchor;
297 if ( aCnt.getPropertyValue("AnchorName") >>= sAnchor )
300 if ( !sAnchor.isEmpty() )
302 _rAnchor = sAnchor;
303 bRet = true;
306 else
308 SAL_WARN( "sfx.appl", "Property 'AnchorName' is missing" );
311 catch (const css::uno::Exception&)
315 return bRet;
318 namespace {
320 class SfxHelp_Impl
322 public:
323 static OUString GetHelpText( const OUString& aCommandURL, const OUString& rModule );
328 OUString SfxHelp_Impl::GetHelpText( const OUString& aCommandURL, const OUString& rModule )
330 // create help url
331 OUStringBuffer aHelpURL( SfxHelp::CreateHelpURL( aCommandURL, rModule ) );
332 // added 'active' parameter
333 sal_Int32 nIndex = aHelpURL.lastIndexOf( '#' );
334 if ( nIndex < 0 )
335 nIndex = aHelpURL.getLength();
336 aHelpURL.insert( nIndex, "&Active=true" );
337 // load help string
338 return SfxContentHelper::GetActiveHelpString( aHelpURL.makeStringAndClear() );
341 SfxHelp::SfxHelp()
342 : bIsDebug(false)
343 , bLaunchingHelp(false)
345 // read the environment variable "HELP_DEBUG"
346 // if it's set, you will see debug output on active help
347 OUString sHelpDebug;
348 OUString sEnvVarName( "HELP_DEBUG" );
349 osl_getEnvironment( sEnvVarName.pData, &sHelpDebug.pData );
350 bIsDebug = !sHelpDebug.isEmpty();
353 SfxHelp::~SfxHelp()
357 static OUString getDefaultModule_Impl()
359 OUString sDefaultModule;
360 SvtModuleOptions aModOpt;
361 if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
362 sDefaultModule = "swriter";
363 else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
364 sDefaultModule = "scalc";
365 else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
366 sDefaultModule = "simpress";
367 else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
368 sDefaultModule = "sdraw";
369 else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::MATH ) )
370 sDefaultModule = "smath";
371 else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::CHART ) )
372 sDefaultModule = "schart";
373 else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::BASIC ) )
374 sDefaultModule = "sbasic";
375 else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
376 sDefaultModule = "sdatabase";
377 else
379 SAL_WARN( "sfx.appl", "getDefaultModule_Impl(): no module installed" );
381 return sDefaultModule;
384 static OUString getCurrentModuleIdentifier_Impl()
386 OUString sIdentifier;
387 Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext();
388 Reference < XModuleManager2 > xModuleManager = ModuleManager::create(xContext);
389 Reference < XDesktop2 > xDesktop = Desktop::create(xContext);
390 Reference < XFrame > xCurrentFrame = xDesktop->getCurrentFrame();
392 if ( xCurrentFrame.is() )
396 sIdentifier = xModuleManager->identify( xCurrentFrame );
398 catch (const css::frame::UnknownModuleException&)
400 SAL_INFO( "sfx.appl", "SfxHelp::getCurrentModuleIdentifier_Impl(): unknown module (help in help?)" );
402 catch (const Exception&)
404 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelp::getCurrentModuleIdentifier_Impl(): exception of XModuleManager::identify()" );
408 return sIdentifier;
411 namespace
413 OUString MapModuleIdentifier(const OUString &rFactoryShortName)
415 OUString aFactoryShortName(rFactoryShortName);
417 // Map some module identifiers to their "real" help module string.
418 if ( aFactoryShortName == "chart2" )
419 aFactoryShortName = "schart" ;
420 else if ( aFactoryShortName == "BasicIDE" )
421 aFactoryShortName = "sbasic";
422 else if ( aFactoryShortName == "sweb"
423 || aFactoryShortName == "sglobal"
424 || aFactoryShortName == "swxform" )
425 aFactoryShortName = "swriter" ;
426 else if ( aFactoryShortName == "dbquery"
427 || aFactoryShortName == "dbbrowser"
428 || aFactoryShortName == "dbrelation"
429 || aFactoryShortName == "dbtable"
430 || aFactoryShortName == "dbapp"
431 || aFactoryShortName == "dbreport"
432 || aFactoryShortName == "dbtdata"
433 || aFactoryShortName == "swreport"
434 || aFactoryShortName == "swform" )
435 aFactoryShortName = "sdatabase";
436 else if ( aFactoryShortName == "sbibliography"
437 || aFactoryShortName == "sabpilot"
438 || aFactoryShortName == "scanner"
439 || aFactoryShortName == "spropctrlr"
440 || aFactoryShortName == "StartModule" )
441 aFactoryShortName.clear();
443 return aFactoryShortName;
447 OUString SfxHelp::GetHelpModuleName_Impl(const OUString& rHelpID)
449 OUString aFactoryShortName;
451 //rhbz#1438876 detect preferred module for this help id, e.g. csv dialog
452 //for calc import before any toplevel is created and so context is
453 //otherwise unknown. Cosmetic, same help is shown in any case because its
454 //in the shared section, but title bar would state "Writer" when context is
455 //expected to be "Calc"
456 OUString sRemainder;
457 if (rHelpID.startsWith("modules/", &sRemainder))
459 sal_Int32 nEndModule = sRemainder.indexOf('/');
460 aFactoryShortName = nEndModule != -1 ? sRemainder.copy(0, nEndModule) : sRemainder;
463 if (aFactoryShortName.isEmpty())
465 OUString aModuleIdentifier = getCurrentModuleIdentifier_Impl();
466 if (!aModuleIdentifier.isEmpty())
470 Reference < XModuleManager2 > xModuleManager(
471 ModuleManager::create(::comphelper::getProcessComponentContext()) );
472 Sequence< PropertyValue > lProps;
473 xModuleManager->getByName( aModuleIdentifier ) >>= lProps;
474 auto pProp = std::find_if(lProps.begin(), lProps.end(),
475 [](const PropertyValue& rProp) { return rProp.Name == "ooSetupFactoryShortName"; });
476 if (pProp != lProps.end())
477 pProp->Value >>= aFactoryShortName;
479 catch (const Exception&)
481 TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelp::GetHelpModuleName_Impl()" );
486 if (!aFactoryShortName.isEmpty())
487 aFactoryShortName = MapModuleIdentifier(aFactoryShortName);
488 if (aFactoryShortName.isEmpty())
489 aFactoryShortName = getDefaultModule_Impl();
491 return aFactoryShortName;
494 OUString SfxHelp::CreateHelpURL_Impl( const OUString& aCommandURL, const OUString& rModuleName )
496 // build up the help URL
497 OUStringBuffer aHelpURL("vnd.sun.star.help://");
498 bool bHasAnchor = false;
499 OUString aAnchor;
501 OUString aModuleName( rModuleName );
502 if (aModuleName.isEmpty())
503 aModuleName = getDefaultModule_Impl();
505 aHelpURL.append(aModuleName);
507 if ( aCommandURL.isEmpty() )
508 aHelpURL.append("/start");
509 else
511 aHelpURL.append('/');
512 aHelpURL.append(rtl::Uri::encode(aCommandURL,
513 rtl_UriCharClassRelSegment,
514 rtl_UriEncodeKeepEscapes,
515 RTL_TEXTENCODING_UTF8));
517 OUStringBuffer aTempURL = aHelpURL;
518 AppendConfigToken( aTempURL, true );
519 bHasAnchor = GetHelpAnchor_Impl(aTempURL.makeStringAndClear(), aAnchor);
522 AppendConfigToken( aHelpURL, true );
524 if ( bHasAnchor )
526 aHelpURL.append('#');
527 aHelpURL.append(aAnchor);
530 return aHelpURL.makeStringAndClear();
533 static SfxHelpWindow_Impl* impl_createHelp(Reference< XFrame2 >& rHelpTask ,
534 Reference< XFrame >& rHelpContent)
536 Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
538 // otherwise - create new help task
539 Reference< XFrame2 > xHelpTask(
540 xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::TASKS | FrameSearchFlag::CREATE),
541 UNO_QUERY);
542 if (!xHelpTask.is())
543 return nullptr;
545 // create all internal windows and sub frames ...
546 Reference< css::awt::XWindow > xParentWindow = xHelpTask->getContainerWindow();
547 VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow( xParentWindow );
548 VclPtrInstance<SfxHelpWindow_Impl> pHelpWindow( xHelpTask, pParentWindow );
549 Reference< css::awt::XWindow > xHelpWindow = VCLUnoHelper::GetInterface( pHelpWindow );
551 Reference< XFrame > xHelpContent;
552 if (xHelpTask->setComponent( xHelpWindow, Reference< XController >() ))
554 // Customize UI ...
555 xHelpTask->setName("OFFICE_HELP_TASK");
557 Reference< XPropertySet > xProps(xHelpTask, UNO_QUERY);
558 if (xProps.is())
559 xProps->setPropertyValue(
560 "Title",
561 makeAny(SfxResId(STR_HELP_WINDOW_TITLE)));
563 pHelpWindow->setContainerWindow( xParentWindow );
564 xParentWindow->setVisible(true);
565 xHelpWindow->setVisible(true);
567 // This sub frame is created internally (if we called new SfxHelpWindow_Impl() ...)
568 // It should exist :-)
569 xHelpContent = xHelpTask->findFrame("OFFICE_HELP", FrameSearchFlag::CHILDREN);
572 if (!xHelpContent.is())
574 pHelpWindow.disposeAndClear();
575 return nullptr;
578 xHelpContent->setName("OFFICE_HELP");
580 rHelpTask = xHelpTask;
581 rHelpContent = xHelpContent;
582 return pHelpWindow;
585 OUString SfxHelp::GetHelpText( const OUString& aCommandURL, const vcl::Window* pWindow )
587 OUString sModuleName = GetHelpModuleName_Impl(aCommandURL);
588 auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, getCurrentModuleIdentifier_Impl());
589 OUString sRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
590 OUString sHelpText = SfxHelp_Impl::GetHelpText( sRealCommand.isEmpty() ? aCommandURL : sRealCommand, sModuleName );
592 OString aNewHelpId;
594 if (pWindow && sHelpText.isEmpty())
596 // no help text found -> try with parent help id.
597 vcl::Window* pParent = pWindow->GetParent();
598 while ( pParent )
600 aNewHelpId = pParent->GetHelpId();
601 sHelpText = SfxHelp_Impl::GetHelpText( OStringToOUString(aNewHelpId, RTL_TEXTENCODING_UTF8), sModuleName );
602 if (!sHelpText.isEmpty())
603 pParent = nullptr;
604 else
605 pParent = pParent->GetParent();
608 if (bIsDebug && sHelpText.isEmpty())
609 aNewHelpId.clear();
612 // add some debug information?
613 if ( bIsDebug )
615 sHelpText += "\n-------------\n" +
616 sModuleName + ": " + aCommandURL;
617 if ( !aNewHelpId.isEmpty() )
619 sHelpText += " - " +
620 OStringToOUString(aNewHelpId, RTL_TEXTENCODING_UTF8);
624 return sHelpText;
627 OUString SfxHelp::GetHelpText(const OUString& aCommandURL, const weld::Widget* pWidget)
629 OUString sModuleName = GetHelpModuleName_Impl(aCommandURL);
630 auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, getCurrentModuleIdentifier_Impl());
631 OUString sRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
632 OUString sHelpText = SfxHelp_Impl::GetHelpText( sRealCommand.isEmpty() ? aCommandURL : sRealCommand, sModuleName );
634 OString aNewHelpId;
636 if (pWidget && sHelpText.isEmpty())
638 // no help text found -> try with parent help id.
639 std::unique_ptr<weld::Widget> xParent(pWidget->weld_parent());
640 while (xParent)
642 aNewHelpId = xParent->get_help_id();
643 sHelpText = SfxHelp_Impl::GetHelpText( OStringToOUString(aNewHelpId, RTL_TEXTENCODING_UTF8), sModuleName );
644 if (!sHelpText.isEmpty())
645 xParent.reset();
646 else
647 xParent = xParent->weld_parent();
650 if (bIsDebug && sHelpText.isEmpty())
651 aNewHelpId.clear();
654 // add some debug information?
655 if ( bIsDebug )
657 sHelpText += "\n-------------\n" +
658 sModuleName + ": " + aCommandURL;
659 if ( !aNewHelpId.isEmpty() )
661 sHelpText += " - " +
662 OStringToOUString(aNewHelpId, RTL_TEXTENCODING_UTF8);
666 return sHelpText;
669 OUString SfxHelp::GetURLHelpText(std::u16string_view aURL)
671 SvtSecurityOptions aSecOpt;
672 bool bCtrlClickHlink = aSecOpt.IsOptionSet(SvtSecurityOptions::EOption::CtrlClickHyperlink);
674 // "ctrl-click to follow link:" for not MacOS
675 // "⌘-click to follow link:" for MacOs
676 vcl::KeyCode aCode(KEY_SPACE);
677 vcl::KeyCode aModifiedCode(KEY_SPACE, KEY_MOD1);
678 OUString aModStr(aModifiedCode.GetName());
679 aModStr = aModStr.replaceFirst(aCode.GetName(), "");
680 aModStr = aModStr.replaceAll("+", "");
681 OUString aHelpStr
682 = bCtrlClickHlink ? SfxResId(STR_CTRLCLICKHYPERLINK) : SfxResId(STR_CLICKHYPERLINK);
683 aHelpStr = aHelpStr.replaceFirst("%{key}", aModStr);
684 aHelpStr = aHelpStr.replaceFirst("%{link}", aURL);
685 return aHelpStr;
688 void SfxHelp::SearchKeyword( const OUString& rKeyword )
690 Start_Impl(OUString(), static_cast<vcl::Window*>(nullptr), rKeyword);
693 bool SfxHelp::Start( const OUString& rURL, const vcl::Window* pWindow )
695 if (bLaunchingHelp)
696 return true;
697 bLaunchingHelp = true;
698 bool bRet = Start_Impl( rURL, pWindow, OUString() );
699 bLaunchingHelp = false;
700 return bRet;
703 bool SfxHelp::Start(const OUString& rURL, weld::Widget* pWidget)
705 if (bLaunchingHelp)
706 return true;
707 bLaunchingHelp = true;
708 bool bRet = Start_Impl(rURL, pWidget, OUString());
709 bLaunchingHelp = false;
710 return bRet;
713 /// Redirect the vnd.sun.star.help:// urls to http://help.libreoffice.org
714 static bool impl_showOnlineHelp( const OUString& rURL )
716 static constexpr OUStringLiteral aInternal(u"vnd.sun.star.help://");
717 if ( rURL.getLength() <= aInternal.getLength() || !rURL.startsWith(aInternal) )
718 return false;
720 OUString aHelpLink = officecfg::Office::Common::Help::HelpRootURL::get();
721 OUString aTarget = OUString::Concat("Target=") + rURL.subView(aInternal.getLength());
722 aTarget = aTarget.replaceAll("%2F", "/").replaceAll("?", "&");
723 aHelpLink += aTarget;
725 if (comphelper::LibreOfficeKit::isActive())
727 if(SfxViewShell* pViewShell = SfxViewShell::Current())
729 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED,
730 aHelpLink.toUtf8().getStr());
731 return true;
733 return false;
738 #ifdef MACOSX
739 LSOpenCFURLRef(CFURLCreateWithString(kCFAllocatorDefault,
740 CFStringCreateWithCString(kCFAllocatorDefault,
741 aHelpLink.toUtf8().getStr(),
742 kCFStringEncodingUTF8),
743 nullptr),
744 nullptr);
745 #else
746 sfx2::openUriExternally(aHelpLink, false);
747 #endif
748 return true;
750 catch (const Exception&)
753 return false;
756 namespace {
758 bool rewriteFlatpakHelpRootUrl(OUString * helpRootUrl) {
759 assert(helpRootUrl != nullptr);
760 //TODO: this function for now assumes that the passed-in *helpRootUrl references
761 // /app/libreoffice/help (which belongs to the org.libreoffice.LibreOffice.Help
762 // extension); it replaces it with the corresponding file URL as seen outside the flatpak
763 // sandbox:
764 struct Failure: public std::exception {};
765 try {
766 static auto const url = [] {
767 // From /.flatpak-info [Instance] section, read
768 // app-path=<path>
769 // app-extensions=...;org.libreoffice.LibreOffice.Help=<sha>;...
770 // lines:
771 osl::File ini("file:///.flatpak-info");
772 auto err = ini.open(osl_File_OpenFlag_Read);
773 if (err != osl::FileBase::E_None) {
774 SAL_WARN("sfx.appl", "LIBO_FLATPAK mode failure opening /.flatpak-info: " << err);
775 throw Failure();
777 OUString path;
778 OUString extensions;
779 bool havePath = false;
780 bool haveExtensions = false;
781 for (bool instance = false; !(havePath && haveExtensions);) {
782 rtl::ByteSequence bytes;
783 err = ini.readLine(bytes);
784 if (err != osl::FileBase::E_None) {
785 SAL_WARN(
786 "sfx.appl",
787 "LIBO_FLATPAK mode reading /.flatpak-info fails with " << err
788 << " before [Instance] app-path");
789 throw Failure();
791 std::string_view const line(
792 reinterpret_cast<char const *>(bytes.getConstArray()), bytes.getLength());
793 if (instance) {
794 static constexpr auto keyPath = std::string_view("app-path=");
795 static constexpr auto keyExtensions = std::string_view("app-extensions=");
796 if (!havePath && line.length() >= keyPath.size()
797 && line.substr(0, keyPath.size()) == keyPath.data())
799 auto const value = line.substr(keyPath.size());
800 if (!rtl_convertStringToUString(
801 &path.pData, value.data(), value.length(),
802 osl_getThreadTextEncoding(),
803 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
804 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
805 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
807 SAL_WARN(
808 "sfx.appl",
809 "LIBO_FLATPAK mode failure converting app-path \"" << value
810 << "\" encoding");
811 throw Failure();
813 havePath = true;
814 } else if (!haveExtensions && line.length() >= keyExtensions.size()
815 && line.substr(0, keyExtensions.size()) == keyExtensions.data())
817 auto const value = line.substr(keyExtensions.size());
818 if (!rtl_convertStringToUString(
819 &extensions.pData, value.data(), value.length(),
820 osl_getThreadTextEncoding(),
821 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
822 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
823 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
825 SAL_WARN(
826 "sfx.appl",
827 "LIBO_FLATPAK mode failure converting app-extensions \"" << value
828 << "\" encoding");
829 throw Failure();
831 haveExtensions = true;
832 } else if (line.length() > 0 && line[0] == '[') {
833 SAL_WARN(
834 "sfx.appl",
835 "LIBO_FLATPAK mode /.flatpak-info lacks [Instance] app-path and"
836 " app-extensions");
837 throw Failure();
839 } else if (line == "[Instance]") {
840 instance = true;
843 ini.close();
844 // Extract <sha> from ...;org.libreoffice.LibreOffice.Help=<sha>;...:
845 OUString sha;
846 for (sal_Int32 i = 0;;) {
847 OUString elem = extensions.getToken(0, ';', i);
848 if (elem.startsWith("org.libreoffice.LibreOffice.Help=", &sha)) {
849 break;
851 if (i == -1) {
852 SAL_WARN(
853 "sfx.appl",
854 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-extensions \""
855 << extensions << "\" org.libreoffice.LibreOffice.Help");
856 throw Failure();
859 // Assuming that <path> is of the form
860 // /.../app/org.libreoffice.LibreOffice/<arch>/<branch>/<sha'>/files
861 // rewrite it as
862 // /.../runtime/org.libreoffice.LibreOffice.Help/<arch>/<branch>/<sha>/files
863 // because the extension's files are stored at a different place than the app's files,
864 // so use this hack until flatpak itself provides a better solution:
865 static constexpr auto segments = OUStringLiteral(u"/app/org.libreoffice.LibreOffice/");
866 auto const i1 = path.lastIndexOf(segments);
867 // use lastIndexOf instead of indexOf, in case the user-controlled prefix /.../
868 // happens to contain such segments
869 if (i1 == -1) {
870 SAL_WARN(
871 "sfx.appl",
872 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
873 << "\" doesn't contain /app/org.libreoffice.LibreOffice/");
874 throw Failure();
876 auto const i2 = i1 + segments.getLength();
877 auto i3 = path.indexOf('/', i2);
878 if (i3 == -1) {
879 SAL_WARN(
880 "sfx.appl",
881 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
882 << "\" doesn't contain branch segment");
883 throw Failure();
885 i3 = path.indexOf('/', i3 + 1);
886 if (i3 == -1) {
887 SAL_WARN(
888 "sfx.appl",
889 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
890 << "\" doesn't contain sha segment");
891 throw Failure();
893 ++i3;
894 auto const i4 = path.indexOf('/', i3);
895 if (i4 == -1) {
896 SAL_WARN(
897 "sfx.appl",
898 "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path
899 << "\" doesn't contain files segment");
900 throw Failure();
902 path = path.subView(0, i1) + OUString::Concat("/runtime/org.libreoffice.LibreOffice.Help/")
903 + path.subView(i2, i3 - i2) + sha + path.subView(i4);
904 // Turn <path> into a file URL:
905 OUString url_;
906 err = osl::FileBase::getFileURLFromSystemPath(path, url_);
907 if (err != osl::FileBase::E_None) {
908 SAL_WARN(
909 "sfx.appl",
910 "LIBO_FLATPAK mode failure converting app-path \"" << path << "\" to URL: "
911 << err);
912 throw Failure();
914 return url_;
915 }();
916 *helpRootUrl = url;
917 return true;
918 } catch (Failure &) {
919 return false;
925 // add <noscript> meta for browsers without javascript
927 #define SHTML1 "<!DOCTYPE HTML><html lang=\"en-US\"><head><meta charset=\"UTF-8\">"
928 #define SHTML2 "<noscript><meta http-equiv=\"refresh\" content=\"0; url='"
929 #define SHTML3 "/noscript.html'\"></noscript><meta http-equiv=\"refresh\" content=\"1; url='"
930 #define SHTML4 "'\"><script type=\"text/javascript\"> window.location.href = \""
931 #define SHTML5 "\";</script><title>Help Page Redirection</title></head><body></body></html>"
933 // use a tempfile since e.g. xdg-open doesn't support URL-parameters with file:// URLs
934 static bool impl_showOfflineHelp( const OUString& rURL )
936 OUString aBaseInstallPath = getHelpRootURL();
937 // For the flatpak case, find the pathname outside the flatpak sandbox that corresponds to
938 // aBaseInstallPath, because that is what needs to be stored in aTempFile below:
939 if (flatpak::isFlatpak() && !rewriteFlatpakHelpRootUrl(&aBaseInstallPath)) {
940 return false;
943 OUString aHelpLink( aBaseInstallPath + "/index.html?" );
944 OUString aTarget = OUString::Concat("Target=") + rURL.subView(RTL_CONSTASCII_LENGTH("vnd.sun.star.help://"));
945 aTarget = aTarget.replaceAll("%2F","/").replaceAll("?","&");
946 aHelpLink += aTarget;
948 // Get a html tempfile (for the flatpak case, create it in XDG_CACHE_HOME instead of /tmp for
949 // technical reasons, so that it can be accessed by the browser running outside the sandbox):
950 OUString const aExtension(".html");
951 OUString * parent = nullptr;
952 if (flatpak::isFlatpak() && !flatpak::createTemporaryHtmlDirectory(&parent)) {
953 return false;
955 ::utl::TempFile aTempFile("NewHelp", true, &aExtension, parent, false );
957 SvStream* pStream = aTempFile.GetStream(StreamMode::WRITE);
958 pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8);
960 OUString aTempStr = SHTML1 SHTML2 +
961 aBaseInstallPath + "/" + HelpLocaleString() + SHTML3 +
962 aHelpLink + SHTML4 +
963 aHelpLink + SHTML5;
965 pStream->WriteUnicodeOrByteText(aTempStr);
967 aTempFile.CloseStream();
970 #ifdef MACOSX
971 LSOpenCFURLRef(CFURLCreateWithString(kCFAllocatorDefault,
972 CFStringCreateWithCString(kCFAllocatorDefault,
973 aTempFile.GetURL().toUtf8().getStr(),
974 kCFStringEncodingUTF8),
975 nullptr),
976 nullptr);
977 #else
978 sfx2::openUriExternally(aTempFile.GetURL(), false);
979 #endif
980 return true;
982 catch (const Exception&)
985 aTempFile.EnableKillingFile();
986 return false;
989 namespace
991 // tdf#119579 skip floating windows as potential parent for missing help dialog
992 const vcl::Window* GetBestParent(const vcl::Window* pWindow)
994 while (pWindow)
996 if (pWindow->IsSystemWindow() && pWindow->GetType() != WindowType::FLOATINGWINDOW)
997 break;
998 pWindow = pWindow->GetParent();
1000 return pWindow;
1004 namespace {
1006 class HelpManualMessage : public weld::MessageDialogController
1008 private:
1009 std::unique_ptr<weld::CheckButton> m_xHideOfflineHelpCB;
1011 public:
1012 HelpManualMessage(weld::Widget* pParent)
1013 : MessageDialogController(pParent, "sfx/ui/helpmanual.ui", "onlinehelpmanual", "hidedialog")
1014 , m_xHideOfflineHelpCB(m_xBuilder->weld_check_button("hidedialog"))
1016 LanguageTag aLangTag = Application::GetSettings().GetUILanguageTag();
1017 OUString sLocaleString = SvtLanguageTable::GetLanguageString(aLangTag.getLanguageType());
1018 OUString sPrimText = get_primary_text();
1019 set_primary_text(sPrimText.replaceAll("$UILOCALE", sLocaleString));
1022 bool GetOfflineHelpPopUp() const { return !m_xHideOfflineHelpCB->get_active(); }
1027 bool SfxHelp::Start_Impl(const OUString& rURL, const vcl::Window* pWindow, const OUString& rKeyword)
1029 OUStringBuffer aHelpRootURL("vnd.sun.star.help://");
1030 AppendConfigToken(aHelpRootURL, true);
1031 SfxContentHelper::GetResultSet(aHelpRootURL.makeStringAndClear());
1033 /* rURL may be
1034 * - a "real" URL
1035 * - a HelpID (formerly a long, now a string)
1036 * If rURL is a URL, CreateHelpURL should be called for this URL
1037 * If rURL is an arbitrary string, the same should happen, but the URL should be tried out
1038 * if it delivers real help content. In case only the Help Error Document is returned, the
1039 * parent of the window for that help was called, is asked for its HelpID.
1040 * For compatibility reasons this upward search is not implemented for "real" URLs.
1041 * Help keyword search now is implemented as own method; in former versions it
1042 * was done via Help::Start, but this implementation conflicted with the upward search.
1044 OUString aHelpURL;
1045 INetURLObject aParser( rURL );
1046 INetProtocol nProtocol = aParser.GetProtocol();
1048 switch ( nProtocol )
1050 case INetProtocol::VndSunStarHelp:
1051 // already a vnd.sun.star.help URL -> nothing to do
1052 aHelpURL = rURL;
1053 break;
1054 default:
1056 OUString aHelpModuleName(GetHelpModuleName_Impl(rURL));
1057 OUString aRealCommand;
1059 if ( nProtocol == INetProtocol::Uno )
1061 // Command can be just an alias to another command.
1062 auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rURL, getCurrentModuleIdentifier_Impl());
1063 aRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
1066 // no URL, just a HelpID (maybe empty in case of keyword search)
1067 aHelpURL = CreateHelpURL_Impl( aRealCommand.isEmpty() ? rURL : aRealCommand, aHelpModuleName );
1069 if ( impl_hasHelpInstalled() && pWindow && SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
1071 // no help found -> try with parent help id.
1072 vcl::Window* pParent = pWindow->GetParent();
1073 while ( pParent )
1075 OString aHelpId = pParent->GetHelpId();
1076 aHelpURL = CreateHelpURL( OStringToOUString(aHelpId, RTL_TEXTENCODING_UTF8), aHelpModuleName );
1078 if ( !SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
1080 break;
1082 else
1084 pParent = pParent->GetParent();
1085 if (!pParent)
1087 // create help url of start page ( helpid == 0 -> start page)
1088 aHelpURL = CreateHelpURL( OUString(), aHelpModuleName );
1093 break;
1097 if ( comphelper::LibreOfficeKit::isActive() )
1099 impl_showOnlineHelp( aHelpURL );
1100 return true;
1102 #ifdef MACOSX
1103 if (@available(macOS 10.14, *)) {
1104 // Workaround: Safari sandboxing prevents it from accessing files in the LibreOffice.app folder
1105 // force online-help instead if Safari is default browser.
1106 CFURLRef pBrowser = LSCopyDefaultApplicationURLForURL(
1107 CFURLCreateWithString(
1108 kCFAllocatorDefault,
1109 static_cast<CFStringRef>(@"https://www.libreoffice.org"),
1110 nullptr),
1111 kLSRolesAll, nullptr);
1112 if([static_cast<NSString*>(CFURLGetString(pBrowser)) isEqualToString:@"file:///Applications/Safari.app/"]) {
1113 impl_showOnlineHelp( aHelpURL );
1114 return true;
1117 #endif
1119 // If the HTML or no help is installed, but aHelpURL nevertheless references valid help content,
1120 // that implies that this help content belongs to an extension (and thus would not be available
1121 // in neither the offline nor online HTML help); in that case, fall through to the "old-help to
1122 // display" code below:
1123 if (SfxContentHelper::IsHelpErrorDocument(aHelpURL))
1125 if ( impl_hasHTMLHelpInstalled() && impl_showOfflineHelp(aHelpURL) )
1127 return true;
1130 if ( !impl_hasHelpInstalled() )
1132 SvtHelpOptions aHelpOptions;
1133 bool bShowOfflineHelpPopUp = aHelpOptions.IsOfflineHelpPopUp();
1135 pWindow = GetBestParent(pWindow);
1137 TopLevelWindowLocker aBusy;
1139 if(bShowOfflineHelpPopUp)
1141 weld::Window* pWeldWindow = pWindow ? pWindow->GetFrameWeld() : nullptr;
1142 aBusy.incBusy(pWeldWindow);
1143 HelpManualMessage aQueryBox(pWeldWindow);
1144 short OnlineHelpBox = aQueryBox.run();
1145 bShowOfflineHelpPopUp = OnlineHelpBox != RET_OK;
1146 aHelpOptions.SetOfflineHelpPopUp(aQueryBox.GetOfflineHelpPopUp());
1147 aBusy.decBusy();
1149 if(!bShowOfflineHelpPopUp)
1151 if ( impl_showOnlineHelp( aHelpURL ) )
1152 return true;
1153 else
1155 weld::Window* pWeldWindow = pWindow ? pWindow->GetFrameWeld() : nullptr;
1156 aBusy.incBusy(pWeldWindow);
1157 NoHelpErrorBox aErrBox(pWeldWindow);
1158 aErrBox.run();
1159 aBusy.decBusy();
1160 return false;
1163 else
1165 return false;
1170 // old-help to display
1171 Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
1173 // check if help window is still open
1174 // If not, create a new one and return access directly to the internal sub frame showing the help content
1175 // search must be done here; search one desktop level could return an arbitrary frame
1176 Reference< XFrame2 > xHelp(
1177 xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::CHILDREN),
1178 UNO_QUERY);
1179 Reference< XFrame > xHelpContent = xDesktop->findFrame(
1180 "OFFICE_HELP",
1181 FrameSearchFlag::CHILDREN);
1183 SfxHelpWindow_Impl* pHelpWindow = nullptr;
1184 if (!xHelp.is())
1185 pHelpWindow = impl_createHelp(xHelp, xHelpContent);
1186 else
1187 pHelpWindow = static_cast<SfxHelpWindow_Impl*>(VCLUnoHelper::GetWindow(xHelp->getComponentWindow()).get());
1188 if (!xHelp.is() || !xHelpContent.is() || !pHelpWindow)
1189 return false;
1191 SAL_INFO("sfx.appl", "HelpId = " << aHelpURL);
1193 pHelpWindow->SetHelpURL( aHelpURL );
1194 pHelpWindow->loadHelpContent(aHelpURL);
1195 if (!rKeyword.isEmpty())
1196 pHelpWindow->OpenKeyword( rKeyword );
1198 Reference < css::awt::XTopWindow > xTopWindow( xHelp->getContainerWindow(), UNO_QUERY );
1199 if ( xTopWindow.is() )
1200 xTopWindow->toFront();
1202 return true;
1205 bool SfxHelp::Start_Impl(const OUString& rURL, weld::Widget* pWidget, const OUString& rKeyword)
1207 OUStringBuffer aHelpRootURL("vnd.sun.star.help://");
1208 AppendConfigToken(aHelpRootURL, true);
1209 SfxContentHelper::GetResultSet(aHelpRootURL.makeStringAndClear());
1211 /* rURL may be
1212 * - a "real" URL
1213 * - a HelpID (formerly a long, now a string)
1214 * If rURL is a URL, CreateHelpURL should be called for this URL
1215 * If rURL is an arbitrary string, the same should happen, but the URL should be tried out
1216 * if it delivers real help content. In case only the Help Error Document is returned, the
1217 * parent of the window for that help was called, is asked for its HelpID.
1218 * For compatibility reasons this upward search is not implemented for "real" URLs.
1219 * Help keyword search now is implemented as own method; in former versions it
1220 * was done via Help::Start, but this implementation conflicted with the upward search.
1222 OUString aHelpURL;
1223 INetURLObject aParser( rURL );
1224 INetProtocol nProtocol = aParser.GetProtocol();
1226 switch ( nProtocol )
1228 case INetProtocol::VndSunStarHelp:
1229 // already a vnd.sun.star.help URL -> nothing to do
1230 aHelpURL = rURL;
1231 break;
1232 default:
1234 OUString aHelpModuleName(GetHelpModuleName_Impl(rURL));
1235 OUString aRealCommand;
1237 if ( nProtocol == INetProtocol::Uno )
1239 // Command can be just an alias to another command.
1240 auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rURL, getCurrentModuleIdentifier_Impl());
1241 aRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
1244 // no URL, just a HelpID (maybe empty in case of keyword search)
1245 aHelpURL = CreateHelpURL_Impl( aRealCommand.isEmpty() ? rURL : aRealCommand, aHelpModuleName );
1247 if ( impl_hasHelpInstalled() && pWidget && SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
1249 bool bUseFinalFallback = true;
1250 // no help found -> try ids of parents.
1251 pWidget->help_hierarchy_foreach([&aHelpModuleName, &aHelpURL, &bUseFinalFallback](const OString& rHelpId){
1252 if (rHelpId.isEmpty())
1253 return false;
1254 aHelpURL = CreateHelpURL( OStringToOUString(rHelpId, RTL_TEXTENCODING_UTF8), aHelpModuleName);
1255 bool bFinished = !SfxContentHelper::IsHelpErrorDocument(aHelpURL);
1256 if (bFinished)
1257 bUseFinalFallback = false;
1258 return bFinished;
1261 if (bUseFinalFallback)
1263 // create help url of start page ( helpid == 0 -> start page)
1264 aHelpURL = CreateHelpURL( OUString(), aHelpModuleName );
1267 break;
1271 if ( comphelper::LibreOfficeKit::isActive() )
1273 impl_showOnlineHelp( aHelpURL );
1274 return true;
1277 // If the HTML or no help is installed, but aHelpURL nevertheless references valid help content,
1278 // that implies that help content belongs to an extension (and thus would not be available
1279 // in neither the offline nor online HTML help); in that case, fall through to the "old-help to
1280 // display" code below:
1281 if (SfxContentHelper::IsHelpErrorDocument(aHelpURL))
1283 if ( impl_hasHTMLHelpInstalled() && impl_showOfflineHelp(aHelpURL) )
1285 return true;
1288 if ( !impl_hasHelpInstalled() )
1290 SvtHelpOptions aHelpOptions;
1291 bool bShowOfflineHelpPopUp = aHelpOptions.IsOfflineHelpPopUp();
1293 TopLevelWindowLocker aBusy;
1295 if(bShowOfflineHelpPopUp)
1297 aBusy.incBusy(pWidget);
1298 HelpManualMessage aQueryBox(pWidget);
1299 short OnlineHelpBox = aQueryBox.run();
1300 bShowOfflineHelpPopUp = OnlineHelpBox != RET_OK;
1301 aHelpOptions.SetOfflineHelpPopUp(aQueryBox.GetOfflineHelpPopUp());
1302 aBusy.decBusy();
1304 if(!bShowOfflineHelpPopUp)
1306 if ( impl_showOnlineHelp( aHelpURL ) )
1307 return true;
1308 else
1310 aBusy.incBusy(pWidget);
1311 NoHelpErrorBox aErrBox(pWidget);
1312 aErrBox.run();
1313 aBusy.decBusy();
1314 return false;
1317 else
1319 return false;
1325 // old-help to display
1326 Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
1328 // check if help window is still open
1329 // If not, create a new one and return access directly to the internal sub frame showing the help content
1330 // search must be done here; search one desktop level could return an arbitrary frame
1331 Reference< XFrame2 > xHelp(
1332 xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::CHILDREN),
1333 UNO_QUERY);
1334 Reference< XFrame > xHelpContent = xDesktop->findFrame(
1335 "OFFICE_HELP",
1336 FrameSearchFlag::CHILDREN);
1338 SfxHelpWindow_Impl* pHelpWindow = nullptr;
1339 if (!xHelp.is())
1340 pHelpWindow = impl_createHelp(xHelp, xHelpContent);
1341 else
1342 pHelpWindow = static_cast<SfxHelpWindow_Impl*>(VCLUnoHelper::GetWindow(xHelp->getComponentWindow()).get());
1343 if (!xHelp.is() || !xHelpContent.is() || !pHelpWindow)
1344 return false;
1346 SAL_INFO("sfx.appl", "HelpId = " << aHelpURL);
1348 pHelpWindow->SetHelpURL( aHelpURL );
1349 pHelpWindow->loadHelpContent(aHelpURL);
1350 if (!rKeyword.isEmpty())
1351 pHelpWindow->OpenKeyword( rKeyword );
1353 Reference < css::awt::XTopWindow > xTopWindow( xHelp->getContainerWindow(), UNO_QUERY );
1354 if ( xTopWindow.is() )
1355 xTopWindow->toFront();
1357 return true;
1360 OUString SfxHelp::CreateHelpURL(const OUString& aCommandURL, const OUString& rModuleName)
1362 SfxHelp* pHelp = static_cast< SfxHelp* >(Application::GetHelp());
1363 return pHelp ? SfxHelp::CreateHelpURL_Impl( aCommandURL, rModuleName ) : OUString();
1366 OUString SfxHelp::GetDefaultHelpModule()
1368 return getDefaultModule_Impl();
1371 OUString SfxHelp::GetCurrentModuleIdentifier()
1373 return getCurrentModuleIdentifier_Impl();
1376 bool SfxHelp::IsHelpInstalled()
1378 return impl_hasHelpInstalled();
1381 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */