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 <sfx2/sfxhelp.hxx>
24 #include <com/sun/star/uno/Reference.h>
25 #include <com/sun/star/frame/Desktop.hpp>
26 #include <com/sun/star/frame/XFrame2.hpp>
27 #include <com/sun/star/frame/XComponentLoader.hpp>
28 #include <com/sun/star/lang/XComponent.hpp>
29 #include <comphelper/processfactory.hxx>
30 #include <com/sun/star/awt/XWindow.hpp>
31 #include <com/sun/star/awt/XTopWindow.hpp>
32 #include <com/sun/star/awt/PosSize.hpp>
33 #include <com/sun/star/frame/XDesktop.hpp>
34 #include <com/sun/star/util/URLTransformer.hpp>
35 #include <com/sun/star/util/XURLTransformer.hpp>
36 #include <com/sun/star/frame/XDispatch.hpp>
37 #include <com/sun/star/frame/XDispatchProvider.hpp>
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include <com/sun/star/frame/FrameSearchFlag.hpp>
40 #include <toolkit/helper/vclunohelper.hxx>
41 #include <com/sun/star/frame/ModuleManager.hpp>
42 #include <com/sun/star/system/SystemShellExecute.hpp>
43 #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
44 #include <unotools/configmgr.hxx>
45 #include <unotools/configitem.hxx>
46 #include <svtools/helpopt.hxx>
47 #include <unotools/moduleoptions.hxx>
48 #include <tools/urlobj.hxx>
49 #include <ucbhelper/content.hxx>
50 #include <unotools/pathoptions.hxx>
51 #include <rtl/ustring.hxx>
52 #include <osl/process.h>
53 #include <osl/file.hxx>
54 #include <unotools/bootstrap.hxx>
55 #include <rtl/uri.hxx>
56 #include <vcl/msgbox.hxx>
57 #include <svtools/ehdl.hxx>
58 #include <svtools/sfxecode.hxx>
60 #include "newhelp.hxx"
61 #include <sfx2/objsh.hxx>
62 #include <sfx2/docfac.hxx>
63 #include <sfx2/sfxresid.hxx>
66 #include <sfx2/sfxuno.hxx>
67 #include <vcl/svapp.hxx>
68 #include <sfx2/frame.hxx>
69 #include <rtl/strbuf.hxx>
70 #include <rtl/string.hxx>
72 using namespace ::com::sun::star::beans
;
73 using namespace ::com::sun::star::frame
;
74 using namespace ::com::sun::star::uno
;
75 using namespace ::com::sun::star::util
;
76 using namespace ::com::sun::star::lang
;
77 using namespace ::com::sun::star::system
;
79 class NoHelpErrorBox
: public ErrorBox
82 NoHelpErrorBox( Window
* _pParent
);
84 virtual void RequestHelp( const HelpEvent
& rHEvt
);
87 NoHelpErrorBox::NoHelpErrorBox( Window
* _pParent
) :
89 ErrorBox( _pParent
, WB_OK
, SfxResId( RID_STR_HLPFILENOTEXIST
).toString() )
91 // Error message: "No help available"
94 void NoHelpErrorBox::RequestHelp( const HelpEvent
& )
96 // do nothing, because no help available
99 static bool impl_hasHelpInstalled( const OUString
&rLang
);
101 /// Return the locale we prefer for displaying help
102 static OUString
HelpLocaleString()
104 static OUString aLocaleStr
;
105 if (aLocaleStr
.isEmpty())
107 const OUString
aEnglish( "en" );
108 // detect installed locale
109 aLocaleStr
= utl::ConfigManager::getLocale();
110 bool bOk
= !aLocaleStr
.isEmpty();
112 aLocaleStr
= aEnglish
;
115 OUString aBaseInstallPath
;
116 utl::Bootstrap::locateBaseInstallation(aBaseInstallPath
);
117 static const char *szHelpPath
= "/help/";
119 OUString sHelpPath
= aBaseInstallPath
+
120 OUString::createFromAscii(szHelpPath
) + aLocaleStr
;
121 osl::DirectoryItem aDirItem
;
123 if (osl::DirectoryItem::get(sHelpPath
, aDirItem
) != osl::FileBase::E_None
)
126 OUString
sLang(aLocaleStr
);
127 sal_Int32 nSepPos
= sLang
.indexOf( '-' );
131 sLang
= sLang
.copy( 0, nSepPos
);
132 sHelpPath
= aBaseInstallPath
+
133 OUString::createFromAscii(szHelpPath
) + sLang
;
134 if (osl::DirectoryItem::get(sHelpPath
, aDirItem
) != osl::FileBase::E_None
)
139 // if not OK, and not even English installed, we use online help, and
140 // have to preserve the full locale name
141 if ( !bOk
&& impl_hasHelpInstalled( aEnglish
) )
142 aLocaleStr
= aEnglish
;
147 void AppendConfigToken( OUStringBuffer
& rURL
, sal_Bool bQuestionMark
, const OUString
&rLang
)
149 OUString
aLocaleStr( rLang
);
150 if ( aLocaleStr
.isEmpty() )
151 aLocaleStr
= HelpLocaleString();
153 // query part exists?
155 // no, so start with '?'
158 // yes, so only append with '&'
162 rURL
.append("Language=");
163 rURL
.append(aLocaleStr
);
164 rURL
.append("&System=");
165 rURL
.append(SvtHelpOptions().GetSystem());
166 rURL
.append("&Version=");
167 rURL
.append(utl::ConfigManager::getProductVersion());
170 sal_Bool
GetHelpAnchor_Impl( const OUString
& _rURL
, OUString
& _rAnchor
)
172 sal_Bool bRet
= sal_False
;
177 ::ucbhelper::Content
aCnt( INetURLObject( _rURL
).GetMainURL( INetURLObject::NO_DECODE
),
178 Reference
< ::com::sun::star::ucb::XCommandEnvironment
>(),
179 comphelper::getProcessComponentContext() );
180 if ( ( aCnt
.getPropertyValue("AnchorName") >>= sAnchor
) )
183 if ( !sAnchor
.isEmpty() )
191 SAL_WARN( "sfx.appl", "Property 'AnchorName' is missing" );
194 catch (const ::com::sun::star::uno::Exception
&)
204 static OUString
GetHelpText( const OUString
& aCommandURL
, const OUString
& rModule
);
207 OUString
SfxHelp_Impl::GetHelpText( const OUString
& aCommandURL
, const OUString
& rModule
)
210 OUStringBuffer
aHelpURL( SfxHelp::CreateHelpURL( aCommandURL
, rModule
) );
211 // added 'active' parameter
212 sal_Int32 nIndex
= aHelpURL
.lastIndexOf( '#' );
214 nIndex
= aHelpURL
.getLength();
215 aHelpURL
.insert( nIndex
, "&Active=true" );
217 return SfxContentHelper::GetActiveHelpString( aHelpURL
.makeStringAndClear() );
221 bIsDebug( sal_False
),
224 // read the environment variable "HELP_DEBUG"
225 // if it's set, you will see debug output on active help
228 OUString
sEnvVarName( "HELP_DEBUG" );
229 osl_getEnvironment( sEnvVarName
.pData
, &sHelpDebug
.pData
);
230 bIsDebug
= !sHelpDebug
.isEmpty();
233 pImp
= new SfxHelp_Impl();
241 OUString
getDefaultModule_Impl()
243 OUString sDefaultModule
;
244 SvtModuleOptions aModOpt
;
245 if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::E_SWRITER
) )
246 sDefaultModule
= "swriter";
247 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::E_SCALC
) )
248 sDefaultModule
= "scalc";
249 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::E_SIMPRESS
) )
250 sDefaultModule
= "simpress";
251 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::E_SDRAW
) )
252 sDefaultModule
= "sdraw";
253 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::E_SMATH
) )
254 sDefaultModule
= "smath";
255 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::E_SCHART
) )
256 sDefaultModule
= "schart";
257 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::E_SBASIC
) )
258 sDefaultModule
= "sbasic";
259 else if ( aModOpt
.IsModuleInstalled( SvtModuleOptions::E_SDATABASE
) )
260 sDefaultModule
= "sdatabase";
263 SAL_WARN( "sfx.appl", "getDefaultModule_Impl(): no module installed" );
265 return sDefaultModule
;
268 OUString
getCurrentModuleIdentifier_Impl()
270 OUString sIdentifier
;
271 Reference
< XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
272 Reference
< XModuleManager2
> xModuleManager
= ModuleManager::create(xContext
);
273 Reference
< XDesktop2
> xDesktop
= Desktop::create(xContext
);
274 Reference
< XFrame
> xCurrentFrame
= xDesktop
->getCurrentFrame();
276 if ( xCurrentFrame
.is() )
280 sIdentifier
= xModuleManager
->identify( xCurrentFrame
);
282 catch (const ::com::sun::star::frame::UnknownModuleException
&)
284 DBG_WARNING( "SfxHelp::getCurrentModuleIdentifier_Impl(): unknown module (help in help?)" );
286 catch (const Exception
&)
288 SAL_WARN( "sfx.appl", "SfxHelp::getCurrentModuleIdentifier_Impl(): exception of XModuleManager::identify()" );
295 OUString
SfxHelp::GetHelpModuleName_Impl()
297 OUString aFactoryShortName
;
298 OUString aModuleIdentifier
= getCurrentModuleIdentifier_Impl();
300 if ( !aModuleIdentifier
.isEmpty() )
304 Reference
< XModuleManager2
> xModuleManager(
305 ModuleManager::create(::comphelper::getProcessComponentContext()) );
306 Sequence
< PropertyValue
> lProps
;
307 xModuleManager
->getByName( aModuleIdentifier
) >>= lProps
;
308 for ( sal_Int32 i
= 0; i
< lProps
.getLength(); ++i
)
310 if ( lProps
[i
].Name
== "ooSetupFactoryShortName" )
312 lProps
[i
].Value
>>= aFactoryShortName
;
317 catch (const Exception
&)
319 SAL_WARN( "sfx.appl", "SfxHelp::GetHelpModuleName_Impl(): exception of XNameAccess::getByName()" );
323 OUString sDefaultModule
= getDefaultModule_Impl();
324 if ( !aFactoryShortName
.isEmpty() )
326 // Map some module identifiers to their "real" help module string.
327 if ( aFactoryShortName
== "chart2" )
328 aFactoryShortName
= "schart" ;
329 else if ( aFactoryShortName
== "BasicIDE" )
330 aFactoryShortName
= "sbasic";
331 else if ( aFactoryShortName
.startsWith("sweb")
332 || aFactoryShortName
.startsWith("sglobal")
333 || aFactoryShortName
.startsWith("swxform") )
334 aFactoryShortName
= "swriter" ;
335 else if ( aFactoryShortName
.startsWith("dbquery")
336 || aFactoryShortName
.startsWith("dbbrowser")
337 || aFactoryShortName
.startsWith("dbrelation")
338 || aFactoryShortName
.startsWith("dbtable")
339 || aFactoryShortName
.startsWith("dbapp")
340 || aFactoryShortName
.startsWith("dbreport")
341 || aFactoryShortName
.startsWith("swreport")
342 || aFactoryShortName
.startsWith("dbbrowser")
343 || aFactoryShortName
.startsWith("swform") )
344 aFactoryShortName
= "sdatabase";
345 else if ( aFactoryShortName
.startsWith("sbibliography")
346 || aFactoryShortName
.startsWith("StartModule") )
347 aFactoryShortName
= sDefaultModule
;
350 aFactoryShortName
= sDefaultModule
;
352 return aFactoryShortName
;
355 OUString
SfxHelp::CreateHelpURL_Impl( const OUString
& aCommandURL
, const OUString
& rModuleName
)
357 // build up the help URL
358 OUStringBuffer
aHelpURL("vnd.sun.star.help://");
359 sal_Bool bHasAnchor
= sal_False
;
362 OUString
aModuleName( rModuleName
);
363 if (aModuleName
.isEmpty())
364 aModuleName
= getDefaultModule_Impl();
366 aHelpURL
.append(aModuleName
);
368 if ( aCommandURL
.isEmpty() )
369 aHelpURL
.append("/start");
372 aHelpURL
.append('/');
373 aHelpURL
.append(rtl::Uri::encode(aCommandURL
,
374 rtl_UriCharClassRelSegment
,
375 rtl_UriEncodeKeepEscapes
,
376 RTL_TEXTENCODING_UTF8
));
378 OUStringBuffer aTempURL
= aHelpURL
;
379 AppendConfigToken( aTempURL
, sal_True
);
380 bHasAnchor
= GetHelpAnchor_Impl(aTempURL
.makeStringAndClear(), aAnchor
);
383 AppendConfigToken( aHelpURL
, sal_True
);
387 aHelpURL
.append('#');
388 aHelpURL
.append(aAnchor
);
391 return aHelpURL
.makeStringAndClear();
394 SfxHelpWindow_Impl
* impl_createHelp(Reference
< XFrame2
>& rHelpTask
,
395 Reference
< XFrame
>& rHelpContent
)
397 Reference
< XDesktop2
> xDesktop
= Desktop::create( ::comphelper::getProcessComponentContext() );
399 // otherwise - create new help task
400 Reference
< XFrame2
> xHelpTask(
401 xDesktop
->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::TASKS
| FrameSearchFlag::CREATE
),
406 // create all internal windows and sub frames ...
407 Reference
< ::com::sun::star::awt::XWindow
> xParentWindow
= xHelpTask
->getContainerWindow();
408 Window
* pParentWindow
= VCLUnoHelper::GetWindow( xParentWindow
);
409 SfxHelpWindow_Impl
* pHelpWindow
= new SfxHelpWindow_Impl( xHelpTask
, pParentWindow
, WB_DOCKBORDER
);
410 Reference
< ::com::sun::star::awt::XWindow
> xHelpWindow
= VCLUnoHelper::GetInterface( pHelpWindow
);
412 Reference
< XFrame
> xHelpContent
;
413 if (xHelpTask
->setComponent( xHelpWindow
, Reference
< XController
>() ))
416 xHelpTask
->setName("OFFICE_HELP_TASK");
418 Reference
< XPropertySet
> xProps(xHelpTask
, UNO_QUERY
);
420 xProps
->setPropertyValue(
422 makeAny(SfxResId(STR_HELP_WINDOW_TITLE
).toString()));
424 pHelpWindow
->setContainerWindow( xParentWindow
);
425 xParentWindow
->setVisible(sal_True
);
426 xHelpWindow
->setVisible(sal_True
);
428 // This sub frame is created internaly (if we called new SfxHelpWindow_Impl() ...)
429 // It should exist :-)
430 xHelpContent
= xHelpTask
->findFrame(OUString("OFFICE_HELP"), FrameSearchFlag::CHILDREN
);
433 if (!xHelpContent
.is())
439 xHelpContent
->setName("OFFICE_HELP");
441 rHelpTask
= xHelpTask
;
442 rHelpContent
= xHelpContent
;
446 OUString
SfxHelp::GetHelpText( const OUString
& aCommandURL
, const Window
* pWindow
)
448 OUString sModuleName
= GetHelpModuleName_Impl();
449 OUString sHelpText
= SfxHelp_Impl::GetHelpText( aCommandURL
, sModuleName
);
453 if (pWindow
&& sHelpText
.isEmpty())
455 // no help text found -> try with parent help id.
456 Window
* pParent
= pWindow
->GetParent();
459 aNewHelpId
= pParent
->GetHelpId();
460 sHelpText
= SfxHelp_Impl::GetHelpText( OStringToOUString(aNewHelpId
, RTL_TEXTENCODING_UTF8
), sModuleName
);
461 if (!sHelpText
.isEmpty())
464 pParent
= pParent
->GetParent();
467 if (bIsDebug
&& sHelpText
.isEmpty())
468 aNewHelpId
= OString();
471 // add some debug information?
474 sHelpText
+= "\n-------------\n";
475 sHelpText
+= sModuleName
;
477 sHelpText
+= aCommandURL
;
478 if ( !aNewHelpId
.isEmpty() )
481 sHelpText
+= OStringToOUString(aNewHelpId
, RTL_TEXTENCODING_UTF8
);
488 /// Check for built-in help
489 static bool impl_hasHelpInstalled( const OUString
&rLang
= OUString() )
491 OUStringBuffer
aHelpRootURL("vnd.sun.star.help://");
492 AppendConfigToken(aHelpRootURL
, sal_True
, rLang
);
493 std::vector
< OUString
> aFactories
= SfxContentHelper::GetResultSet(aHelpRootURL
.makeStringAndClear());
495 return !aFactories
.empty();
498 sal_Bool
SfxHelp::SearchKeyword( const OUString
& rKeyword
)
500 return Start_Impl( OUString(), NULL
, rKeyword
);
503 sal_Bool
SfxHelp::Start( const OUString
& rURL
, const Window
* pWindow
)
505 return Start_Impl( rURL
, pWindow
, OUString() );
508 /// Redirect the vnd.sun.star.help:// urls to http://help.libreoffice.org
509 static bool impl_showOnlineHelp( const OUString
& rURL
)
511 OUString
aInternal( "vnd.sun.star.help://" );
512 if ( rURL
.getLength() <= aInternal
.getLength() || !rURL
.startsWith(aInternal
) )
515 OUString
aHelpLink( "http://help.libreoffice.org/" );
516 aHelpLink
+= rURL
.copy( aInternal
.getLength() );
517 aHelpLink
= aHelpLink
.replaceAll("%2F","/");
520 Reference
< XSystemShellExecute
> xSystemShell(
521 SystemShellExecute::create(::comphelper::getProcessComponentContext()) );
523 xSystemShell
->execute( aHelpLink
, OUString(), SystemShellExecuteFlags::URIS_ONLY
);
526 catch (const Exception
&)
532 sal_Bool
SfxHelp::Start_Impl(const OUString
& rURL
, const Window
* pWindow
, const OUString
& rKeyword
)
534 OUStringBuffer
aHelpRootURL("vnd.sun.star.help://");
535 AppendConfigToken(aHelpRootURL
, sal_True
);
536 SfxContentHelper::GetResultSet(aHelpRootURL
.makeStringAndClear());
540 - a HelpID (formerly a long, now a string)
541 If rURL is a URL, CreateHelpURL should be called for this URL
542 If rURL is an arbitrary string, the same should happen, but the URL should be tried out
543 if it delivers real help content. In case only the Help Error Document is returned, the
544 parent of the window for that help was called, is asked for its HelpID.
545 For compatibility reasons this upward search is not implemented for "real" URLs.
546 Help keyword search now is implemented as own method; in former versions it
547 was done via Help::Start, but this implementation conflicted with the upward search.
550 INetURLObject
aParser( rURL
);
551 INetProtocol nProtocol
= aParser
.GetProtocol();
555 case INET_PROT_VND_SUN_STAR_HELP
:
556 // already a vnd.sun.star.help URL -> nothing to do
561 OUString
aHelpModuleName( GetHelpModuleName_Impl() );
562 // no URL, just a HelpID (maybe empty in case of keyword search)
563 aHelpURL
= CreateHelpURL_Impl( rURL
, aHelpModuleName
);
565 if ( impl_hasHelpInstalled() && pWindow
&& SfxContentHelper::IsHelpErrorDocument( aHelpURL
) )
567 // no help found -> try with parent help id.
568 Window
* pParent
= pWindow
->GetParent();
569 bool bTriedTabPage
= false;
572 OString aHelpId
= pParent
->GetHelpId();
573 aHelpURL
= CreateHelpURL( OStringToOUString(aHelpId
, RTL_TEXTENCODING_UTF8
), aHelpModuleName
);
574 if ( !SfxContentHelper::IsHelpErrorDocument( aHelpURL
) )
580 pParent
= pParent
->GetParent();
583 // create help url of start page ( helpid == 0 -> start page)
584 aHelpURL
= CreateHelpURL( OUString(), aHelpModuleName
);
586 else if (pParent
->IsDialog() && !bTriedTabPage
)
588 //During help fallback, before we ask a dialog for its help
589 //see if it has a TabControl and ask the active tab of
591 bTriedTabPage
= true;
592 Dialog
*pDialog
= ((Dialog
*)pParent
);
593 TabControl
*pCtrl
= pDialog
->hasBuilder() ? pDialog
->get
<TabControl
>("tabcontrol") : NULL
;
594 TabPage
* pTabPage
= pCtrl
? pCtrl
->GetTabPage(pCtrl
->GetCurPageId()) : NULL
;
595 Window
*pTabChild
= pTabPage
? pTabPage
->GetWindow(WINDOW_FIRSTCHILD
) : NULL
;
606 if ( !impl_hasHelpInstalled() )
608 if ( impl_showOnlineHelp( aHelpURL
) )
612 NoHelpErrorBox
aErrBox( const_cast< Window
* >( pWindow
) );
618 Reference
< XDesktop2
> xDesktop
= Desktop::create( ::comphelper::getProcessComponentContext() );
620 // check if help window is still open
621 // If not, create a new one and return access directly to the internal sub frame showing the help content
622 // search must be done here; search one desktop level could return an arbitraty frame
623 Reference
< XFrame2
> xHelp(
624 xDesktop
->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::CHILDREN
),
626 Reference
< XFrame
> xHelpContent
= xDesktop
->findFrame(
627 OUString("OFFICE_HELP"),
628 FrameSearchFlag::CHILDREN
);
630 SfxHelpWindow_Impl
* pHelpWindow
= 0;
632 pHelpWindow
= impl_createHelp(xHelp
, xHelpContent
);
634 pHelpWindow
= (SfxHelpWindow_Impl
*)VCLUnoHelper::GetWindow(xHelp
->getComponentWindow());
635 if (!xHelp
.is() || !xHelpContent
.is() || !pHelpWindow
)
639 OStringBuffer
aTmp("SfxHelp: HelpId = ");
640 aTmp
.append(OUStringToOString(aHelpURL
, RTL_TEXTENCODING_UTF8
));
641 OSL_TRACE( aTmp
.getStr() );
644 pHelpWindow
->SetHelpURL( aHelpURL
);
645 pHelpWindow
->loadHelpContent(aHelpURL
);
646 if (!rKeyword
.isEmpty())
647 pHelpWindow
->OpenKeyword( rKeyword
);
649 Reference
< ::com::sun::star::awt::XTopWindow
> xTopWindow( xHelp
->getContainerWindow(), UNO_QUERY
);
650 if ( xTopWindow
.is() )
651 xTopWindow
->toFront();
656 OUString
SfxHelp::CreateHelpURL(const OUString
& aCommandURL
, const OUString
& rModuleName
)
658 SfxHelp
* pHelp
= static_cast< SfxHelp
* >(Application::GetHelp());
659 return pHelp
? pHelp
->CreateHelpURL_Impl( aCommandURL
, rModuleName
) : OUString();
662 OUString
SfxHelp::GetDefaultHelpModule()
664 return getDefaultModule_Impl();
667 OUString
SfxHelp::GetCurrentModuleIdentifier()
669 return getCurrentModuleIdentifier_Impl();
672 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */