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