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 { 261 // return value 262 Any aRet; aRet <<= sal_False; 263 264 try 265 { 266 ::rtl::OUString aBaseInstallPath; 267 Bootstrap::PathStatus aBaseLocateResult = 268 Bootstrap::locateBaseInstallation(aBaseInstallPath); 269 if (aBaseLocateResult != Bootstrap::PATH_EXISTS) 270 { 271 // base install noit found 272 // prepare termination 273 // m_bTerminate = sal_True; 274 // Application::PostUserEvent( STATIC_LINK( 0, License, Terminate ) ); 275 aRet <<= sal_False; 276 return aRet; 277 } 278 // determine the filename of the license to show 279 ::rtl::OUString aLangString; 280 ::com::sun::star::lang::Locale aLocale; 281 ::rtl::OString aMgrName = ::rtl::OString("fwe"); 282 AllSettings aSettings(Application::GetSettings()); 283 aLocale = aSettings.GetUILocale(); 284 ResMgr* pResMgr = ResMgr::SearchCreateResMgr( aMgrName.getStr(), aLocale); 285 286 aLangString = aLocale.Language; 287 if ( aLocale.Country.getLength() != 0 ) 288 { 289 aLangString += ::rtl::OUString::createFromAscii("-"); 290 aLangString += aLocale.Country; 291 if ( aLocale.Variant.getLength() != 0 ) 292 { 293 aLangString += ::rtl::OUString::createFromAscii("-"); 294 aLangString += aLocale.Variant; 295 } 296 } 297 #if defined(WNT) || defined(OS2) 298 ::rtl::OUString aLicensePath = 299 aBaseInstallPath + ::rtl::OUString::createFromAscii(szLicensePath) 300 + ::rtl::OUString::createFromAscii(szWNTLicenseName) 301 + ::rtl::OUString::createFromAscii("_") 302 + aLangString 303 + ::rtl::OUString::createFromAscii(szWNTLicenseExt); 304 #else 305 ::rtl::OUString aLicensePath = 306 aBaseInstallPath + ::rtl::OUString::createFromAscii(szLicensePath) 307 + ::rtl::OUString::createFromAscii(szUNXLicenseName) 308 + ::rtl::OUString::createFromAscii("_") 309 + aLangString 310 + ::rtl::OUString::createFromAscii(szUNXLicenseExt); 311 #endif 312 // check if we need to show the license at all 313 // open org.openoffice.Setup/Office/ooLicenseAcceptDate 314 ::rtl::OUString sConfigSrvc = SERVICENAME_CFGPROVIDER; 315 ::rtl::OUString sAccessSrvc = ::rtl::OUString::createFromAscii("com.sun.star.configuration.ConfigurationUpdateAccess"); 316 ::rtl::OUString sReadSrvc = SERVICENAME_CFGREADACCESS; 317 318 // get configuration provider 319 Reference< XMultiServiceFactory > theConfigProvider = Reference< XMultiServiceFactory >( 320 m_xFactory->createInstance(sConfigSrvc), UNO_QUERY_THROW); 321 Sequence< Any > theArgs(1); 322 NamedValue v; 323 v.Name = ::rtl::OUString::createFromAscii("NodePath"); 324 v.Value <<= ::rtl::OUString::createFromAscii("org.openoffice.Setup/Office"); 325 theArgs[0] <<= v; 326 Reference< XPropertySet > pset = Reference< XPropertySet >( 327 theConfigProvider->createInstanceWithArguments(sAccessSrvc, theArgs), UNO_QUERY_THROW); 328 329 // if we find a date there, compare it to baseinstall license date 330 ::rtl::OUString aAcceptDate; 331 if (pset->getPropertyValue(::rtl::OUString::createFromAscii("ooLicenseAcceptDate")) >>= aAcceptDate) 332 { 333 // get LicenseFileDate from base install 334 ::rtl::OUString aLicenseURL = aLicensePath; 335 /* 336 if (FileBase::getFileURLFromSystemPath(aLicensePath, aLicenseURL) != FileBase::E_None) 337 return makeAny(sal_False); 338 */ 339 DirectoryItem aDirItem; 340 if (DirectoryItem::get(aLicenseURL, aDirItem) != FileBase::E_None) 341 return makeAny(sal_False); 342 FileStatus aStatus(FileStatusMask_All); 343 if (aDirItem.getFileStatus(aStatus) != FileBase::E_None) 344 return makeAny(sal_False); 345 TimeValue aTimeVal = aStatus.getModifyTime(); 346 oslDateTime aDateTimeVal; 347 if (!osl_getDateTimeFromTimeValue(&aTimeVal, &aDateTimeVal)) 348 return makeAny(sal_False); 349 350 // compare dates 351 DateTime aLicenseDateTime = _oslDateTimeToDateTime(aDateTimeVal); 352 DateTime aAcceptDateTime; 353 if (!_parseDateTime(aAcceptDate, aAcceptDateTime)) 354 return makeAny(sal_False); 355 356 if ( aAcceptDateTime > aLicenseDateTime ) 357 return makeAny(sal_True); 358 } 359 // prepare to show 360 // display license dialog 361 LicenseDialog* pDialog = new LicenseDialog(aLicensePath, pResMgr); 362 sal_Bool bAgreed = (pDialog->Execute() == 1); 363 delete pDialog; 364 365 if (bAgreed) { 366 367 // write org.openoffice.Setup/ooLicenseAcceptDate 368 aAcceptDate = _getCurrentDateString(); 369 pset->setPropertyValue(::rtl::OUString::createFromAscii("ooLicenseAcceptDate"), makeAny(aAcceptDate)); 370 Reference< XChangesBatch >(pset, UNO_QUERY_THROW)->commitChanges(); 371 372 // enable quickstarter 373 sal_Bool bQuickstart( sal_True ); 374 sal_Bool bAutostart( sal_True ); 375 Sequence< Any > aSeq( 2 ); 376 aSeq[0] <<= bQuickstart; 377 aSeq[1] <<= bAutostart; 378 379 Reference < XInitialization > xQuickstart( ::comphelper::getProcessServiceFactory()->createInstance( 380 ::rtl::OUString::createFromAscii( "com.sun.star.office.Quickstart" )),UNO_QUERY ); 381 if ( xQuickstart.is() ) 382 xQuickstart->initialize( aSeq ); 383 384 aRet <<= sal_True; 385 } 386 else 387 { 388 // license was not accepted 389 // prepare termination 390 // m_bTerminate = sal_True; 391 // Application::PostUserEvent( STATIC_LINK( 0, License, Terminate ) ); 392 aRet <<= sal_False; 393 } 394 } 395 catch (RuntimeException&) 396 { 397 // license could not be verified 398 aRet <<= sal_False; 399 } 400 return aRet; 401 } 402 403 void SAL_CALL License::close(sal_Bool /*bDeliverOwnership*/) 404 { 405 if (!m_bTerminate) 406 throw CloseVetoException(); 407 } 408 void SAL_CALL License::addCloseListener(const css::uno::Reference< css::util::XCloseListener >&) 409 { 410 } 411 void SAL_CALL License::removeCloseListener(const css::uno::Reference< css::util::XCloseListener >&) 412 { 413 } 414 415 416 //************************************************************************ 417 // License Dialog 418 //************************************************************************ 419 420 LicenseDialog::LicenseDialog(const ::rtl::OUString & aLicensePath, ResMgr *pResMgr) : 421 ModalDialog(NULL, ResId(DLG_LICENSE, *pResMgr)), 422 aLicenseML(this, ResId(ML_LICENSE, *pResMgr)), 423 aInfo1FT(this, ResId(FT_INFO1, *pResMgr)), 424 aInfo2FT(this, ResId(FT_INFO2, *pResMgr)), 425 aInfo3FT(this, ResId(FT_INFO3, *pResMgr)), 426 aInfo2_1FT(this, ResId(FT_INFO2_1, *pResMgr)), 427 aInfo3_1FT(this, ResId(FT_INFO3_1, *pResMgr)), 428 aFixedLine(this, ResId(FL_DIVIDE, *pResMgr)), 429 aPBPageDown(this, ResId(PB_PAGEDOWN, *pResMgr)), 430 aPBDecline( this, ResId(PB_DECLINE, *pResMgr) ), 431 aPBAccept( this, ResId(PB_ACCEPT, *pResMgr) ), 432 aArrow(this, ResId(IMG_ARROW, *pResMgr)), 433 aStrAccept( ResId(LICENSE_ACCEPT, *pResMgr) ), 434 aStrNotAccept( ResId(LICENSE_NOTACCEPT, *pResMgr) ), 435 bEndReached(sal_False) 436 { 437 FreeResource(); 438 439 aLicenseML.SetEndReachedHdl( LINK(this, LicenseDialog, EndReachedHdl) ); 440 aLicenseML.SetScrolledHdl( LINK(this, LicenseDialog, ScrolledHdl) ); 441 442 aPBPageDown.SetClickHdl( LINK(this, LicenseDialog, PageDownHdl) ); 443 aPBDecline.SetClickHdl( LINK(this, LicenseDialog, DeclineBtnHdl) ); 444 aPBAccept.SetClickHdl( LINK(this, LicenseDialog, AcceptBtnHdl) ); 445 446 // We want a automatic repeating page down button 447 WinBits aStyle = aPBPageDown.GetStyle(); 448 aStyle |= WB_REPEAT; 449 aPBPageDown.SetStyle( aStyle ); 450 451 String aText = aInfo2FT.GetText(); 452 aText.SearchAndReplaceAll( UniString::CreateFromAscii("%PAGEDOWN"), aPBPageDown.GetText() ); 453 aInfo2FT.SetText( aText ); 454 455 aPBDecline.SetText( aStrNotAccept ); 456 aPBAccept.SetText( aStrAccept ); 457 458 aPBAccept.Disable(); 459 460 // load license text 461 File aLicenseFile(aLicensePath); 462 if ( aLicenseFile.open(OpenFlag_Read) == FileBase::E_None) 463 { 464 DirectoryItem d; 465 DirectoryItem::get(aLicensePath, d); 466 FileStatus fs(FileStatusMask_FileSize); 467 d.getFileStatus(fs); 468 sal_uInt64 nBytesRead = 0; 469 sal_uInt64 nPosition = 0; 470 sal_uInt32 nBytes = (sal_uInt32)fs.getFileSize(); 471 sal_Char *pBuffer = new sal_Char[nBytes]; 472 // FileBase RC r = FileBase::E_None; 473 while (aLicenseFile.read(pBuffer+nPosition, nBytes-nPosition, nBytesRead) == FileBase::E_None 474 && nPosition + nBytesRead < nBytes) 475 { 476 nPosition += nBytesRead; 477 } 478 ::rtl::OUString aLicenseString(pBuffer, nBytes, RTL_TEXTENCODING_UTF8, 479 OSTRING_TO_OUSTRING_CVTFLAGS | RTL_TEXTTOUNICODE_FLAGS_GLOBAL_SIGNATURE); 480 delete[] pBuffer; 481 aLicenseML.SetText(aLicenseString); 482 } 483 484 } 485 486 LicenseDialog::~LicenseDialog() 487 { 488 } 489 490 IMPL_LINK( LicenseDialog, PageDownHdl, PushButton *, EMPTYARG ) 491 { 492 aLicenseML.ScrollDown( SCROLL_PAGEDOWN ); 493 return 0; 494 } 495 496 IMPL_LINK( LicenseDialog, EndReachedHdl, LicenseView *, EMPTYARG ) 497 { 498 bEndReached = sal_True; 499 500 EnableControls(); 501 502 return 0; 503 } 504 505 IMPL_LINK( LicenseDialog, ScrolledHdl, LicenseView *, EMPTYARG ) 506 { 507 EnableControls(); 508 509 return 0; 510 } 511 512 IMPL_LINK( LicenseDialog, DeclineBtnHdl, PushButton *, EMPTYARG ) 513 { 514 EndDialog(0); 515 return 0; 516 } 517 IMPL_LINK( LicenseDialog, AcceptBtnHdl, PushButton *, EMPTYARG ) 518 { 519 EndDialog(1); 520 return 0; 521 } 522 523 524 void LicenseDialog::EnableControls() 525 { 526 if( !bEndReached && 527 ( aLicenseML.IsEndReached() || !aLicenseML.GetText().Len() ) ) 528 bEndReached = sal_True; 529 530 if ( bEndReached ) 531 { 532 Point aPos( aInfo1FT.GetPosPixel().X(), 533 aInfo3_1FT.GetPosPixel().Y() ); 534 aArrow.SetPosPixel( aPos ); 535 aPBAccept.Enable(); 536 } 537 else 538 { 539 Point aPos( aInfo1FT.GetPosPixel().X(), 540 aInfo2_1FT.GetPosPixel().Y() ); 541 aArrow.SetPosPixel( aPos ); 542 aPBAccept.Disable(); 543 } 544 545 if ( aLicenseML.IsEndReached() ) 546 aPBPageDown.Disable(); 547 else 548 aPBPageDown.Enable(); 549 550 } 551 552 553 LicenseView::LicenseView( Window* pParent, const ResId& rResId ) 554 : MultiLineEdit( pParent, rResId ) 555 { 556 SetLeftMargin( 5 ); 557 558 mbEndReached = IsEndReached(); 559 560 StartListening( *GetTextEngine() ); 561 } 562 563 LicenseView::~LicenseView() 564 { 565 maEndReachedHdl = Link(); 566 maScrolledHdl = Link(); 567 568 EndListeningAll(); 569 } 570 571 void LicenseView::ScrollDown( ScrollType eScroll ) 572 { 573 ScrollBar* pScroll = GetVScrollBar(); 574 575 if ( pScroll ) 576 pScroll->DoScrollAction( eScroll ); 577 } 578 579 sal_Bool LicenseView::IsEndReached() const 580 { 581 sal_Bool bEndReached; 582 583 ExtTextView* pView = GetTextView(); 584 ExtTextEngine* pEdit = GetTextEngine(); 585 sal_uLong nHeight = pEdit->GetTextHeight(); 586 Size aOutSize = pView->GetWindow()->GetOutputSizePixel(); 587 Point aBottom( 0, aOutSize.Height() ); 588 589 if ( (sal_uLong) pView->GetDocPos( aBottom ).Y() >= nHeight - 1 ) 590 bEndReached = sal_True; 591 else 592 bEndReached = sal_False; 593 594 return bEndReached; 595 } 596 597 void LicenseView::Notify( SfxBroadcaster&, const SfxHint& rHint ) 598 { 599 if ( rHint.IsA( TYPE(TextHint) ) ) 600 { 601 sal_Bool bLastVal = EndReached(); 602 sal_uLong nId = ((const TextHint&)rHint).GetId(); 603 604 if ( nId == TEXT_HINT_PARAINSERTED ) 605 { 606 if ( bLastVal ) 607 mbEndReached = IsEndReached(); 608 } 609 else if ( nId == TEXT_HINT_VIEWSCROLLED ) 610 { 611 if ( ! mbEndReached ) 612 mbEndReached = IsEndReached(); 613 maScrolledHdl.Call( this ); 614 } 615 616 if ( EndReached() && !bLastVal ) 617 { 618 maEndReachedHdl.Call( this ); 619 } 620 } 621 } 622 623 } // namespace framework 624