xref: /aoo42x/main/svx/inc/svx/framelinkarray.hxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #ifndef SVX_FRAMELINKARRAY_HXX
29 #define SVX_FRAMELINKARRAY_HXX
30 
31 #include <svx/framelink.hxx>
32 
33 #include <memory>
34 
35 #include <vector>
36 #include "svx/svxdllapi.h"
37 
38 namespace svx {
39 namespace frame {
40 
41 // ============================================================================
42 
43 
44 struct Cell
45 {
46     Style               maLeft;
47     Style               maRight;
48     Style               maTop;
49     Style               maBottom;
50     Style               maTLBR;
51     Style               maBLTR;
52     long                mnAddLeft;
53     long                mnAddRight;
54     long                mnAddTop;
55     long                mnAddBottom;
56     bool                mbMergeOrig;
57     bool                mbOverlapX;
58     bool                mbOverlapY;
59 
60     explicit            Cell();
61 
62     inline bool         IsMerged() const { return mbMergeOrig || mbOverlapX || mbOverlapY; }
63     inline bool         IsOverlapped() const { return mbOverlapX || mbOverlapY; }
64 
65     void                MirrorSelfX( bool bMirrorStyles, bool bSwapDiag );
66     void                MirrorSelfY( bool bMirrorStyles, bool bSwapDiag );
67 };
68 
69 typedef std::vector< long >     LongVec;
70 typedef std::vector< Cell >     CellVec;
71 
72 struct ArrayImpl
73 {
74     CellVec             maCells;
75     LongVec             maWidths;
76     LongVec             maHeights;
77     mutable LongVec     maXCoords;
78     mutable LongVec     maYCoords;
79     size_t              mnWidth;
80     size_t              mnHeight;
81     size_t              mnFirstClipCol;
82     size_t              mnFirstClipRow;
83     size_t              mnLastClipCol;
84     size_t              mnLastClipRow;
85     mutable bool        mbXCoordsDirty;
86     mutable bool        mbYCoordsDirty;
87     bool                mbDiagDblClip;
88 
89     explicit            ArrayImpl( size_t nWidth, size_t nHeight, bool bDiagDblClip );
90 
91     inline bool         IsValidPos( size_t nCol, size_t nRow ) const
92                             { return (nCol < mnWidth) && (nRow < mnHeight); }
93     inline size_t       GetIndex( size_t nCol, size_t nRow ) const
94                             { return nRow * mnWidth + nCol; }
95 
96     const Cell&         GetCell( size_t nCol, size_t nRow ) const;
97     Cell&               GetCellAcc( size_t nCol, size_t nRow );
98 
99     size_t              GetMergedFirstCol( size_t nCol, size_t nRow ) const;
100     size_t              GetMergedFirstRow( size_t nCol, size_t nRow ) const;
101     size_t              GetMergedLastCol( size_t nCol, size_t nRow ) const;
102     size_t              GetMergedLastRow( size_t nCol, size_t nRow ) const;
103 
104     const Cell&         GetMergedOriginCell( size_t nCol, size_t nRow ) const;
105     Cell&               GetMergedOriginCellAcc( size_t nCol, size_t nRow );
106 
107     bool                IsMergedOverlappedLeft( size_t nCol, size_t nRow ) const;
108     bool                IsMergedOverlappedRight( size_t nCol, size_t nRow ) const;
109     bool                IsMergedOverlappedTop( size_t nCol, size_t nRow ) const;
110     bool                IsMergedOverlappedBottom( size_t nCol, size_t nRow ) const;
111 
112     bool                IsInClipRange( size_t nCol, size_t nRow ) const;
113     bool                IsColInClipRange( size_t nCol ) const;
114     bool                IsRowInClipRange( size_t nRow ) const;
115 
116     inline size_t       GetMirrorCol( size_t nCol ) const { return mnWidth - nCol - 1; }
117     inline size_t       GetMirrorRow( size_t nRow ) const { return mnHeight - nRow - 1; }
118 
119     long                GetColPosition( size_t nCol ) const;
120     long                GetRowPosition( size_t nRow ) const;
121 
122     long                GetColWidth( size_t nFirstCol, size_t nLastCol ) const;
123     long                GetRowHeight( size_t nFirstRow, size_t nLastRow ) const;
124 
125     double              GetHorDiagAngle( size_t nCol, size_t nRow, bool bSimple = false ) const;
126     double              GetVerDiagAngle( size_t nCol, size_t nRow, bool bSimple = false ) const;
127 };
128 
129 /** Stores frame styles of an array of cells, supports merged ranges.
130 
131     This class is able to store the frame styles of an array of cells and to
132     draw the entire array or parts of it to any output device.
133 
134     Every cell contains the style of the left, right, top, bottom, top-left to
135     bottom-right, and bottom-left to top-right frame border.
136 
137     On drawing, the thicker frame border of neighbored cells is selected
138     automatically. All borders are drawn "connected", that means, the correct
139     start and end coordinates of all lines of the borders are calculated,
140     especaially, if they are drawn together with diagonal frame borders.
141 
142     This array fully supports merged cell ranges. In a merged range, the frame
143     borders of the top-left cell is extended to the entire range, and all other
144     cells in that range are overlapped. Again, all connected frame borders,
145     also diagonals and frame borders from adjacent merged ranges, are handled
146     automatically.
147 
148     Additionally, a clipping range can be set. If such a range is used, all
149     frame borders outside this range are completely ignored, and are not used
150     in the connected border calculation anymore.
151 
152     The array can be mirrored in both directions. It is possible to specify,
153     whether to mirror the double frame styles, and whether to swap diagonal
154     frame borders.
155  */
156 class SVX_DLLPUBLIC Array
157 {
158 public:
159     /** Constructs an empty array. */
160     explicit            Array();
161 
162     /** Constructs an array with the specified width and height. */
163     explicit            Array( size_t nWidth, size_t nHeight );
164 
165     /** Destructs the array. */
166                         ~Array();
167 
168     // array size and column/row indexes --------------------------------------
169 
170     /** Reinitializes the array with the specified size. Clears all styles. */
171     void                Initialize( size_t nWidth, size_t nHeight );
172 
173     /** Clears all line styles, column widths, row heights, merge data, and the clip range. */
174     void                Clear();
175 
176     /** Returns the number of columns in the array. */
177     size_t              GetColCount() const;
178 
179     /** Returns the number of rows in the array. */
180     size_t              GetRowCount() const;
181 
182     /** Returns the number of cells in the array. */
183     size_t              GetCellCount() const;
184 
185     /** Returns the column index of the specified cell index. */
186     size_t              GetColFromIndex( size_t nCellIndex ) const;
187 
188     /** Returns the row index of the specified cell index. */
189     size_t              GetRowFromIndex( size_t nCellIndex ) const;
190 
191     /** Returns the cell index from the cell address (nCol,nRow). */
192     size_t              GetCellIndex( size_t nCol, size_t nRow, bool bRTL = false) const;
193 
194     // cell border styles -----------------------------------------------------
195 
196     /** Sets the left frame style of the cell (nCol,nRow). Ignores merged ranges. */
197     void                SetCellStyleLeft( size_t nCol, size_t nRow, const Style& rStyle );
198 
199     /** Sets the right frame style of the cell (nCol,nRow). Ignores merged ranges. */
200     void                SetCellStyleRight( size_t nCol, size_t nRow, const Style& rStyle );
201 
202     /** Sets the top frame style of the cell (nCol,nRow). Ignores merged ranges. */
203     void                SetCellStyleTop( size_t nCol, size_t nRow, const Style& rStyle );
204 
205     /** Sets the bottom frame style of the specified cell (nCol,nRow). Ignores merged ranges. */
206     void                SetCellStyleBottom( size_t nCol, size_t nRow, const Style& rStyle );
207 
208     /** Sets the top-left to bottom-right frame style of the cell (nCol,nRow). Ignores merged ranges. */
209     void                SetCellStyleTLBR( size_t nCol, size_t nRow, const Style& rStyle );
210 
211     /** Sets the bottom-left to top-right frame style of the cell (nCol,nRow). Ignores merged ranges. */
212     void                SetCellStyleBLTR( size_t nCol, size_t nRow, const Style& rStyle );
213 
214     /** Sets both diagonal frame styles of the specified cell (nCol,nRow). Ignores merged ranges. */
215     void                SetCellStyleDiag( size_t nCol, size_t nRow, const Style& rTLBR, const Style& rBLTR );
216 
217     /** Sets the left frame style of the specified column. Ignores merged ranges. */
218     void                SetColumnStyleLeft( size_t nCol, const Style& rStyle );
219 
220     /** Sets the right frame style of the specified column. Ignores merged ranges. */
221     void                SetColumnStyleRight( size_t nCol, const Style& rStyle );
222 
223     /** Sets the top frame style of the specified row. Ignores merged ranges. */
224     void                SetRowStyleTop( size_t nRow, const Style& rStyle );
225 
226     /** Sets the bottom frame style of the specified row. Ignores merged ranges. */
227     void                SetRowStyleBottom( size_t nRow, const Style& rStyle );
228 
229     /** Returns the left frame style of the cell (nCol,nRow).
230         @param bSimple
231             true = Ignores merged ranges and neighbor cells;
232             false = Returns thicker of own left style or right style of the cell to the left.
233                 Returns the style only if visible (i.e. at left border of a merged range).
234         @return
235             The left frame style or an invisible style for invalid cell addresses. */
236     const Style&        GetCellStyleLeft( size_t nCol, size_t nRow, bool bSimple = false ) const;
237 
238     /** Returns the right frame style of the cell (nCol,nRow).
239         @param bSimple
240             true = Ignores merged ranges and neighbor cells;
241             false = Returns thicker of own right style or left style of the cell to the right.
242                 Returns the style only if visible (i.e. at right border of a merged range).
243         @return
244             The left frame style or an invisible style for invalid cell addresses. */
245     const Style&        GetCellStyleRight( size_t nCol, size_t nRow, bool bSimple = false ) const;
246 
247     /** Returns the top frame style of the cell (nCol,nRow).
248         @param bSimple
249             true = Ignores merged ranges and neighbor cells;
250             false = Returns thicker of own top style or bottom style of the cell above.
251                 Returns the style only if visible (i.e. at top border of a merged range).
252         @return
253             The top frame style or an invisible style for invalid cell addresses. */
254     const Style&        GetCellStyleTop( size_t nCol, size_t nRow, bool bSimple = false ) const;
255 
256     /** Returns the top frame style of the cell (nCol,nRow).
257         @param bSimple
258             true = Ignores merged ranges and neighbor cells;
259             false = Returns thicker of own top style or bottom style of the cell above.
260                 Returns the style only if visible (i.e. at top border of a merged range).
261         @return
262             The top frame style or an invisible style for invalid cell addresses. */
263     const Style&        GetCellStyleBottom( size_t nCol, size_t nRow, bool bSimple = false ) const;
264 
265     /** Returns the top-left to bottom-right frame style of the cell (nCol,nRow).
266         @param bSimple
267             true = Ignores merged ranges;
268             false = Returns the visible style (i.e. from top-left corner of a merged range).
269         @return
270             The top-left to bottom-right frame style or an invisible style for invalid cell addresses. */
271     const Style&        GetCellStyleTLBR( size_t nCol, size_t nRow, bool bSimple = false ) const;
272 
273     /** Returns the bottom-left to top-right frame style of the cell (nCol,nRow).
274         @param bSimple
275             true = Ignores merged ranges;
276             false = Returns the visible style (i.e. from top-left corner of a merged range).
277         @return
278             The bottom-left to top-right frame style or an invisible style for invalid cell addresses. */
279     const Style&        GetCellStyleBLTR( size_t nCol, size_t nRow, bool bSimple = false ) const;
280 
281     /** Returns the top-left to bottom-right frame style of the cell (nCol,nRow).
282         @return
283             The top-left to bottom-right frame style, if the cell is not part of
284             a merged range, or if (nCol,nRow) is the top-left corner of a merged
285             range (useful to find connected frame styles).
286             An invisible style for invalid cell addresses. */
287     const Style&        GetCellStyleTL( size_t nCol, size_t nRow ) const;
288 
289     /** Returns the top-left to bottom-right frame style of the cell (nCol,nRow).
290         @return
291             The top-left to bottom-right frame style, if the cell is not part of
292             a merged range, or if (nCol,nRow) is the bottom-right corner of a
293             merged range (useful to find connected frame styles).
294             An invisible style for invalid cell addresses. */
295     const Style&        GetCellStyleBR( size_t nCol, size_t nRow ) const;
296 
297     /** Returns the bottom-left to top-right frame style of the cell (nCol,nRow).
298         @return
299             The bottom-left to top-right frame style, if the cell is not part of
300             a merged range, or if (nCol,nRow) is the bottom-left corner of a
301             merged range (useful to find connected frame styles).
302             An invisible style for invalid cell addresses. */
303     const Style&        GetCellStyleBL( size_t nCol, size_t nRow ) const;
304 
305     /** Returns the bottom-left to top-right frame style of the cell (nCol,nRow).
306         @return
307             The bottom-left to top-right frame style, if the cell is not part of
308             a merged range, or if (nCol,nRow) is the top-right corner of a
309             merged range (useful to find connected frame styles).
310             An invisible style for invalid cell addresses. */
311     const Style&        GetCellStyleTR( size_t nCol, size_t nRow ) const;
312 
313     // cell merging -----------------------------------------------------------
314 
315     /** Inserts a new merged cell range.
316         @precond  The range must not intersect other merged ranges. */
317     void                SetMergedRange( size_t nFirstCol, size_t nFirstRow, size_t nLastCol, size_t nLastRow );
318 
319     /** Removes the merged cell range that contains (nCol,nRow). */
320     void                RemoveMergedRange( size_t nCol, size_t nRow );
321 
322     /** Sets an additional left width for the merged range that contains (nCol,nRow).
323         @descr  Useful to handle merged ranges that are not completely part of the array.
324         @precond  The merged range must be at the left border of the array. */
325     void                SetAddMergedLeftSize( size_t nCol, size_t nRow, long nAddSize );
326 
327     /** Sets an additional right width for the merged range that contains (nCol,nRow).
328         @descr  Useful to handle merged ranges that are not completely part of the array.
329         @precond  The merged range must be at the right border of the array. */
330     void                SetAddMergedRightSize( size_t nCol, size_t nRow, long nAddSize );
331 
332     /** Sets an additional top height for the merged range that contains (nCol,nRow).
333         @descr  Useful to handle merged ranges that are not completely part of the array.
334         @precond  The merged range must be at the top border of the array. */
335     void                SetAddMergedTopSize( size_t nCol, size_t nRow, long nAddSize );
336 
337     /** Sets an additional bottom height for the merged range that contains (nCol,nRow).
338         @descr  Useful to handle merged ranges that are not completely part of the array.
339         @precond  The merged range must be at the bottom border of the array. */
340     void                SetAddMergedBottomSize( size_t nCol, size_t nRow, long nAddSize );
341 
342     /** Returns true, if the cell (nCol,nRow) is part of a merged range. */
343     bool                IsMerged( size_t nCol, size_t nRow ) const;
344 
345     /** Returns true, if the cell (nCol,nRow) is the top-left corner of a merged range. */
346     bool                IsMergedOrigin( size_t nCol, size_t nRow ) const;
347 
348     /** Returns true, if the cell (nCol,nRow) is overlapped by a merged range. */
349     bool                IsMergedOverlapped( size_t nCol, size_t nRow ) const;
350 
351     /** Returns true, if the left border of the cell (nCol,nRow) is overlapped by a merged range. */
352     bool                IsMergedOverlappedLeft( size_t nCol, size_t nRow ) const;
353 
354     /** Returns true, if the right border of the cell (nCol,nRow) is overlapped by a merged range. */
355     bool                IsMergedOverlappedRight( size_t nCol, size_t nRow ) const;
356 
357     /** Returns true, if the top border of the cell (nCol,nRow) is overlapped by a merged range. */
358     bool                IsMergedOverlappedTop( size_t nCol, size_t nRow ) const;
359 
360     /** Returns true, if the bottom border of the cell (nCol,nRow) is overlapped by a merged range. */
361     bool                IsMergedOverlappedBottom( size_t nCol, size_t nRow ) const;
362 
363     /** Returns the address of the top-left cell of the merged range that contains (nCol,nRow). */
364     void                GetMergedOrigin( size_t& rnFirstCol, size_t& rnFirstRow, size_t nCol, size_t nRow ) const;
365 
366     /** Returns the range size of the merged range thst contains (nCol,nRow). */
367     void                GetMergedSize( size_t& rnWidth, size_t& rnHeight, size_t nCol, size_t nRow ) const;
368 
369     /** Returns the top-left and bottom-right address of the merged range that contains (nCol,nRow). */
370     void                GetMergedRange( size_t& rnFirstCol, size_t& rnFirstRow,
371                             size_t& rnLastCol, size_t& rnLastRow, size_t nCol, size_t nRow ) const;
372 
373     // clipping ---------------------------------------------------------------
374 
375     /** Sets a clipping range.
376         @descr
377             No cell borders outside of this clipping range will be drawn. In
378             difference to simply using the DrawRange() function with the same
379             range, a clipping range causes the drawing functions to completely
380             ignore the frame styles connected from outside. This is used i.e.
381             in Calc to print single pages and to draw the print preview.
382             Partly visible diagonal frame borders in merged ranges are correctly
383             clipped too. This array can handle only one clip range at a time. */
384     void                SetClipRange( size_t nFirstCol, size_t nFirstRow, size_t nLastCol, size_t nLastRow );
385 
386     /** Removes the clipping range set with the SetClipRange() function. */
387     void                RemoveClipRange();
388 
389     /** Returns true, if the cell (bCol,nRow) is inside the current clip range. */
390     bool                IsInClipRange( size_t nCol, size_t nRow ) const;
391 
392     /** Returns the rectangle (output coordinates) of the current clipping range. */
393     Rectangle           GetClipRangeRectangle() const;
394 
395     // cell coordinates -------------------------------------------------------
396 
397     /** Sets the X output coordinate of the left column. */
398     void                SetXOffset( long nXOffset );
399 
400     /** Sets the Y output coordinate of the top row. */
401     void                SetYOffset( long nYOffset );
402 
403     /** Sets the output width of the specified column. */
404     void                SetColWidth( size_t nCol, long nWidth );
405 
406     /** Sets the output height of the specified row. */
407     void                SetRowHeight( size_t nRow, long nHeight );
408 
409     /** Sets the same output width for all columns. */
410     void                SetAllColWidths( long nWidth );
411 
412     /** Sets the same output height for all rows. */
413     void                SetAllRowHeights( long nHeight );
414 
415     /** Returns the X output coordinate of the left border of the specified column.
416         @descr  The column index <array-width> returns the X output coordinate
417                 of the right array border. */
418     long                GetColPosition( size_t nCol ) const;
419 
420     /** Returns the Y output coordinate of the top border of the specified row.
421         @descr  The row index <array-height> returns the Y output coordinate
422                 of the bottom array border. */
423     long                GetRowPosition( size_t nRow ) const;
424 
425     /** Returns the output width of the specified column. */
426     long                GetColWidth( size_t nCol ) const;
427 
428     /** Returns the output width of the specified range of columns. */
429     long                GetColWidth( size_t nFirstCol, size_t nLastCol ) const;
430 
431     /** Returns the output height of the specified row. */
432     long                GetRowHeight( size_t nRow ) const;
433 
434     /** Returns the output height of the specified range of rows. */
435     long                GetRowHeight( size_t nFirstRow, size_t nLastRow ) const;
436 
437     /** Returns the output width of the entire array. */
438     long                GetWidth() const;
439 
440     /** Returns the output height of the entire array. */
441     long                GetHeight() const;
442 
443     /** Returns the top-left output position of the cell (nCol,nRow).
444         @param bSimple
445             true = Ignores merged ranges;
446             false = Returns output position of top-left corner of merged ranges. */
447     Point               GetCellPosition( size_t nCol, size_t nRow, bool bSimple = false ) const;
448 
449     /** Returns the output size of the cell (nCol,nRow).
450         @param bSimple
451             true = Ignores merged ranges;
452             false = Returns total output size of merged ranges. */
453     Size                GetCellSize( size_t nCol, size_t nRow, bool bSimple = false ) const;
454 
455     /** Returns the output rectangle of the cell (nCol,nRow).
456         @param bSimple
457             true = Ignores merged ranges;
458             false = Returns total output rectangle of merged ranges. */
459     Rectangle           GetCellRect( size_t nCol, size_t nRow, bool bSimple = false ) const;
460 
461     // diagonal frame borders -------------------------------------------------
462 
463     /** Returns the angle between horizontal and diagonal border of the cell (nCol,nRow).
464         @param bSimple
465             true = Ignores merged ranges;
466             false = Returns the horizontal angle of merged ranges. */
467     double              GetHorDiagAngle( size_t nCol, size_t nRow, bool bSimple = false ) const;
468 
469     /** Returns the angle between vertical and diagonal border of the cell (nCol,nRow).
470         @param bSimple
471             true = Ignores merged ranges;
472             false = Returns the vertical angle of merged ranges. */
473     double              GetVerDiagAngle( size_t nCol, size_t nRow, bool bSimple = false ) const;
474 
475     /** Specifies whether to use polygon clipping to draw diagonal frame borders.
476         @descr
477             If enabled, diagonal frame borders are drawn interrupted, if they are
478             crossed by a double frame border. Polygon clipping is very expensive
479             and should only be used for very small output devices (i.e. in the
480             Border tab page). Default after construction is OFF. */
481     void                SetUseDiagDoubleClipping( bool bSet );
482 
483     /** Returns true, if polygon clipping is used to draw diagonal frame borders. */
484     bool                GetUseDiagDoubleClipping() const;
485 
486     // mirroring --------------------------------------------------------------
487 
488     /** Mirrors the entire array horizontally.
489         @param bMirrorStyles
490             true = Swap primary and secondary line of all vertical double frame borders.
491         @param bSwapDiag
492             true = Swap top-left to bottom-right and bottom-left to top-right frame borders. */
493     void                MirrorSelfX( bool bMirrorStyles, bool bSwapDiag );
494 
495     /** Mirrors the entire array vertically.
496         @param bMirrorStyles
497             true = Swap primary and secondary line of all horizontal double frame borders.
498         @param bSwapDiag
499             true = Swap top-left to bottom-right and bottom-left to top-right frame borders. */
500     void                MirrorSelfY( bool bMirrorStyles, bool bSwapDiag );
501 
502     // drawing ----------------------------------------------------------------
503 
504     /** Draws the cell (nCol,nRow), if it is inside the clipping range.
505         @param pForceColor
506             If not NULL, only this color will be used to draw all frame borders. */
507     void                DrawCell( OutputDevice& rDev, size_t nCol, size_t nRow,
508                             const Color* pForceColor = 0 ) const;
509 
510     /** Draws the part of the specified range, that is inside the clipping range.
511         @param pForceColor
512             If not NULL, only this color will be used to draw all frame borders. */
513     void                DrawRange( OutputDevice& rDev,
514                             size_t nFirstCol, size_t nFirstRow,
515                             size_t nLastCol, size_t nLastRow,
516                             const Color* pForceColor = 0 ) const;
517 
518     /** Draws the part of the array, that is inside the clipping range.
519         @param pForceColor
520             If not NULL, only this color will be used to draw all frame borders. */
521     void                DrawArray( OutputDevice& rDev, const Color* pForceColor = 0 ) const;
522 
523     // ------------------------------------------------------------------------
524 
525 private:
526     typedef std::auto_ptr< ArrayImpl > ArrayImplPtr;
527 
528     ArrayImplPtr        mxImpl;
529 };
530 
531 // ============================================================================
532 
533 } // namespace frame
534 } // namespace svx
535 
536 #endif
537 
538