326 iContactListBox->ActivateL(); |
326 iContactListBox->ActivateL(); |
327 |
327 |
328 GfxTransEffect::Register( iContactListBox, |
328 GfxTransEffect::Register( iContactListBox, |
329 KGfxContactListBoxUid, EFalse ); |
329 KGfxContactListBoxUid, EFalse ); |
330 |
330 |
|
331 iCoeEnv->AddForegroundObserverL( *this ); |
|
332 |
331 // Do delayed initialization of PCS. PCS constructions takes a long time. |
333 // Do delayed initialization of PCS. PCS constructions takes a long time. |
332 // On the other hand, easy dialing initialization is done in phone application |
334 // On the other hand, easy dialing initialization is done in phone application |
333 // constructor, so it contributes the whole system boot time. These are good |
335 // constructor, so it contributes the whole system boot time. These are good |
334 // reasons not to construct PCS in easy dialing constructor, but do it later as |
336 // reasons not to construct PCS in easy dialing constructor, but do it later as |
335 // an asynchronous operation. |
337 // an asynchronous operation. |
459 // |
461 // |
460 TKeyResponse CEasyDialingPlugin::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType) |
462 TKeyResponse CEasyDialingPlugin::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType) |
461 { |
463 { |
462 TKeyResponse keyResponse = EKeyWasNotConsumed; |
464 TKeyResponse keyResponse = EKeyWasNotConsumed; |
463 |
465 |
464 if ( aKeyEvent.iCode == 0 && aKeyEvent.iScanCode != EStdKeyDevice3) |
466 if ( aType != EEventKey ) |
465 { |
467 { |
466 return keyResponse; |
468 return keyResponse; |
467 } |
469 } |
468 |
470 |
469 TInt keyCode = aKeyEvent.iCode; |
471 TInt keyCode = aKeyEvent.iCode; |
470 |
472 |
471 // Swap right and left key codes in mirrored layout. This needs to be done |
473 // Swap right and left key codes in mirrored layout. |
472 // also for action menu grid as CAknGrid handles arrow keys like this: |
|
473 // left key = next column (not item!) and right is previous column and |
|
474 // grid layout (LtR/RtL) is not taken into account when movement is done |
|
475 // in columns (always like in LtR layout). So if right key should move |
|
476 // focus to the right also in mirroded layout, key codes must be switched. |
|
477 // This kind of approach is used also e.g. in application grid. |
|
478 if ( AknLayoutUtils::LayoutMirrored() ) |
474 if ( AknLayoutUtils::LayoutMirrored() ) |
479 { |
475 { |
480 if ( keyCode == EKeyRightArrow ) keyCode = EKeyLeftArrow; |
476 if ( keyCode == EKeyRightArrow ) keyCode = EKeyLeftArrow; |
481 else if ( keyCode == EKeyLeftArrow ) keyCode = EKeyRightArrow; |
477 else if ( keyCode == EKeyLeftArrow ) keyCode = EKeyRightArrow; |
482 } |
478 } |
532 |
528 |
533 else |
529 else |
534 { |
530 { |
535 // then focus jumps off the component. |
531 // then focus jumps off the component. |
536 SetFocus( EFalse ); |
532 SetFocus( EFalse ); |
|
533 |
|
534 // FEP hasn't had chance to handle this key event as focus was not in the editor field. |
|
535 // Give that chance now by consuming original event and simulating a new one. |
|
536 // The simulation must be asynchronous because FEP focusing state is updated |
|
537 // with high priority active object. |
|
538 // As an exception, dialer simulated keys are given directly to editor. They are |
|
539 // not handled by FEP anyway, and asynchronous handling would be problematic for |
|
540 // the * key multitapping logic of Phone. |
|
541 TBool simulatedByDialer = (aKeyEvent.iModifiers&EModifierNumLock) && |
|
542 (aKeyEvent.iModifiers&EModifierKeypad); |
|
543 if ( !simulatedByDialer ) |
|
544 { |
|
545 keyResponse = EKeyWasConsumed; |
|
546 AsyncSimulateKeyEvent( aKeyEvent ); |
|
547 } |
537 } |
548 } |
538 } |
549 } |
539 |
550 |
540 else if ( iNumberOfNames > 0 ) // not in focus but there are contacts to show |
551 else if ( iNumberOfNames > 0 ) // not in focus but there are contacts to show |
541 { |
552 { |
964 RArray<TInt> fieldOrder; |
975 RArray<TInt> fieldOrder; |
965 CleanupClosePushL( fieldOrder ); |
976 CleanupClosePushL( fieldOrder ); |
966 |
977 |
967 // Current implementation searches only from default database. |
978 // Current implementation searches only from default database. |
968 // Later this may be expanded to search SIM contacts as well. |
979 // Later this may be expanded to search SIM contacts as well. |
969 HBufC* default_cdb = VPbkContactStoreUris::DefaultCntDbUri().AllocLC(); |
980 const TDesC& defaultCdb = VPbkContactStoreUris::DefaultCntDbUri(); |
970 |
981 |
971 iPredictiveContactSearchHandler->GetDataOrderL( *default_cdb, fieldOrder ); |
982 iPredictiveContactSearchHandler->GetDataOrderL( defaultCdb, fieldOrder ); |
972 |
983 TInt pcsIndex = fieldOrder.Find( aIndex ); |
973 for ( TInt i = 0; i < fieldOrder.Count(); i++) |
984 |
974 { |
|
975 if ( fieldOrder[i] == aIndex ) |
|
976 { |
|
977 CleanupStack::PopAndDestroy( default_cdb ); |
|
978 CleanupStack::PopAndDestroy( &fieldOrder ); |
|
979 return i; |
|
980 } |
|
981 } |
|
982 |
|
983 CleanupStack::PopAndDestroy( default_cdb ); |
|
984 CleanupStack::PopAndDestroy( &fieldOrder ); |
985 CleanupStack::PopAndDestroy( &fieldOrder ); |
985 return KErrNotFound; |
986 return pcsIndex; |
986 } |
987 } |
987 |
988 |
988 |
989 |
989 // ----------------------------------------------------------------------------- |
990 // ----------------------------------------------------------------------------- |
990 // HandlePsResultsUpdateL |
991 // HandlePsResultsUpdateL |
1104 // Update the view |
1105 // Update the view |
1105 // --------------- |
1106 // --------------- |
1106 iNumberOfNames = iListBoxModel->Count(); |
1107 iNumberOfNames = iListBoxModel->Count(); |
1107 if ( iNumberOfNames ) |
1108 if ( iNumberOfNames ) |
1108 { |
1109 { |
|
1110 TInt oldRectHeight = iContactListBox->Rect().Height(); |
|
1111 |
1109 iContactListBox->SetRectToNumberOfItems( iNumberOfNames ); |
1112 iContactListBox->SetRectToNumberOfItems( iNumberOfNames ); |
|
1113 |
|
1114 // If the window is resized (->position changes too) while it's visible, |
|
1115 // it has to be redrawn immediately or otherwise listbox will flash |
|
1116 // in a wrong place on the screen during execution of this method |
|
1117 // HandlePsResultsUpdateL. In the worst case it'll flash on top of |
|
1118 // dialer's numeric keypad when listbox is made smaller. |
|
1119 if ( oldRectHeight != iContactListBox->Rect().Height() ) |
|
1120 { |
|
1121 iContactListBox->DrawNow(); |
|
1122 } |
|
1123 |
1110 iContactListBox->HandleItemAdditionL(); |
1124 iContactListBox->HandleItemAdditionL(); |
|
1125 |
1111 // Scroll the list to bottom |
1126 // Scroll the list to bottom |
1112 iContactListBox->ScrollToMakeItemVisible( iNumberOfNames-1 ); |
1127 iContactListBox->ScrollToMakeItemVisible( iNumberOfNames-1 ); |
|
1128 |
1113 ShowContactListBoxWithEffect(); |
1129 ShowContactListBoxWithEffect(); |
1114 } |
1130 } |
1115 else |
1131 else |
1116 { |
1132 { |
1117 HideContactListBoxWithEffect(); |
1133 HideContactListBoxWithEffect(); |
1216 iContactLauncher->LaunchAppL( *launchParameters, this ); |
1232 iContactLauncher->LaunchAppL( *launchParameters, this ); |
1217 |
1233 |
1218 // Ownership of parameter transferred to CCA launcher => pop but do not destroy. |
1234 // Ownership of parameter transferred to CCA launcher => pop but do not destroy. |
1219 CleanupStack::Pop( launchParameters ); |
1235 CleanupStack::Pop( launchParameters ); |
1220 |
1236 |
1221 |
1237 if ( !IsVisible() ) |
1222 iContactLauncherActive = ETrue; |
1238 { |
1223 CAknAppUi* appUi = static_cast<CAknAppUi*>( iCoeEnv->AppUi() ); |
1239 // MCCAConnection::LaunchAppL uses CActiveSchedulerWait to hide asynchronous |
1224 appUi->HandleCommandL( EPhoneCmdBlockingDialogLaunched ); |
1240 // opening of CCA launcher. It is possible that during opening of CCA launcher |
|
1241 // phone has moved to in-call view (at least in case of an incoming call). |
|
1242 // In that case we need to close CCA launcher again. |
|
1243 iContactLauncher->CloseAppL(); |
|
1244 } |
|
1245 else |
|
1246 { |
|
1247 iContactLauncherActive = ETrue; |
|
1248 CAknAppUi* appUi = static_cast<CAknAppUi*>( iCoeEnv->AppUi() ); |
|
1249 appUi->HandleCommandL( EPhoneCmdBlockingDialogLaunched ); |
|
1250 } |
1225 } |
1251 } |
1226 |
1252 |
1227 |
1253 |
1228 // ----------------------------------------------------------------------------- |
1254 // ----------------------------------------------------------------------------- |
1229 // CreateListBoxContactStringL |
1255 // CreateListBoxContactStringL |
1453 { |
1479 { |
1454 // Input blocker can't block controls higher on the control stack than |
1480 // Input blocker can't block controls higher on the control stack than |
1455 // ECoeStackPriorityDialog so we will get HandleCommandL calls from |
1481 // ECoeStackPriorityDialog so we will get HandleCommandL calls from |
1456 // phoneappui (CBA) when input blocker is active (=not NULL). |
1482 // phoneappui (CBA) when input blocker is active (=not NULL). |
1457 |
1483 |
1458 if ( iInputBlocker && aCommand != EEasyDialingCallHandlingActivated ) |
1484 if ( iInputBlocker && aCommand != EEasyDialingCallHandlingActivated && |
|
1485 aCommand != EEasyDialingVkbOpened && aCommand != EEasyDialingVkbClosed ) |
1459 { |
1486 { |
1460 // Some action is already being launched since iInputBlocker exists. |
1487 // Some action is already being launched since iInputBlocker exists. |
1461 // Only call activation command requires always action from this plugin. |
1488 // Only call activation command requires always action from this plugin. |
|
1489 // Vkb status flag should be updated always too (invokes no action). |
1462 return ETrue; |
1490 return ETrue; |
1463 } |
1491 } |
1464 |
1492 |
1465 |
1493 |
1466 TBool ret(EFalse); |
1494 TBool ret(EFalse); |
1548 // ----------------------------------------------------------------------------- |
1588 // ----------------------------------------------------------------------------- |
1549 // |
1589 // |
1550 TBool CEasyDialingPlugin::IsEnabled() const |
1590 TBool CEasyDialingPlugin::IsEnabled() const |
1551 { |
1591 { |
1552 return ( iCenrepListener && iCenrepListener->Value() != 0 ); |
1592 return ( iCenrepListener && iCenrepListener->Value() != 0 ); |
|
1593 } |
|
1594 |
|
1595 // ----------------------------------------------------------------------------- |
|
1596 // AsyncSimulateKeyEvent |
|
1597 // |
|
1598 // ----------------------------------------------------------------------------- |
|
1599 // |
|
1600 void CEasyDialingPlugin::AsyncSimulateKeyEvent( const TKeyEvent& aKeyEvent ) |
|
1601 { |
|
1602 // Do the simulation only if input hasn't been blocked |
|
1603 if ( !iInputBlocker ) |
|
1604 { |
|
1605 iKeyEventToSimulate = aKeyEvent; |
|
1606 iActionToBeLaunched = ESimulateKeyEvent; |
|
1607 iAsyncCallBack->SetPriority( CActive::EPriorityStandard ); |
|
1608 iAsyncCallBack->CallBack(); // activates callback request |
|
1609 } |
1553 } |
1610 } |
1554 |
1611 |
1555 // ----------------------------------------------------------------------------- |
1612 // ----------------------------------------------------------------------------- |
1556 // AsyncActionLaunchL |
1613 // AsyncActionLaunchL |
1557 // Use asynchronous callback to launch action. While action is being launched, |
1614 // Use asynchronous callback to launch action. While action is being launched, |
1559 // coming in. Both end key and application key work despite of input blocker. |
1616 // coming in. Both end key and application key work despite of input blocker. |
1560 // Input blocker can't block phoneappui's cba so extra check is needed in |
1617 // Input blocker can't block phoneappui's cba so extra check is needed in |
1561 // HandleCommandL method. |
1618 // HandleCommandL method. |
1562 // ----------------------------------------------------------------------------- |
1619 // ----------------------------------------------------------------------------- |
1563 // |
1620 // |
1564 void CEasyDialingPlugin::AsyncActionLaunchL( const TEasyDialingAction aAction ) |
1621 void CEasyDialingPlugin::AsyncActionLaunchL( TEasyDialingAction aAction ) |
1565 { |
1622 { |
1566 iActionToBeLaunched = aAction; |
1623 iActionToBeLaunched = aAction; |
1567 |
1624 |
1568 CancelActionLaunchAndInputBlock(); |
1625 CancelActionLaunchAndInputBlock(); |
1569 |
1626 |
1616 // |
1673 // |
1617 // ----------------------------------------------------------------------------- |
1674 // ----------------------------------------------------------------------------- |
1618 // |
1675 // |
1619 void CEasyDialingPlugin::DoLaunchActionL( ) |
1676 void CEasyDialingPlugin::DoLaunchActionL( ) |
1620 { |
1677 { |
1621 // If ELaunchCurrentContact, then we launch cca launcher. |
1678 if ( iActionToBeLaunched == EInitializePcs ) |
1622 if ( iActionToBeLaunched == ELaunchCurrentContact ) |
|
1623 { |
|
1624 LaunchCurrentContactL(); |
|
1625 return; |
|
1626 } |
|
1627 else if ( iActionToBeLaunched == ELaunchSearch ) |
|
1628 { |
|
1629 LaunchSearchL(); |
|
1630 return; |
|
1631 } |
|
1632 else if ( iActionToBeLaunched == EInitializePcs ) |
|
1633 { |
1679 { |
1634 PERF_MEASURE_START |
1680 PERF_MEASURE_START |
1635 InitPredictiveContactSearchL(); |
1681 InitPredictiveContactSearchL(); |
1636 PERF_MEASURE_STOP |
1682 PERF_MEASURE_STOP |
1637 |
1683 |
|
1684 return; |
|
1685 } |
|
1686 else if ( !IsVisible() ) |
|
1687 { |
|
1688 // If ED is not visible, don't launch the action. This can happen if |
|
1689 // we get incoming call in the middle of action launching. |
|
1690 return; |
|
1691 } |
|
1692 // If ELaunchCurrentContact, then we launch cca launcher. |
|
1693 else if ( iActionToBeLaunched == ELaunchCurrentContact ) |
|
1694 { |
|
1695 LaunchCurrentContactL(); |
|
1696 return; |
|
1697 } |
|
1698 else if ( iActionToBeLaunched == ELaunchSearch ) |
|
1699 { |
|
1700 LaunchSearchL(); |
|
1701 return; |
|
1702 } |
|
1703 else if ( iActionToBeLaunched == ESimulateKeyEvent ) |
|
1704 { |
|
1705 iEikonEnv->SimulateKeyEventL( iKeyEventToSimulate, EEventKey ); |
1638 return; |
1706 return; |
1639 } |
1707 } |
1640 |
1708 |
1641 // If not for launching current contact or performing search, |
1709 // If not for launching current contact or performing search, |
1642 // the action is launching some communication method. |
1710 // the action is launching some communication method. |
1785 // ----------------------------------------------------------------------------- |
1853 // ----------------------------------------------------------------------------- |
1786 // From MAknInputBlockCancelHandler. |
1854 // From MAknInputBlockCancelHandler. |
1787 // Called when input block is cancelled. |
1855 // Called when input block is cancelled. |
1788 // ----------------------------------------------------------------------------- |
1856 // ----------------------------------------------------------------------------- |
1789 // |
1857 // |
1790 void CEasyDialingPlugin::AknInputBlockCancel() |
1858 void CEasyDialingPlugin::AknInputBlockCancel() |
1791 { |
1859 { |
1792 LOGSTRING("EasyDialingPlugin: AknInputBlockCancel"); |
1860 LOGSTRING("EasyDialingPlugin: AknInputBlockCancel"); |
1793 |
1861 |
1794 // iInputBlocker will be deleted right after this callback by CAknInputBlock |
1862 // iInputBlocker will be deleted right after this callback by CAknInputBlock |
1795 // cause we are using CAknInputBlock::SetCancelDelete method. |
1863 // cause we are using CAknInputBlock::SetCancelDelete method. |
1796 iInputBlocker = NULL; |
1864 iInputBlocker = NULL; |
1797 } |
1865 } |
|
1866 |
|
1867 // ----------------------------------------------------------------------------- |
|
1868 // HandleGainingForeground |
|
1869 // From MCoeForegroundObserver. |
|
1870 // ----------------------------------------------------------------------------- |
|
1871 // |
|
1872 void CEasyDialingPlugin::HandleGainingForeground() |
|
1873 { |
|
1874 } |
|
1875 |
|
1876 // ----------------------------------------------------------------------------- |
|
1877 // HandleLosingForeground |
|
1878 // From MCoeForegroundObserver. |
|
1879 // ----------------------------------------------------------------------------- |
|
1880 // |
|
1881 void CEasyDialingPlugin::HandleLosingForeground() |
|
1882 { |
|
1883 // Make sure contact data manager is not left in paused state when |
|
1884 // ED loses foreground while scrolling is active. |
|
1885 iContactDataManager->Pause( EFalse ); |
|
1886 |
|
1887 // Simulate an EButton1Up event for scrollbar so that it will be in correct |
|
1888 // state if e.g. some popup appears while scrollbar is dragged. |
|
1889 // No need to check if scrollbar has received button1Down event or |
|
1890 // is indeed dragged currently: no harm done if button1Up is simulated |
|
1891 // in vain. |
|
1892 CEikScrollBarFrame* scrollBarFrame = iContactListBox->ScrollBarFrame(); |
|
1893 if ( scrollBarFrame ) |
|
1894 { |
|
1895 CEikScrollBar* scrollBar = scrollBarFrame->VerticalScrollBar(); |
|
1896 if ( scrollBar && scrollBar->IsVisible() ) |
|
1897 { |
|
1898 TPointerEvent simulatedPointerEvent( TPointerEvent::EButton1Up, 0, |
|
1899 TPoint(), TPoint() ); |
|
1900 TRAP_IGNORE( scrollBar->HandlePointerEventL( simulatedPointerEvent ) ); |
|
1901 } |
|
1902 } |
|
1903 } |
1798 |
1904 |
1799 // ----------------------------------------------------------------------------- |
1905 // ----------------------------------------------------------------------------- |
1800 // CEasyDialingPlugin::DoHandleContactsChangedL |
1906 // CEasyDialingPlugin::DoHandleContactsChangedL |
1801 // ----------------------------------------------------------------------------- |
1907 // ----------------------------------------------------------------------------- |
1802 // |
1908 // |
1824 // Use appear/disappear effects when in foreground and when listbox truly |
1930 // Use appear/disappear effects when in foreground and when listbox truly |
1825 // changes visiblity. |
1931 // changes visiblity. |
1826 // ----------------------------------------------------------------------------- |
1932 // ----------------------------------------------------------------------------- |
1827 // |
1933 // |
1828 void CEasyDialingPlugin::ShowContactListBoxWithEffect() |
1934 void CEasyDialingPlugin::ShowContactListBoxWithEffect() |
1829 { |
1935 { |
1830 CAknAppUi* appUi = static_cast<CAknAppUi*>( iCoeEnv->AppUi() ); |
1936 if ( !IsVisible() ) |
1831 |
1937 { |
|
1938 // never show listbox if easydialing is not visible |
|
1939 return; |
|
1940 } |
1832 // Show effect only if listbox is about to come visible. |
1941 // Show effect only if listbox is about to come visible. |
1833 if ( !iContactListBox->IsVisible() && |
1942 else if ( !iContactListBox->IsVisible() && CanListBoxEffectBeUsed() ) |
1834 appUi && appUi->IsForeground() && |
|
1835 GfxTransEffect::IsRegistered( iContactListBox ) ) |
|
1836 { |
1943 { |
1837 GfxTransEffect::Begin( iContactListBox, KGfxContactListBoxOpenEffect ); |
1944 GfxTransEffect::Begin( iContactListBox, KGfxContactListBoxOpenEffect ); |
1838 iContactListBox->MakeVisible( ETrue ); |
1945 iContactListBox->MakeVisible( ETrue ); |
1839 GfxTransEffect::SetDemarcation( iContactListBox, iContactListBox->Rect() ); |
1946 GfxTransEffect::SetDemarcation( iContactListBox, iContactListBox->Rect() ); |
1840 GfxTransEffect::End( iContactListBox ); |
1947 GfxTransEffect::End( iContactListBox ); |
1849 // CEasyDialingPlugin::HideContactListBoxWithEffect |
1956 // CEasyDialingPlugin::HideContactListBoxWithEffect |
1850 // ----------------------------------------------------------------------------- |
1957 // ----------------------------------------------------------------------------- |
1851 // |
1958 // |
1852 void CEasyDialingPlugin::HideContactListBoxWithEffect() |
1959 void CEasyDialingPlugin::HideContactListBoxWithEffect() |
1853 { |
1960 { |
1854 CAknAppUi* appUi = static_cast<CAknAppUi*>( iCoeEnv->AppUi() ); |
|
1855 |
|
1856 // Show effect only if listbox is about to disappear from the screen. |
1961 // Show effect only if listbox is about to disappear from the screen. |
1857 if ( iContactListBox->IsVisible() && |
1962 if ( iContactListBox->IsVisible() && CanListBoxEffectBeUsed() ) |
1858 appUi && appUi->IsForeground() && |
|
1859 GfxTransEffect::IsRegistered( iContactListBox ) ) |
|
1860 { |
1963 { |
1861 GfxTransEffect::Begin( iContactListBox, KGfxContactListBoxCloseEffect ); |
1964 GfxTransEffect::Begin( iContactListBox, KGfxContactListBoxCloseEffect ); |
1862 iContactListBox->MakeVisible( EFalse ); |
1965 iContactListBox->MakeVisible( EFalse ); |
1863 GfxTransEffect::SetDemarcation( iContactListBox, iContactListBox->Rect() ); |
1966 GfxTransEffect::SetDemarcation( iContactListBox, iContactListBox->Rect() ); |
1864 GfxTransEffect::End( iContactListBox ); |
1967 GfxTransEffect::End( iContactListBox ); |
1865 } |
1968 } |
1866 else |
1969 else |
1867 { |
1970 { |
1868 iContactListBox->MakeVisible( EFalse ); |
1971 iContactListBox->MakeVisible( EFalse ); |
1869 } |
1972 } |
|
1973 } |
|
1974 |
|
1975 // ----------------------------------------------------------------------------- |
|
1976 // CEasyDialingPlugin::CanListBoxEffectBeUsed |
|
1977 // ----------------------------------------------------------------------------- |
|
1978 // |
|
1979 TBool CEasyDialingPlugin::CanListBoxEffectBeUsed() const |
|
1980 { |
|
1981 TBool canBeUsed( EFalse ); |
|
1982 |
|
1983 CAknAppUi* appUi = static_cast<CAknAppUi*>( iCoeEnv->AppUi() ); |
|
1984 |
|
1985 // Note that when vkb is open, phone still keeps foreground and focus so |
|
1986 // vkb status must be checked separately (vkb's window group has just higher |
|
1987 // priority than phone's window group). |
|
1988 if ( appUi && appUi->IsForeground() && |
|
1989 !iVirtualKeyboardOpen && |
|
1990 GfxTransEffect::IsEnabled() && |
|
1991 GfxTransEffect::IsRegistered( iContactListBox ) ) |
|
1992 { |
|
1993 canBeUsed = ETrue; |
|
1994 } |
|
1995 |
|
1996 return canBeUsed; |
1870 } |
1997 } |
1871 |
1998 |
1872 /* |
1999 /* |
1873 * ============================================================================== |
2000 * ============================================================================== |
1874 * |
2001 * |