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_framework.hxx"
30 
31 //_________________________________________________________________________________________________________________
32 //	my own includes
33 //_________________________________________________________________________________________________________________
34 #include <services/license.hxx>
35 #include <threadhelp/resetableguard.hxx>
36 #include <macros/debug.hxx>
37 #include <services.h>
38 
39 // local header for UI implementation
40 #include "services/licensedlg.hxx"
41 #include "classes/resource.hrc"
42 
43 //_________________________________________________________________________________________________________________
44 //	interface includes
45 //_________________________________________________________________________________________________________________
46 
47 #include <com/sun/star/frame/XDesktop.hpp>
48 #include <com/sun/star/lang/XInitialization.hpp>
49 #include <com/sun/star/beans/XPropertySet.hpp>
50 #include <com/sun/star/uno/Any.hxx>
51 #include <com/sun/star/util/XChangesBatch.hpp>
52 #include <com/sun/star/beans/NamedValue.hpp>
53 #include <com/sun/star/lang/XComponent.hpp>
54 
55 
56 //_________________________________________________________________________________________________________________
57 //	includes of other projects
58 //_________________________________________________________________________________________________________________
59 
60 #include <rtl/ustrbuf.hxx>
61 #include <rtl/strbuf.hxx>
62 #include <rtl/ustring.hxx>
63 #include <rtl/string.hxx>
64 #include <unotools/bootstrap.hxx>
65 #include <osl/file.hxx>
66 #include <svtools/xtextedt.hxx>
67 #include <vcl/svapp.hxx>
68 #include <comphelper/processfactory.hxx>
69 #include <tools/date.hxx>
70 #include <tools/time.hxx>
71 #include <tools/datetime.hxx>
72 #include <osl/file.hxx>
73 #include <osl/time.h>
74 
75 //_________________________________________________________________________________________________________________
76 //	namespace
77 //_________________________________________________________________________________________________________________
78 
79 namespace framework{
80 using namespace utl;
81 using namespace ::osl							;
82 using namespace ::cppu							;
83 using namespace ::com::sun::star::uno			;
84 using namespace ::com::sun::star::beans			;
85 using namespace ::com::sun::star::lang			;
86 using namespace ::com::sun::star::util			;
87 using namespace ::com::sun::star::frame 		;
88 
89 //_________________________________________________________________________________________________________________
90 //	non exported const
91 //_________________________________________________________________________________________________________________
92 
93 // license file name
94 static const char *szLicensePath = "/share/readme";
95 #ifdef UNX
96 static const char *szUNXLicenseName = "/LICENSE";
97 static const char *szUNXLicenseExt = "";
98 #elif defined(WNT) || defined(OS2)
99 static const char *szWNTLicenseName = "/license";
100 static const char *szWNTLicenseExt = ".txt";
101 #endif
102 
103 //_________________________________________________________________________________________________________________
104 //	non exported definitions
105 //_________________________________________________________________________________________________________________
106 
107 //_________________________________________________________________________________________________________________
108 //	declarations
109 //_________________________________________________________________________________________________________________
110 
111 //*****************************************************************************************************************
112 //	constructor
113 //*****************************************************************************************************************
114 License::License( const Reference< XMultiServiceFactory >& xFactory )
115 		//	Init baseclasses first
116 		//	Attention:
117 		//		Don't change order of initialization!
118 		//      ThreadHelpBase is a struct with a mutex as member. We can't use a mutex as member, while
119 		//		we must garant right initialization and a valid value of this! First initialize
120 		//		baseclasses and then members. And we need the mutex for other baseclasses !!!
121         :   ThreadHelpBase  ( &Application::GetSolarMutex() )
122         ,   OWeakObject     (                               )
123 		// Init member
124         ,   m_xFactory      ( xFactory                      )
125         ,   m_bTerminate    ( sal_False                     )
126 {
127 }
128 
129 //*****************************************************************************************************************
130 //	destructor
131 //*****************************************************************************************************************
132 License::~License()
133 {
134 }
135 
136 //*****************************************************************************************************************
137 //	XInterface, XTypeProvider, XServiceInfo
138 //*****************************************************************************************************************
139 
140 DEFINE_XINTERFACE_4					(	License						   ,
141 										OWeakObject					   ,
142 										DIRECT_INTERFACE(XTypeProvider ),
143 										DIRECT_INTERFACE(XServiceInfo  ),
144 										DIRECT_INTERFACE(XJob	       ),
145                                         DIRECT_INTERFACE(XCloseable    )
146 									)
147 
148 DEFINE_XTYPEPROVIDER_4				(	License	,
149 										XTypeProvider	,
150 										XServiceInfo	,
151 										XJob            ,
152                                         XCloseable
153 									)
154 
155 DEFINE_XSERVICEINFO_MULTISERVICE	(	License,
156                                         OWeakObject                 ,
157 										SERVICENAME_LICENSE			,
158 										IMPLEMENTATIONNAME_LICENSE
159 									)
160 
161 DEFINE_INIT_SERVICE                 (   License,
162                                         {
163                                         }
164                                     )
165 
166 
167 #if 0
168 IMPL_STATIC_LINK_NOINSTANCE( License, Terminate, void*, EMPTYARG )
169 {
170     /*
171     Reference< XMultiServiceFactory > xFactory = comphelper::getProcessServiceFactory();
172     Reference< XDesktop > xDesktop(xFactory->createInstance(
173         ::rtl::OUString::createFromAscii("com.sun.star.frame.Desktop")), UNO_QUERY);
174     if (xDesktop.is())
175         xDesktop->terminate();
176     */
177     /*
178     _exit(0);
179     */
180 	return 0;
181 }
182 #endif
183 
184 static DateTime _oslDateTimeToDateTime(const oslDateTime& aDateTime)
185 {
186     return DateTime(
187         Date(aDateTime.Day, aDateTime.Month, aDateTime.Year),
188         Time(aDateTime.Hours, aDateTime.Minutes, aDateTime.Seconds));
189 }
190 
191 static ::rtl::OUString _makeDateTimeString (const DateTime& aDateTime, sal_Bool bUTC = sal_False)
192 {
193     ::rtl::OStringBuffer aDateTimeString;
194     aDateTimeString.append((sal_Int32)aDateTime.GetYear());
195     aDateTimeString.append("-");
196     if (aDateTime.GetMonth()<10) aDateTimeString.append("0");
197     aDateTimeString.append((sal_Int32)aDateTime.GetMonth());
198     aDateTimeString.append("-");
199     if (aDateTime.GetDay()<10) aDateTimeString.append("0");
200     aDateTimeString.append((sal_Int32)aDateTime.GetDay());
201     aDateTimeString.append("T");
202     if (aDateTime.GetHour()<10) aDateTimeString.append("0");
203     aDateTimeString.append((sal_Int32)aDateTime.GetHour());
204     aDateTimeString.append(":");
205     if (aDateTime.GetMin()<10) aDateTimeString.append("0");
206     aDateTimeString.append((sal_Int32)aDateTime.GetMin());
207     aDateTimeString.append(":");
208     if (aDateTime.GetSec()<10) aDateTimeString.append("0");
209     aDateTimeString.append((sal_Int32)aDateTime.GetSec());
210     if (bUTC) aDateTimeString.append("Z");
211 
212     return OStringToOUString(aDateTimeString.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
213 }
214 
215 static sal_Bool _parseDateTime(const ::rtl::OUString& aString, DateTime& aDateTime)
216 {
217     // take apart a canonical literal xsd:dateTime string
218     //CCYY-MM-DDThh:mm:ss(Z)
219 
220     ::rtl::OUString aDateTimeString = aString.trim();
221 
222     // check length
223     if (aDateTimeString.getLength() < 19 || aDateTimeString.getLength() > 20)
224         return sal_False;
225 
226     sal_Int32 nDateLength = 10;
227     sal_Int32 nTimeLength = 8;
228 
229     ::rtl::OUString aDateTimeSep = ::rtl::OUString::createFromAscii("T");
230     ::rtl::OUString aDateSep = ::rtl::OUString::createFromAscii("-");
231     ::rtl::OUString aTimeSep = ::rtl::OUString::createFromAscii(":");
232     ::rtl::OUString aUTCString = ::rtl::OUString::createFromAscii("Z");
233 
234     ::rtl::OUString aDateString = aDateTimeString.copy(0, nDateLength);
235     ::rtl::OUString aTimeString = aDateTimeString.copy(nDateLength+1, nTimeLength);
236 
237     sal_Int32 nIndex = 0;
238     sal_Int32 nYear = aDateString.getToken(0, '-', nIndex).toInt32();
239     sal_Int32 nMonth = aDateString.getToken(0, '-', nIndex).toInt32();
240     sal_Int32 nDay = aDateString.getToken(0, '-', nIndex).toInt32();
241     nIndex = 0;
242     sal_Int32 nHour = aTimeString.getToken(0, ':', nIndex).toInt32();
243     sal_Int32 nMinute = aTimeString.getToken(0, ':', nIndex).toInt32();
244     sal_Int32 nSecond = aTimeString.getToken(0, ':', nIndex).toInt32();
245 
246     Date tmpDate((sal_uInt16)nDay, (sal_uInt16)nMonth, (sal_uInt16)nYear);
247     Time tmpTime(nHour, nMinute, nSecond);
248     DateTime tmpDateTime(tmpDate, tmpTime);
249     if (aString.indexOf(aUTCString) < 0)
250         tmpDateTime.ConvertToUTC();
251 
252     aDateTime = tmpDateTime;
253     return sal_True;
254 }
255 
256 static ::rtl::OUString _getCurrentDateString()
257 {
258     ::rtl::OUString aString;
259     return _makeDateTimeString(DateTime());
260 }
261 
262 // execution of license check...
263 css::uno::Any SAL_CALL License::execute(const css::uno::Sequence< css::beans::NamedValue >& )
264     throw( css::lang::IllegalArgumentException, css::uno::Exception)
265 {
266     // return value
267     Any aRet; aRet <<= sal_False;
268 
269     try
270     {
271         ::rtl::OUString aBaseInstallPath;
272         Bootstrap::PathStatus aBaseLocateResult =
273             Bootstrap::locateBaseInstallation(aBaseInstallPath);
274         if (aBaseLocateResult != Bootstrap::PATH_EXISTS)
275         {
276             // base install noit found
277             // prepare termination
278             // m_bTerminate = sal_True;
279             // Application::PostUserEvent( STATIC_LINK( 0, License, Terminate ) );
280             aRet <<= sal_False;
281             return aRet;
282         }
283         // determine the filename of the license to show
284 	    ::rtl::OUString  aLangString;
285         ::com::sun::star::lang::Locale aLocale;
286         ::rtl::OString aMgrName = ::rtl::OString("fwe");
287         AllSettings aSettings(Application::GetSettings());
288         aLocale = aSettings.GetUILocale();
289         ResMgr* pResMgr = ResMgr::SearchCreateResMgr(aMgrName, aLocale);
290 
291         aLangString = aLocale.Language;
292         if ( aLocale.Country.getLength() != 0 )
293         {
294             aLangString += ::rtl::OUString::createFromAscii("-");
295             aLangString += aLocale.Country;
296 	        if ( aLocale.Variant.getLength() != 0 )
297             {
298 	            aLangString += ::rtl::OUString::createFromAscii("-");
299 	            aLangString += aLocale.Variant;
300             }
301         }
302 #if defined(WNT) || defined(OS2)
303         ::rtl::OUString aLicensePath =
304             aBaseInstallPath + ::rtl::OUString::createFromAscii(szLicensePath)
305             + ::rtl::OUString::createFromAscii(szWNTLicenseName)
306             + ::rtl::OUString::createFromAscii("_")
307             + aLangString
308             + ::rtl::OUString::createFromAscii(szWNTLicenseExt);
309 #else
310         ::rtl::OUString aLicensePath =
311             aBaseInstallPath + ::rtl::OUString::createFromAscii(szLicensePath)
312             + ::rtl::OUString::createFromAscii(szUNXLicenseName)
313             + ::rtl::OUString::createFromAscii("_")
314             + aLangString
315             + ::rtl::OUString::createFromAscii(szUNXLicenseExt);
316 #endif
317         // check if we need to show the license at all
318         // open org.openoffice.Setup/Office/ooLicenseAcceptDate
319         ::rtl::OUString sConfigSrvc = SERVICENAME_CFGPROVIDER;
320         ::rtl::OUString sAccessSrvc = ::rtl::OUString::createFromAscii("com.sun.star.configuration.ConfigurationUpdateAccess");
321         ::rtl::OUString sReadSrvc   = SERVICENAME_CFGREADACCESS;
322 
323         // get configuration provider
324         Reference< XMultiServiceFactory > theConfigProvider = Reference< XMultiServiceFactory >(
325         m_xFactory->createInstance(sConfigSrvc), UNO_QUERY_THROW);
326         Sequence< Any > theArgs(1);
327         NamedValue v;
328         v.Name = ::rtl::OUString::createFromAscii("NodePath");
329         v.Value <<= ::rtl::OUString::createFromAscii("org.openoffice.Setup/Office");
330         theArgs[0] <<= v;
331         Reference< XPropertySet > pset = Reference< XPropertySet >(
332             theConfigProvider->createInstanceWithArguments(sAccessSrvc, theArgs), UNO_QUERY_THROW);
333 
334         // if we find a date there, compare it to baseinstall license date
335         ::rtl::OUString aAcceptDate;
336         if (pset->getPropertyValue(::rtl::OUString::createFromAscii("ooLicenseAcceptDate")) >>= aAcceptDate)
337         {
338             // get LicenseFileDate from base install
339             ::rtl::OUString aLicenseURL = aLicensePath;
340             /*
341             if (FileBase::getFileURLFromSystemPath(aLicensePath, aLicenseURL) != FileBase::E_None)
342                 return makeAny(sal_False);
343                 */
344             DirectoryItem aDirItem;
345             if (DirectoryItem::get(aLicenseURL, aDirItem) != FileBase::E_None)
346                 return makeAny(sal_False);
347             FileStatus aStatus(FileStatusMask_All);
348             if (aDirItem.getFileStatus(aStatus) != FileBase::E_None)
349                 return makeAny(sal_False);
350             TimeValue aTimeVal = aStatus.getModifyTime();
351             oslDateTime aDateTimeVal;
352             if (!osl_getDateTimeFromTimeValue(&aTimeVal, &aDateTimeVal))
353                 return makeAny(sal_False);
354 
355             // compare dates
356             DateTime aLicenseDateTime = _oslDateTimeToDateTime(aDateTimeVal);
357             DateTime aAcceptDateTime;
358             if (!_parseDateTime(aAcceptDate, aAcceptDateTime))
359                 return makeAny(sal_False);
360 
361             if ( aAcceptDateTime > aLicenseDateTime )
362                 return makeAny(sal_True);
363         }
364         // prepare to show
365         // display license dialog
366         LicenseDialog* pDialog = new LicenseDialog(aLicensePath, pResMgr);
367         sal_Bool bAgreed = (pDialog->Execute() == 1);
368         delete pDialog;
369 
370         if (bAgreed) {
371 
372             // write org.openoffice.Setup/ooLicenseAcceptDate
373             aAcceptDate = _getCurrentDateString();
374             pset->setPropertyValue(::rtl::OUString::createFromAscii("ooLicenseAcceptDate"), makeAny(aAcceptDate));
375             Reference< XChangesBatch >(pset, UNO_QUERY_THROW)->commitChanges();
376 
377             // enable quickstarter
378             sal_Bool bQuickstart( sal_True );
379             sal_Bool bAutostart( sal_True );
380             Sequence< Any > aSeq( 2 );
381             aSeq[0] <<= bQuickstart;
382             aSeq[1] <<= bAutostart;
383 
384             Reference < XInitialization > xQuickstart( ::comphelper::getProcessServiceFactory()->createInstance(
385                 ::rtl::OUString::createFromAscii( "com.sun.star.office.Quickstart" )),UNO_QUERY );
386             if ( xQuickstart.is() )
387                 xQuickstart->initialize( aSeq );
388 
389             aRet <<= sal_True;
390         }
391         else
392         {
393             // license was not accepted
394             // prepare termination
395             // m_bTerminate = sal_True;
396             // Application::PostUserEvent( STATIC_LINK( 0, License, Terminate ) );
397             aRet <<= sal_False;
398         }
399     }
400     catch (RuntimeException&)
401     {
402         // license could not be verified
403         aRet <<= sal_False;
404     }
405     return aRet;
406 }
407 
408 void SAL_CALL License::close(sal_Bool /*bDeliverOwnership*/) throw (css::util::CloseVetoException)
409 {
410     if (!m_bTerminate)
411         throw CloseVetoException();
412 }
413 void SAL_CALL License::addCloseListener(const css::uno::Reference< css::util::XCloseListener >&)
414     throw (css::uno::RuntimeException)
415 {
416 }
417 void SAL_CALL License::removeCloseListener(const css::uno::Reference< css::util::XCloseListener >&)
418     throw (css::uno::RuntimeException)
419 {
420 }
421 
422 
423 //************************************************************************
424 //   License Dialog
425 //************************************************************************
426 
427 LicenseDialog::LicenseDialog(const ::rtl::OUString & aLicensePath, ResMgr *pResMgr) :
428     ModalDialog(NULL, ResId(DLG_LICENSE, *pResMgr)),
429     aLicenseML(this, ResId(ML_LICENSE, *pResMgr)),
430     aInfo1FT(this, ResId(FT_INFO1, *pResMgr)),
431     aInfo2FT(this, ResId(FT_INFO2, *pResMgr)),
432     aInfo3FT(this, ResId(FT_INFO3, *pResMgr)),
433     aInfo2_1FT(this, ResId(FT_INFO2_1, *pResMgr)),
434     aInfo3_1FT(this, ResId(FT_INFO3_1, *pResMgr)),
435     aFixedLine(this, ResId(FL_DIVIDE, *pResMgr)),
436     aPBPageDown(this, ResId(PB_PAGEDOWN, *pResMgr)),
437     aPBDecline( this, ResId(PB_DECLINE, *pResMgr) ),
438     aPBAccept( this, ResId(PB_ACCEPT, *pResMgr) ),
439     aArrow(this, ResId(IMG_ARROW, *pResMgr)),
440     aStrAccept( ResId(LICENSE_ACCEPT, *pResMgr) ),
441     aStrNotAccept( ResId(LICENSE_NOTACCEPT, *pResMgr) ),
442     bEndReached(sal_False)
443 {
444     FreeResource();
445 
446     aLicenseML.SetEndReachedHdl( LINK(this, LicenseDialog, EndReachedHdl) );
447     aLicenseML.SetScrolledHdl( LINK(this, LicenseDialog, ScrolledHdl) );
448 
449     aPBPageDown.SetClickHdl( LINK(this, LicenseDialog, PageDownHdl) );
450     aPBDecline.SetClickHdl( LINK(this, LicenseDialog, DeclineBtnHdl) );
451     aPBAccept.SetClickHdl( LINK(this, LicenseDialog, AcceptBtnHdl) );
452 
453     // We want a automatic repeating page down button
454     WinBits aStyle = aPBPageDown.GetStyle();
455     aStyle |= WB_REPEAT;
456     aPBPageDown.SetStyle( aStyle );
457 
458     String aText = aInfo2FT.GetText();
459     aText.SearchAndReplaceAll( UniString::CreateFromAscii("%PAGEDOWN"), aPBPageDown.GetText() );
460     aInfo2FT.SetText( aText );
461 
462     aPBDecline.SetText( aStrNotAccept );
463     aPBAccept.SetText( aStrAccept );
464 
465     aPBAccept.Disable();
466 
467     // load license text
468     File aLicenseFile(aLicensePath);
469     if ( aLicenseFile.open(OpenFlag_Read) == FileBase::E_None)
470     {
471         DirectoryItem d;
472         DirectoryItem::get(aLicensePath, d);
473         FileStatus fs(FileStatusMask_FileSize);
474         d.getFileStatus(fs);
475         sal_uInt64 nBytesRead = 0;
476         sal_uInt64 nPosition = 0;
477         sal_uInt32 nBytes = (sal_uInt32)fs.getFileSize();
478         sal_Char *pBuffer = new sal_Char[nBytes];
479         // FileBase RC r = FileBase::E_None;
480         while (aLicenseFile.read(pBuffer+nPosition, nBytes-nPosition, nBytesRead) == FileBase::E_None
481             && nPosition + nBytesRead < nBytes)
482         {
483             nPosition += nBytesRead;
484         }
485         ::rtl::OUString aLicenseString(pBuffer, nBytes, RTL_TEXTENCODING_UTF8,
486                 OSTRING_TO_OUSTRING_CVTFLAGS | RTL_TEXTTOUNICODE_FLAGS_GLOBAL_SIGNATURE);
487         delete[] pBuffer;
488         aLicenseML.SetText(aLicenseString);
489     }
490 
491 }
492 
493 LicenseDialog::~LicenseDialog()
494 {
495 }
496 
497 IMPL_LINK( LicenseDialog, PageDownHdl, PushButton *, EMPTYARG )
498 {
499     aLicenseML.ScrollDown( SCROLL_PAGEDOWN );
500     return 0;
501 }
502 
503 IMPL_LINK( LicenseDialog, EndReachedHdl, LicenseView *, EMPTYARG )
504 {
505     bEndReached = sal_True;
506 
507     EnableControls();
508 
509     return 0;
510 }
511 
512 IMPL_LINK( LicenseDialog, ScrolledHdl, LicenseView *, EMPTYARG )
513 {
514     EnableControls();
515 
516     return 0;
517 }
518 
519 IMPL_LINK( LicenseDialog, DeclineBtnHdl, PushButton *, EMPTYARG )
520 {
521     EndDialog(0);
522     return 0;
523 }
524 IMPL_LINK( LicenseDialog, AcceptBtnHdl, PushButton *, EMPTYARG )
525 {
526     EndDialog(1);
527     return 0;
528 }
529 
530 
531 void LicenseDialog::EnableControls()
532 {
533     if( !bEndReached &&
534         ( aLicenseML.IsEndReached() || !aLicenseML.GetText().Len() ) )
535         bEndReached = sal_True;
536 
537     if ( bEndReached )
538     {
539         Point aPos( aInfo1FT.GetPosPixel().X(),
540                 aInfo3_1FT.GetPosPixel().Y() );
541         aArrow.SetPosPixel( aPos );
542         aPBAccept.Enable();
543     }
544     else
545     {
546         Point aPos( aInfo1FT.GetPosPixel().X(),
547                 aInfo2_1FT.GetPosPixel().Y() );
548         aArrow.SetPosPixel( aPos );
549         aPBAccept.Disable();
550     }
551 
552     if ( aLicenseML.IsEndReached() )
553         aPBPageDown.Disable();
554     else
555         aPBPageDown.Enable();
556 
557 }
558 
559 
560 LicenseView::LicenseView( Window* pParent, const ResId& rResId )
561     : MultiLineEdit( pParent, rResId )
562 {
563     SetLeftMargin( 5 );
564 
565     mbEndReached = IsEndReached();
566 
567 	StartListening( *GetTextEngine() );
568 }
569 
570 LicenseView::~LicenseView()
571 {
572     maEndReachedHdl = Link();
573     maScrolledHdl   = Link();
574 
575     EndListeningAll();
576 }
577 
578 void LicenseView::ScrollDown( ScrollType eScroll )
579 {
580     ScrollBar*  pScroll = GetVScrollBar();
581 
582     if ( pScroll )
583         pScroll->DoScrollAction( eScroll );
584 }
585 
586 sal_Bool LicenseView::IsEndReached() const
587 {
588     sal_Bool bEndReached;
589 
590     ExtTextView*    pView = GetTextView();
591     ExtTextEngine*  pEdit = GetTextEngine();
592     sal_uLong           nHeight = pEdit->GetTextHeight();
593     Size            aOutSize = pView->GetWindow()->GetOutputSizePixel();
594     Point           aBottom( 0, aOutSize.Height() );
595 
596     if ( (sal_uLong) pView->GetDocPos( aBottom ).Y() >= nHeight - 1 )
597         bEndReached = sal_True;
598     else
599         bEndReached = sal_False;
600 
601     return bEndReached;
602 }
603 
604 void LicenseView::Notify( SfxBroadcaster&, const SfxHint& rHint )
605 {
606     if ( rHint.IsA( TYPE(TextHint) ) )
607     {
608         sal_Bool    bLastVal = EndReached();
609         sal_uLong   nId = ((const TextHint&)rHint).GetId();
610 
611         if ( nId == TEXT_HINT_PARAINSERTED )
612         {
613             if ( bLastVal )
614                 mbEndReached = IsEndReached();
615         }
616         else if ( nId == TEXT_HINT_VIEWSCROLLED )
617         {
618             if ( ! mbEndReached )
619                 mbEndReached = IsEndReached();
620             maScrolledHdl.Call( this );
621         }
622 
623         if ( EndReached() && !bLastVal )
624         {
625             maEndReachedHdl.Call( this );
626         }
627     }
628 }
629 
630 }		//	namespace framework
631 
632