xref: /trunk/main/sfx2/source/appl/sfxhelp.cxx (revision cdf0e10c)
1  /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sfx2.hxx"
30 
31 #include "sfx2/sfxhelp.hxx"
32 
33 #include <set>
34 #include <algorithm>
35 #include <com/sun/star/uno/Reference.h>
36 #include <com/sun/star/frame/XFrame.hpp>
37 #include <com/sun/star/frame/XComponentLoader.hpp>
38 #include <com/sun/star/lang/XComponent.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/awt/PosSize.hpp>
43 #include <com/sun/star/frame/XDesktop.hpp>
44 #include <com/sun/star/util/XURLTransformer.hpp>
45 #include <com/sun/star/frame/XDispatch.hpp>
46 #include <com/sun/star/frame/XDispatchProvider.hpp>
47 #include <com/sun/star/container/XNameAccess.hpp>
48 #include <com/sun/star/beans/XPropertySet.hpp>
49 #include <com/sun/star/frame/FrameSearchFlag.hpp>
50 #include <toolkit/helper/vclunohelper.hxx>
51 #include <com/sun/star/frame/XModuleManager.hpp>
52 #include <unotools/configmgr.hxx>
53 #include <unotools/configitem.hxx>
54 #include <svtools/helpopt.hxx>
55 #include <unotools/moduleoptions.hxx>
56 #include <tools/urlobj.hxx>
57 #include <unotools/configmgr.hxx>
58 #include <ucbhelper/content.hxx>
59 #include <unotools/pathoptions.hxx>
60 #include <rtl/ustring.hxx>
61 #include <osl/process.h>
62 #include <osl/file.hxx>
63 #include <unotools/bootstrap.hxx>
64 #include <rtl/uri.hxx>
65 #include <vcl/msgbox.hxx>
66 #include <svtools/ehdl.hxx>
67 #include <svtools/sfxecode.hxx>
68 
69 #define _SVSTDARR_STRINGSDTOR
70 #define _SVSTDARR_ULONGSSORT
71 #include <svl/svstdarr.hxx>
72 
73 #include "newhelp.hxx"
74 #include <sfx2/objsh.hxx>
75 #include <sfx2/docfac.hxx>
76 #include "sfx2/sfxresid.hxx"
77 #include "helper.hxx"
78 #include "app.hrc"
79 #include <sfx2/sfxuno.hxx>
80 #include <vcl/svapp.hxx>
81 #include <sfx2/frame.hxx>
82 #include <rtl/string.hxx>
83 
84 using namespace ::com::sun::star::beans;
85 using namespace ::com::sun::star::frame;
86 using namespace ::com::sun::star::uno;
87 using namespace ::com::sun::star::util;
88 using namespace ::com::sun::star::frame;
89 using namespace ::com::sun::star::lang;
90 
91 #define ERROR_TAG	String( DEFINE_CONST_UNICODE("Error: ") )
92 #define PATH_TAG	String( DEFINE_CONST_UNICODE("\nPath: ") )
93 
94 // class NoHelpErrorBox --------------------------------------------------
95 
96 class NoHelpErrorBox : public ErrorBox
97 {
98 public:
99     NoHelpErrorBox( Window* _pParent );
100 
101     virtual void    RequestHelp( const HelpEvent& rHEvt );
102 };
103 
104 NoHelpErrorBox::NoHelpErrorBox( Window* _pParent ) :
105 
106     ErrorBox( _pParent, WB_OK, String( SfxResId( RID_STR_HLPFILENOTEXIST ) ) )
107 {
108     // Error message: "No help available"
109 }
110 
111 void NoHelpErrorBox::RequestHelp( const HelpEvent& )
112 {
113     // do nothing, because no help available
114 }
115 
116 // -----------------------------------------------------------------------
117 
118 #define STARTERLIST 0
119 
120 rtl::OUString HelpLocaleString()
121 {
122 	static rtl::OUString aLocaleStr;
123 	if (!aLocaleStr.getLength())
124 	{
125 		// detect installed locale
126 		Any aLocale =
127 			::utl::ConfigManager::GetConfigManager()->GetDirectConfigProperty(
128 			   ::utl::ConfigManager::LOCALE );
129         aLocale >>= aLocaleStr;
130         bool bOk = aLocaleStr.getLength() != 0;
131 		if ( bOk )
132 		{
133 			rtl::OUString aBaseInstallPath;
134 			// utl::Bootstrap::PathStatus aBaseLocateResult =
135 			utl::Bootstrap::locateBaseInstallation(aBaseInstallPath);
136 			static const char *szHelpPath = "/help/";
137 
138 			rtl::OUString sHelpPath = aBaseInstallPath +
139 				rtl::OUString::createFromAscii(szHelpPath) + aLocaleStr;
140 			osl::DirectoryItem aDirItem;
141 
142 			if (!osl::DirectoryItem::get(sHelpPath, aDirItem) == osl::FileBase::E_None)
143 			{
144 				bOk = false;
145 				String sLang(aLocaleStr);
146 				xub_StrLen nSepPos = sLang.Search( '-' );
147 				if (nSepPos != STRING_NOTFOUND)
148 				{
149 					bOk = true;
150         			sLang = sLang.Copy( 0, nSepPos );
151 					sHelpPath = aBaseInstallPath +
152 						rtl::OUString::createFromAscii(szHelpPath) + sLang;
153 					if (!osl::DirectoryItem::get(sHelpPath, aDirItem) == osl::FileBase::E_None)
154 						bOk = false;
155 				}
156 			}
157 		}
158 		if (!bOk)
159 			aLocaleStr = rtl::OUString( DEFINE_CONST_UNICODE("en") );
160 	}
161 	return aLocaleStr;
162 }
163 
164 void AppendConfigToken_Impl( String& rURL, sal_Bool bQuestionMark )
165 {
166 	::rtl::OUString aLocaleStr(HelpLocaleString());
167 
168 	// query part exists?
169 	if ( bQuestionMark )
170 		// no, so start with '?'
171 		rURL += '?';
172 	else
173 		// yes, so only append with '&'
174 		rURL += '&';
175 
176 	// set parameters
177 	rURL += DEFINE_CONST_UNICODE("Language=");
178 	rURL += String( aLocaleStr );
179 	rURL += DEFINE_CONST_UNICODE("&System=");
180 	rURL += SvtHelpOptions().GetSystem();
181 
182 }
183 
184 // -----------------------------------------------------------------------
185 
186 sal_Bool GetHelpAnchor_Impl( const String& _rURL, String& _rAnchor )
187 {
188 	sal_Bool bRet = sal_False;
189 	::rtl::OUString sAnchor;
190 
191     // --> OD 2009-07-01 #159496#
192     // do not release solar mutex due to crash regarding accessibility
193 //    sal_uIntPtr nSolarCount = Application::ReleaseSolarMutex();
194     // <--
195 	try
196 	{
197 		::ucbhelper::Content aCnt( INetURLObject( _rURL ).GetMainURL( INetURLObject::NO_DECODE ),
198 							 Reference< ::com::sun::star::ucb::XCommandEnvironment > () );
199 		if ( ( aCnt.getPropertyValue( ::rtl::OUString::createFromAscii( "AnchorName" ) ) >>= sAnchor ) )
200 		{
201 
202 			if ( sAnchor.getLength() > 0 )
203 			{
204 				_rAnchor = String( sAnchor );
205 				bRet = sal_True;
206 			}
207 		}
208 		else
209 		{
210 			DBG_ERRORFILE( "Property 'AnchorName' is missing" );
211 		}
212 	}
213 	catch( ::com::sun::star::uno::Exception& )
214 	{
215 	}
216     // --> OD 2009-07-01 #159496#
217 //    Application::AcquireSolarMutex( nSolarCount );
218     // <--
219 
220 	return bRet;
221 }
222 
223 // -----------------------------------------------------------------------
224 
225 class SfxHelpOptions_Impl : public utl::ConfigItem
226 {
227 private:
228     std::set < rtl::OString > m_aIds;
229 
230 public:
231                     SfxHelpOptions_Impl();
232                     ~SfxHelpOptions_Impl();
233 
234     bool            HasId( const rtl::OString& rId ) { return m_aIds.size() ? m_aIds.find( rId ) != m_aIds.end() : false; }
235     virtual void            Notify( const com::sun::star::uno::Sequence< rtl::OUString >& aPropertyNames );
236     virtual void            Commit();
237 };
238 
239 static Sequence< ::rtl::OUString > GetPropertyNames()
240 {
241 	static const char* aPropNames[] =
242 	{
243         "HelpAgentStarterList",
244 	};
245 
246     const int nCount = sizeof( aPropNames ) / sizeof( const char* );
247 	Sequence< ::rtl::OUString > aNames( nCount );
248 	::rtl::OUString* pNames = aNames.getArray();
249 	::rtl::OUString* pEnd	= pNames + aNames.getLength();
250 	int i = 0;
251 	for ( ; pNames != pEnd; ++pNames )
252 		*pNames = ::rtl::OUString::createFromAscii( aPropNames[i++] );
253 
254 	return aNames;
255 }
256 
257 // -----------------------------------------------------------------------
258 
259 SfxHelpOptions_Impl::SfxHelpOptions_Impl()
260     : ConfigItem( ::rtl::OUString::createFromAscii("Office.SFX/Help") )
261 {
262 	Sequence< ::rtl::OUString > aNames = GetPropertyNames();
263 	Sequence< Any > aValues = GetProperties( aNames );
264 	EnableNotification( aNames );
265 	const Any* pValues = aValues.getConstArray();
266 	DBG_ASSERT( aValues.getLength() == aNames.getLength(), "GetProperties failed" );
267 	if ( aValues.getLength() == aNames.getLength() )
268 	{
269 		for ( int nProp = 0; nProp < aNames.getLength(); nProp++ )
270 		{
271 			DBG_ASSERT( pValues[nProp].hasValue(), "property value missing" );
272 			if ( pValues[nProp].hasValue() )
273 			{
274 				switch ( nProp )
275 				{
276                     case STARTERLIST :
277                     {
278                         ::rtl::OUString aCodedList;
279                         if ( pValues[nProp] >>= aCodedList )
280                         {
281                             rtl::OString aTmp( aCodedList, aCodedList.getLength(), RTL_TEXTENCODING_UTF8 );
282                             sal_Int32 nIndex = 0;
283                             do
284                             {
285                                 rtl::OString aToken = aTmp.getToken( 0, ',', nIndex );
286                                 if ( aToken.getLength() )
287                                     m_aIds.insert( aToken );
288                             }
289                             while ( nIndex >= 0 );
290                         }
291                         else {
292                             DBG_ERRORFILE( "Wrong property type!" );
293                         }
294 
295                         break;
296                     }
297 
298 					default:
299                         DBG_ERRORFILE( "Wrong property!" );
300                         break;
301 				}
302 			}
303 		}
304 	}
305 }
306 
307 SfxHelpOptions_Impl::~SfxHelpOptions_Impl()
308 {
309 }
310 
311 
312 void SfxHelpOptions_Impl::Notify( const com::sun::star::uno::Sequence< rtl::OUString >& )
313 {
314 }
315 
316 void SfxHelpOptions_Impl::Commit()
317 {
318 }
319 
320 // class SfxHelp_Impl ----------------------------------------------------
321 
322 class SfxHelp_Impl
323 {
324 private:
325 	sal_Bool							m_bIsDebug;		// environment variable "help_debug=1"
326     SfxHelpOptions_Impl*				m_pOpt;			// the options
327 	::std::vector< ::rtl::OUString >	m_aModulesList;	// list of all installed modules
328 	void					Load();
329 
330 public:
331     SfxHelp_Impl( sal_Bool bDebug );
332     ~SfxHelp_Impl();
333 
334     SfxHelpOptions_Impl*	GetOptions();
335     static String           GetHelpText( const rtl::OUString& aCommandURL, const String& rModule );
336 	sal_Bool				HasModule( const ::rtl::OUString& rModule );			// module installed
337 	sal_Bool				IsHelpInstalled();										// module list not empty
338 };
339 
340 SfxHelp_Impl::SfxHelp_Impl( sal_Bool bDebug ) :
341 
342 	m_bIsDebug		( bDebug ),
343     m_pOpt      	( NULL )
344 
345 {
346 }
347 
348 SfxHelp_Impl::~SfxHelp_Impl()
349 {
350     delete m_pOpt;
351 }
352 
353 void SfxHelp_Impl::Load()
354 {
355 	// fill modules list
356 	// create the help url (empty, without module and helpid)
357 	String sHelpURL( DEFINE_CONST_UNICODE("vnd.sun.star.help://") );
358 	AppendConfigToken_Impl( sHelpURL, sal_True );
359 
360 	// open ucb content and get the list of the help modules
361 	// the list contains strings with three tokens "ui title \t type \t url"
362 	Sequence< ::rtl::OUString > aAllModulesList = SfxContentHelper::GetResultSet( sHelpURL );
363 	sal_Int32 nLen = aAllModulesList.getLength();
364 	m_aModulesList.reserve( nLen + 1 );
365 	const ::rtl::OUString* pBegin = aAllModulesList.getConstArray();
366 	const ::rtl::OUString* pEnd	= pBegin + nLen;
367 	for ( ; pBegin != pEnd; ++pBegin )
368 	{
369 		// get one module string
370 		String sModule( *pBegin );
371 		// extract the url
372 		String sURL = sModule.GetToken( 2, '\t' );
373 		// insert the module (the host part of the "vnd.sun.star.help" url)
374 		m_aModulesList.push_back( ::rtl::OUString( INetURLObject( sURL ).GetHost() ) );
375 	}
376 }
377 
378 String SfxHelp_Impl::GetHelpText( const rtl::OUString& aCommandURL, const String& rModule )
379 {
380 	// create help url
381     String aHelpURL = SfxHelp::CreateHelpURL( aCommandURL, rModule );
382 	// added 'active' parameter
383 	aHelpURL.Insert( String( DEFINE_CONST_UNICODE("&Active=true") ), aHelpURL.SearchBackward( '#' ) );
384 	// load help string
385 	return SfxContentHelper::GetActiveHelpString( aHelpURL );
386 }
387 
388 SfxHelpOptions_Impl* SfxHelp_Impl::GetOptions()
389 {
390 	// create if not exists
391     if ( !m_pOpt )
392         m_pOpt = new SfxHelpOptions_Impl;
393     return m_pOpt;
394 }
395 
396 sal_Bool SfxHelp_Impl::HasModule( const ::rtl::OUString& rModule )
397 {
398 	if ( !m_aModulesList.size() )
399 		Load();
400 	return ( ::std::find( m_aModulesList.begin(), m_aModulesList.end(), rModule ) != m_aModulesList.end() );
401 }
402 
403 sal_Bool SfxHelp_Impl::IsHelpInstalled()
404 {
405 	if ( !m_aModulesList.size() )
406 		Load();
407 	return ( m_aModulesList.begin() != m_aModulesList.end() );
408 }
409 
410 // class SfxHelp ---------------------------------------------------------
411 /* some test code for HID conversion - please don't remove
412 
413 #include <tools/stream.hxx>
414 void TestHids()
415 {
416     static const char* aModules[] =
417     {
418         "swriter",
419         "scalc",
420         "simpress",
421         "sdraw",
422         "sdatabase",
423         "smath",
424         "schart",
425         "sbasic"
426     };
427 
428     SvFileStream* pOut[] =
429     {
430         0,0,0,0,0,0,0,0,0
431     };
432 
433     String aIn = String::CreateFromAscii("/data/OOo/replacer/hidsin.lst");
434     String aOut = String::CreateFromAscii("/data/OOo/replacer/");
435     SvFileStream aInStrm( aIn, STREAM_READ );
436     ByteString aBuffer;
437     while ( aInStrm.ReadLine( aBuffer ) )
438     {
439         ByteString aHid = aBuffer.GetToken(0, ' ');
440         ByteString aNr  = aBuffer.GetToken(1, ' ');
441         bool bFound=false;
442         for (sal_Int32 n= 0; n<8; n++)
443         {
444             bFound = false;
445 	        String aHelpURL = SfxHelp::CreateHelpURL( String( aNr, RTL_TEXTENCODING_UTF8 ), String( aModules[n], RTL_TEXTENCODING_UTF8 ) );
446             if ( !SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
447             {
448                 if (!pOut[n])
449                 {
450                     String aTmp( aOut );
451                     aTmp += String( aModules[n], RTL_TEXTENCODING_UTF8 );
452                     aTmp += String::CreateFromAscii(".lst");
453                     pOut[n] = new SvFileStream( aTmp, STREAM_WRITE | STREAM_TRUNC );
454                 }
455                 pOut[n]->WriteLine( aHid );
456                 bFound = true;
457                 break;
458             }
459         }
460 
461         if (!bFound)
462         {
463             if (!pOut[8])
464             {
465                 String aTmp( aOut );
466                 aTmp += String( "notfound", RTL_TEXTENCODING_UTF8 );
467                 aTmp += String::CreateFromAscii(".lst");
468                 pOut[8] = new SvFileStream( aTmp, STREAM_WRITE | STREAM_TRUNC );
469             }
470             pOut[8]->WriteLine( aHid );
471         }
472     }
473 
474     for (sal_Int32 n= 0; n<9; n++)
475         DELETEZ( pOut[n] );
476 }
477 
478 void TestHids2()
479 {
480     static const char* aModules[] =
481     {
482         "swriter",
483         "scalc",
484         "simpress",
485         "smath",
486         "sbasic"
487     };
488 
489     String aOut = String::CreateFromAscii("/data/OOo/replacer/");
490     aOut += String::CreateFromAscii("lost.lst");
491     SvFileStream aOutStrm( aOut, STREAM_WRITE | STREAM_TRUNC );
492     for (sal_Int32 n= 0; n<5; n++)
493     {
494         String aIn = String::CreateFromAscii("/data/OOo/replacer/help/");
495         aIn += String::CreateFromAscii( aModules[n] );
496         aIn += String::CreateFromAscii(".lst");
497         SvFileStream aInStrm( aIn, STREAM_READ );
498         ByteString aBuffer;
499         while ( aInStrm.ReadLine( aBuffer ) )
500         {
501             String aHelpURL = SfxHelp::CreateHelpURL( String( aBuffer, RTL_TEXTENCODING_UTF8 ), String( aModules[n], RTL_TEXTENCODING_UTF8 ) );
502             if ( SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
503                 aOutStrm.WriteLine( aBuffer );
504         }
505     }
506 }
507 
508 #include <tools/stream.hxx>
509 void TestHids3()
510 {
511     static const char* aModules[] =
512     {
513         "swriter",
514         "scalc",
515         "simpress",
516         "sdraw",
517         "sdatabase",
518         "smath",
519         "schart",
520         "sbasic"
521     };
522 
523     SvFileStream* pOut[] =
524     {
525         0,0,0,0,0,0,0,0,0
526     };
527 
528     String aIn = String::CreateFromAscii("/data/OOo/replacer/hidsin.lst");
529     String aOut = String::CreateFromAscii("/data/OOo/replacer/quickhelp/");
530     SvFileStream aInStrm( aIn, STREAM_READ );
531     ByteString aBuffer;
532     while ( aInStrm.ReadLine( aBuffer ) )
533     {
534         ByteString aHid = aBuffer.GetToken(0, ' ');
535         ByteString aNr  = aBuffer.GetToken(1, ' ');
536         bool bFound=false;
537         for (sal_Int32 n= 0; n<8; n++)
538         {
539             bFound = false;
540             String aHelpURL = SfxHelp::CreateHelpURL( String( aNr, RTL_TEXTENCODING_UTF8 ), String( aModules[n], RTL_TEXTENCODING_UTF8 ) );
541 	        if ( SfxContentHelper::GetActiveHelpString( aHelpURL ).Len() )
542 //            if ( SfxHelp_Impl::GetHelpText( String( aNr, RTL_TEXTENCODING_UTF8 ), String( aModules[n], RTL_TEXTENCODING_UTF8 ) ).Len() )
543             {
544                 if (!pOut[n])
545                 {
546                     String aTmp( aOut );
547                     aTmp += String( aModules[n], RTL_TEXTENCODING_UTF8 );
548                     aTmp += String::CreateFromAscii(".lst");
549                     pOut[n] = new SvFileStream( aTmp, STREAM_WRITE | STREAM_TRUNC );
550                 }
551                 pOut[n]->WriteLine( aHid );
552                 bFound = true;
553                 break;
554             }
555         }
556 
557         if (!bFound)
558         {
559             if (!pOut[8])
560             {
561                 String aTmp( aOut );
562                 aTmp += String( "notfound", RTL_TEXTENCODING_UTF8 );
563                 aTmp += String::CreateFromAscii(".lst");
564                 pOut[8] = new SvFileStream( aTmp, STREAM_WRITE | STREAM_TRUNC );
565             }
566             pOut[8]->WriteLine( aHid );
567         }
568     }
569 
570     for (sal_Int32 n= 0; n<9; n++)
571         DELETEZ( pOut[n] );
572 }
573 
574 void TestHids4()
575 {
576     static const char* aModules[] =
577     {
578         "swriter",
579         "scalc",
580         "simpress",
581         "smath",
582         "sbasic"
583     };
584 
585     String aOut = String::CreateFromAscii("/data/OOo/replacer/quickhelp/");
586     aOut += String::CreateFromAscii("lost.lst");
587     SvFileStream aOutStrm( aOut, STREAM_WRITE | STREAM_TRUNC );
588     for (sal_Int32 n= 0; n<5; n++)
589     {
590         String aIn = String::CreateFromAscii("/data/OOo/replacer/quickhelp/");
591         aIn += String::CreateFromAscii( aModules[n] );
592         aIn += String::CreateFromAscii(".lst");
593         SvFileStream aInStrm( aIn, STREAM_READ );
594         ByteString aBuffer;
595         while ( aInStrm.ReadLine( aBuffer ) )
596         {
597             String aHelpURL = SfxHelp::CreateHelpURL( String( aBuffer, RTL_TEXTENCODING_UTF8 ), String( aModules[n], RTL_TEXTENCODING_UTF8 ) );
598 	        if ( !SfxContentHelper::GetActiveHelpString( aHelpURL ).Len() )
599                 aOutStrm.WriteLine( aBuffer );
600         }
601     }
602 }
603 */
604 
605 SfxHelp::SfxHelp() :
606 
607 	bIsDebug( sal_False ),
608     pImp	( NULL )
609 
610 {
611 	// read the environment variable "HELP_DEBUG"
612 	// if it's set, you will see debug output on active help
613 	{
614 		::rtl::OUString sHelpDebug;
615 		::rtl::OUString sEnvVarName( RTL_CONSTASCII_USTRINGPARAM( "HELP_DEBUG" ) );
616 		osl_getEnvironment( sEnvVarName.pData, &sHelpDebug.pData );
617 		bIsDebug = ( 0 != sHelpDebug.getLength() );
618 	}
619 
620 	pImp = new SfxHelp_Impl( bIsDebug );
621 
622     ::rtl::OUString aLocaleStr = HelpLocaleString();
623 
624     sal_Int32 nSepPos = aLocaleStr.indexOf( '_' );
625     if ( nSepPos != -1 )
626     {
627         aLanguageStr = aLocaleStr.copy( 0, nSepPos );
628         aCountryStr = aLocaleStr.copy( nSepPos+1 );
629     }
630     else
631     {
632         nSepPos = aLocaleStr.indexOf( '-' );
633         if ( nSepPos != -1 )
634         {
635             aLanguageStr = aLocaleStr.copy( 0, nSepPos );
636             aCountryStr = aLocaleStr.copy( nSepPos+1 );
637         }
638         else
639         {
640             aLanguageStr = aLocaleStr;
641         }
642     }
643 }
644 
645 SfxHelp::~SfxHelp()
646 {
647     delete pImp;
648 }
649 
650 ::rtl::OUString getDefaultModule_Impl()
651 {
652     rtl::OUString sDefaultModule;
653     SvtModuleOptions aModOpt;
654     if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SWRITER ) )
655         sDefaultModule = DEFINE_CONST_UNICODE("swriter");
656     else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SCALC ) )
657         sDefaultModule = DEFINE_CONST_UNICODE("scalc");
658     else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SIMPRESS ) )
659         sDefaultModule = DEFINE_CONST_UNICODE("simpress");
660     else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SDRAW ) )
661         sDefaultModule = DEFINE_CONST_UNICODE("sdraw");
662     else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SMATH ) )
663         sDefaultModule = DEFINE_CONST_UNICODE("smath");
664     else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SCHART ) )
665         sDefaultModule = DEFINE_CONST_UNICODE("schart");
666     else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SBASIC ) )
667         sDefaultModule = DEFINE_CONST_UNICODE("sbasic");
668     else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::E_SDATABASE ) )
669         sDefaultModule = DEFINE_CONST_UNICODE("sdatabase");
670     else
671     {
672         DBG_ERRORFILE( "getDefaultModule_Impl(): no module installed" );
673     }
674     return sDefaultModule;
675 }
676 
677 ::rtl::OUString getCurrentModuleIdentifier_Impl()
678 {
679     ::rtl::OUString sIdentifier;
680     Reference < XFrame > xCurrentFrame;
681     Reference < XModuleManager > xModuleManager( ::comphelper::getProcessServiceFactory()->createInstance(
682         DEFINE_CONST_UNICODE("com.sun.star.frame.ModuleManager") ), UNO_QUERY );
683     Reference < XDesktop > xDesktop( ::comphelper::getProcessServiceFactory()->createInstance(
684         DEFINE_CONST_UNICODE("com.sun.star.frame.Desktop") ), UNO_QUERY );
685     if ( xDesktop.is() )
686         xCurrentFrame = xDesktop->getCurrentFrame();
687 
688     if ( xCurrentFrame.is() && xModuleManager.is() )
689     {
690         try
691         {
692             sIdentifier = xModuleManager->identify( xCurrentFrame );
693         }
694         catch ( ::com::sun::star::frame::UnknownModuleException& )
695         {
696             DBG_WARNING( "SfxHelp::getCurrentModuleIdentifier_Impl(): unknown module (help in help?)" );
697         }
698         catch ( Exception& )
699         {
700             DBG_ERRORFILE( "SfxHelp::getCurrentModuleIdentifier_Impl(): exception of XModuleManager::identify()" );
701         }
702     }
703 
704     return sIdentifier;
705 }
706 
707 String SfxHelp::GetHelpModuleName_Impl()
708 {
709     String sModuleName;
710     rtl::OUString aFactoryShortName;
711     rtl::OUString aModuleIdentifier = getCurrentModuleIdentifier_Impl();
712 
713     if ( aModuleIdentifier.getLength() > 0 )
714     {
715         try
716         {
717             Reference < XModuleManager > xModuleManager(
718                 ::comphelper::getProcessServiceFactory()->createInstance(
719                     DEFINE_CONST_UNICODE("com.sun.star.frame.ModuleManager") ), UNO_QUERY );
720             Sequence< PropertyValue > lProps;
721             Reference< ::com::sun::star::container::XNameAccess > xCont( xModuleManager, UNO_QUERY);
722             if ( xCont.is() )
723                 xCont->getByName( aModuleIdentifier ) >>= lProps;
724             for ( sal_Int32 i = 0; i < lProps.getLength(); ++i )
725             {
726                 if ( lProps[i].Name.equalsAscii("ooSetupFactoryShortName") )
727                 {
728                     lProps[i].Value >>= aFactoryShortName;
729                     break;
730                 }
731             }
732         }
733         catch ( Exception& )
734         {
735             DBG_ERRORFILE( "SfxHelp::GetHelpModuleName_Impl(): exception of XNameAccess::getByName()" );
736         }
737     }
738 
739     rtl::OUString sDefaultModule = getDefaultModule_Impl();
740     if ( aFactoryShortName.getLength() > 0 )
741     {
742         // Map some module identifiers to their "real" help module string.
743         if ( aFactoryShortName.equalsAscii( "chart2" ) )
744             aFactoryShortName = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "schart" ) );
745         else if ( aFactoryShortName.equalsAscii( "BasicIDE" ) )
746             aFactoryShortName = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "sbasic" ) );
747         else if ( aFactoryShortName.equalsAscii( "sweb" )
748                 || aFactoryShortName.equalsAscii( "sglobal" )
749                 || aFactoryShortName.equalsAscii( "swxform" ) )
750             aFactoryShortName = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "swriter" ) );
751         else if ( aFactoryShortName.equalsAscii( "dbquery" )
752                 || aFactoryShortName.equalsAscii( "dbbrowser" )
753                 || aFactoryShortName.equalsAscii( "dbrelation" )
754                 || aFactoryShortName.equalsAscii( "dbtable" )
755                 || aFactoryShortName.equalsAscii( "dbapp" )
756                 || aFactoryShortName.equalsAscii( "dbreport" )
757                 || aFactoryShortName.equalsAscii( "swreport" )
758                 || aFactoryShortName.equalsAscii( "dbbrowser" )
759                 || aFactoryShortName.equalsAscii( "swform" ) )
760             aFactoryShortName = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "sdatabase" ) );
761         else if ( aFactoryShortName.equalsAscii( "sbibliography" )
762                 || aFactoryShortName.equalsAscii( "StartModule" ) )
763             aFactoryShortName = sDefaultModule;
764     }
765     else
766         aFactoryShortName = sDefaultModule;
767 
768     sModuleName = String( aFactoryShortName );
769     return sModuleName;
770 }
771 
772 String	SfxHelp::CreateHelpURL_Impl( const String& aCommandURL, const String& rModuleName )
773 {
774 	// build up the help URL
775     String aHelpURL;
776 	sal_Bool bHasAnchor = sal_False;
777 	String aAnchor;
778 
779 	String aModuleName( rModuleName );
780 	if ( aModuleName.Len() == 0 )
781         aModuleName = getDefaultModule_Impl();
782 
783     aHelpURL = String::CreateFromAscii("vnd.sun.star.help://");
784     aHelpURL += aModuleName;
785 
786     if ( !aCommandURL.Len() )
787         aHelpURL += String::CreateFromAscii("/start");
788     else
789     {
790         aHelpURL += '/';
791         aHelpURL += String( rtl::Uri::encode( aCommandURL,
792                                               rtl_UriCharClassRelSegment,
793                                               rtl_UriEncodeKeepEscapes,
794                                               RTL_TEXTENCODING_UTF8 ));
795 
796 		String aTempURL = aHelpURL;
797 	    AppendConfigToken_Impl( aTempURL, sal_True );
798 		bHasAnchor = GetHelpAnchor_Impl( aTempURL, aAnchor );
799 	}
800 
801     AppendConfigToken_Impl( aHelpURL, sal_True );
802 
803 	if ( bHasAnchor )
804 	{
805 		aHelpURL += '#';
806 		aHelpURL += aAnchor;
807 	}
808 
809     return aHelpURL;
810 }
811 
812 SfxHelpWindow_Impl* impl_createHelp(Reference< XFrame >& rHelpTask   ,
813                                     Reference< XFrame >& rHelpContent)
814 {
815     Reference < XFrame > xDesktop( ::comphelper::getProcessServiceFactory()->createInstance(
816 		DEFINE_CONST_UNICODE("com.sun.star.frame.Desktop") ), UNO_QUERY );
817 
818     // otherwhise - create new help task
819     Reference< XFrame > xHelpTask = xDesktop->findFrame(
820         ::rtl::OUString(DEFINE_CONST_UNICODE("OFFICE_HELP_TASK")),
821         FrameSearchFlag::TASKS | FrameSearchFlag::CREATE);
822     if (!xHelpTask.is())
823         return 0;
824 
825     // create all internal windows and sub frames ...
826     Reference< ::com::sun::star::awt::XWindow > xParentWindow = xHelpTask->getContainerWindow();
827     Window*                                     pParentWindow = VCLUnoHelper::GetWindow( xParentWindow );
828     SfxHelpWindow_Impl*                         pHelpWindow   = new SfxHelpWindow_Impl( xHelpTask, pParentWindow, WB_DOCKBORDER );
829     Reference< ::com::sun::star::awt::XWindow > xHelpWindow   = VCLUnoHelper::GetInterface( pHelpWindow );
830 
831     Reference< XFrame > xHelpContent;
832     if (xHelpTask->setComponent( xHelpWindow, Reference< XController >() ))
833     {
834         // Customize UI ...
835         xHelpTask->setName( ::rtl::OUString(DEFINE_CONST_UNICODE("OFFICE_HELP_TASK")) );
836 
837         Reference< XPropertySet > xProps(xHelpTask, UNO_QUERY);
838         if (xProps.is())
839             xProps->setPropertyValue(
840                 DEFINE_CONST_UNICODE("Title"),
841                 makeAny(::rtl::OUString(String(SfxResId(STR_HELP_WINDOW_TITLE)))));
842 
843         pHelpWindow->setContainerWindow( xParentWindow );
844         xParentWindow->setVisible(sal_True);
845         xHelpWindow->setVisible(sal_True);
846 
847         // This sub frame is created internaly (if we called new SfxHelpWindow_Impl() ...)
848         // It should exist :-)
849         xHelpContent = xHelpTask->findFrame(::rtl::OUString(DEFINE_CONST_UNICODE("OFFICE_HELP")), FrameSearchFlag::CHILDREN);
850     }
851 
852     if (!xHelpContent.is())
853         delete pHelpWindow;
854 
855     xHelpContent->setName(::rtl::OUString(DEFINE_CONST_UNICODE("OFFICE_HELP")));
856 
857     rHelpTask    = xHelpTask;
858     rHelpContent = xHelpContent;
859     return pHelpWindow;
860 }
861 
862 XubString SfxHelp::GetHelpText( const String& aCommandURL, const Window* pWindow )
863 {
864     String sModuleName = GetHelpModuleName_Impl();
865     String sHelpText = pImp->GetHelpText( aCommandURL, sModuleName );
866 
867 	ByteString aNewHelpId;
868 
869 	if ( pWindow && !sHelpText.Len() )
870 	{
871 		// no help text found -> try with parent help id.
872 		Window* pParent = pWindow->GetParent();
873 		while ( pParent )
874 		{
875 			aNewHelpId = pParent->GetHelpId();
876 			sHelpText = pImp->GetHelpText( String( aNewHelpId, RTL_TEXTENCODING_UTF8 ), sModuleName );
877 			if ( sHelpText.Len() > 0 )
878 				pParent = NULL;
879 			else
880 				pParent = pParent->GetParent();
881 		}
882 
883 		if ( bIsDebug && !sHelpText.Len() )
884 			aNewHelpId.Erase();
885 	}
886 
887     // add some debug information?
888     if ( bIsDebug )
889     {
890         sHelpText += DEFINE_CONST_UNICODE("\n-------------\n");
891         sHelpText += String( sModuleName );
892         sHelpText += DEFINE_CONST_UNICODE(": ");
893         sHelpText += aCommandURL;
894 		if ( aNewHelpId.Len() )
895 		{
896 			sHelpText += DEFINE_CONST_UNICODE(" - ");
897 			sHelpText += String( aNewHelpId, RTL_TEXTENCODING_UTF8 );
898 		}
899     }
900 
901     return sHelpText;
902 }
903 
904 sal_Bool SfxHelp::SearchKeyword( const XubString& rKeyword )
905 {
906 	return Start_Impl( String(), NULL, rKeyword );
907 }
908 
909 sal_Bool SfxHelp::Start( const String& rURL, const Window* pWindow )
910 {
911     return Start_Impl( rURL, pWindow, String() );
912 }
913 
914 sal_Bool SfxHelp::Start_Impl( const String& rURL, const Window* pWindow, const String& rKeyword )
915 {
916     // check if help is available
917     String aHelpRootURL( DEFINE_CONST_OUSTRING("vnd.sun.star.help://") );
918     AppendConfigToken_Impl( aHelpRootURL, sal_True );
919     Sequence< ::rtl::OUString > aFactories = SfxContentHelper::GetResultSet( aHelpRootURL );
920     if ( 0 == aFactories.getLength() )
921     {
922         // no factories -> no help -> error message and return
923         NoHelpErrorBox aErrBox( const_cast< Window* >( pWindow ) );
924         aErrBox.Execute();
925         return sal_False;
926     }
927 
928 	/* rURL may be
929 		- a "real" URL
930 		- a HelpID (formerly a long, now a string)
931 	   If rURL is a URL, CreateHelpURL should be called for this URL
932 	   If rURL is an arbitrary string, the same should happen, but the URL should be tried out
933 	   if it delivers real help content. In case only the Help Error Document is returned, the
934 	   parent of the window for that help was called, is asked for its HelpID.
935 	   For compatibility reasons this upward search is not implemented for "real" URLs.
936 	   Help keyword search now is implemented as own method; in former versions it
937 	   was done via Help::Start, but this implementation conflicted with the upward search.
938 	*/
939 	String aHelpURL;
940 	INetURLObject aParser( rURL );
941     INetProtocol nProtocol = aParser.GetProtocol();
942 	String aHelpModuleName( GetHelpModuleName_Impl() );
943 	switch ( nProtocol )
944 	{
945 		case INET_PROT_VND_SUN_STAR_HELP:
946 			// already a vnd.sun.star.help URL -> nothing to do
947 			aHelpURL = rURL;
948 			break;
949 		default:
950 		{
951 			// no URL, just a HelpID (maybe empty in case of keyword search)
952 			aHelpURL  = CreateHelpURL_Impl( rURL, aHelpModuleName );
953 			if ( pWindow && SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
954 			{
955 				// no help found -> try with parent help id.
956 				Window* pParent = pWindow->GetParent();
957 				while ( pParent )
958 				{
959 					ByteString aHelpId = pParent->GetHelpId();
960 					aHelpURL = CreateHelpURL( String( aHelpId, RTL_TEXTENCODING_UTF8 ), aHelpModuleName );
961 					if ( !SfxContentHelper::IsHelpErrorDocument( aHelpURL ) )
962 						break;
963 					else
964 					{
965 						pParent = pParent->GetParent();
966 						if ( !pParent )
967 							// create help url of start page ( helpid == 0 -> start page)
968 							aHelpURL = CreateHelpURL( String(), aHelpModuleName );
969 					}
970 				}
971 			}
972 			break;
973 		}
974 	}
975 
976     Reference < XFrame > xDesktop( ::comphelper::getProcessServiceFactory()->createInstance(
977 		DEFINE_CONST_UNICODE("com.sun.star.frame.Desktop") ), UNO_QUERY );
978 
979     // check if help window is still open
980     // If not, create a new one and return access directly to the internal sub frame showing the help content
981 	// search must be done here; search one desktop level could return an arbitraty frame
982     Reference< XFrame > xHelp = xDesktop->findFrame(
983         ::rtl::OUString(DEFINE_CONST_UNICODE("OFFICE_HELP_TASK")),
984         FrameSearchFlag::CHILDREN);
985     Reference< XFrame > xHelpContent = xDesktop->findFrame(
986         ::rtl::OUString(DEFINE_CONST_UNICODE("OFFICE_HELP")),
987         FrameSearchFlag::CHILDREN);
988 
989     SfxHelpWindow_Impl* pHelpWindow = 0;
990     if (!xHelp.is())
991         pHelpWindow = impl_createHelp(xHelp, xHelpContent);
992     else
993         pHelpWindow = (SfxHelpWindow_Impl*)VCLUnoHelper::GetWindow(xHelp->getComponentWindow());
994     if (!xHelp.is() || !xHelpContent.is() || !pHelpWindow)
995         return sal_False;
996 
997 #ifdef DBG_UTIL
998     ByteString aTmp("SfxHelp: HelpId = ");
999     aTmp += ByteString( aHelpURL, RTL_TEXTENCODING_UTF8 );
1000     DBG_TRACE( aTmp.GetBuffer() );
1001 #endif
1002 
1003     pHelpWindow->SetHelpURL( aHelpURL );
1004     pHelpWindow->loadHelpContent(aHelpURL);
1005     if ( rKeyword.Len() )
1006 		pHelpWindow->OpenKeyword( rKeyword );
1007 
1008 	Reference < ::com::sun::star::awt::XTopWindow > xTopWindow( xHelp->getContainerWindow(), UNO_QUERY );
1009     if ( xTopWindow.is() )
1010         xTopWindow->toFront();
1011 
1012     return sal_True;
1013 }
1014 
1015 String SfxHelp::CreateHelpURL( const String& aCommandURL, const String& rModuleName )
1016 {
1017 	String aURL;
1018 	SfxHelp* pHelp = SAL_STATIC_CAST( SfxHelp*, Application::GetHelp() );
1019 	if ( pHelp )
1020 		aURL = pHelp->CreateHelpURL_Impl( aCommandURL, rModuleName );
1021 	return aURL;
1022 }
1023 
1024 void SfxHelp::OpenHelpAgent( SfxFrame*, const rtl::OString& sHelpId )
1025 {
1026 	SfxHelp* pHelp = SAL_STATIC_CAST( SfxHelp*, Application::GetHelp() );
1027 	if ( pHelp )
1028 		pHelp->OpenHelpAgent( sHelpId );
1029 }
1030 
1031 void SfxHelp::OpenHelpAgent( const rtl::OString& sHelpId )
1032 {
1033 	if ( SvtHelpOptions().IsHelpAgentAutoStartMode() )
1034 	{
1035 			SfxHelpOptions_Impl *pOpt = pImp->GetOptions();
1036     		if ( !pOpt->HasId( sHelpId ) )
1037         		return;
1038 
1039 			try
1040 			{
1041 				URL aURL;
1042                 aURL.Complete = CreateHelpURL_Impl( String( ByteString(sHelpId), RTL_TEXTENCODING_UTF8 ), GetHelpModuleName_Impl() );
1043         		Reference < XURLTransformer > xTrans( ::comphelper::getProcessServiceFactory()->createInstance(
1044 					::rtl::OUString::createFromAscii("com.sun.star.util.URLTransformer" ) ), UNO_QUERY );
1045         		xTrans->parseStrict(aURL);
1046 
1047 				Reference < XFrame > xCurrentFrame;
1048 				Reference < XDesktop > xDesktop( ::comphelper::getProcessServiceFactory()->createInstance(
1049 					DEFINE_CONST_UNICODE("com.sun.star.frame.Desktop") ), UNO_QUERY );
1050 				if ( xDesktop.is() )
1051 					xCurrentFrame = xDesktop->getCurrentFrame();
1052 
1053         		Reference< XDispatchProvider > xDispProv( xCurrentFrame, UNO_QUERY );
1054 				Reference< XDispatch > xHelpDispatch;
1055 				if ( xDispProv.is() )
1056 					xHelpDispatch = xDispProv->queryDispatch(
1057 						aURL, ::rtl::OUString::createFromAscii("_helpagent"),
1058 						FrameSearchFlag::PARENT | FrameSearchFlag::SELF );
1059 
1060         		DBG_ASSERT( xHelpDispatch.is(), "OpenHelpAgent: could not get a dispatcher!" );
1061 				if ( xHelpDispatch.is() )
1062 					xHelpDispatch->dispatch( aURL, Sequence< PropertyValue >() );
1063 			}
1064 			catch( const Exception& )
1065 			{
1066         		DBG_ERRORFILE( "OpenHelpAgent: caught an exception while executing the dispatch!" );
1067 			}
1068 	}
1069 }
1070 
1071 String SfxHelp::GetDefaultHelpModule()
1072 {
1073     return getDefaultModule_Impl();
1074 }
1075 
1076 ::rtl::OUString SfxHelp::GetCurrentModuleIdentifier()
1077 {
1078     return getCurrentModuleIdentifier_Impl();
1079 }
1080 
1081