xref: /AOO41X/main/svtools/source/inc/svimpbox.hxx (revision 01aa44aa134af97080e2cf8e8bf3a0a4cd1cffe0)
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 #ifndef _SVIMPLBOX_HXX
25 #define _SVIMPLBOX_HXX
26 
27 #ifndef _SELENG_HXX
28 #include <vcl/seleng.hxx>
29 #endif
30 #ifndef _SCRBAR_HXX
31 #include <vcl/scrbar.hxx>
32 #endif
33 #include <vcl/vclevent.hxx>
34 // #102891# ----------------
35 #include <unotools/intlwrapper.hxx>
36 // #97680# -----------------
37 #include <vector>
38 #include "svtaccessiblefactory.hxx"
39 
40 class SvTreeListBox;
41 class Point;
42 class DropEvent;
43 class SvLBoxTreeList;
44 class SvImpLBox;
45 class SvLBoxEntry;
46 class SvLBoxItem;
47 class SvLBoxTab;
48 class TabBar;
49 
50 class ImpLBSelEng : public FunctionSet
51 {
52     SvImpLBox*          pImp;
53     SelectionEngine*    pSelEng;
54     SvTreeListBox*      pView;
55 
56 public:
57     ImpLBSelEng( SvImpLBox* pImp, SelectionEngine* pSelEng,
58                  SvTreeListBox* pView );
59     virtual ~ImpLBSelEng();
60     void        BeginDrag();
61     void        CreateAnchor();
62     void        DestroyAnchor();
63     sal_Bool        SetCursorAtPoint( const Point& rPoint,
64                     sal_Bool bDontSelectAtCursor=sal_False );
65     sal_Bool        IsSelectionAtPoint( const Point& rPoint );
66     void        DeselectAtPoint( const Point& rPoint );
67     void        DeselectAll();
68 };
69 
70 // Flags fuer nFlag
71 #define F_VER_SBARSIZE_WITH_HBAR        0x0001
72 #define F_HOR_SBARSIZE_WITH_VBAR        0x0002
73 #define F_IGNORE_NEXT_MOUSEMOVE         0x0004  // OS/2 only
74 #define F_IN_SCROLLING                  0x0008
75 #define F_DESEL_ALL                     0x0010
76 #define F_START_EDITTIMER               0x0020  // MAC only
77 #define F_IGNORE_SELECT                 0x0040
78 #define F_IN_RESIZE                     0x0080
79 #define F_REMOVED_ENTRY_INVISIBLE       0x0100
80 #define F_REMOVED_RECALC_MOST_RIGHT     0x0200
81 #define F_IGNORE_CHANGED_TABS           0x0400
82 #define F_PAINTED                       0x0800
83 #define F_IN_PAINT                      0x1000
84 #define F_ENDSCROLL_SET_VIS_SIZE        0x2000
85 #define F_FILLING                       0x4000
86 
87 
88 class SvImpLBox
89 {
90 friend class ImpLBSelEng;
91 friend class SvTreeListBox;
92 private:
93     SvTreeListBox*      pView;
94     SvLBoxTreeList*     pTree;
95     SvLBoxEntry*        pCursor;
96     SvLBoxEntry*        pStartEntry;
97     SvLBoxEntry*        pAnchor;
98     SvLBoxEntry*        pMostRightEntry;
99     SvLBoxButton*       pActiveButton;
100     SvLBoxEntry*        pActiveEntry;
101     SvLBoxTab*          pActiveTab;
102     TabBar*             pTabBar;
103 
104     ScrollBar           aVerSBar;
105     ScrollBar           aHorSBar;
106     ScrollBarBox        aScrBarBox;
107 
108     ::svt::AccessibleFactoryAccess
109                         m_aFactoryAccess;
110 
111     static Image*       s_pDefCollapsed;
112     static Image*       s_pDefExpanded;
113     static Image*       s_pDefCollapsedHC;
114     static Image*       s_pDefExpandedHC;
115     static oslInterlockedCount  s_nImageRefCount; /// When 0 all static images will be destroyed
116 
117     // Node Bitmaps
118     enum ImageType
119     {
120         itNodeExpanded = 0,     // node is expanded ( usually a bitmap showing a minus )
121         itNodeCollapsed,        // node is collapsed ( usually a bitmap showing a plus )
122         itNodeDontKnow,         // don't know the node state
123         itEntryDefExpanded,     // default for expanded entries
124         itEntryDefCollapsed,    // default for collapsed entries
125 
126         IT_IMAGE_COUNT
127     };
128 
129     // all our images
130     Image               m_aNodeAndEntryImages[ IT_IMAGE_COUNT ];
131     // plus the high contrast versions
132     Image               m_aNodeAndEntryImages_HC[ IT_IMAGE_COUNT ];
133 
134     // wg. kompat. hier
135     Size                aOutputSize;
136     SelectionEngine     aSelEng;
137     ImpLBSelEng         aFctSet;
138     Timer               aAsyncBeginDragTimer;
139     Point               aAsyncBeginDragPos;
140 
141     long                nYoffsNodeBmp;
142     long                nNodeBmpTabDistance; // typisch kleiner 0
143     long                nNodeBmpWidth;
144     long                nNextVerVisSize;
145     long                nMostRight;
146     sal_uLong               nVisibleCount;  // Anzahl Zeilen im Control
147     sal_uLong               nCurUserEvent; //-1 == kein Userevent amn Laufen
148     short               nHorSBarHeight, nVerSBarWidth;
149     sal_uInt16              nFlags;
150     sal_uInt16              nCurTabPos;
151 
152     WinBits             m_nStyle;
153     ExtendedWinBits     nExtendedWinBits;
154     sal_Bool                bSimpleTravel : 1; // ist sal_True bei SINGLE_SELECTION
155     sal_Bool                bUpdateMode : 1;
156     sal_Bool                bInVScrollHdl : 1;
157     sal_Bool                bAsyncBeginDrag : 1;
158     sal_Bool                bSubLstOpRet : 1;   // open/close sublist with return/enter, defaulted with sal_False
159     sal_Bool                bSubLstOpLR : 1;    // open/close sublist with cursor left/right, defaulted with sal_False
160     sal_Bool                bContextMenuHandling : 1;
161     sal_Bool                bIsCellFocusEnabled : 1;
162 
163     sal_Bool            bAreChildrenTransient;
164 
165     Point               aEditClickPos;
166     Timer               aEditTimer;
167 
168     // #102891# -------------------
169     IntlWrapper *       pIntlWrapper;
170 
171     // #97680# --------------------
172     std::vector< short > aContextBmpWidthVector;
173 
174     DECL_LINK( EditTimerCall, Timer * );
175 
176     DECL_LINK( BeginDragHdl, void* );
177     DECL_LINK( MyUserEvent,void*);
178     void                StopUserEvent();
179 
180     void                InvalidateEntriesFrom( long nY ) const;
181     void                InvalidateEntry( long nY ) const;
182     void                ShowVerSBar();
183     // setzt Thumb auf FirstEntryToDraw
184     void                SyncVerThumb();
185     sal_Bool                IsLineVisible( long nY ) const;
186     long                GetEntryLine( SvLBoxEntry* pEntry ) const;
187     void                FillView();
188     void                CursorDown();
189     void                CursorUp();
190     void                KeyLeftRight( long nDiff );
191     void                PageDown( sal_uInt16 nDelta );
192     void                PageUp( sal_uInt16 nDelta );
193 
194     void                SetCursor( SvLBoxEntry* pEntry, sal_Bool bForceNoSelect = sal_False );
195 
196     void                DrawNet();
197 
198     // ScrollBar-Handler
199     DECL_LINK( ScrollUpDownHdl, ScrollBar * );
200     DECL_LINK( ScrollLeftRightHdl, ScrollBar * );
201     DECL_LINK( EndScrollHdl, ScrollBar * );
202 
203     void                SetNodeBmpYOffset( const Image& );
204     void                SetNodeBmpTabDistance();
205 
206     // Selection-Engine
207     SvLBoxEntry*        MakePointVisible( const Point& rPoint,
208                             sal_Bool bNotifyScroll=sal_True );
209 
210     void                SetAnchorSelection( SvLBoxEntry* pOld,
211                             SvLBoxEntry* pNewCursor );
212     void                BeginDrag();
213     sal_Bool                ButtonDownCheckCtrl( const MouseEvent& rMEvt,
214                             SvLBoxEntry* pEntry, long nY    );
215     sal_Bool                MouseMoveCheckCtrl( const MouseEvent& rMEvt,
216                             SvLBoxEntry* pEntry );
217     sal_Bool                ButtonUpCheckCtrl( const MouseEvent& rMEvt );
218     sal_Bool                ButtonDownCheckExpand( const MouseEvent&,
219                             SvLBoxEntry*,long nY );
220 
221     void                PositionScrollBars( Size& rOSize, sal_uInt16 nMask );
222     sal_uInt16              AdjustScrollBars( Size& rSize );
223 
224     void                BeginScroll();
225     void                EndScroll();
InScroll() const226     sal_Bool                InScroll() const { return (sal_Bool)(nFlags & F_IN_SCROLLING)!=0;}
227     Rectangle           GetVisibleArea() const;
228     sal_Bool                EntryReallyHit(SvLBoxEntry* pEntry,const Point& rPos,long nLine);
229     void                InitScrollBarBox();
230     SvLBoxTab*          NextTab( SvLBoxTab* );
231 
232     sal_Bool                SetMostRight( SvLBoxEntry* pEntry );
233     void                FindMostRight( SvLBoxEntry* EntryToIgnore );
234     void                FindMostRight( SvLBoxEntry* pParent, SvLBoxEntry* EntryToIgnore );
235     void                FindMostRight_Impl( SvLBoxEntry* pParent,SvLBoxEntry* EntryToIgnore  );
236     void                NotifyTabsChanged();
237 
IsExpandable() const238     inline sal_Bool         IsExpandable() const        // if element at cursor can be expanded in general
239                             { return pCursor->HasChilds() || pCursor->HasChildsOnDemand(); }
IsNowExpandable() const240     inline sal_Bool         IsNowExpandable() const     // if element at cursor can be expanded at this moment
241                             { return IsExpandable() && !pView->IsExpanded( pCursor ); }
242 
243     static  void        implInitDefaultNodeImages();
244 
245     // #102891# -------------------
246     void                UpdateIntlWrapper();
247 
248     // #97680# --------------------
249     short               UpdateContextBmpWidthVector( SvLBoxEntry* pEntry, short nWidth );
250     void                UpdateContextBmpWidthMax( SvLBoxEntry* pEntry );
251     void                UpdateContextBmpWidthVectorFromMovedEntry( SvLBoxEntry* pEntry );
252 
253     void                CalcCellFocusRect( SvLBoxEntry* pEntry, Rectangle& rRect );
254 
AreChildrenTransient() const255     inline sal_Bool     AreChildrenTransient() const { return bAreChildrenTransient; }
SetChildrenNotTransient()256     inline void         SetChildrenNotTransient() { bAreChildrenTransient = sal_False; }
257 
258 public:
259     SvImpLBox( SvTreeListBox* pView, SvLBoxTreeList*, WinBits nWinStyle );
260     ~SvImpLBox();
261 
262     void                Clear();
263     void                SetStyle( WinBits i_nWinStyle );
264     void                SetExtendedWindowBits( ExtendedWinBits _nBits );
GetExtendedWindowBits() const265     ExtendedWinBits     GetExtendedWindowBits() const { return nExtendedWinBits; }
SetModel(SvLBoxTreeList * pModel)266     void                SetModel( SvLBoxTreeList* pModel ) { pTree = pModel;}
267 
268     void                EntryInserted( SvLBoxEntry*);
269     void                RemovingEntry( SvLBoxEntry* pEntry );
270     void                EntryRemoved();
271     void                MovingEntry( SvLBoxEntry* pEntry );
272     void                EntryMoved( SvLBoxEntry* pEntry );
273     void                TreeInserted( SvLBoxEntry* pEntry );
274 
275     void                IndentChanged( short nIndentPixel );
276     void                EntryExpanded( SvLBoxEntry* pEntry );
277     void                EntryCollapsed( SvLBoxEntry* pEntry );
278     void                CollapsingEntry( SvLBoxEntry* pEntry );
279     void                EntrySelected( SvLBoxEntry*, sal_Bool bSelect );
280 
281     void                Paint( const Rectangle& rRect );
282     void                RepaintSelectionItems();
283     void                MouseButtonDown( const MouseEvent& );
284     void                MouseButtonUp( const MouseEvent& );
285     void                MouseMove( const MouseEvent&);
286     sal_Bool                KeyInput( const KeyEvent& );
287     void                Resize();
288     void                GetFocus();
289     void                LoseFocus();
290     void                UpdateAll(
291                             sal_Bool bInvalidateCompleteView= sal_True,
292                             sal_Bool bUpdateVerSBar = sal_True );
293     void                SetEntryHeight( short nHeight );
294     void                PaintEntry( SvLBoxEntry* pEntry );
295     void                InvalidateEntry( SvLBoxEntry* );
296     void                RecalcFocusRect();
297 
298     inline void         SelectEntry( SvLBoxEntry* pEntry, sal_Bool bSelect );
299     void                SetDragDropMode( DragDropMode eDDMode );
300     void                SetSelectionMode( SelectionMode eSelMode  );
SetAddMode(sal_Bool)301     void                SetAddMode( sal_Bool ) { aSelEng.AddAlways(sal_False); }
IsAddMode() const302     sal_Bool                IsAddMode() const { return aSelEng.IsAlwaysAdding(); }
303 
GetCurrentEntry() const304     SvLBoxEntry*        GetCurrentEntry() const { return pCursor; }
305     sal_Bool                IsEntryInView( SvLBoxEntry* ) const;
306     SvLBoxEntry*        GetEntry( const Point& rPos ) const;
307     // gibt letzten Eintrag zurueck, falls Pos unter letztem Eintrag
308     SvLBoxEntry*        GetClickedEntry( const Point& ) const;
GetCurEntry() const309     SvLBoxEntry*        GetCurEntry() const { return pCursor; }
310     void                SetCurEntry( SvLBoxEntry* );
311     Point               GetEntryPosition( SvLBoxEntry* ) const;
312     void                MakeVisible( SvLBoxEntry* pEntry, sal_Bool bMoveToTop=sal_False );
313 
314     void                PaintDDCursor( SvLBoxEntry* );
315 
316     // Images
317     inline Image&       implGetImageLocation( const ImageType _eType, BmpColorMode _eMode );
318     inline Image&       implGetImageLocationWithFallback( const ImageType _eType, BmpColorMode _eMode ) const;
319 
320     inline void         SetExpandedNodeBmp( const Image& _rImg, BmpColorMode _eMode = BMP_COLOR_NORMAL );
321     inline void         SetCollapsedNodeBmp( const Image& _rImg, BmpColorMode _eMode = BMP_COLOR_NORMAL  );
322     inline void         SetDontKnowNodeBmp( const Image& rImg, BmpColorMode _eMode = BMP_COLOR_NORMAL );
323 
324     inline const Image& GetExpandedNodeBmp( BmpColorMode _eMode = BMP_COLOR_NORMAL ) const;
325     inline const Image& GetCollapsedNodeBmp( BmpColorMode _eMode = BMP_COLOR_NORMAL ) const;
326     inline const Image& GetDontKnowNodeBmp( BmpColorMode _eMode = BMP_COLOR_NORMAL ) const;
327 
328     inline void         SetDefaultEntryExpBmp( const Image& _rImg, BmpColorMode _eMode = BMP_COLOR_NORMAL );
329     inline void         SetDefaultEntryColBmp( const Image& _rImg, BmpColorMode _eMode = BMP_COLOR_NORMAL );
330     inline const Image& GetDefaultEntryExpBmp( BmpColorMode _eMode = BMP_COLOR_NORMAL );
331     inline const Image& GetDefaultEntryColBmp( BmpColorMode _eMode = BMP_COLOR_NORMAL );
332 
333     static const Image& GetDefaultExpandedNodeImage( BmpColorMode _eMode = BMP_COLOR_NORMAL );
334     static const Image& GetDefaultCollapsedNodeImage( BmpColorMode _eMode = BMP_COLOR_NORMAL );
335 
GetOutputSize() const336     const Size&         GetOutputSize() const { return aOutputSize;}
337     void                KeyUp( sal_Bool bPageUp, sal_Bool bNotifyScroll = sal_True );
338     void                KeyDown( sal_Bool bPageDown, sal_Bool bNotifyScroll = sal_True );
339     void                Command( const CommandEvent& rCEvt );
340 
341     void                Invalidate();
DestroyAnchor()342     void                DestroyAnchor() { pAnchor=0; aSelEng.Reset(); }
343     void                SelAllDestrAnch( sal_Bool bSelect,
344                             sal_Bool bDestroyAnchor = sal_True,
345                             sal_Bool bSingleSelToo = sal_False );
346     void                ShowCursor( sal_Bool bShow );
347 
348     sal_Bool                RequestHelp( const HelpEvent& rHEvt );
349     void                EndSelection();
350     sal_Bool                IsNodeButton( const Point& rPosPixel, SvLBoxEntry* pEntry ) const;
351     void                RepaintScrollBars();
EnableAsyncDrag(sal_Bool b)352     void                EnableAsyncDrag( sal_Bool b) { bAsyncBeginDrag = b; }
353     void                SetUpdateMode( sal_Bool );
354     void                SetUpdateModeFast( sal_Bool );
GetUpdateMode() const355     sal_Bool                GetUpdateMode() const { return bUpdateMode; }
356     Rectangle           GetClipRegionRect() const;
HasHorScrollBar() const357     sal_Bool                HasHorScrollBar() const { return aHorSBar.IsVisible(); }
358     void                ShowFocusRect( const SvLBoxEntry* pEntry );
359     void                SetTabBar( TabBar* pTabBar );
360     void                CancelPendingEdit();
361 
362     void                CallEventListeners( sal_uLong nEvent, void* pData = NULL );
363 
364     /** Enables, that one cell of a tablistbox entry can be focused */
IsCellFocusEnabled() const365     inline sal_Bool         IsCellFocusEnabled() const { return bIsCellFocusEnabled; }
EnableCellFocus()366     inline void         EnableCellFocus() { bIsCellFocusEnabled = sal_True; }
367     bool                SetCurrentTabPos( sal_uInt16 _nNewPos );
GetCurrentTabPos() const368     inline sal_uInt16       GetCurrentTabPos() const { return nCurTabPos; }
369 
370     bool                IsSelectable( const SvLBoxEntry* pEntry );
371 };
372 
implGetImageLocation(const ImageType _eType,BmpColorMode _eMode)373 inline Image& SvImpLBox::implGetImageLocation( const ImageType _eType, BmpColorMode _eMode )
374 {
375     DBG_ASSERT( ( BMP_COLOR_HIGHCONTRAST == _eMode ) || ( BMP_COLOR_NORMAL == _eMode ),
376         "SvImpLBox::implGetImageLocation: invalid mode!" );
377     DBG_ASSERT( ( _eType >= 0 ) && ( _eType < IT_IMAGE_COUNT ),
378         "SvImpLBox::implGetImageLocation: invalid image index (will crash)!" );
379 
380     Image* _pSet = ( BMP_COLOR_HIGHCONTRAST == _eMode ) ? m_aNodeAndEntryImages_HC : m_aNodeAndEntryImages;
381     return *( _pSet + (sal_Int32)_eType );
382 }
383 
implGetImageLocationWithFallback(const ImageType _eType,BmpColorMode _eMode) const384 inline Image& SvImpLBox::implGetImageLocationWithFallback( const ImageType _eType, BmpColorMode _eMode ) const
385 {
386     Image& rImage = const_cast< SvImpLBox* >( this )->implGetImageLocation( _eType, _eMode );
387     if ( !rImage )
388         // fallback to normal images in case the one for the special mode has not been set
389         rImage = const_cast< SvImpLBox* >( this )->implGetImageLocation( _eType, BMP_COLOR_NORMAL );
390     return rImage;
391 }
392 
SetDontKnowNodeBmp(const Image & rImg,BmpColorMode _eMode)393 inline void SvImpLBox::SetDontKnowNodeBmp( const Image& rImg, BmpColorMode _eMode )
394 {
395     implGetImageLocation( itNodeDontKnow, _eMode ) = rImg;
396 }
397 
SetExpandedNodeBmp(const Image & rImg,BmpColorMode _eMode)398 inline void SvImpLBox::SetExpandedNodeBmp( const Image& rImg, BmpColorMode _eMode )
399 {
400     implGetImageLocation( itNodeExpanded, _eMode ) = rImg;
401     SetNodeBmpYOffset( rImg );
402 }
403 
SetCollapsedNodeBmp(const Image & rImg,BmpColorMode _eMode)404 inline void SvImpLBox::SetCollapsedNodeBmp( const Image& rImg, BmpColorMode _eMode )
405 {
406     implGetImageLocation( itNodeCollapsed, _eMode ) = rImg;
407     SetNodeBmpYOffset( rImg );
408 }
409 
GetDontKnowNodeBmp(BmpColorMode _eMode) const410 inline const Image& SvImpLBox::GetDontKnowNodeBmp( BmpColorMode _eMode ) const
411 {
412     return implGetImageLocationWithFallback( itNodeDontKnow, _eMode );
413 }
414 
GetExpandedNodeBmp(BmpColorMode _eMode) const415 inline const Image& SvImpLBox::GetExpandedNodeBmp( BmpColorMode _eMode ) const
416 {
417     return implGetImageLocationWithFallback( itNodeExpanded, _eMode );
418 }
419 
GetCollapsedNodeBmp(BmpColorMode _eMode) const420 inline const Image& SvImpLBox::GetCollapsedNodeBmp( BmpColorMode _eMode ) const
421 {
422     return implGetImageLocationWithFallback( itNodeCollapsed, _eMode );
423 }
424 
SetDefaultEntryExpBmp(const Image & _rImg,BmpColorMode _eMode)425 inline void SvImpLBox::SetDefaultEntryExpBmp( const Image& _rImg, BmpColorMode _eMode )
426 {
427     implGetImageLocation( itEntryDefExpanded, _eMode ) = _rImg;
428 }
429 
SetDefaultEntryColBmp(const Image & _rImg,BmpColorMode _eMode)430 inline void SvImpLBox::SetDefaultEntryColBmp( const Image& _rImg, BmpColorMode _eMode )
431 {
432     implGetImageLocation( itEntryDefCollapsed, _eMode ) = _rImg;
433 }
434 
GetDefaultEntryExpBmp(BmpColorMode _eMode)435 inline const Image& SvImpLBox::GetDefaultEntryExpBmp( BmpColorMode _eMode )
436 {
437     return implGetImageLocationWithFallback( itEntryDefExpanded, _eMode );
438 }
439 
GetDefaultEntryColBmp(BmpColorMode _eMode)440 inline const Image& SvImpLBox::GetDefaultEntryColBmp( BmpColorMode _eMode )
441 {
442     return implGetImageLocationWithFallback( itEntryDefCollapsed, _eMode );
443 }
444 
GetEntryPosition(SvLBoxEntry * pEntry) const445 inline Point SvImpLBox::GetEntryPosition( SvLBoxEntry* pEntry ) const
446 {
447     return Point( 0, GetEntryLine( pEntry ) );
448 }
449 
PaintEntry(SvLBoxEntry * pEntry)450 inline void SvImpLBox::PaintEntry( SvLBoxEntry* pEntry )
451 {
452     long nY = GetEntryLine( pEntry );
453     pView->PaintEntry( pEntry, nY );
454 }
455 
IsLineVisible(long nY) const456 inline sal_Bool SvImpLBox::IsLineVisible( long nY ) const
457 {
458     sal_Bool bRet = sal_True;
459     if ( nY < 0 || nY >= aOutputSize.Height() )
460         bRet = sal_False;
461     return bRet;
462 }
463 
TreeInserted(SvLBoxEntry * pInsTree)464 inline void SvImpLBox::TreeInserted( SvLBoxEntry* pInsTree )
465 {
466     EntryInserted( pInsTree );
467 }
468 
469 #endif // #ifndef _SVIMPLBOX_HXX
470 
471