1*5900e8ecSAndrew Rist /**************************************************************
2cdf0e10cSrcweir  *
3*5900e8ecSAndrew Rist  * Licensed to the Apache Software Foundation (ASF) under one
4*5900e8ecSAndrew Rist  * or more contributor license agreements.  See the NOTICE file
5*5900e8ecSAndrew Rist  * distributed with this work for additional information
6*5900e8ecSAndrew Rist  * regarding copyright ownership.  The ASF licenses this file
7*5900e8ecSAndrew Rist  * to you under the Apache License, Version 2.0 (the
8*5900e8ecSAndrew Rist  * "License"); you may not use this file except in compliance
9*5900e8ecSAndrew Rist  * with the License.  You may obtain a copy of the License at
10*5900e8ecSAndrew Rist  *
11*5900e8ecSAndrew Rist  *   http://www.apache.org/licenses/LICENSE-2.0
12*5900e8ecSAndrew Rist  *
13*5900e8ecSAndrew Rist  * Unless required by applicable law or agreed to in writing,
14*5900e8ecSAndrew Rist  * software distributed under the License is distributed on an
15*5900e8ecSAndrew Rist  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*5900e8ecSAndrew Rist  * KIND, either express or implied.  See the License for the
17*5900e8ecSAndrew Rist  * specific language governing permissions and limitations
18*5900e8ecSAndrew Rist  * under the License.
19*5900e8ecSAndrew Rist  *
20*5900e8ecSAndrew Rist  *************************************************************/
21*5900e8ecSAndrew Rist 
22*5900e8ecSAndrew Rist 
23cdf0e10cSrcweir 
24cdf0e10cSrcweir #include "precompiled_svtools.hxx"
25cdf0e10cSrcweir 
26cdf0e10cSrcweir #include "tabbargeometry.hxx"
27cdf0e10cSrcweir 
28cdf0e10cSrcweir #include <basegfx/range/b2drange.hxx>
29cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx>
30cdf0e10cSrcweir #include <basegfx/numeric/ftools.hxx>
31cdf0e10cSrcweir 
32cdf0e10cSrcweir #include <vcl/window.hxx>
33cdf0e10cSrcweir 
34cdf0e10cSrcweir #include <algorithm>
35cdf0e10cSrcweir 
36cdf0e10cSrcweir // the width (or height, depending on alignment) of the scroll buttons
37cdf0e10cSrcweir #define BUTTON_FLOW_WIDTH       20
38cdf0e10cSrcweir // the space between the scroll buttons and the items
39cdf0e10cSrcweir #define BUTTON_FLOW_SPACE       2
40cdf0e10cSrcweir // outer space to apply between the tab bar borders and any content. Note that those refer to a "normalized" geometry,
41cdf0e10cSrcweir // i.e. if the tab bar were aligned at the top
42cdf0e10cSrcweir #define OUTER_SPACE_LEFT        2
43cdf0e10cSrcweir #define OUTER_SPACE_TOP         4
44cdf0e10cSrcweir #define OUTER_SPACE_RIGHT       4
45cdf0e10cSrcweir #define OUTER_SPACE_BOTTOM      2
46cdf0e10cSrcweir 
47cdf0e10cSrcweir // outer space to apply between the area for the items, and the actual items. They refer to a normalized geometry.
48cdf0e10cSrcweir #define ITEMS_INSET_LEFT        4
49cdf0e10cSrcweir #define ITEMS_INSET_TOP         3
50cdf0e10cSrcweir #define ITEMS_INSET_RIGHT       4
51cdf0e10cSrcweir #define ITEMS_INSET_BOTTOM      0
52cdf0e10cSrcweir 
53cdf0e10cSrcweir //......................................................................................................................
54cdf0e10cSrcweir namespace svt
55cdf0e10cSrcweir {
56cdf0e10cSrcweir //......................................................................................................................
57cdf0e10cSrcweir 
58cdf0e10cSrcweir 	//==================================================================================================================
59cdf0e10cSrcweir 	//= helper
60cdf0e10cSrcweir 	//==================================================================================================================
61cdf0e10cSrcweir     namespace
62cdf0e10cSrcweir     {
63cdf0e10cSrcweir 	    //--------------------------------------------------------------------------------------------------------------
lcl_transform(Rectangle & io_rRect,const::basegfx::B2DHomMatrix & i_rTransformation)64cdf0e10cSrcweir         static void lcl_transform( Rectangle& io_rRect, const ::basegfx::B2DHomMatrix& i_rTransformation )
65cdf0e10cSrcweir         {
66cdf0e10cSrcweir             ::basegfx::B2DRange aRect( io_rRect.Left(), io_rRect.Top(), io_rRect.Right(), io_rRect.Bottom() );
67cdf0e10cSrcweir             aRect.transform( i_rTransformation );
68cdf0e10cSrcweir             io_rRect.Left() = long( aRect.getMinX() );
69cdf0e10cSrcweir             io_rRect.Top() = long( aRect.getMinY() );
70cdf0e10cSrcweir             io_rRect.Right() = long( aRect.getMaxX() );
71cdf0e10cSrcweir             io_rRect.Bottom() = long( aRect.getMaxY() );
72cdf0e10cSrcweir         }
73cdf0e10cSrcweir 
74cdf0e10cSrcweir 	    //--------------------------------------------------------------------------------------------------------------
75cdf0e10cSrcweir         /** transforms the given, possible rotated playground,
76cdf0e10cSrcweir         */
lcl_rotate(const Rectangle & i_rReference,Rectangle & io_rArea,const bool i_bRight)77cdf0e10cSrcweir         void lcl_rotate( const Rectangle& i_rReference, Rectangle& io_rArea, const bool i_bRight )
78cdf0e10cSrcweir         {
79cdf0e10cSrcweir             // step 1: move the to-be-upper-left corner (left/bottom) of the rectangle to (0,0)
80cdf0e10cSrcweir             ::basegfx::B2DHomMatrix aTransformation;
81cdf0e10cSrcweir             aTransformation.translate(
82cdf0e10cSrcweir                 i_bRight ? -i_rReference.Left() : -i_rReference.Right(),
83cdf0e10cSrcweir                 i_bRight ? -i_rReference.Bottom() : -i_rReference.Top()
84cdf0e10cSrcweir             );
85cdf0e10cSrcweir 
86cdf0e10cSrcweir             // step 2: rotate by -90 degrees
87cdf0e10cSrcweir             aTransformation.rotate( i_bRight ? +F_PI2 : -F_PI2 );
88cdf0e10cSrcweir                 // note:
89cdf0e10cSrcweir                 // on the screen, the ordinate goes top-down, while basegfx calculates in a system where the
90cdf0e10cSrcweir                 // ordinate goes bottom-up; thus the "wrong" sign before F_PI2 here
91cdf0e10cSrcweir 
92cdf0e10cSrcweir             // step 3: move back to original coordinates
93cdf0e10cSrcweir             aTransformation.translate( i_rReference.Left(), i_rReference.Top() );
94cdf0e10cSrcweir 
95cdf0e10cSrcweir             // apply transformation
96cdf0e10cSrcweir             lcl_transform( io_rArea, aTransformation );
97cdf0e10cSrcweir         }
98cdf0e10cSrcweir     }
99cdf0e10cSrcweir 
100cdf0e10cSrcweir 	//------------------------------------------------------------------------------------------------------------------
lcl_mirrorHorizontally(const Rectangle & i_rReferenceArea,Rectangle & io_rArea)101cdf0e10cSrcweir     void lcl_mirrorHorizontally( const Rectangle& i_rReferenceArea, Rectangle& io_rArea )
102cdf0e10cSrcweir     {
103cdf0e10cSrcweir         io_rArea.Left() = i_rReferenceArea.Left() + i_rReferenceArea.Right() - io_rArea.Left();
104cdf0e10cSrcweir         io_rArea.Right() = i_rReferenceArea.Left() + i_rReferenceArea.Right() - io_rArea.Right();
105cdf0e10cSrcweir         ::std::swap( io_rArea.Left(), io_rArea.Right() );
106cdf0e10cSrcweir     }
107cdf0e10cSrcweir 
108cdf0e10cSrcweir 	//------------------------------------------------------------------------------------------------------------------
lcl_mirrorVertically(const Rectangle & i_rReferenceArea,Rectangle & io_rArea)109cdf0e10cSrcweir     void lcl_mirrorVertically( const Rectangle& i_rReferenceArea, Rectangle& io_rArea )
110cdf0e10cSrcweir     {
111cdf0e10cSrcweir         io_rArea.Top() = i_rReferenceArea.Top() + i_rReferenceArea.Bottom() - io_rArea.Top();
112cdf0e10cSrcweir         io_rArea.Bottom() = i_rReferenceArea.Top() + i_rReferenceArea.Bottom() - io_rArea.Bottom();
113cdf0e10cSrcweir         ::std::swap( io_rArea.Top(), io_rArea.Bottom() );
114cdf0e10cSrcweir     }
115cdf0e10cSrcweir 
116cdf0e10cSrcweir 	//==================================================================================================================
117cdf0e10cSrcweir 	//= NormalizedArea
118cdf0e10cSrcweir 	//==================================================================================================================
119cdf0e10cSrcweir 	//------------------------------------------------------------------------------------------------------------------
NormalizedArea()120cdf0e10cSrcweir     NormalizedArea::NormalizedArea()
121cdf0e10cSrcweir         :m_aReference()
122cdf0e10cSrcweir     {
123cdf0e10cSrcweir     }
124cdf0e10cSrcweir 
125cdf0e10cSrcweir 	//------------------------------------------------------------------------------------------------------------------
NormalizedArea(const Rectangle & i_rReference,const bool i_bIsVertical)126cdf0e10cSrcweir     NormalizedArea::NormalizedArea( const Rectangle& i_rReference, const bool i_bIsVertical )
127cdf0e10cSrcweir         :m_aReference( i_bIsVertical ? Rectangle( i_rReference.TopLeft(), Size( i_rReference.GetHeight(), i_rReference.GetWidth() ) ) : i_rReference )
128cdf0e10cSrcweir     {
129cdf0e10cSrcweir     }
130cdf0e10cSrcweir 
131cdf0e10cSrcweir 	//------------------------------------------------------------------------------------------------------------------
getTransformed(const Rectangle & i_rArea,const TabAlignment i_eTargetAlignment) const132cdf0e10cSrcweir     Rectangle NormalizedArea::getTransformed( const Rectangle& i_rArea, const TabAlignment i_eTargetAlignment ) const
133cdf0e10cSrcweir     {
134cdf0e10cSrcweir         Rectangle aResult( i_rArea );
135cdf0e10cSrcweir 
136cdf0e10cSrcweir         if  (   ( i_eTargetAlignment == TABS_RIGHT )
137cdf0e10cSrcweir             ||  ( i_eTargetAlignment == TABS_LEFT )
138cdf0e10cSrcweir             )
139cdf0e10cSrcweir         {
140cdf0e10cSrcweir             lcl_rotate( m_aReference, aResult, true );
141cdf0e10cSrcweir 
142cdf0e10cSrcweir             if ( i_eTargetAlignment == TABS_LEFT )
143cdf0e10cSrcweir             {
144cdf0e10cSrcweir                 Rectangle aReference( m_aReference );
145cdf0e10cSrcweir                 aReference.Transpose();
146cdf0e10cSrcweir                 lcl_mirrorHorizontally( aReference, aResult );
147cdf0e10cSrcweir             }
148cdf0e10cSrcweir         }
149cdf0e10cSrcweir         else
150cdf0e10cSrcweir         if  ( i_eTargetAlignment == TABS_BOTTOM )
151cdf0e10cSrcweir         {
152cdf0e10cSrcweir             lcl_mirrorVertically( m_aReference, aResult );
153cdf0e10cSrcweir         }
154cdf0e10cSrcweir 
155cdf0e10cSrcweir         return aResult;
156cdf0e10cSrcweir     }
157cdf0e10cSrcweir 
158cdf0e10cSrcweir 	//------------------------------------------------------------------------------------------------------------------
getNormalized(const Rectangle & i_rArea,const TabAlignment i_eTargetAlignment) const159cdf0e10cSrcweir     Rectangle NormalizedArea::getNormalized( const Rectangle& i_rArea, const TabAlignment i_eTargetAlignment ) const
160cdf0e10cSrcweir     {
161cdf0e10cSrcweir         Rectangle aResult( i_rArea );
162cdf0e10cSrcweir 
163cdf0e10cSrcweir         if  (   ( i_eTargetAlignment == TABS_RIGHT )
164cdf0e10cSrcweir             ||  ( i_eTargetAlignment == TABS_LEFT )
165cdf0e10cSrcweir             )
166cdf0e10cSrcweir         {
167cdf0e10cSrcweir             Rectangle aReference( m_aReference );
168cdf0e10cSrcweir             lcl_rotate( m_aReference, aReference, true );
169cdf0e10cSrcweir 
170cdf0e10cSrcweir             if ( i_eTargetAlignment == TABS_LEFT )
171cdf0e10cSrcweir             {
172cdf0e10cSrcweir                 lcl_mirrorHorizontally( aReference, aResult );
173cdf0e10cSrcweir             }
174cdf0e10cSrcweir 
175cdf0e10cSrcweir             lcl_rotate( aReference, aResult, false );
176cdf0e10cSrcweir         }
177cdf0e10cSrcweir         else
178cdf0e10cSrcweir         if  ( i_eTargetAlignment == TABS_BOTTOM )
179cdf0e10cSrcweir         {
180cdf0e10cSrcweir             lcl_mirrorVertically( m_aReference, aResult );
181cdf0e10cSrcweir         }
182cdf0e10cSrcweir         return aResult;
183cdf0e10cSrcweir     }
184cdf0e10cSrcweir 
185cdf0e10cSrcweir 	//==================================================================================================================
186cdf0e10cSrcweir 	//= TabBarGeometry
187cdf0e10cSrcweir 	//==================================================================================================================
188cdf0e10cSrcweir 	//------------------------------------------------------------------------------------------------------------------
TabBarGeometry(const TabItemContent i_eItemContent)189cdf0e10cSrcweir     TabBarGeometry::TabBarGeometry( const TabItemContent i_eItemContent )
190cdf0e10cSrcweir         :m_eTabItemContent( i_eItemContent )
191cdf0e10cSrcweir         ,m_aItemsInset()
192cdf0e10cSrcweir         ,m_aButtonBackRect()
193cdf0e10cSrcweir         ,m_aItemsRect()
194cdf0e10cSrcweir         ,m_aButtonForwardRect()
195cdf0e10cSrcweir     {
196cdf0e10cSrcweir         m_aItemsInset.Left()   = ITEMS_INSET_LEFT;
197cdf0e10cSrcweir         m_aItemsInset.Top()    = ITEMS_INSET_TOP;
198cdf0e10cSrcweir         m_aItemsInset.Right()  = ITEMS_INSET_RIGHT;
199cdf0e10cSrcweir         m_aItemsInset.Bottom() = ITEMS_INSET_BOTTOM;
200cdf0e10cSrcweir     }
201cdf0e10cSrcweir 
202cdf0e10cSrcweir 	//------------------------------------------------------------------------------------------------------------------
~TabBarGeometry()203cdf0e10cSrcweir     TabBarGeometry::~TabBarGeometry()
204cdf0e10cSrcweir     {
205cdf0e10cSrcweir     }
206cdf0e10cSrcweir 
207cdf0e10cSrcweir     //------------------------------------------------------------------------------------------------------------------
impl_fitItems(ItemDescriptors & io_rItems) const208cdf0e10cSrcweir     bool TabBarGeometry::impl_fitItems( ItemDescriptors& io_rItems ) const
209cdf0e10cSrcweir     {
210cdf0e10cSrcweir         if ( io_rItems.empty() )
211cdf0e10cSrcweir             // nothing to do, "no items" perfectly fit into any space we have ...
212cdf0e10cSrcweir             return true;
213cdf0e10cSrcweir 
214cdf0e10cSrcweir         // the available size
215cdf0e10cSrcweir         Size aOutputSize( getItemsRect().GetSize() );
216cdf0e10cSrcweir         // shrunk by the outer space
217cdf0e10cSrcweir         aOutputSize.Width() -= m_aItemsInset.Right();
218cdf0e10cSrcweir         aOutputSize.Height() -= m_aItemsInset.Bottom();
219cdf0e10cSrcweir         const Rectangle aFitInto( Point( 0, 0 ), aOutputSize );
220cdf0e10cSrcweir 
221cdf0e10cSrcweir         TabItemContent eItemContent( getItemContent() );
222cdf0e10cSrcweir         if ( eItemContent == TABITEM_AUTO )
223cdf0e10cSrcweir         {
224cdf0e10cSrcweir             // the "content modes" to try
225cdf0e10cSrcweir             TabItemContent eTryThis[] =
226cdf0e10cSrcweir             {
227cdf0e10cSrcweir                 TABITEM_IMAGE_ONLY,     // assumed to have the smallest rects
228cdf0e10cSrcweir                 TABITEM_TEXT_ONLY,
229cdf0e10cSrcweir                 TABITEM_IMAGE_AND_TEXT  // assumed to have the largest rects
230cdf0e10cSrcweir             };
231cdf0e10cSrcweir 
232cdf0e10cSrcweir 
233cdf0e10cSrcweir             // determine which of the different version fits
234cdf0e10cSrcweir             eItemContent = eTryThis[0];
235cdf0e10cSrcweir             size_t nTryIndex = 2;
236cdf0e10cSrcweir             while ( nTryIndex > 0 )
237cdf0e10cSrcweir             {
238cdf0e10cSrcweir                 const Point aBottomRight( io_rItems.rbegin()->GetRect( eTryThis[ nTryIndex ] ).BottomRight() );
239cdf0e10cSrcweir                 if ( aFitInto.IsInside( aBottomRight ) )
240cdf0e10cSrcweir                 {
241cdf0e10cSrcweir                     eItemContent = eTryThis[ nTryIndex ];
242cdf0e10cSrcweir                     break;
243cdf0e10cSrcweir                 }
244cdf0e10cSrcweir                 --nTryIndex;
245cdf0e10cSrcweir             }
246cdf0e10cSrcweir         }
247cdf0e10cSrcweir 
248cdf0e10cSrcweir         // propagate to the items
249cdf0e10cSrcweir         for (   ItemDescriptors::iterator item = io_rItems.begin();
250cdf0e10cSrcweir                 item != io_rItems.end();
251cdf0e10cSrcweir                 ++item
252cdf0e10cSrcweir             )
253cdf0e10cSrcweir         {
254cdf0e10cSrcweir             item->eContent = eItemContent;
255cdf0e10cSrcweir         }
256cdf0e10cSrcweir 
257cdf0e10cSrcweir         const ItemDescriptor& rLastItem( *io_rItems.rbegin() );
258cdf0e10cSrcweir         const Point aLastItemBottomRight( rLastItem.GetCurrentRect().BottomRight() );
259cdf0e10cSrcweir         return  aFitInto.Left() <= aLastItemBottomRight.X()
260cdf0e10cSrcweir             &&  aFitInto.Right() >= aLastItemBottomRight.X();
261cdf0e10cSrcweir     }
262cdf0e10cSrcweir 
263cdf0e10cSrcweir 	//------------------------------------------------------------------------------------------------------------------
getOptimalSize(ItemDescriptors & io_rItems,const bool i_bMinimalSize) const264cdf0e10cSrcweir     Size TabBarGeometry::getOptimalSize( ItemDescriptors& io_rItems, const bool i_bMinimalSize ) const
265cdf0e10cSrcweir     {
266cdf0e10cSrcweir         if ( io_rItems.empty() )
267cdf0e10cSrcweir             return Size(
268cdf0e10cSrcweir                 m_aItemsInset.Left() + m_aItemsInset.Right(),
269cdf0e10cSrcweir                 m_aItemsInset.Top() + m_aItemsInset.Bottom()
270cdf0e10cSrcweir             );
271cdf0e10cSrcweir 
272cdf0e10cSrcweir         // the rect of the last item
273cdf0e10cSrcweir         const Rectangle& rLastItemRect( i_bMinimalSize ? io_rItems.rbegin()->aIconOnlyArea : io_rItems.rbegin()->aCompleteArea );
274cdf0e10cSrcweir         return Size(
275cdf0e10cSrcweir                     rLastItemRect.Left() + 1 + m_aItemsInset.Right(),
276cdf0e10cSrcweir                     rLastItemRect.Top() + 1 + rLastItemRect.Bottom() + m_aItemsInset.Bottom()
277cdf0e10cSrcweir                 );
278cdf0e10cSrcweir     }
279cdf0e10cSrcweir 
280cdf0e10cSrcweir 	//------------------------------------------------------------------------------------------------------------------
relayout(const Size & i_rActualOutputSize,ItemDescriptors & io_rItems)281cdf0e10cSrcweir     void TabBarGeometry::relayout( const Size& i_rActualOutputSize, ItemDescriptors& io_rItems )
282cdf0e10cSrcweir     {
283cdf0e10cSrcweir         // assume all items fit
284cdf0e10cSrcweir         Point aButtonBackPos( OUTER_SPACE_LEFT, OUTER_SPACE_TOP );
285cdf0e10cSrcweir         m_aButtonBackRect = Rectangle( aButtonBackPos, Size( 1, 1 ) );
286cdf0e10cSrcweir         m_aButtonBackRect.SetEmpty();
287cdf0e10cSrcweir 
288cdf0e10cSrcweir         Point aButtonForwardPos( i_rActualOutputSize.Width(), OUTER_SPACE_TOP );
289cdf0e10cSrcweir         m_aButtonForwardRect = Rectangle( aButtonForwardPos, Size( 1, 1 ) );
290cdf0e10cSrcweir         m_aButtonForwardRect.SetEmpty();
291cdf0e10cSrcweir 
292cdf0e10cSrcweir         Point aItemsPos( OUTER_SPACE_LEFT, 0 );
293cdf0e10cSrcweir         Size aItemsSize( i_rActualOutputSize.Width() - OUTER_SPACE_LEFT - OUTER_SPACE_RIGHT, i_rActualOutputSize.Height() );
294cdf0e10cSrcweir         m_aItemsRect = Rectangle( aItemsPos, aItemsSize );
295cdf0e10cSrcweir 
296cdf0e10cSrcweir         if ( !impl_fitItems( io_rItems ) )
297cdf0e10cSrcweir         {
298cdf0e10cSrcweir             // assumption was wrong, the items do not fit => calculate rects for the scroll buttons
299cdf0e10cSrcweir             const Size aButtonSize( BUTTON_FLOW_WIDTH, i_rActualOutputSize.Height() - OUTER_SPACE_TOP - OUTER_SPACE_BOTTOM );
300cdf0e10cSrcweir 
301cdf0e10cSrcweir             aButtonBackPos = Point( OUTER_SPACE_LEFT, OUTER_SPACE_TOP );
302cdf0e10cSrcweir             m_aButtonBackRect = Rectangle( aButtonBackPos, aButtonSize );
303cdf0e10cSrcweir 
304cdf0e10cSrcweir             aButtonForwardPos = Point( i_rActualOutputSize.Width() - BUTTON_FLOW_WIDTH - OUTER_SPACE_RIGHT, OUTER_SPACE_TOP );
305cdf0e10cSrcweir             m_aButtonForwardRect = Rectangle( aButtonForwardPos, aButtonSize );
306cdf0e10cSrcweir 
307cdf0e10cSrcweir             aItemsPos.X() = aButtonBackPos.X() + aButtonSize.Width() + BUTTON_FLOW_SPACE;
308cdf0e10cSrcweir             aItemsSize.Width() = aButtonForwardPos.X() - BUTTON_FLOW_SPACE - aItemsPos.X();
309cdf0e10cSrcweir             m_aItemsRect = Rectangle( aItemsPos, aItemsSize );
310cdf0e10cSrcweir 
311cdf0e10cSrcweir             // fit items, again. In the TABITEM_AUTO case, the smaller playground for the items might lead to another
312cdf0e10cSrcweir             // item content.
313cdf0e10cSrcweir             impl_fitItems( io_rItems );
314cdf0e10cSrcweir         }
315cdf0e10cSrcweir     }
316cdf0e10cSrcweir 
317cdf0e10cSrcweir 	//------------------------------------------------------------------------------------------------------------------
getFirstItemPosition() const318cdf0e10cSrcweir     Point TabBarGeometry::getFirstItemPosition() const
319cdf0e10cSrcweir     {
320cdf0e10cSrcweir         return Point( m_aItemsInset.Left(), m_aItemsInset.Top() );
321cdf0e10cSrcweir     }
322cdf0e10cSrcweir 
323cdf0e10cSrcweir //......................................................................................................................
324cdf0e10cSrcweir } // namespace svt
325cdf0e10cSrcweir //......................................................................................................................
326