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