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