xref: /trunk/main/toolkit/source/layout/core/box.cxx (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 #include "box.hxx"
29 
30 #include <tools/debug.hxx>
31 #include <sal/macros.h>
32 
33 // fixed point precision for distributing error
34 #define FIXED_PT 16
35 
36 namespace layoutimpl
37 {
38 
39 using namespace css;
40 
41 Box::ChildProps::ChildProps( Box::ChildData *pData )
42 {
43     addProp( RTL_CONSTASCII_USTRINGPARAM( "Expand" ),
44              ::getCppuType( static_cast< const sal_Bool* >( NULL ) ),
45              &(pData->mbExpand) );
46     addProp( RTL_CONSTASCII_USTRINGPARAM( "Fill" ),
47              ::getCppuType( static_cast< const sal_Bool* >( NULL ) ),
48              &(pData->mbFill) );
49     addProp( RTL_CONSTASCII_USTRINGPARAM( "Padding" ),
50              ::getCppuType( static_cast< const sal_Int32* >( NULL ) ),
51              &(pData->mnPadding) );
52 }
53 
54 Box::ChildData::ChildData( uno::Reference< awt::XLayoutConstrains > const& xChild )
55     : Box_Base::ChildData( xChild )
56     , mnPadding( 0 )
57     , mbExpand( true )
58     , mbFill( true )
59 {
60 }
61 
62 Box::ChildData*
63 Box::createChild( uno::Reference< awt::XLayoutConstrains > const& xChild )
64     {
65     return new ChildData( xChild );
66     }
67 
68 Box::ChildProps*
69 Box::createChildProps( Box_Base::ChildData *pData )
70 {
71     return new ChildProps( static_cast<Box::ChildData*> ( pData ) );
72 }
73 
74 Box::Box( bool horizontal )
75     : Box_Base()
76     , mnSpacing( 0 )
77     , mbHomogeneous( false )
78     , mbHorizontal( horizontal )
79 {
80     addProp( RTL_CONSTASCII_USTRINGPARAM( "Homogeneous" ),
81              ::getCppuType( static_cast< const sal_Bool* >( NULL ) ),
82              &mbHomogeneous );
83     addProp( RTL_CONSTASCII_USTRINGPARAM( "Spacing" ),
84              ::getCppuType( static_cast< const sal_Int32* >( NULL ) ),
85              &mnSpacing );
86     mbHasFlowChildren = false;
87 }
88 
89 awt::Size
90 Box::calculateSize( long nWidth )
91 {
92     int nVisibleChildren = 0;
93     // primary vs secundary axis (instead of a X and Y)
94     int nPrimSize = 0;
95     int nSecSize = 0;
96     int nFlowMinWidth = 0;  // in case the box only has flow children
97 
98     mbHasFlowChildren = false;
99 
100     for ( std::list<Box_Base::ChildData *>::const_iterator it
101               = maChildren.begin(); it != maChildren.end(); it++ )
102     {
103         ChildData *child = static_cast<Box::ChildData*> ( *it );
104         if ( !child->isVisible() )
105             continue;
106 
107         uno::Reference< awt::XLayoutContainer > xChildCont( child->mxChild, uno::UNO_QUERY );
108         bool bFlow = xChildCont.is() && xChildCont->hasHeightForWidth();
109 
110         awt::Size aChildSize = child->maRequisition = child->mxChild->getMinimumSize();
111 
112         if ( !mbHorizontal /*vertical*/ && bFlow )
113         {
114             if ( nFlowMinWidth == 0 || nFlowMinWidth > aChildSize.Width )
115                 nFlowMinWidth = aChildSize.Width;
116             mbHasFlowChildren = true;
117         }
118         else
119         {
120             int size = primDim( aChildSize ) + child->mnPadding * 2;
121             if ( mbHomogeneous )
122                 nPrimSize = SAL_MAX( nPrimSize, size );
123             else
124                 nPrimSize += size;
125 
126             nSecSize = SAL_MAX( nSecSize, secDim( aChildSize ) );
127         }
128         nVisibleChildren++;
129     }
130 
131     if ( nVisibleChildren )
132     {
133         if ( mbHomogeneous )
134             nPrimSize *= nVisibleChildren;
135         nPrimSize += (nVisibleChildren - 1) * mnSpacing;
136     }
137 
138     if ( mbHasFlowChildren )
139     {
140         if ( nWidth == 0 )
141             nWidth = nSecSize ? nSecSize : nFlowMinWidth;
142         for ( std::list<Box_Base::ChildData *>::const_iterator it
143                   = maChildren.begin(); it != maChildren.end(); it++ )
144         {
145             ChildData *child = static_cast<Box::ChildData*> ( *it );
146             if ( !child->isVisible() )
147                 continue;
148 
149             uno::Reference< awt::XLayoutContainer > xChildCont( child->mxChild, uno::UNO_QUERY );
150             bool bFlow = xChildCont.is() && xChildCont->hasHeightForWidth();
151 
152             if ( bFlow )
153                 nPrimSize += xChildCont->getHeightForWidth( nWidth );
154         }
155     }
156 
157     nPrimSize += mnBorderWidth * 2;
158     nSecSize += mnBorderWidth * 2;
159     return awt::Size( mbHorizontal ? nPrimSize : nSecSize,
160                       mbHorizontal ? nSecSize : nPrimSize );
161 }
162 
163 awt::Size SAL_CALL
164 Box::getMinimumSize() throw(uno::RuntimeException)
165 {
166     maRequisition = calculateSize();
167     return maRequisition;
168 }
169 
170 sal_Bool SAL_CALL
171 Box::hasHeightForWidth()
172     throw(uno::RuntimeException)
173 {
174     return mbHasFlowChildren;
175 }
176 
177 sal_Int32 SAL_CALL
178 Box::getHeightForWidth( sal_Int32 nWidth )
179     throw(uno::RuntimeException)
180 {
181     if ( hasHeightForWidth() )
182         return calculateSize( nWidth ).Height;
183     return maRequisition.Height;
184 }
185 
186 void SAL_CALL
187 Box::allocateArea( const awt::Rectangle &newArea )
188     throw (uno::RuntimeException)
189 {
190     maAllocation = newArea;
191     int nVisibleChildren = 0, nExpandChildren = 0;
192 
193     for ( std::list<Box_Base::ChildData *>::const_iterator it
194               = maChildren.begin(); it != maChildren.end(); it++ )
195     {
196         ChildData *child = static_cast<Box::ChildData*> ( *it );
197         if ( child->isVisible() )
198         {
199             nVisibleChildren++;
200             if ( child->mbExpand )
201                 nExpandChildren++;
202         }
203     }
204     if ( !nVisibleChildren )
205         return;
206 
207     // split rectangle for dimension helpers
208     awt::Point newPoint( newArea.X, newArea.Y );
209     awt::Size newSize( newArea.Width, newArea.Height );
210 
211     int nExtraSpace;
212     if ( mbHomogeneous )
213         nExtraSpace = ( ( primDim( newSize ) - mnBorderWidth * 2 -
214                           ( nVisibleChildren - 1 ) * mnSpacing )) / nVisibleChildren;
215     else if ( nExpandChildren )
216     {
217         int reqSize = primDim( maRequisition );
218         if ( !mbHorizontal && hasHeightForWidth() )
219             reqSize = getHeightForWidth( newArea.Width );
220         nExtraSpace = ( primDim( newSize ) - reqSize ) / nExpandChildren;
221     }
222     else
223         nExtraSpace = 0;
224 
225     int nChildPrimPoint, nChildSecPoint, nChildPrimSize, nChildSecSize;
226 
227     int nStartPoint = primDim( newPoint ) + mnBorderWidth;
228     int nBoxSecSize = SAL_MAX( 1, secDim( newSize ) - mnBorderWidth * 2 );
229 
230     for ( std::list<Box_Base::ChildData *>::const_iterator it
231               = maChildren.begin(); it != maChildren.end(); it++ )
232     {
233         ChildData *child = static_cast<Box::ChildData*> ( *it );
234         if ( !child->isVisible() )
235             continue;
236 
237         awt::Point aChildPos;
238         int nBoxPrimSize;  // of the available box space
239 
240         if ( mbHomogeneous )
241             nBoxPrimSize = nExtraSpace;
242         else
243         {
244             uno::Reference< awt::XLayoutContainer > xChildCont( child->mxChild, uno::UNO_QUERY );
245             bool bFlow = xChildCont.is() && xChildCont->hasHeightForWidth();
246             if ( !mbHorizontal && bFlow )
247                 nBoxPrimSize = xChildCont->getHeightForWidth( newArea.Width );
248             else
249                 nBoxPrimSize = primDim( child->maRequisition );
250             nBoxPrimSize += child->mnPadding;
251             if ( child->mbExpand )
252                 nBoxPrimSize += nExtraSpace;
253         }
254 
255         nChildPrimPoint = nStartPoint + child->mnPadding;
256         nChildSecPoint = secDim( newPoint ) + mnBorderWidth;
257 
258         nChildSecSize = nBoxSecSize;
259         if ( child->mbFill )
260             nChildPrimSize = SAL_MAX( 1, nBoxPrimSize - child->mnPadding);
261         else
262         {
263             nChildPrimSize = primDim( child->maRequisition );
264             nChildPrimPoint += (nBoxPrimSize - nChildPrimSize) / 2;
265 
266             nChildSecPoint += (nBoxSecSize - nChildSecSize) / 2;
267         }
268 
269         awt::Rectangle area;
270         area.X = mbHorizontal ? nChildPrimPoint : nChildSecPoint;
271         area.Y = mbHorizontal ? nChildSecPoint : nChildPrimPoint;
272         area.Width = mbHorizontal ? nChildPrimSize : nChildSecSize;
273         area.Height = mbHorizontal ? nChildSecSize : nChildPrimSize;
274 
275         allocateChildAt( child->mxChild, area );
276 
277         nStartPoint += nBoxPrimSize + mnSpacing + child->mnPadding;
278     }
279 }
280 
281 } // namespace layoutimpl
282