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