xref: /trunk/main/vcl/source/window/arrange.cxx (revision 9f62ea84)
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 #include "precompiled_vcl.hxx"
25 
26 #include "svdata.hxx"
27 
28 #include "vcl/arrange.hxx"
29 #include "vcl/edit.hxx"
30 #include "vcl/svapp.hxx"
31 
32 #include "com/sun/star/beans/PropertyValue.hpp"
33 #include "com/sun/star/awt/Rectangle.hpp"
34 
35 #include "osl/diagnose.h"
36 
37 using namespace vcl;
38 using namespace com::sun::star;
39 
40 // ----------------------------------------
41 // vcl::WindowArranger
42 //-----------------------------------------
43 
getDefaultBorder()44 long WindowArranger::getDefaultBorder()
45 {
46     ImplSVData* pSVData = ImplGetSVData();
47     long nResult = pSVData->maAppData.mnDefaultLayoutBorder;
48     if( nResult < 0 )
49     {
50         OutputDevice* pDefDev = Application::GetDefaultDevice();
51         if( pDefDev )
52         {
53             Size aBorder( pDefDev->LogicToPixel( Size( 3, 3 ), MapMode( MAP_APPFONT ) ) );
54             nResult = pSVData->maAppData.mnDefaultLayoutBorder = aBorder.Height();
55         }
56     }
57     return nResult > 0 ? nResult : 0;
58 }
59 
~WindowArranger()60 WindowArranger::~WindowArranger()
61 {}
62 
setParent(WindowArranger * i_pParent)63 void WindowArranger::setParent( WindowArranger* i_pParent )
64 {
65     OSL_VERIFY( i_pParent->m_pParentWindow == m_pParentWindow || m_pParentWindow == NULL );
66 
67     m_pParentArranger = i_pParent;
68     m_pParentWindow = i_pParent->m_pParentWindow;
69     setParentWindow( m_pParentWindow );
70 }
71 
setParentWindow(Window * i_pNewParent)72 void WindowArranger::setParentWindow( Window* i_pNewParent )
73 {
74     m_pParentWindow = i_pNewParent;
75 
76     size_t nEle = countElements();
77     for( size_t i = 0; i < nEle; i++ )
78     {
79         Element* pEle = getElement( i );
80         if( pEle ) // sanity check
81         {
82             #if OSL_DEBUG_LEVEL > 0
83             if( pEle->m_pElement )
84             {
85                 OSL_VERIFY( pEle->m_pElement->GetParent() == i_pNewParent );
86             }
87             #endif
88             if( pEle->m_pChild )
89                 pEle->m_pChild->setParentWindow( i_pNewParent );
90         }
91     }
92 }
93 
show(bool i_bShow,bool i_bImmediateUpdate)94 void WindowArranger::show( bool i_bShow, bool i_bImmediateUpdate )
95 {
96     size_t nEle = countElements();
97     for( size_t i = 0; i < nEle; i++ )
98     {
99         Element* pEle = getElement( i );
100         if( pEle ) // sanity check
101         {
102             pEle->m_bHidden = ! i_bShow;
103             if( pEle->m_pElement )
104                 pEle->m_pElement->Show( i_bShow );
105             if( pEle->m_pChild.get() )
106                 pEle->m_pChild->show( i_bShow, false );
107         }
108     }
109     if( m_pParentArranger )
110     {
111         nEle = m_pParentArranger->countElements();
112         for( size_t i = 0; i < nEle; i++ )
113         {
114             Element* pEle = m_pParentArranger->getElement( i );
115             if( pEle && pEle->m_pChild.get() == this )
116             {
117                 pEle->m_bHidden = ! i_bShow;
118                 break;
119             }
120         }
121     }
122     if( i_bImmediateUpdate )
123     {
124         // find the topmost parent
125         WindowArranger* pResize = this;
126         while( pResize->m_pParentArranger )
127             pResize = pResize->m_pParentArranger;
128         pResize->resize();
129     }
130 }
131 
isVisible() const132 bool WindowArranger::isVisible() const
133 {
134     size_t nEle = countElements();
135     for( size_t i = 0; i < nEle; i++ )
136     {
137         const Element* pEle = getConstElement( i );
138         if( pEle->isVisible() )
139             return true;
140     }
141     return false;
142 }
143 
isVisible() const144 bool WindowArranger::Element::isVisible() const
145 {
146     bool bVisible = false;
147     if( ! m_bHidden )
148     {
149         if( m_pElement )
150             bVisible = m_pElement->IsVisible();
151         else if( m_pChild )
152             bVisible = m_pChild->isVisible();
153     }
154     return bVisible;
155 }
156 
getExpandPriority() const157 sal_Int32 WindowArranger::Element::getExpandPriority() const
158 {
159     sal_Int32 nPrio = m_nExpandPriority;
160     if( m_pChild && m_nExpandPriority >= 0 )
161     {
162         size_t nElements = m_pChild->countElements();
163         for( size_t i = 0; i < nElements; i++ )
164         {
165             sal_Int32 nCPrio = m_pChild->getExpandPriority( i );
166             if( nCPrio > nPrio )
167                 nPrio = nCPrio;
168         }
169     }
170     return nPrio;
171 }
172 
getOptimalSize(WindowSizeType i_eType) const173 Size WindowArranger::Element::getOptimalSize( WindowSizeType i_eType ) const
174 {
175     Size aResult;
176     if( ! m_bHidden )
177     {
178         bool bVisible = false;
179         if( m_pElement && m_pElement->IsVisible() )
180         {
181             aResult = m_pElement->GetOptimalSize( i_eType );
182             bVisible = true;
183         }
184         else if( m_pChild && m_pChild->isVisible() )
185         {
186             aResult = m_pChild->getOptimalSize( i_eType );
187             bVisible = true;
188         }
189         if( bVisible )
190         {
191             if( aResult.Width() < m_aMinSize.Width() )
192                 aResult.Width() = m_aMinSize.Width();
193             if( aResult.Height() < m_aMinSize.Height() )
194                 aResult.Height() = m_aMinSize.Height();
195             aResult.Width() += getBorderValue( m_nLeftBorder ) + getBorderValue( m_nRightBorder );
196             aResult.Height() += getBorderValue( m_nTopBorder ) + getBorderValue( m_nBottomBorder );
197         }
198     }
199 
200     return aResult;
201 }
202 
setPosSize(const Point & i_rPos,const Size & i_rSize)203 void WindowArranger::Element::setPosSize( const Point& i_rPos, const Size& i_rSize )
204 {
205     Point aPoint( i_rPos );
206     Size aSize( i_rSize );
207     aPoint.X() += getBorderValue( m_nLeftBorder );
208     aPoint.Y() += getBorderValue( m_nTopBorder );
209     aSize.Width() -= getBorderValue( m_nLeftBorder ) + getBorderValue( m_nRightBorder );
210     aSize.Height() -= getBorderValue( m_nTopBorder ) + getBorderValue( m_nBottomBorder );
211     if( m_pElement )
212         m_pElement->SetPosSizePixel( aPoint, aSize );
213     else if( m_pChild )
214         m_pChild->setManagedArea( Rectangle( aPoint, aSize ) );
215 }
216 
getProperties() const217 uno::Sequence< beans::PropertyValue > WindowArranger::getProperties() const
218 {
219     uno::Sequence< beans::PropertyValue > aRet( 3 );
220     aRet[0].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "OuterBorder" ) );
221     aRet[0].Value = uno::makeAny( sal_Int32( getBorderValue( m_nOuterBorder ) ) );
222     aRet[1].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ManagedArea" ) );
223     awt::Rectangle aArea( m_aManagedArea.getX(), m_aManagedArea.getY(), m_aManagedArea.getWidth(), m_aManagedArea.getHeight() );
224     aRet[1].Value = uno::makeAny( aArea );
225     aRet[2].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Visible" ) );
226     aRet[2].Value = uno::makeAny( sal_Bool( isVisible() ) );
227     return aRet;
228 }
229 
setProperties(const uno::Sequence<beans::PropertyValue> & i_rProps)230 void WindowArranger::setProperties( const uno::Sequence< beans::PropertyValue >& i_rProps )
231 {
232     const beans::PropertyValue* pProps = i_rProps.getConstArray();
233     bool bResize = false;
234     for( sal_Int32 i = 0; i < i_rProps.getLength(); i++ )
235     {
236         if( pProps[i].Name.equalsAscii( "OuterBorder" ) )
237         {
238             sal_Int32 nVal = 0;
239             if( pProps[i].Value >>= nVal )
240             {
241                 if( getBorderValue( m_nOuterBorder ) != nVal )
242                 {
243                     m_nOuterBorder = nVal;
244                     bResize = true;
245                 }
246             }
247         }
248         else if( pProps[i].Name.equalsAscii( "ManagedArea" ) )
249         {
250             awt::Rectangle aArea( 0, 0, 0, 0 );
251             if( pProps[i].Value >>= aArea )
252             {
253                 m_aManagedArea.setX( aArea.X );
254                 m_aManagedArea.setY( aArea.Y );
255                 m_aManagedArea.setWidth( aArea.Width );
256                 m_aManagedArea.setHeight( aArea.Height );
257                 bResize = true;
258             }
259         }
260         else if( pProps[i].Name.equalsAscii( "Visible" ) )
261         {
262             sal_Bool bVal = sal_False;
263             if( pProps[i].Value >>= bVal )
264             {
265                 show( bVal, false );
266                 bResize = true;
267             }
268         }
269     }
270     if( bResize )
271         resize();
272 }
273 
274 
275 // ----------------------------------------
276 // vcl::RowOrColumn
277 //-----------------------------------------
278 
~RowOrColumn()279 RowOrColumn::~RowOrColumn()
280 {
281     for( std::vector< WindowArranger::Element >::iterator it = m_aElements.begin();
282          it != m_aElements.end(); ++it )
283     {
284         it->deleteChild();
285     }
286 }
287 
getOptimalSize(WindowSizeType i_eType) const288 Size RowOrColumn::getOptimalSize( WindowSizeType i_eType ) const
289 {
290     Size aRet( 0, 0 );
291     long nDistance = getBorderValue( m_nBorderWidth );
292     for( std::vector< WindowArranger::Element >::const_iterator it = m_aElements.begin();
293          it != m_aElements.end(); ++it )
294     {
295         if( it->isVisible() )
296         {
297             // get the size of type of the managed element
298             Size aElementSize( it->getOptimalSize( i_eType ) );
299             if( m_bColumn )
300             {
301                 // add the distance between elements
302                 aRet.Height() += nDistance;
303                 // check if the width needs adjustment
304                 if( aRet.Width() < aElementSize.Width() )
305                     aRet.Width() = aElementSize.Width();
306                 aRet.Height() += aElementSize.Height();
307             }
308             else
309             {
310                 // add the distance between elements
311                 aRet.Width() += nDistance;
312                 // check if the height needs adjustment
313                 if( aRet.Height() < aElementSize.Height() )
314                     aRet.Height() = aElementSize.Height();
315                 aRet.Width() += aElementSize.Width();
316             }
317         }
318     }
319 
320     if( aRet.Width() != 0 || aRet.Height() != 0 )
321     {
322         // subtract the border for the first element
323         if( m_bColumn )
324             aRet.Height() -= nDistance;
325         else
326             aRet.Width() -= nDistance;
327 
328         // add the outer border
329         long nOuterBorder = getBorderValue( m_nOuterBorder );
330         aRet.Width() += 2*nOuterBorder;
331         aRet.Height() += 2*nOuterBorder;
332     }
333 
334     return aRet;
335 }
336 
distributeRowWidth(std::vector<Size> & io_rSizes,long,long i_nExtraWidth)337 void RowOrColumn::distributeRowWidth( std::vector<Size>& io_rSizes, long /*i_nUsedWidth*/, long i_nExtraWidth )
338 {
339     if( ! io_rSizes.empty() && io_rSizes.size() == m_aElements.size() )
340     {
341         // find all elements with the highest expand priority
342         size_t nElements = m_aElements.size();
343         std::vector< size_t > aIndices;
344         sal_Int32 nHighPrio = 0;
345         for( size_t i = 0; i < nElements; i++ )
346         {
347             if( m_aElements[ i ].isVisible() )
348             {
349                 sal_Int32 nCurPrio = m_aElements[ i ].getExpandPriority();
350                 if( nCurPrio > nHighPrio )
351                 {
352                     aIndices.clear();
353                     nHighPrio = nCurPrio;
354                 }
355                 if( nCurPrio == nHighPrio )
356                     aIndices.push_back( i );
357             }
358         }
359 
360         // distribute extra space evenly among collected elements
361         nElements = aIndices.size();
362         if( nElements > 0 )
363         {
364             long nDelta = i_nExtraWidth / nElements;
365             for( size_t i = 0; i < nElements; i++ )
366             {
367                 io_rSizes[ aIndices[i] ].Width() += nDelta;
368                 i_nExtraWidth -= nDelta;
369             }
370             // add the last pixels to the last row element
371             if( i_nExtraWidth > 0 && nElements > 0 )
372                 io_rSizes[aIndices.back()].Width() += i_nExtraWidth;
373         }
374     }
375 }
376 
distributeColumnHeight(std::vector<Size> & io_rSizes,long,long i_nExtraHeight)377 void RowOrColumn::distributeColumnHeight( std::vector<Size>& io_rSizes, long /*i_nUsedHeight*/, long i_nExtraHeight )
378 {
379     if( ! io_rSizes.empty() && io_rSizes.size() == m_aElements.size() )
380     {
381         // find all elements with the highest expand priority
382         size_t nElements = m_aElements.size();
383         std::vector< size_t > aIndices;
384         sal_Int32 nHighPrio = 3;
385         for( size_t i = 0; i < nElements; i++ )
386         {
387             if( m_aElements[ i ].isVisible() )
388             {
389                 sal_Int32 nCurPrio = m_aElements[ i ].getExpandPriority();
390                 if( nCurPrio > nHighPrio )
391                 {
392                     aIndices.clear();
393                     nHighPrio = nCurPrio;
394                 }
395                 if( nCurPrio == nHighPrio )
396                     aIndices.push_back( i );
397             }
398         }
399 
400         // distribute extra space evenly among collected elements
401         nElements = aIndices.size();
402         if( nElements > 0 )
403         {
404             long nDelta = i_nExtraHeight / nElements;
405             for( size_t i = 0; i < nElements; i++ )
406             {
407                 io_rSizes[ aIndices[i] ].Height() += nDelta;
408                 i_nExtraHeight -= nDelta;
409             }
410             // add the last pixels to the last row element
411             if( i_nExtraHeight > 0 && nElements > 0 )
412                 io_rSizes[aIndices.back()].Height() += i_nExtraHeight;
413         }
414     }
415 }
416 
resize()417 void RowOrColumn::resize()
418 {
419     // check if we can get optimal size, else fallback to minimal size
420     Size aOptSize( getOptimalSize( WINDOWSIZE_PREFERRED ) );
421     WindowSizeType eType = WINDOWSIZE_PREFERRED;
422     if( m_bColumn )
423     {
424         if( aOptSize.Height() > m_aManagedArea.GetHeight() )
425             eType = WINDOWSIZE_MINIMUM;
426     }
427     else
428     {
429         if( aOptSize.Width() > m_aManagedArea.GetWidth() )
430             eType = WINDOWSIZE_MINIMUM;
431     }
432 
433     size_t nElements = m_aElements.size();
434     // get all element sizes for sizing
435     std::vector<Size> aElementSizes( nElements );
436     long nDistance = getBorderValue( m_nBorderWidth );
437     long nOuterBorder = getBorderValue( m_nOuterBorder );
438     long nUsedWidth = 2*nOuterBorder - (nElements ? nDistance : 0);
439     for( size_t i = 0; i < nElements; i++ )
440     {
441         if( m_aElements[i].isVisible() )
442         {
443             aElementSizes[i] = m_aElements[i].getOptimalSize( eType );
444             if( m_bColumn )
445             {
446                 aElementSizes[i].Width() = m_aManagedArea.GetWidth() - 2 * nOuterBorder;
447                 nUsedWidth += aElementSizes[i].Height() + nDistance;
448             }
449             else
450             {
451                 aElementSizes[i].Height() = m_aManagedArea.GetHeight() - 2 * nOuterBorder;
452                 nUsedWidth += aElementSizes[i].Width() + nDistance;
453             }
454         }
455     }
456 
457     long nExtraWidth = (m_bColumn ? m_aManagedArea.GetHeight() : m_aManagedArea.GetWidth()) - nUsedWidth;
458     if( nExtraWidth > 0 )
459     {
460         if( m_bColumn )
461             distributeColumnHeight( aElementSizes, nUsedWidth, nExtraWidth );
462         else
463             distributeRowWidth( aElementSizes, nUsedWidth, nExtraWidth );
464     }
465 
466     // get starting position
467     Point aElementPos( m_aManagedArea.TopLeft() );
468     // outer border
469     aElementPos.X() += nOuterBorder;
470     aElementPos.Y() += nOuterBorder;
471 
472     // position managed windows
473     for( size_t i = 0; i < nElements; i++ )
474     {
475         // get the size of type of the managed element
476         if( m_aElements[i].isVisible() )
477         {
478             m_aElements[i].setPosSize( aElementPos, aElementSizes[i] );
479             if( m_bColumn )
480                 aElementPos.Y() += nDistance + aElementSizes[i].Height();
481             else
482                 aElementPos.X() += nDistance + aElementSizes[i].Width();
483         }
484     }
485 }
486 
addWindow(Window * i_pWindow,sal_Int32 i_nExpandPrio,const Size & i_rMinSize,size_t i_nIndex)487 size_t RowOrColumn::addWindow( Window* i_pWindow, sal_Int32 i_nExpandPrio, const Size& i_rMinSize, size_t i_nIndex )
488 {
489     size_t nIndex = i_nIndex;
490     if( i_nIndex >= m_aElements.size() )
491     {
492         nIndex = m_aElements.size();
493         m_aElements.push_back( WindowArranger::Element( i_pWindow, boost::shared_ptr<WindowArranger>(), i_nExpandPrio, i_rMinSize ) );
494     }
495     else
496     {
497         std::vector< WindowArranger::Element >::iterator it = m_aElements.begin();
498         while( i_nIndex-- )
499             ++it;
500         m_aElements.insert( it, WindowArranger::Element( i_pWindow, boost::shared_ptr<WindowArranger>(), i_nExpandPrio, i_rMinSize ) );
501     }
502     return nIndex;
503 }
504 
addChild(boost::shared_ptr<WindowArranger> const & i_pChild,sal_Int32 i_nExpandPrio,size_t i_nIndex)505 size_t RowOrColumn::addChild( boost::shared_ptr<WindowArranger> const & i_pChild, sal_Int32 i_nExpandPrio, size_t i_nIndex )
506 {
507     size_t nIndex = i_nIndex;
508     if( i_nIndex >= m_aElements.size() )
509     {
510         nIndex = m_aElements.size();
511         m_aElements.push_back( WindowArranger::Element( NULL, i_pChild, i_nExpandPrio ) );
512     }
513     else
514     {
515         std::vector< WindowArranger::Element >::iterator it = m_aElements.begin();
516         while( i_nIndex-- )
517             ++it;
518         m_aElements.insert( it, WindowArranger::Element( NULL, i_pChild, i_nExpandPrio ) );
519     }
520     return nIndex;
521 }
522 
remove(Window * i_pWindow)523 void RowOrColumn::remove( Window* i_pWindow )
524 {
525     if( i_pWindow )
526     {
527         for( std::vector< WindowArranger::Element >::iterator it = m_aElements.begin();
528             it != m_aElements.end(); ++it )
529         {
530             if( it->m_pElement == i_pWindow )
531             {
532                 m_aElements.erase( it );
533                 return;
534             }
535         }
536     }
537 }
538 
remove(boost::shared_ptr<WindowArranger> const & i_pChild)539 void RowOrColumn::remove( boost::shared_ptr<WindowArranger> const & i_pChild )
540 {
541     if( i_pChild )
542     {
543         for( std::vector< WindowArranger::Element >::iterator it = m_aElements.begin();
544             it != m_aElements.end(); ++it )
545         {
546             if( it->m_pChild == i_pChild )
547             {
548                 m_aElements.erase( it );
549                 return;
550             }
551         }
552     }
553 }
554 
555 // ----------------------------------------
556 // vcl::LabeledElement
557 //-----------------------------------------
558 
~LabeledElement()559 LabeledElement::~LabeledElement()
560 {
561     m_aLabel.deleteChild();
562     m_aElement.deleteChild();
563 }
564 
getOptimalSize(WindowSizeType i_eType) const565 Size LabeledElement::getOptimalSize( WindowSizeType i_eType ) const
566 {
567     Size aRet( m_aLabel.getOptimalSize( WINDOWSIZE_MINIMUM ) );
568     if( aRet.Width() != 0 )
569     {
570         if( m_nLabelColumnWidth != 0 )
571             aRet.Width() = m_nLabelColumnWidth;
572         else
573             aRet.Width() += getBorderValue( m_nDistance );
574     }
575     Size aElementSize( m_aElement.getOptimalSize( i_eType ) );
576     aRet.Width() += aElementSize.Width();
577     if( aElementSize.Height() > aRet.Height() )
578         aRet.Height() = aElementSize.Height();
579     if( aRet.Height() != 0 )
580         aRet.Height() += 2 * getBorderValue( m_nOuterBorder );
581 
582     return aRet;
583 }
584 
resize()585 void LabeledElement::resize()
586 {
587     Size aLabelSize( m_aLabel.getOptimalSize( WINDOWSIZE_MINIMUM ) );
588     Size aElementSize( m_aElement.getOptimalSize( WINDOWSIZE_PREFERRED ) );
589     long nDistance = getBorderValue( m_nDistance );
590     long nOuterBorder = getBorderValue( m_nOuterBorder );
591     if( nDistance + aLabelSize.Width() + aElementSize.Width() > m_aManagedArea.GetWidth() )
592         aElementSize = m_aElement.getOptimalSize( WINDOWSIZE_MINIMUM );
593 
594     // align label and element vertically in LabeledElement
595     long nYOff = (m_aManagedArea.GetHeight() - 2*nOuterBorder - aLabelSize.Height()) / 2;
596     Point aPos( m_aManagedArea.Left(),
597                 m_aManagedArea.Top() + nOuterBorder + nYOff );
598     Size aSize( aLabelSize );
599     if( m_nLabelColumnWidth != 0 )
600         aSize.Width() = m_nLabelColumnWidth;
601     m_aLabel.setPosSize( aPos, aSize );
602 
603     aPos.X() += aSize.Width() + nDistance;
604     nYOff = (m_aManagedArea.GetHeight() - 2*nOuterBorder - aElementSize.Height()) / 2;
605     aPos.Y() = m_aManagedArea.Top() + nOuterBorder + nYOff;
606     aSize.Width() = aElementSize.Width();
607     aSize.Height() = m_aManagedArea.GetHeight() - 2*nOuterBorder;
608 
609     // label style
610     // 0: position left and right
611     // 1: keep the element close to label and grow it
612     // 2: keep the element close and don't grow it
613     if( m_nLabelStyle == 0)
614     {
615         if( aPos.X() + aSize.Width() < m_aManagedArea.Right() )
616             aPos.X() = m_aManagedArea.Right() - aSize.Width();
617     }
618     else if( m_nLabelStyle == 1 )
619     {
620         if( aPos.X() + aSize.Width() < m_aManagedArea.Right() )
621             aSize.Width() = m_aManagedArea.Right() - aPos.X();
622     }
623     m_aElement.setPosSize( aPos, aSize );
624 }
625 
setLabel(Window * i_pLabel)626 void LabeledElement::setLabel( Window* i_pLabel )
627 {
628     m_aLabel.m_pElement = i_pLabel;
629     m_aLabel.m_pChild.reset();
630 }
631 
setLabel(boost::shared_ptr<WindowArranger> const & i_pLabel)632 void LabeledElement::setLabel( boost::shared_ptr<WindowArranger> const & i_pLabel )
633 {
634     m_aLabel.m_pElement = NULL;
635     m_aLabel.m_pChild = i_pLabel;
636 }
637 
setElement(Window * i_pElement)638 void LabeledElement::setElement( Window* i_pElement )
639 {
640     m_aElement.m_pElement = i_pElement;
641     m_aElement.m_pChild.reset();
642 }
643 
setElement(boost::shared_ptr<WindowArranger> const & i_pElement)644 void LabeledElement::setElement( boost::shared_ptr<WindowArranger> const & i_pElement )
645 {
646     m_aElement.m_pElement = NULL;
647     m_aElement.m_pChild = i_pElement;
648 }
649 
650 // ----------------------------------------
651 // vcl::LabelColumn
652 //-----------------------------------------
~LabelColumn()653 LabelColumn::~LabelColumn()
654 {
655 }
656 
getLabelWidth() const657 long LabelColumn::getLabelWidth() const
658 {
659     long nWidth = 0;
660 
661     size_t nEle = countElements();
662     for( size_t i = 0; i < nEle; i++ )
663     {
664         const Element* pEle = getConstElement( i );
665         if( pEle && pEle->m_pChild.get() )
666         {
667             const LabeledElement* pLabel = dynamic_cast< const LabeledElement* >(pEle->m_pChild.get());
668             if( pLabel )
669             {
670                 Window* pLW = pLabel->getWindow( 0 );
671                 if( pLW )
672                 {
673                     Size aLabSize( pLW->GetOptimalSize( WINDOWSIZE_MINIMUM ) );
674                     long nLB = 0;
675                     pLabel->getBorders(0, &nLB);
676                     aLabSize.Width() += getBorderValue( nLB );
677                     if( aLabSize.Width() > nWidth )
678                         nWidth = aLabSize.Width();
679                 }
680             }
681         }
682     }
683     return nWidth + getBorderValue( getBorderWidth() );
684 }
685 
getOptimalSize(WindowSizeType i_eType) const686 Size LabelColumn::getOptimalSize( WindowSizeType i_eType ) const
687 {
688     long nWidth = getLabelWidth();
689     long nOuterBorder = getBorderValue( m_nOuterBorder );
690     Size aColumnSize;
691 
692     // every child is a LabeledElement
693     size_t nEle = countElements();
694     for( size_t i = 0; i < nEle; i++ )
695     {
696         Size aElementSize;
697         const Element* pEle = getConstElement( i );
698         if( pEle && pEle->m_pChild.get() )
699         {
700             const LabeledElement* pLabel = dynamic_cast< const LabeledElement* >(pEle->m_pChild.get());
701             if( pLabel ) // we have a label
702             {
703                 aElementSize = pLabel->getLabelSize( WINDOWSIZE_MINIMUM );
704                 if( aElementSize.Width() )
705                     aElementSize.Width() = nWidth;
706                 Size aSize( pLabel->getElementSize( i_eType ) );
707                 aElementSize.Width() += aSize.Width();
708                 if( aSize.Height() > aElementSize.Height() )
709                     aElementSize.Height() = aSize.Height();
710             }
711             else // a non label, just treat it as a row
712             {
713                 aElementSize = pEle->getOptimalSize( i_eType );
714             }
715         }
716         else if( pEle && pEle->m_pElement ) // a general window, treat is as a row
717         {
718             aElementSize = pEle->getOptimalSize( i_eType );
719         }
720         if( aElementSize.Width() )
721         {
722             aElementSize.Width() += 2*nOuterBorder;
723             if( aElementSize.Width() > aColumnSize.Width() )
724                 aColumnSize.Width() = aElementSize.Width();
725         }
726         if( aElementSize.Height() )
727         {
728             aColumnSize.Height() += getBorderValue( getBorderWidth() ) + aElementSize.Height();
729         }
730     }
731     if( nEle > 0 && aColumnSize.Height() )
732     {
733         aColumnSize.Height() -= getBorderValue( getBorderWidth() ); // for the first element
734         aColumnSize.Height() += 2*nOuterBorder;
735     }
736     return aColumnSize;
737 }
738 
resize()739 void LabelColumn::resize()
740 {
741     long nWidth = getLabelWidth();
742     size_t nEle = countElements();
743     for( size_t i = 0; i < nEle; i++ )
744     {
745         Element* pEle = getElement( i );
746         if( pEle && pEle->m_pChild.get() )
747         {
748             LabeledElement* pLabel = dynamic_cast< LabeledElement* >(pEle->m_pChild.get());
749             if( pLabel )
750                 pLabel->setLabelColumnWidth( nWidth );
751         }
752     }
753     RowOrColumn::resize();
754 }
755 
addRow(Window * i_pLabel,boost::shared_ptr<WindowArranger> const & i_rElement,long i_nIndent)756 size_t LabelColumn::addRow( Window* i_pLabel, boost::shared_ptr<WindowArranger> const& i_rElement, long i_nIndent )
757 {
758     boost::shared_ptr< LabeledElement > xLabel( new LabeledElement( this, 1 ) );
759     xLabel->setLabel( i_pLabel );
760     xLabel->setBorders( 0, i_nIndent, 0, 0, 0 );
761     xLabel->setElement( i_rElement );
762     size_t nIndex = addChild( xLabel );
763     resize();
764     return nIndex;
765 }
766 
addRow(Window * i_pLabel,Window * i_pElement,long i_nIndent,const Size & i_rElementMinSize)767 size_t LabelColumn::addRow( Window* i_pLabel, Window* i_pElement, long i_nIndent, const Size& i_rElementMinSize )
768 {
769     boost::shared_ptr< LabeledElement > xLabel( new LabeledElement( this, 1 ) );
770     xLabel->setLabel( i_pLabel );
771     xLabel->setBorders( 0, i_nIndent, 0, 0, 0 );
772     xLabel->setElement( i_pElement );
773     xLabel->setMinimumSize( 1, i_rElementMinSize );
774     size_t nIndex = addChild( xLabel );
775     resize();
776     return nIndex;
777 }
778 
779 // ----------------------------------------
780 // vcl::Indenter
781 //-----------------------------------------
782 
~Indenter()783 Indenter::~Indenter()
784 {
785     m_aElement.deleteChild();
786 }
787 
getOptimalSize(WindowSizeType i_eType) const788 Size Indenter::getOptimalSize( WindowSizeType i_eType ) const
789 {
790     Size aSize( m_aElement.getOptimalSize( i_eType ) );
791     long nOuterBorder = getBorderValue( m_nOuterBorder );
792     long nIndent = getBorderValue( m_nIndent );
793     aSize.Width()  += 2*nOuterBorder + nIndent;
794     aSize.Height() += 2*nOuterBorder;
795     return aSize;
796 }
797 
resize()798 void Indenter::resize()
799 {
800     long nOuterBorder = getBorderValue( m_nOuterBorder );
801     long nIndent = getBorderValue( m_nIndent );
802     Point aPt( m_aManagedArea.TopLeft() );
803     aPt.X() += nOuterBorder + nIndent;
804     aPt.Y() += nOuterBorder;
805     Size aSz( m_aManagedArea.GetSize() );
806     aSz.Width()  -= 2*nOuterBorder + nIndent;
807     aSz.Height() -= 2*nOuterBorder;
808     m_aElement.setPosSize( aPt, aSz );
809 }
810 
setWindow(Window * i_pWindow,sal_Int32 i_nExpandPrio)811 void Indenter::setWindow( Window* i_pWindow, sal_Int32 i_nExpandPrio )
812 {
813     OSL_VERIFY( (m_aElement.m_pElement == 0 && m_aElement.m_pChild == 0) || i_pWindow == 0 );
814     OSL_VERIFY( i_pWindow == 0 || i_pWindow->GetParent() == m_pParentWindow );
815     m_aElement.m_pElement = i_pWindow;
816     m_aElement.m_nExpandPriority = i_nExpandPrio;
817 }
818 
setChild(boost::shared_ptr<WindowArranger> const & i_pChild,sal_Int32 i_nExpandPrio)819 void Indenter::setChild( boost::shared_ptr<WindowArranger> const & i_pChild, sal_Int32 i_nExpandPrio )
820 {
821     OSL_VERIFY( (m_aElement.m_pElement == 0 && m_aElement.m_pChild == 0 ) || i_pChild == 0 );
822     m_aElement.m_pChild = i_pChild;
823     m_aElement.m_nExpandPriority = i_nExpandPrio;
824 }
825 
826 // ----------------------------------------
827 // vcl::MatrixArranger
828 //-----------------------------------------
~MatrixArranger()829 MatrixArranger::~MatrixArranger()
830 {
831 }
832 
getOptimalSize(WindowSizeType i_eType,std::vector<long> & o_rColumnWidths,std::vector<long> & o_rRowHeights,std::vector<sal_Int32> & o_rColumnPrio,std::vector<sal_Int32> & o_rRowPrio) const833 Size MatrixArranger::getOptimalSize( WindowSizeType i_eType,
834                                      std::vector<long>& o_rColumnWidths, std::vector<long>& o_rRowHeights,
835                                      std::vector<sal_Int32>& o_rColumnPrio, std::vector<sal_Int32>& o_rRowPrio
836                                     ) const
837 {
838     long nOuterBorder = getBorderValue( m_nOuterBorder );
839     Size aMatrixSize( 2*nOuterBorder, 2*nOuterBorder );
840 
841     // first find out the current number of rows and columns
842     sal_uInt32 nRows = 0, nColumns = 0;
843     for( std::vector< MatrixElement >::const_iterator it = m_aElements.begin();
844             it != m_aElements.end(); ++it )
845     {
846         if( it->m_nX >= nColumns )
847             nColumns = it->m_nX+1;
848         if( it->m_nY >= nRows )
849             nRows = it->m_nY+1;
850     }
851 
852     // now allocate row and column depth vectors
853     o_rColumnWidths = std::vector< long >( nColumns, 0 );
854     o_rRowHeights   = std::vector< long >( nRows, 0 );
855     o_rColumnPrio   = std::vector< sal_Int32 >( nColumns, 0 );
856     o_rRowPrio      = std::vector< sal_Int32 >( nRows, 0 );
857 
858     // get sizes an allocate them into rows/columns
859     for( std::vector< MatrixElement >::const_iterator it = m_aElements.begin();
860             it != m_aElements.end(); ++it )
861     {
862         Size aSize( it->getOptimalSize( i_eType ) );
863         if( aSize.Width() > o_rColumnWidths[ it->m_nX ] )
864             o_rColumnWidths[ it->m_nX ] = aSize.Width();
865         if( aSize.Height() > o_rRowHeights[ it->m_nY ] )
866             o_rRowHeights[ it->m_nY ] = aSize.Height();
867         if( it->m_nExpandPriority > o_rColumnPrio[ it->m_nX ] )
868             o_rColumnPrio[ it->m_nX ] = it->m_nExpandPriority;
869         if( it->m_nExpandPriority > o_rRowPrio[ it->m_nY ] )
870             o_rRowPrio[ it->m_nY ] = it->m_nExpandPriority;
871     }
872 
873     // add up sizes
874     long nDistanceX = getBorderValue( m_nBorderX );
875     long nDistanceY = getBorderValue( m_nBorderY );
876     for( sal_uInt32 i = 0; i < nColumns; i++ )
877         aMatrixSize.Width() += o_rColumnWidths[i] + nDistanceX;
878     if( nColumns > 0 )
879         aMatrixSize.Width() -= nDistanceX;
880 
881     for( sal_uInt32 i = 0; i < nRows; i++ )
882         aMatrixSize.Height() += o_rRowHeights[i] + nDistanceY;
883     if( nRows > 0 )
884         aMatrixSize.Height() -= nDistanceY;
885 
886     return aMatrixSize;
887 }
888 
getOptimalSize(WindowSizeType i_eType) const889 Size MatrixArranger::getOptimalSize( WindowSizeType i_eType ) const
890 {
891     std::vector<long> aColumnWidths, aRowHeights;
892     std::vector<sal_Int32> aColumnPrio, aRowPrio;
893     return getOptimalSize( i_eType, aColumnWidths, aRowHeights, aColumnPrio, aRowPrio );
894 }
895 
distributeExtraSize(std::vector<long> & io_rSizes,const std::vector<sal_Int32> & i_rPrios,long i_nExtraWidth)896 void MatrixArranger::distributeExtraSize( std::vector<long>& io_rSizes, const std::vector<sal_Int32>& i_rPrios, long i_nExtraWidth )
897 {
898     if( ! io_rSizes.empty()  && io_rSizes.size() == i_rPrios.size() ) // sanity check
899     {
900         // find all elements with the highest expand priority
901         size_t nElements = io_rSizes.size();
902         std::vector< size_t > aIndices;
903         sal_Int32 nHighPrio = 0;
904         for( size_t i = 0; i < nElements; i++ )
905         {
906             sal_Int32 nCurPrio = i_rPrios[ i ];
907             if( nCurPrio > nHighPrio )
908             {
909                 aIndices.clear();
910                 nHighPrio = nCurPrio;
911             }
912             if( nCurPrio == nHighPrio )
913                 aIndices.push_back( i );
914         }
915 
916         // distribute extra space evenly among collected elements
917         nElements = aIndices.size();
918         if( nElements > 0 )
919         {
920             long nDelta = i_nExtraWidth / nElements;
921             for( size_t i = 0; i < nElements; i++ )
922             {
923                 io_rSizes[ aIndices[i] ] += nDelta;
924                 i_nExtraWidth -= nDelta;
925             }
926             // add the last pixels to the last row element
927             if( i_nExtraWidth > 0 && nElements > 0 )
928                 io_rSizes[aIndices.back()] += i_nExtraWidth;
929         }
930     }
931 }
932 
933 
resize()934 void MatrixArranger::resize()
935 {
936     // assure that we have at least one row and column
937     if( m_aElements.empty() )
938         return;
939 
940     // check if we can get optimal size, else fallback to minimal size
941     std::vector<long> aColumnWidths, aRowHeights;
942     std::vector<sal_Int32> aColumnPrio, aRowPrio;
943     Size aOptSize( getOptimalSize( WINDOWSIZE_PREFERRED, aColumnWidths, aRowHeights, aColumnPrio, aRowPrio ) );
944     if( aOptSize.Height() > m_aManagedArea.GetHeight() ||
945         aOptSize.Width() > m_aManagedArea.GetWidth() )
946     {
947         std::vector<long> aMinColumnWidths, aMinRowHeights;
948         getOptimalSize( WINDOWSIZE_MINIMUM, aMinColumnWidths, aMinRowHeights, aColumnPrio, aRowPrio );
949         if( aOptSize.Height() > m_aManagedArea.GetHeight() )
950             aRowHeights = aMinRowHeights;
951         if( aOptSize.Width() > m_aManagedArea.GetWidth() )
952             aColumnWidths = aMinColumnWidths;
953     }
954 
955     // distribute extra space available
956     long nExtraSize = m_aManagedArea.GetWidth();
957     for( size_t i = 0; i < aColumnWidths.size(); ++i )
958         nExtraSize -= aColumnWidths[i] + m_nBorderX;
959     if( nExtraSize > 0 )
960         distributeExtraSize( aColumnWidths, aColumnPrio, nExtraSize );
961     nExtraSize =  m_aManagedArea.GetHeight();
962     for( size_t i = 0; i < aRowHeights.size(); ++i )
963         nExtraSize -= aRowHeights[i] + m_nBorderY;
964     if( nExtraSize > 0 )
965         distributeExtraSize( aRowHeights, aRowPrio, nExtraSize );
966 
967     // prepare offsets
968     long nDistanceX = getBorderValue( m_nBorderX );
969     long nDistanceY = getBorderValue( m_nBorderY );
970     long nOuterBorder = getBorderValue( m_nOuterBorder );
971     std::vector<long> aColumnX( aColumnWidths.size() );
972     aColumnX[0] = m_aManagedArea.Left() + nOuterBorder;
973     for( size_t i = 1; i < aColumnX.size(); i++ )
974         aColumnX[i] = aColumnX[i-1] + aColumnWidths[i-1] + nDistanceX;
975 
976     std::vector<long> aRowY( aRowHeights.size() );
977     aRowY[0] = m_aManagedArea.Top() + nOuterBorder;
978     for( size_t i = 1; i < aRowY.size(); i++ )
979         aRowY[i] = aRowY[i-1] + aRowHeights[i-1] + nDistanceY;
980 
981     // now iterate over the elements and assign their positions
982     for( std::vector< MatrixElement >::iterator it = m_aElements.begin();
983          it != m_aElements.end(); ++it )
984     {
985         Point aCellPos( aColumnX[it->m_nX], aRowY[it->m_nY] );
986         Size  aCellSize( aColumnWidths[it->m_nX], aRowHeights[it->m_nY] );
987         it->setPosSize( aCellPos, aCellSize );
988     }
989 }
990 
addWindow(Window * i_pWindow,sal_uInt32 i_nX,sal_uInt32 i_nY,sal_Int32 i_nExpandPrio,const Size & i_rMinSize)991 size_t MatrixArranger::addWindow( Window* i_pWindow, sal_uInt32 i_nX, sal_uInt32 i_nY, sal_Int32 i_nExpandPrio, const Size& i_rMinSize )
992 {
993     sal_uInt64 nMapValue = getMap( i_nX, i_nY );
994     std::map< sal_uInt64, size_t >::const_iterator it = m_aMatrixMap.find( nMapValue );
995     size_t nIndex = 0;
996     if( it == m_aMatrixMap.end() )
997     {
998         m_aMatrixMap[ nMapValue ] = nIndex = m_aElements.size();
999         m_aElements.push_back( MatrixElement( i_pWindow, i_nX, i_nY, boost::shared_ptr<WindowArranger>(), i_nExpandPrio, i_rMinSize ) );
1000     }
1001     else
1002     {
1003         MatrixElement& rEle( m_aElements[ it->second ] );
1004         rEle.m_pElement = i_pWindow;
1005         rEle.m_pChild.reset();
1006         rEle.m_nExpandPriority = i_nExpandPrio;
1007         rEle.m_aMinSize = i_rMinSize;
1008         rEle.m_nX = i_nX;
1009         rEle.m_nY = i_nY;
1010         nIndex = it->second;
1011     }
1012     return nIndex;
1013 }
1014 
remove(Window * i_pWindow)1015 void MatrixArranger::remove( Window* i_pWindow )
1016 {
1017     if( i_pWindow )
1018     {
1019         for( std::vector< MatrixElement >::iterator it = m_aElements.begin();
1020             it != m_aElements.end(); ++it )
1021         {
1022             if( it->m_pElement == i_pWindow )
1023             {
1024                 m_aMatrixMap.erase( getMap( it->m_nX, it->m_nY ) );
1025                 m_aElements.erase( it );
1026                 return;
1027             }
1028         }
1029     }
1030 }
1031 
addChild(boost::shared_ptr<WindowArranger> const & i_pChild,sal_uInt32 i_nX,sal_uInt32 i_nY,sal_Int32 i_nExpandPrio)1032 size_t MatrixArranger::addChild( boost::shared_ptr<WindowArranger> const &i_pChild, sal_uInt32 i_nX, sal_uInt32 i_nY, sal_Int32 i_nExpandPrio )
1033 {
1034     sal_uInt64 nMapValue = getMap( i_nX, i_nY );
1035     std::map< sal_uInt64, size_t >::const_iterator it = m_aMatrixMap.find( nMapValue );
1036     size_t nIndex = 0;
1037     if( it == m_aMatrixMap.end() )
1038     {
1039         m_aMatrixMap[ nMapValue ] = nIndex = m_aElements.size();
1040         m_aElements.push_back( MatrixElement( NULL, i_nX, i_nY, i_pChild, i_nExpandPrio ) );
1041     }
1042     else
1043     {
1044         MatrixElement& rEle( m_aElements[ it->second ] );
1045         rEle.m_pElement = 0;
1046         rEle.m_pChild = i_pChild;
1047         rEle.m_nExpandPriority = i_nExpandPrio;
1048         rEle.m_nX = i_nX;
1049         rEle.m_nY = i_nY;
1050         nIndex = it->second;
1051     }
1052     return nIndex;
1053 }
1054 
remove(boost::shared_ptr<WindowArranger> const & i_pChild)1055 void MatrixArranger::remove( boost::shared_ptr<WindowArranger> const &i_pChild )
1056 {
1057     if( i_pChild )
1058     {
1059         for( std::vector< MatrixElement >::iterator it = m_aElements.begin();
1060             it != m_aElements.end(); ++it )
1061         {
1062             if( it->m_pChild == i_pChild )
1063             {
1064                 m_aMatrixMap.erase( getMap( it->m_nX, it->m_nY ) );
1065                 m_aElements.erase( it );
1066                 return;
1067             }
1068         }
1069     }
1070 }
1071 
1072