build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / control / tabctrl.cxx
blobd53dbdda89ccd5a8c497f686b7600a72a5860909
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "tools/debug.hxx"
21 #include "tools/rc.h"
23 #include <vcl/svapp.hxx>
24 #include <vcl/help.hxx>
25 #include <vcl/event.hxx>
26 #include <vcl/menu.hxx>
27 #include <vcl/button.hxx>
28 #include <vcl/tabpage.hxx>
29 #include <vcl/tabctrl.hxx>
30 #include <vcl/controllayout.hxx>
31 #include <vcl/layout.hxx>
32 #include <vcl/lstbox.hxx>
33 #include <vcl/settings.hxx>
34 #include <vcl/uitest/uiobject.hxx>
35 #include <vcl/builderfactory.hxx>
36 #include <svids.hrc>
38 #include "controldata.hxx"
39 #include "svdata.hxx"
40 #include "window.h"
42 #include <unordered_map>
43 #include <vector>
45 struct ImplTabItem
47 sal_uInt16 mnId;
48 VclPtr<TabPage> mpTabPage;
49 OUString maText;
50 OUString maFormatText;
51 OUString maHelpText;
52 OString maHelpId;
53 OString maTabName;
54 Rectangle maRect;
55 sal_uInt16 mnLine;
56 bool mbFullVisible;
57 bool mbEnabled;
58 Image maTabImage;
60 ImplTabItem()
61 : mnId( 0 ), mpTabPage( nullptr ),
62 mnLine( 0 ), mbFullVisible( false ), mbEnabled( true )
66 struct ImplTabCtrlData
68 std::unordered_map< int, int > maLayoutPageIdToLine;
69 std::unordered_map< int, int > maLayoutLineToPageId;
70 Point maItemsOffset; // offset of the tabitems
71 std::vector< ImplTabItem > maItemList;
72 VclPtr<ListBox> mpListBox;
75 // for the Tab positions
76 #define TAB_PAGERECT 0xFFFF
78 void TabControl::ImplInit( vcl::Window* pParent, WinBits nStyle )
80 mbLayoutDirty = true;
82 if ( !(nStyle & WB_NOTABSTOP) )
83 nStyle |= WB_TABSTOP;
84 if ( !(nStyle & WB_NOGROUP) )
85 nStyle |= WB_GROUP;
86 if ( !(nStyle & WB_NODIALOGCONTROL) )
87 nStyle |= WB_DIALOGCONTROL;
89 Control::ImplInit( pParent, nStyle, nullptr );
91 mnLastWidth = 0;
92 mnLastHeight = 0;
93 mnMaxPageWidth = 0;
94 mnActPageId = 0;
95 mnCurPageId = 0;
96 mbFormat = true;
97 mbRestoreHelpId = false;
98 mbSmallInvalidate = false;
99 mpTabCtrlData = new ImplTabCtrlData;
100 mpTabCtrlData->mpListBox = nullptr;
102 ImplInitSettings( true, true, true );
104 if( (nStyle & WB_DROPDOWN) )
106 mpTabCtrlData->mpListBox = VclPtr<ListBox>::Create( this, WB_DROPDOWN );
107 mpTabCtrlData->mpListBox->SetPosSizePixel( Point( 0, 0 ), Size( 200, 20 ) );
108 mpTabCtrlData->mpListBox->SetSelectHdl( LINK( this, TabControl, ImplListBoxSelectHdl ) );
109 mpTabCtrlData->mpListBox->Show();
112 // if the tabcontrol is drawn (ie filled) by a native widget, make sure all controls will have transparent background
113 // otherwise they will paint with a wrong background
114 if( IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire) )
115 EnableChildTransparentMode();
117 if (pParent && pParent->IsDialog())
118 pParent->AddChildEventListener( LINK( this, TabControl, ImplWindowEventListener ) );
121 const vcl::Font& TabControl::GetCanonicalFont( const StyleSettings& _rStyle ) const
123 return _rStyle.GetTabFont();
126 const Color& TabControl::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
128 return _rStyle.GetTabTextColor();
131 void TabControl::ImplInitSettings( bool bFont,
132 bool bForeground, bool bBackground )
134 Control::ImplInitSettings( bFont, bForeground );
136 if ( bBackground )
138 vcl::Window* pParent = GetParent();
139 if ( !IsControlBackground() &&
140 (pParent->IsChildTransparentModeEnabled()
141 || IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire)
142 || IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire) ) )
145 // set transparent mode for NWF tabcontrols to have
146 // the background always cleared properly
147 EnableChildTransparentMode();
148 SetParentClipMode( ParentClipMode::NoClip );
149 SetPaintTransparent( true );
150 SetBackground();
151 ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
153 else
155 EnableChildTransparentMode( false );
156 SetParentClipMode();
157 SetPaintTransparent( false );
159 if ( IsControlBackground() )
160 SetBackground( GetControlBackground() );
161 else
162 SetBackground( pParent->GetBackground() );
167 void TabControl::ImplFreeLayoutData()
169 if( HasLayoutData() )
171 ImplClearLayoutData();
172 mpTabCtrlData->maLayoutPageIdToLine.clear();
173 mpTabCtrlData->maLayoutLineToPageId.clear();
177 TabControl::TabControl( vcl::Window* pParent, WinBits nStyle ) :
178 Control( WINDOW_TABCONTROL )
180 ImplInit( pParent, nStyle );
181 OSL_TRACE("*** TABCONTROL no notabs? %s", ( GetStyle() & WB_NOBORDER ) ? "true" : "false" );
184 TabControl::~TabControl()
186 disposeOnce();
189 void TabControl::dispose()
191 Window *pParent = GetParent();
192 if (pParent && pParent->IsDialog())
193 GetParent()->RemoveChildEventListener( LINK( this, TabControl, ImplWindowEventListener ) );
195 ImplFreeLayoutData();
197 // delete TabCtrl data
198 if (mpTabCtrlData)
199 mpTabCtrlData->mpListBox.disposeAndClear();
200 delete mpTabCtrlData;
201 mpTabCtrlData = nullptr;
202 Control::dispose();
205 ImplTabItem* TabControl::ImplGetItem( sal_uInt16 nId ) const
207 for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
208 it != mpTabCtrlData->maItemList.end(); ++it )
210 if( it->mnId == nId )
211 return &(*it);
214 return nullptr;
217 Size TabControl::ImplGetItemSize( ImplTabItem* pItem, long nMaxWidth )
219 pItem->maFormatText = pItem->maText;
220 Size aSize( GetCtrlTextWidth( pItem->maFormatText ), GetTextHeight() );
221 Size aImageSize( 0, 0 );
222 if( !!pItem->maTabImage )
224 aImageSize = pItem->maTabImage.GetSizePixel();
225 if( !pItem->maFormatText.isEmpty() )
226 aImageSize.Width() += GetTextHeight()/4;
228 aSize.Width() += aImageSize.Width();
229 if( aImageSize.Height() > aSize.Height() )
230 aSize.Height() = aImageSize.Height();
232 aSize.Width() += TAB_TABOFFSET_X*2;
233 aSize.Height() += TAB_TABOFFSET_Y*2;
235 Rectangle aCtrlRegion( Point( 0, 0 ), aSize );
236 Rectangle aBoundingRgn, aContentRgn;
237 const TabitemValue aControlValue(Rectangle(TAB_TABOFFSET_X, TAB_TABOFFSET_Y,
238 aSize.Width() - TAB_TABOFFSET_X * 2,
239 aSize.Height() - TAB_TABOFFSET_Y * 2));
240 if(GetNativeControlRegion( ControlType::TabItem, ControlPart::Entire, aCtrlRegion,
241 ControlState::ENABLED, aControlValue, OUString(),
242 aBoundingRgn, aContentRgn ) )
244 return aContentRgn.GetSize();
247 // For languages with short names (e.g. Chinese), because the space is
248 // normally only one pixel per char
249 if ( pItem->maFormatText.getLength() < TAB_EXTRASPACE_X )
250 aSize.Width() += TAB_EXTRASPACE_X-pItem->maFormatText.getLength();
252 // shorten Text if needed
253 if ( aSize.Width()+4 >= nMaxWidth )
255 OUString aAppendStr("...");
256 pItem->maFormatText += aAppendStr;
259 pItem->maFormatText = pItem->maFormatText.replaceAt( pItem->maFormatText.getLength()-aAppendStr.getLength()-1, 1, "" );
260 aSize.Width() = GetCtrlTextWidth( pItem->maFormatText );
261 aSize.Width() += aImageSize.Width();
262 aSize.Width() += TAB_TABOFFSET_X*2;
264 while ( (aSize.Width()+4 >= nMaxWidth) && (pItem->maFormatText.getLength() > aAppendStr.getLength()) );
265 if ( aSize.Width()+4 >= nMaxWidth )
267 pItem->maFormatText = ".";
268 aSize.Width() = 1;
272 if( pItem->maFormatText.isEmpty() )
274 if( aSize.Height() < aImageSize.Height()+4 ) //leave space for focus rect
275 aSize.Height() = aImageSize.Height()+4;
278 return aSize;
281 // Feel free to move this to some more general place for reuse
282 // http://en.wikipedia.org/wiki/Word_wrap#Minimum_raggedness
283 // Mostly based on Alexey Frunze's nifty example at
284 // http://stackoverflow.com/questions/9071205/balanced-word-wrap-minimum-raggedness-in-php
285 namespace MinimumRaggednessWrap
287 std::deque<size_t> GetEndOfLineIndexes(const std::vector<sal_Int32>& rWidthsOf, sal_Int32 nLineWidth)
289 ++nLineWidth;
291 size_t nWidthsCount = rWidthsOf.size();
292 std::vector<sal_Int32> aCosts(nWidthsCount * nWidthsCount);
294 // cost function c(i, j) that computes the cost of a line consisting of
295 // the words Word[i] to Word[j]
296 for (size_t i = 0; i < nWidthsCount; ++i)
298 for (size_t j = 0; j < nWidthsCount; ++j)
300 if (j >= i)
302 sal_Int32 c = nLineWidth - (j - i);
303 for (size_t k = i; k <= j; ++k)
304 c -= rWidthsOf[k];
305 c = (c >= 0) ? c * c : SAL_MAX_INT32;
306 aCosts[j * nWidthsCount + i] = c;
308 else
310 aCosts[j * nWidthsCount + i] = SAL_MAX_INT32;
315 std::vector<sal_Int32> aFunction(nWidthsCount);
316 std::vector<sal_Int32> aWrapPoints(nWidthsCount);
318 // f(j) in aFunction[], collect wrap points in aWrapPoints[]
319 for (size_t j = 0; j < nWidthsCount; ++j)
321 aFunction[j] = aCosts[j * nWidthsCount];
322 if (aFunction[j] == SAL_MAX_INT32)
324 for (size_t k = 0; k < j; ++k)
326 sal_Int32 s;
327 if (aFunction[k] == SAL_MAX_INT32 || aCosts[j * nWidthsCount + k + 1] == SAL_MAX_INT32)
328 s = SAL_MAX_INT32;
329 else
330 s = aFunction[k] + aCosts[j * nWidthsCount + k + 1];
331 if (aFunction[j] > s)
333 aFunction[j] = s;
334 aWrapPoints[j] = k + 1;
340 std::deque<size_t> aSolution;
342 // no solution
343 if (aFunction[nWidthsCount - 1] == SAL_MAX_INT32)
344 return aSolution;
346 // optimal solution
347 size_t j = nWidthsCount - 1;
348 while (true)
350 aSolution.push_front(j);
351 if (!aWrapPoints[j])
352 break;
353 j = aWrapPoints[j] - 1;
356 return aSolution;
360 bool TabControl::ImplPlaceTabs( long nWidth )
362 if ( nWidth <= 0 )
363 return false;
364 if ( mpTabCtrlData->maItemList.empty() )
365 return false;
367 long nMaxWidth = nWidth;
369 const long nOffsetX = 2 + GetItemsOffset().X();
370 const long nOffsetY = 2 + GetItemsOffset().Y();
372 //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
373 //of ugly bare tabs on lines of their own
375 //collect widths
376 std::vector<sal_Int32> aWidths;
377 for( std::vector<ImplTabItem>::iterator it = mpTabCtrlData->maItemList.begin();
378 it != mpTabCtrlData->maItemList.end(); ++it )
380 aWidths.push_back(ImplGetItemSize( &(*it), nMaxWidth ).Width());
383 //aBreakIndexes will contain the indexes of the last tab on each row
384 std::deque<size_t> aBreakIndexes(MinimumRaggednessWrap::GetEndOfLineIndexes(aWidths, nMaxWidth - nOffsetX - 2));
386 if ( (mnMaxPageWidth > 0) && (mnMaxPageWidth < nMaxWidth) )
387 nMaxWidth = mnMaxPageWidth;
388 nMaxWidth -= GetItemsOffset().X();
390 long nX = nOffsetX;
391 long nY = nOffsetY;
393 sal_uInt16 nLines = 0;
394 sal_uInt16 nCurLine = 0;
396 long nLineWidthAry[100];
397 sal_uInt16 nLinePosAry[101];
398 nLineWidthAry[0] = 0;
399 nLinePosAry[0] = 0;
401 size_t nIndex = 0;
402 sal_uInt16 nPos = 0;
404 for( std::vector<ImplTabItem>::iterator it = mpTabCtrlData->maItemList.begin();
405 it != mpTabCtrlData->maItemList.end(); ++it, ++nIndex )
407 Size aSize = ImplGetItemSize( &(*it), nMaxWidth );
409 bool bNewLine = false;
410 if (!aBreakIndexes.empty() && nIndex > aBreakIndexes.front())
412 aBreakIndexes.pop_front();
413 bNewLine = true;
416 if ( bNewLine && (nWidth > 2+nOffsetX) )
418 if ( nLines == 99 )
419 break;
421 nX = nOffsetX;
422 nY += aSize.Height();
423 nLines++;
424 nLineWidthAry[nLines] = 0;
425 nLinePosAry[nLines] = nPos;
428 Rectangle aNewRect( Point( nX, nY ), aSize );
429 if ( mbSmallInvalidate && (it->maRect != aNewRect) )
430 mbSmallInvalidate = false;
431 it->maRect = aNewRect;
432 it->mnLine = nLines;
433 it->mbFullVisible = true;
435 nLineWidthAry[nLines] += aSize.Width();
436 nX += aSize.Width();
438 if ( it->mnId == mnCurPageId )
439 nCurLine = nLines;
441 nPos++;
444 if ( nLines )
445 { // two or more lines
446 long nLineHeightAry[100];
447 long nIH = mpTabCtrlData->maItemList[0].maRect.Bottom()-2;
449 for ( sal_uInt16 i = 0; i < nLines+1; i++ )
451 if ( i <= nCurLine )
452 nLineHeightAry[i] = nIH*(nLines-(nCurLine-i)) + GetItemsOffset().Y();
453 else
454 nLineHeightAry[i] = nIH*(i-nCurLine-1) + GetItemsOffset().Y();
457 nLinePosAry[nLines+1] = (sal_uInt16)mpTabCtrlData->maItemList.size();
459 long nDX = 0;
460 long nModDX = 0;
461 long nIDX = 0;
463 sal_uInt16 i = 0;
464 sal_uInt16 n = 0;
465 for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
466 it != mpTabCtrlData->maItemList.end(); ++it )
468 if ( i == nLinePosAry[n] )
470 if ( n == nLines+1 )
471 break;
473 nIDX = 0;
474 if( nLinePosAry[n+1]-i > 0 )
476 nDX = ( nWidth - nOffsetX - nLineWidthAry[n] ) / ( nLinePosAry[n+1] - i );
477 nModDX = ( nWidth - nOffsetX - nLineWidthAry[n] ) % ( nLinePosAry[n+1] - i );
479 else
481 // FIXME: this is a case of tabctrl way too small
482 nDX = 0;
483 nModDX = 0;
485 n++;
488 it->maRect.Left() += nIDX;
489 it->maRect.Right() += nIDX + nDX;
490 it->maRect.Top() = nLineHeightAry[n-1];
491 it->maRect.Bottom() = nLineHeightAry[n-1] + nIH;
492 nIDX += nDX;
494 if ( nModDX )
496 nIDX++;
497 it->maRect.Right()++;
498 nModDX--;
501 i++;
504 else
505 { // only one line
506 if(ImplGetSVData()->maNWFData.mbCenteredTabs)
508 int nRightSpace = nMaxWidth;//space left on the right by the tabs
509 for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
510 it != mpTabCtrlData->maItemList.end(); ++it )
512 nRightSpace -= it->maRect.Right()-it->maRect.Left();
514 for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
515 it != mpTabCtrlData->maItemList.end(); ++it )
517 it->maRect.Left() += nRightSpace / 2;
518 it->maRect.Right() += nRightSpace / 2;
523 return true;
526 Rectangle TabControl::ImplGetTabRect( sal_uInt16 nItemPos, long nWidth, long nHeight )
528 Size aWinSize = Control::GetOutputSizePixel();
529 if ( nWidth < 0 )
530 nWidth = aWinSize.Width();
531 if ( nHeight < 0 )
532 nHeight = aWinSize.Height();
534 if ( mpTabCtrlData->maItemList.empty() )
536 long nW = nWidth-TAB_OFFSET*2;
537 long nH = nHeight-TAB_OFFSET*2;
538 return (nW > 0 && nH > 0)
539 ? Rectangle( Point( TAB_OFFSET, TAB_OFFSET ), Size( nW, nH ) )
540 : Rectangle();
543 if ( nItemPos == TAB_PAGERECT )
545 sal_uInt16 nLastPos;
546 if ( mnCurPageId )
547 nLastPos = GetPagePos( mnCurPageId );
548 else
549 nLastPos = 0;
551 Rectangle aRect = ImplGetTabRect( nLastPos, nWidth, nHeight );
552 long nW = nWidth-TAB_OFFSET*2;
553 long nH = nHeight-aRect.Bottom()-TAB_OFFSET*2;
554 aRect = (nW > 0 && nH > 0)
555 ? Rectangle( Point( TAB_OFFSET, aRect.Bottom()+TAB_OFFSET ), Size( nW, nH ) )
556 : Rectangle();
557 return aRect;
560 nWidth -= 1;
562 if ( (nWidth <= 0) || (nHeight <= 0) )
563 return Rectangle();
565 if ( mbFormat || (mnLastWidth != nWidth) || (mnLastHeight != nHeight) )
567 vcl::Font aFont( GetFont() );
568 aFont.SetTransparent( true );
569 SetFont( aFont );
571 bool bRet = ImplPlaceTabs( nWidth );
572 if ( !bRet )
573 return Rectangle();
575 mnLastWidth = nWidth;
576 mnLastHeight = nHeight;
577 mbFormat = false;
580 return size_t(nItemPos) < mpTabCtrlData->maItemList.size() ? mpTabCtrlData->maItemList[nItemPos].maRect : Rectangle();
583 void TabControl::ImplChangeTabPage( sal_uInt16 nId, sal_uInt16 nOldId )
585 ImplFreeLayoutData();
587 ImplTabItem* pOldItem = ImplGetItem( nOldId );
588 ImplTabItem* pItem = ImplGetItem( nId );
589 TabPage* pOldPage = (pOldItem) ? pOldItem->mpTabPage.get() : nullptr;
590 TabPage* pPage = (pItem) ? pItem->mpTabPage.get() : nullptr;
591 vcl::Window* pCtrlParent = GetParent();
593 if ( IsReallyVisible() && IsUpdateMode() )
595 sal_uInt16 nPos = GetPagePos( nId );
596 Rectangle aRect = ImplGetTabRect( nPos );
598 if ( !pOldItem || !pItem || (pOldItem->mnLine != pItem->mnLine) )
600 aRect.Left() = 0;
601 aRect.Top() = 0;
602 aRect.Right() = Control::GetOutputSizePixel().Width();
604 else
606 aRect.Left() -= 3;
607 aRect.Top() -= 2;
608 aRect.Right() += 3;
609 Invalidate( aRect );
610 nPos = GetPagePos( nOldId );
611 aRect = ImplGetTabRect( nPos );
612 aRect.Left() -= 3;
613 aRect.Top() -= 2;
614 aRect.Right() += 3;
616 Invalidate( aRect );
619 if ( pOldPage == pPage )
620 return;
622 Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
624 if ( pOldPage )
626 if ( mbRestoreHelpId )
627 pCtrlParent->SetHelpId( OString() );
628 pOldPage->DeactivatePage();
631 if ( pPage )
633 if ( ( GetStyle() & WB_NOBORDER ) )
635 Rectangle aRectNoTab(Point(0, 0), GetSizePixel());
636 pPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
638 else
639 pPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
641 // activate page here so the controls can be switched
642 // also set the help id of the parent window to that of the tab page
643 if ( GetHelpId().isEmpty() )
645 mbRestoreHelpId = true;
646 pCtrlParent->SetHelpId( pPage->GetHelpId() );
649 pPage->ActivatePage();
650 pPage->Show();
652 if ( pOldPage && pOldPage->HasChildPathFocus() )
654 sal_uInt16 n = 0;
655 vcl::Window* pFirstChild = pPage->ImplGetDlgWindow( n, GetDlgWindowType::First );
656 if ( pFirstChild )
657 pFirstChild->ImplControlFocus( GetFocusFlags::Init );
658 else
659 GrabFocus();
663 if ( pOldPage )
664 pOldPage->Hide();
666 // Invalidate the same region that will be send to NWF
667 // to always allow for bitmap caching
668 // see Window::DrawNativeControl()
669 if( IsNativeControlSupported( ControlType::TabPane, ControlPart::Entire ) )
671 aRect.Left() -= TAB_OFFSET;
672 aRect.Top() -= TAB_OFFSET;
673 aRect.Right() += TAB_OFFSET;
674 aRect.Bottom() += TAB_OFFSET;
677 Invalidate( aRect );
680 bool TabControl::ImplPosCurTabPage()
682 // resize/position current TabPage
683 ImplTabItem* pItem = ImplGetItem( GetCurPageId() );
684 if ( pItem && pItem->mpTabPage )
686 if ( ( GetStyle() & WB_NOBORDER ) )
688 Rectangle aRectNoTab(Point(0, 0), GetSizePixel());
689 pItem->mpTabPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
690 return true;
692 Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
693 pItem->mpTabPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
694 return true;
697 return false;
700 void TabControl::ImplActivateTabPage( bool bNext )
702 sal_uInt16 nCurPos = GetPagePos( GetCurPageId() );
704 if ( bNext )
705 nCurPos = (nCurPos + 1) % GetPageCount();
706 else
708 if ( !nCurPos )
709 nCurPos = GetPageCount()-1;
710 else
711 nCurPos--;
714 SelectTabPage( GetPageId( nCurPos ) );
717 void TabControl::ImplShowFocus()
719 if ( !GetPageCount() || mpTabCtrlData->mpListBox )
720 return;
722 sal_uInt16 nCurPos = GetPagePos( mnCurPageId );
723 Rectangle aRect = ImplGetTabRect( nCurPos );
724 const ImplTabItem& rItem = mpTabCtrlData->maItemList[ nCurPos ];
725 Size aTabSize = aRect.GetSize();
726 Size aImageSize( 0, 0 );
727 long nTextHeight = GetTextHeight();
728 long nTextWidth = GetCtrlTextWidth( rItem.maFormatText );
729 sal_uInt16 nOff;
731 if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::Mono) )
732 nOff = 1;
733 else
734 nOff = 0;
736 if( !! rItem.maTabImage )
738 aImageSize = rItem.maTabImage.GetSizePixel();
739 if( !rItem.maFormatText.isEmpty() )
740 aImageSize.Width() += GetTextHeight()/4;
743 if( !rItem.maFormatText.isEmpty() )
745 // show focus around text
746 aRect.Left() = aRect.Left()+aImageSize.Width()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1-1;
747 aRect.Top() = aRect.Top()+((aTabSize.Height()-nTextHeight)/2)-1-1;
748 aRect.Right() = aRect.Left()+nTextWidth+2;
749 aRect.Bottom() = aRect.Top()+nTextHeight+2;
751 else
753 // show focus around image
754 long nXPos = aRect.Left()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1;
755 long nYPos = aRect.Top();
756 if( aImageSize.Height() < aRect.GetHeight() )
757 nYPos += (aRect.GetHeight() - aImageSize.Height())/2;
759 aRect.Left() = nXPos - 2;
760 aRect.Top() = nYPos - 2;
761 aRect.Right() = aRect.Left() + aImageSize.Width() + 4;
762 aRect.Bottom() = aRect.Top() + aImageSize.Height() + 4;
764 ShowFocus( aRect );
767 void TabControl::ImplDrawItem(vcl::RenderContext& rRenderContext, ImplTabItem* pItem, const Rectangle& rCurRect,
768 bool bFirstInGroup, bool bLastInGroup, bool /* bIsCurrentItem */ )
770 if (pItem->maRect.IsEmpty())
771 return;
773 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
774 Rectangle aRect = pItem->maRect;
775 long nLeftBottom = aRect.Bottom();
776 long nRightBottom = aRect.Bottom();
777 bool bLeftBorder = true;
778 bool bRightBorder = true;
779 sal_uInt16 nOff;
780 bool bNativeOK = false;
782 sal_uInt16 nOff2 = 0;
783 sal_uInt16 nOff3 = 0;
785 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
786 nOff = 1;
787 else
788 nOff = 0;
790 // if this is the active Page, we have to draw a little more
791 if (pItem->mnId == mnCurPageId)
793 nOff2 = 2;
794 if (!ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise)
795 nOff3 = 1;
797 else
799 Point aLeftTestPos = aRect.BottomLeft();
800 Point aRightTestPos = aRect.BottomRight();
801 if (aLeftTestPos.Y() == rCurRect.Bottom())
803 aLeftTestPos.X() -= 2;
804 if (rCurRect.IsInside(aLeftTestPos))
805 bLeftBorder = false;
806 aRightTestPos.X() += 2;
807 if (rCurRect.IsInside(aRightTestPos))
808 bRightBorder = false;
810 else
812 if (rCurRect.IsInside(aLeftTestPos))
813 nLeftBottom -= 2;
814 if (rCurRect.IsInside(aRightTestPos))
815 nRightBottom -= 2;
819 ControlState nState = ControlState::NONE;
821 if (pItem->mnId == mnCurPageId)
823 nState |= ControlState::SELECTED;
824 // only the selected item can be focussed
825 if (HasFocus())
826 nState |= ControlState::FOCUSED;
828 if (IsEnabled())
829 nState |= ControlState::ENABLED;
830 if (IsMouseOver() && pItem->maRect.IsInside(GetPointerPosPixel()))
832 nState |= ControlState::ROLLOVER;
833 for (std::vector<ImplTabItem>::iterator it = mpTabCtrlData->maItemList.begin();
834 it != mpTabCtrlData->maItemList.end(); ++it)
836 if( (&(*it) != pItem) && (it->maRect.IsInside(GetPointerPosPixel())))
838 nState &= ~ControlState::ROLLOVER; // avoid multiple highlighted tabs
839 break;
844 if ( (bNativeOK = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire)) )
846 TabitemValue tiValue(Rectangle(pItem->maRect.Left() + TAB_TABOFFSET_X,
847 pItem->maRect.Top() + TAB_TABOFFSET_Y,
848 pItem->maRect.Right() - TAB_TABOFFSET_X,
849 pItem->maRect.Bottom() - TAB_TABOFFSET_Y));
850 if (pItem->maRect.Left() < 5)
851 tiValue.mnAlignment |= TabitemFlags::LeftAligned;
852 if (pItem->maRect.Right() > mnLastWidth - 5)
853 tiValue.mnAlignment |= TabitemFlags::RightAligned;
854 if (bFirstInGroup)
855 tiValue.mnAlignment |= TabitemFlags::FirstInGroup;
856 if (bLastInGroup)
857 tiValue.mnAlignment |= TabitemFlags::LastInGroup;
859 Rectangle aCtrlRegion( pItem->maRect );
860 bNativeOK = rRenderContext.DrawNativeControl(ControlType::TabItem, ControlPart::Entire,
861 aCtrlRegion, nState, tiValue, OUString() );
864 if (!bNativeOK)
866 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
868 rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
869 rRenderContext.DrawPixel(Point(aRect.Left() + 1 - nOff2, aRect.Top() + 1 - nOff2)); // diagonally indented top-left pixel
870 if (bLeftBorder)
872 rRenderContext.DrawLine(Point(aRect.Left() - nOff2, aRect.Top() + 2 - nOff2),
873 Point(aRect.Left() - nOff2, nLeftBottom - 1));
875 rRenderContext.DrawLine(Point(aRect.Left() + 2 - nOff2, aRect.Top() - nOff2), // top line starting 2px from left border
876 Point(aRect.Right() + nOff2 - 3, aRect.Top() - nOff2)); // ending 3px from right border
878 if (bRightBorder)
880 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
881 rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 2, aRect.Top() + 1 - nOff2),
882 Point(aRect.Right() + nOff2 - 2, nRightBottom - 1));
884 rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
885 rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 1, aRect.Top() + 3 - nOff2),
886 Point(aRect.Right() + nOff2 - 1, nRightBottom - 1));
889 else
891 rRenderContext.SetLineColor(Color(COL_BLACK));
892 rRenderContext.DrawPixel(Point(aRect.Left() + 1 - nOff2, aRect.Top() + 1 - nOff2));
893 rRenderContext.DrawPixel(Point(aRect.Right() + nOff2 - 2, aRect.Top() + 1 - nOff2));
894 if (bLeftBorder)
896 rRenderContext.DrawLine(Point(aRect.Left() - nOff2, aRect.Top() + 2 - nOff2),
897 Point(aRect.Left() - nOff2, nLeftBottom - 1));
899 rRenderContext.DrawLine(Point(aRect.Left() + 2 - nOff2, aRect.Top() - nOff2),
900 Point(aRect.Right() - 3, aRect.Top() - nOff2));
901 if (bRightBorder)
903 rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 1, aRect.Top() + 2 - nOff2),
904 Point(aRect.Right() + nOff2 - 1, nRightBottom - 1));
909 // set font accordingly, current item is painted bold
910 // we set the font attributes always before drawing to be re-entrant (DrawNativeControl may trigger additional paints)
911 vcl::Font aFont(rRenderContext.GetFont());
912 aFont.SetTransparent(true);
913 rRenderContext.SetFont(aFont);
915 Size aTabSize = aRect.GetSize();
916 Size aImageSize(0, 0);
917 long nTextHeight = rRenderContext.GetTextHeight();
918 long nTextWidth = rRenderContext.GetCtrlTextWidth(pItem->maFormatText);
919 if (!!pItem->maTabImage)
921 aImageSize = pItem->maTabImage.GetSizePixel();
922 if (!pItem->maFormatText.isEmpty())
923 aImageSize.Width() += GetTextHeight() / 4;
925 long nXPos = aRect.Left() + ((aTabSize.Width() - nTextWidth - aImageSize.Width()) / 2) - nOff - nOff3;
926 long nYPos = aRect.Top() + ((aTabSize.Height() - nTextHeight) / 2) - nOff3;
927 if (!pItem->maFormatText.isEmpty())
929 DrawTextFlags nStyle = DrawTextFlags::Mnemonic;
930 if (!pItem->mbEnabled)
931 nStyle |= DrawTextFlags::Disable;
933 Color aColor(rStyleSettings.GetTabTextColor());
934 if (nState & ControlState::SELECTED)
935 aColor = rStyleSettings.GetTabHighlightTextColor();
936 else if (nState & ControlState::ROLLOVER)
937 aColor = rStyleSettings.GetTabRolloverTextColor();
939 Color aOldColor(rRenderContext.GetTextColor());
940 rRenderContext.SetTextColor(aColor);
942 const Rectangle aOutRect(nXPos + aImageSize.Width(), nYPos,
943 nXPos + aImageSize.Width() + nTextWidth, nYPos + nTextHeight);
944 DrawControlText(rRenderContext, aOutRect, pItem->maFormatText, nStyle,
945 nullptr, nullptr);
947 rRenderContext.SetTextColor(aOldColor);
950 if (!!pItem->maTabImage)
952 Point aImgTL( nXPos, aRect.Top() );
953 if (aImageSize.Height() < aRect.GetHeight())
954 aImgTL.Y() += (aRect.GetHeight() - aImageSize.Height()) / 2;
955 rRenderContext.DrawImage(aImgTL, pItem->maTabImage, pItem->mbEnabled ? DrawImageFlags::NONE : DrawImageFlags::Disable );
959 bool TabControl::ImplHandleKeyEvent( const KeyEvent& rKeyEvent )
961 bool bRet = false;
963 if ( GetPageCount() > 1 )
965 vcl::KeyCode aKeyCode = rKeyEvent.GetKeyCode();
966 sal_uInt16 nKeyCode = aKeyCode.GetCode();
968 if ( aKeyCode.IsMod1() )
970 if ( aKeyCode.IsShift() || (nKeyCode == KEY_PAGEUP) )
972 if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEUP) )
974 ImplActivateTabPage( false );
975 bRet = true;
978 else
980 if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEDOWN) )
982 ImplActivateTabPage( true );
983 bRet = true;
989 return bRet;
992 IMPL_LINK_NOARG(TabControl, ImplListBoxSelectHdl, ListBox&, void)
994 SelectTabPage( GetPageId( mpTabCtrlData->mpListBox->GetSelectEntryPos() ) );
997 IMPL_LINK( TabControl, ImplWindowEventListener, VclWindowEvent&, rEvent, void )
999 if ( rEvent.GetId() == VCLEVENT_WINDOW_KEYINPUT )
1001 // Do not handle events from TabControl or its children, which is done in Notify(), where the events can be consumed.
1002 if ( !IsWindowOrChild( rEvent.GetWindow() ) )
1004 KeyEvent* pKeyEvent = static_cast< KeyEvent* >(rEvent.GetData());
1005 ImplHandleKeyEvent( *pKeyEvent );
1010 void TabControl::MouseButtonDown( const MouseEvent& rMEvt )
1012 if( mpTabCtrlData->mpListBox.get() == nullptr )
1014 if( rMEvt.IsLeft() )
1016 sal_uInt16 nPageId = GetPageId( rMEvt.GetPosPixel() );
1017 ImplTabItem* pItem = ImplGetItem( nPageId );
1018 if( pItem && pItem->mbEnabled )
1019 SelectTabPage( nPageId );
1024 void TabControl::KeyInput( const KeyEvent& rKEvt )
1026 if( mpTabCtrlData->mpListBox )
1027 mpTabCtrlData->mpListBox->KeyInput( rKEvt );
1028 else if ( GetPageCount() > 1 )
1030 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1031 sal_uInt16 nKeyCode = aKeyCode.GetCode();
1033 if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_RIGHT) )
1035 bool bNext = (nKeyCode == KEY_RIGHT);
1036 ImplActivateTabPage( bNext );
1040 Control::KeyInput( rKEvt );
1043 void TabControl::Paint( vcl::RenderContext& rRenderContext, const Rectangle& rRect)
1045 if (!(GetStyle() & WB_NOBORDER))
1046 ImplPaint(rRenderContext, rRect);
1049 void TabControl::ImplPaint(vcl::RenderContext& rRenderContext, const Rectangle& rRect)
1051 HideFocus();
1053 // reformat if needed
1054 Rectangle aRect = ImplGetTabRect(TAB_PAGERECT);
1056 // find current item
1057 ImplTabItem* pCurItem = nullptr;
1058 for (std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
1059 it != mpTabCtrlData->maItemList.end(); ++it )
1061 if (it->mnId == mnCurPageId)
1063 pCurItem = &(*it);
1064 break;
1068 // Draw the TabPage border
1069 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1070 Rectangle aCurRect;
1071 aRect.Left() -= TAB_OFFSET;
1072 aRect.Top() -= TAB_OFFSET;
1073 aRect.Right() += TAB_OFFSET;
1074 aRect.Bottom() += TAB_OFFSET;
1076 // if we have an invisible tabpage or no tabpage at all the tabpage rect should be
1077 // increased to avoid round corners that might be drawn by a theme
1078 // in this case we're only interested in the top border of the tabpage because the tabitems are used
1079 // standalone (eg impress)
1080 bool bNoTabPage = false;
1081 TabPage* pCurPage = pCurItem ? pCurItem->mpTabPage.get() : nullptr;
1082 if (!pCurPage || !pCurPage->IsVisible())
1084 bNoTabPage = true;
1085 aRect.Left() -= 10;
1086 aRect.Right() += 10;
1089 if (rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire))
1091 const ImplControlValue aControlValue;
1093 ControlState nState = ControlState::ENABLED;
1094 if (!IsEnabled())
1095 nState &= ~ControlState::ENABLED;
1096 if (HasFocus())
1097 nState |= ControlState::FOCUSED;
1099 vcl::Region aClipRgn(rRenderContext.GetActiveClipRegion());
1100 aClipRgn.Intersect(aRect);
1101 if (!rRect.IsEmpty())
1102 aClipRgn.Intersect(rRect);
1104 if (!aClipRgn.IsEmpty())
1106 rRenderContext.DrawNativeControl(ControlType::TabPane, ControlPart::Entire,
1107 aRect, nState, aControlValue, OUString());
1110 if (rRenderContext.IsNativeControlSupported(ControlType::TabHeader, ControlPart::Entire))
1112 Rectangle aHeaderRect(aRect.Left(), 0, aRect.Right(), aRect.Top());
1114 aClipRgn = rRenderContext.GetActiveClipRegion();
1115 aClipRgn.Intersect(aHeaderRect);
1116 if (!rRect.IsEmpty())
1117 aClipRgn.Intersect(rRect);
1119 if (!aClipRgn.IsEmpty())
1121 rRenderContext.DrawNativeControl(ControlType::TabHeader, ControlPart::Entire,
1122 aHeaderRect, nState, aControlValue, OUString());
1126 else
1128 long nTopOff = 1;
1129 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
1130 rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
1131 else
1132 rRenderContext.SetLineColor(Color(COL_BLACK));
1133 if (pCurItem && !pCurItem->maRect.IsEmpty())
1135 aCurRect = pCurItem->maRect;
1136 rRenderContext.DrawLine(aRect.TopLeft(), Point(aCurRect.Left() - 2, aRect.Top()));
1137 if (aCurRect.Right() + 1 < aRect.Right())
1139 rRenderContext.DrawLine(Point(aCurRect.Right(), aRect.Top()), aRect.TopRight());
1141 else
1143 nTopOff = 0;
1146 else
1147 rRenderContext.DrawLine(aRect.TopLeft(), aRect.TopRight());
1149 rRenderContext.DrawLine(aRect.TopLeft(), aRect.BottomLeft());
1151 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
1153 // if we have not tab page the bottom line of the tab page
1154 // directly touches the tab items, so choose a color that fits seamlessly
1155 if (bNoTabPage)
1156 rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
1157 else
1158 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
1159 rRenderContext.DrawLine(Point(1, aRect.Bottom() - 1), Point(aRect.Right() - 1, aRect.Bottom() - 1));
1160 rRenderContext.DrawLine(Point(aRect.Right() - 1, aRect.Top() + nTopOff), Point(aRect.Right() - 1, aRect.Bottom() - 1));
1161 if (bNoTabPage)
1162 rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
1163 else
1164 rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
1165 rRenderContext.DrawLine(Point(0, aRect.Bottom()), Point(aRect.Right(), aRect.Bottom()));
1166 rRenderContext.DrawLine(Point(aRect.Right(), aRect.Top() + nTopOff), Point(aRect.Right(), aRect.Bottom()));
1168 else
1170 rRenderContext.DrawLine(aRect.TopRight(), aRect.BottomRight());
1171 rRenderContext.DrawLine(aRect.BottomLeft(), aRect.BottomRight());
1175 if (!mpTabCtrlData->maItemList.empty() && mpTabCtrlData->mpListBox == nullptr)
1177 // Some native toolkits (GTK+) draw tabs right-to-left, with an
1178 // overlap between adjacent tabs
1179 bool bDrawTabsRTL = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::TabsDrawRtl);
1180 ImplTabItem* pFirstTab = nullptr;
1181 ImplTabItem* pLastTab = nullptr;
1182 size_t idx;
1184 // Event though there is a tab overlap with GTK+, the first tab is not
1185 // overlapped on the left side. Other toolkits ignore this option.
1186 if (bDrawTabsRTL)
1188 pFirstTab = mpTabCtrlData->maItemList.data();
1189 pLastTab = pFirstTab + mpTabCtrlData->maItemList.size();
1190 idx = mpTabCtrlData->maItemList.size() - 1;
1192 else
1194 pLastTab = mpTabCtrlData->maItemList.data();
1195 pFirstTab = pLastTab + mpTabCtrlData->maItemList.size();
1196 idx = 0;
1199 while (idx < mpTabCtrlData->maItemList.size())
1201 ImplTabItem* pItem = &mpTabCtrlData->maItemList[idx];
1203 if (pItem != pCurItem)
1205 vcl::Region aClipRgn(rRenderContext.GetActiveClipRegion());
1206 aClipRgn.Intersect(pItem->maRect);
1207 if (!rRect.IsEmpty())
1208 aClipRgn.Intersect(rRect);
1209 if (!aClipRgn.IsEmpty())
1211 ImplDrawItem(rRenderContext, pItem, aCurRect, false/*bLayout*/,
1212 pItem == pFirstTab, pItem == pLastTab);
1216 if (bDrawTabsRTL)
1217 idx--;
1218 else
1219 idx++;
1222 if (pCurItem)
1224 vcl::Region aClipRgn(rRenderContext.GetActiveClipRegion());
1225 aClipRgn.Intersect(pCurItem->maRect);
1226 if (!rRect.IsEmpty())
1227 aClipRgn.Intersect(rRect);
1228 if (!aClipRgn.IsEmpty())
1230 ImplDrawItem(rRenderContext, pCurItem, aCurRect,
1231 pCurItem == pFirstTab, pCurItem == pLastTab, true);
1236 if (HasFocus())
1237 ImplShowFocus();
1239 mbSmallInvalidate = true;
1242 void TabControl::setAllocation(const Size &rAllocation)
1244 ImplFreeLayoutData();
1246 if ( !IsReallyShown() )
1247 return;
1249 if( mpTabCtrlData->mpListBox )
1251 // get the listbox' preferred size
1252 Size aTabCtrlSize( GetSizePixel() );
1253 long nPrefWidth = mpTabCtrlData->mpListBox->get_preferred_size().Width();
1254 if( nPrefWidth > aTabCtrlSize.Width() )
1255 nPrefWidth = aTabCtrlSize.Width();
1256 Size aNewSize( nPrefWidth, LogicToPixel( Size( 12, 12 ), MapMode( MapUnit::MapAppFont ) ).Height() );
1257 Point aNewPos( (aTabCtrlSize.Width() - nPrefWidth) / 2, 0 );
1258 mpTabCtrlData->mpListBox->SetPosSizePixel( aNewPos, aNewSize );
1261 mbFormat = true;
1263 // resize/position active TabPage
1264 bool bTabPage = ImplPosCurTabPage();
1266 // check what needs to be invalidated
1267 Size aNewSize = rAllocation;
1268 long nNewWidth = aNewSize.Width();
1269 for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
1270 it != mpTabCtrlData->maItemList.end(); ++it )
1272 if ( !it->mbFullVisible ||
1273 (it->maRect.Right()-2 >= nNewWidth) )
1275 mbSmallInvalidate = false;
1276 break;
1280 if ( mbSmallInvalidate )
1282 Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
1283 aRect.Left() -= TAB_OFFSET+TAB_BORDER_LEFT;
1284 aRect.Top() -= TAB_OFFSET+TAB_BORDER_TOP;
1285 aRect.Right() += TAB_OFFSET+TAB_BORDER_RIGHT;
1286 aRect.Bottom() += TAB_OFFSET+TAB_BORDER_BOTTOM;
1287 if ( bTabPage )
1288 Invalidate( aRect, InvalidateFlags::NoChildren );
1289 else
1290 Invalidate( aRect );
1293 else
1295 if ( bTabPage )
1296 Invalidate( InvalidateFlags::NoChildren );
1297 else
1298 Invalidate();
1301 mbLayoutDirty = false;
1304 void TabControl::SetPosSizePixel(const Point& rNewPos, const Size& rNewSize)
1306 Window::SetPosSizePixel(rNewPos, rNewSize);
1307 //if size changed, TabControl::Resize got called already
1308 if (mbLayoutDirty)
1309 setAllocation(rNewSize);
1312 void TabControl::SetSizePixel(const Size& rNewSize)
1314 Window::SetSizePixel(rNewSize);
1315 //if size changed, TabControl::Resize got called already
1316 if (mbLayoutDirty)
1317 setAllocation(rNewSize);
1320 void TabControl::SetPosPixel(const Point& rPos)
1322 Window::SetPosPixel(rPos);
1323 if (mbLayoutDirty)
1324 setAllocation(GetOutputSizePixel());
1327 void TabControl::Resize()
1329 setAllocation(Control::GetOutputSizePixel());
1332 void TabControl::GetFocus()
1334 if( ! mpTabCtrlData->mpListBox )
1336 ImplShowFocus();
1337 SetInputContext( InputContext( GetFont() ) );
1339 else
1341 if( mpTabCtrlData->mpListBox->IsReallyVisible() )
1342 mpTabCtrlData->mpListBox->GrabFocus();
1344 Control::GetFocus();
1347 void TabControl::LoseFocus()
1349 if( mpTabCtrlData && ! mpTabCtrlData->mpListBox )
1350 HideFocus();
1351 Control::LoseFocus();
1354 void TabControl::RequestHelp( const HelpEvent& rHEvt )
1356 sal_uInt16 nItemId = rHEvt.KeyboardActivated() ? mnCurPageId : GetPageId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
1358 if ( nItemId )
1360 if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
1362 OUString aStr = GetHelpText( nItemId );
1363 if ( !aStr.isEmpty() )
1365 Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1366 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1367 aItemRect.Left() = aPt.X();
1368 aItemRect.Top() = aPt.Y();
1369 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1370 aItemRect.Right() = aPt.X();
1371 aItemRect.Bottom() = aPt.Y();
1372 Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
1373 return;
1376 else if ( rHEvt.GetMode() & HelpEventMode::EXTENDED )
1378 OUString aHelpId( OStringToOUString( GetHelpId( nItemId ), RTL_TEXTENCODING_UTF8 ) );
1379 if ( !aHelpId.isEmpty() )
1381 // call Help if existing
1382 Help* pHelp = Application::GetHelp();
1383 if ( pHelp )
1384 pHelp->Start( aHelpId, this );
1385 return;
1389 // for Quick or Ballon Help, we show the text, if it is cut
1390 if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
1392 ImplTabItem* pItem = ImplGetItem( nItemId );
1393 const OUString& rStr = pItem->maText;
1394 if ( rStr != pItem->maFormatText )
1396 Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1397 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1398 aItemRect.Left() = aPt.X();
1399 aItemRect.Top() = aPt.Y();
1400 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1401 aItemRect.Right() = aPt.X();
1402 aItemRect.Bottom() = aPt.Y();
1403 if ( !rStr.isEmpty() )
1405 if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
1406 Help::ShowBalloon( this, aItemRect.Center(), aItemRect, rStr );
1407 else
1408 Help::ShowQuickHelp( this, aItemRect, rStr );
1409 return;
1414 if ( rHEvt.GetMode() & HelpEventMode::QUICK )
1416 ImplTabItem* pItem = ImplGetItem( nItemId );
1417 const OUString& rHelpText = pItem->maHelpText;
1418 // show tooltip if not text but image is set and helptext is available
1419 if ( !rHelpText.isEmpty() && pItem->maText.isEmpty() && !!pItem->maTabImage )
1421 Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1422 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1423 aItemRect.Left() = aPt.X();
1424 aItemRect.Top() = aPt.Y();
1425 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1426 aItemRect.Right() = aPt.X();
1427 aItemRect.Bottom() = aPt.Y();
1428 Help::ShowQuickHelp( this, aItemRect, rHelpText );
1429 return;
1434 Control::RequestHelp( rHEvt );
1437 void TabControl::Command( const CommandEvent& rCEvt )
1439 if( (mpTabCtrlData->mpListBox == nullptr) && (rCEvt.GetCommand() == CommandEventId::ContextMenu) && (GetPageCount() > 1) )
1441 Point aMenuPos;
1442 bool bMenu;
1443 if ( rCEvt.IsMouseEvent() )
1445 aMenuPos = rCEvt.GetMousePosPixel();
1446 bMenu = GetPageId( aMenuPos ) != 0;
1448 else
1450 aMenuPos = ImplGetTabRect( GetPagePos( mnCurPageId ) ).Center();
1451 bMenu = true;
1454 if ( bMenu )
1456 ScopedVclPtrInstance<PopupMenu> aMenu;
1457 for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
1458 it != mpTabCtrlData->maItemList.end(); ++it )
1460 aMenu->InsertItem( it->mnId, it->maText, MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK );
1461 if ( it->mnId == mnCurPageId )
1462 aMenu->CheckItem( it->mnId );
1463 aMenu->SetHelpId( it->mnId, it->maHelpId );
1466 sal_uInt16 nId = aMenu->Execute( this, aMenuPos );
1467 if ( nId && (nId != mnCurPageId) )
1468 SelectTabPage( nId );
1469 return;
1473 Control::Command( rCEvt );
1476 void TabControl::StateChanged( StateChangedType nType )
1478 Control::StateChanged( nType );
1480 if ( nType == StateChangedType::InitShow )
1482 ImplPosCurTabPage();
1483 if( mpTabCtrlData->mpListBox )
1484 Resize();
1486 else if ( nType == StateChangedType::UpdateMode )
1488 if ( IsUpdateMode() )
1489 Invalidate();
1491 else if ( (nType == StateChangedType::Zoom) ||
1492 (nType == StateChangedType::ControlFont) )
1494 ImplInitSettings( true, false, false );
1495 Invalidate();
1497 else if ( nType == StateChangedType::ControlForeground )
1499 ImplInitSettings( false, true, false );
1500 Invalidate();
1502 else if ( nType == StateChangedType::ControlBackground )
1504 ImplInitSettings( false, false, true );
1505 Invalidate();
1509 void TabControl::DataChanged( const DataChangedEvent& rDCEvt )
1511 Control::DataChanged( rDCEvt );
1513 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
1514 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
1515 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
1516 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
1518 ImplInitSettings( true, true, true );
1519 Invalidate();
1523 Rectangle* TabControl::ImplFindPartRect( const Point& rPt )
1525 ImplTabItem* pFoundItem = nullptr;
1526 int nFound = 0;
1527 for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
1528 it != mpTabCtrlData->maItemList.end(); ++it )
1530 if ( it->maRect.IsInside( rPt ) )
1532 // assure that only one tab is highlighted at a time
1533 nFound++;
1534 pFoundItem = &(*it);
1537 // assure that only one tab is highlighted at a time
1538 return nFound == 1 ? &pFoundItem->maRect : nullptr;
1541 bool TabControl::PreNotify( NotifyEvent& rNEvt )
1543 const MouseEvent* pMouseEvt = nullptr;
1545 if( (rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != nullptr )
1547 if( !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
1549 // trigger redraw if mouse over state has changed
1550 if( IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire) )
1552 Rectangle* pRect = ImplFindPartRect( GetPointerPosPixel() );
1553 Rectangle* pLastRect = ImplFindPartRect( GetLastPointerPosPixel() );
1554 if( pRect != pLastRect || (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow()) )
1556 vcl::Region aClipRgn;
1557 if( pLastRect )
1559 // allow for slightly bigger tabitems
1560 // as used by gtk
1561 // TODO: query for the correct sizes
1562 Rectangle aRect(*pLastRect);
1563 aRect.Left()-=2;
1564 aRect.Right()+=2;
1565 aRect.Top()-=3;
1566 aClipRgn.Union( aRect );
1568 if( pRect )
1570 // allow for slightly bigger tabitems
1571 // as used by gtk
1572 // TODO: query for the correct sizes
1573 Rectangle aRect(*pRect);
1574 aRect.Left()-=2;
1575 aRect.Right()+=2;
1576 aRect.Top()-=3;
1577 aClipRgn.Union( aRect );
1579 if( !aClipRgn.IsEmpty() )
1580 Invalidate( aClipRgn );
1586 return Control::PreNotify(rNEvt);
1589 bool TabControl::EventNotify( NotifyEvent& rNEvt )
1591 bool bRet = false;
1593 if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
1594 bRet = ImplHandleKeyEvent( *rNEvt.GetKeyEvent() );
1596 return bRet || Control::EventNotify( rNEvt );
1599 void TabControl::ActivatePage()
1601 maActivateHdl.Call( this );
1604 bool TabControl::DeactivatePage()
1606 return !maDeactivateHdl.IsSet() || maDeactivateHdl.Call( this );
1609 void TabControl::SetTabPageSizePixel( const Size& rSize )
1611 ImplFreeLayoutData();
1613 Size aNewSize( rSize );
1614 aNewSize.Width() += TAB_OFFSET*2;
1615 Rectangle aRect = ImplGetTabRect( TAB_PAGERECT,
1616 aNewSize.Width(), aNewSize.Height() );
1617 aNewSize.Height() += aRect.Top()+TAB_OFFSET;
1618 Window::SetOutputSizePixel( aNewSize );
1621 Size TabControl::GetTabPageSizePixel() const
1623 Rectangle aRect = const_cast<TabControl*>(this)->ImplGetTabRect( TAB_PAGERECT );
1624 return aRect.GetSize();
1627 void TabControl::InsertPage( sal_uInt16 nPageId, const OUString& rText,
1628 sal_uInt16 nPos )
1630 SAL_WARN_IF( !nPageId, "vcl", "TabControl::InsertPage(): PageId == 0" );
1631 SAL_WARN_IF( GetPagePos( nPageId ) != TAB_PAGE_NOTFOUND, "vcl",
1632 "TabControl::InsertPage(): PageId already exists" );
1634 // insert new page item
1635 ImplTabItem* pItem = nullptr;
1636 if( nPos == TAB_APPEND || size_t(nPos) >= mpTabCtrlData->maItemList.size() )
1638 mpTabCtrlData->maItemList.push_back( ImplTabItem() );
1639 pItem = &mpTabCtrlData->maItemList.back();
1640 if( mpTabCtrlData->mpListBox )
1641 mpTabCtrlData->mpListBox->InsertEntry( rText );
1643 else
1645 std::vector< ImplTabItem >::iterator new_it =
1646 mpTabCtrlData->maItemList.insert( mpTabCtrlData->maItemList.begin() + nPos, ImplTabItem() );
1647 pItem = &(*new_it);
1648 if( mpTabCtrlData->mpListBox )
1649 mpTabCtrlData->mpListBox->InsertEntry( rText, nPos);
1651 if( mpTabCtrlData->mpListBox )
1653 if( ! mnCurPageId )
1654 mpTabCtrlData->mpListBox->SelectEntryPos( 0 );
1655 mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
1658 // set current page id
1659 if ( !mnCurPageId )
1660 mnCurPageId = nPageId;
1662 // init new page item
1663 pItem->mnId = nPageId;
1664 pItem->mpTabPage = nullptr;
1665 pItem->maText = rText;
1666 pItem->mbFullVisible = false;
1668 mbFormat = true;
1669 if ( IsUpdateMode() )
1670 Invalidate();
1672 ImplFreeLayoutData();
1673 if( mpTabCtrlData->mpListBox ) // reposition/resize listbox
1674 Resize();
1676 CallEventListeners( VCLEVENT_TABPAGE_INSERTED, reinterpret_cast<void*>(nPageId) );
1679 void TabControl::RemovePage( sal_uInt16 nPageId )
1681 sal_uInt16 nPos = GetPagePos( nPageId );
1683 // does the item exist ?
1684 if ( nPos != TAB_PAGE_NOTFOUND )
1686 //remove page item
1687 std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin() + nPos;
1688 bool bIsCurrentPage = (it->mnId == mnCurPageId);
1689 mpTabCtrlData->maItemList.erase( it );
1690 if( mpTabCtrlData->mpListBox )
1692 mpTabCtrlData->mpListBox->RemoveEntry( nPos );
1693 mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
1696 // If current page is removed, than first page gets the current page
1697 if ( bIsCurrentPage )
1699 mnCurPageId = 0;
1701 if( ! mpTabCtrlData->maItemList.empty() )
1703 // don't do this by simply setting mnCurPageId to pFirstItem->mnId
1704 // this leaves a lot of stuff (such trivia as _showing_ the new current page) undone
1705 // instead, call SetCurPageId
1706 // without this, the next (outside) call to SetCurPageId with the id of the first page
1707 // will result in doing nothing (as we assume that nothing changed, then), and the page
1708 // will never be shown.
1709 // 86875 - 05/11/2001 - frank.schoenheit@germany.sun.com
1711 SetCurPageId( mpTabCtrlData->maItemList[0].mnId );
1715 mbFormat = true;
1716 if ( IsUpdateMode() )
1717 Invalidate();
1719 ImplFreeLayoutData();
1721 CallEventListeners( VCLEVENT_TABPAGE_REMOVED, reinterpret_cast<void*>(nPageId) );
1725 void TabControl::Clear()
1727 // clear item list
1728 mpTabCtrlData->maItemList.clear();
1729 mnCurPageId = 0;
1730 if( mpTabCtrlData->mpListBox )
1731 mpTabCtrlData->mpListBox->Clear();
1733 ImplFreeLayoutData();
1735 mbFormat = true;
1736 if ( IsUpdateMode() )
1737 Invalidate();
1739 CallEventListeners( VCLEVENT_TABPAGE_REMOVEDALL );
1742 void TabControl::EnablePage( sal_uInt16 i_nPageId, bool i_bEnable )
1744 ImplTabItem* pItem = ImplGetItem( i_nPageId );
1746 if ( pItem && pItem->mbEnabled != i_bEnable )
1748 pItem->mbEnabled = i_bEnable;
1749 mbFormat = true;
1750 if( mpTabCtrlData->mpListBox )
1751 mpTabCtrlData->mpListBox->SetEntryFlags( GetPagePos( i_nPageId ),
1752 i_bEnable ? ListBoxEntryFlags::NONE : (ListBoxEntryFlags::DisableSelection | ListBoxEntryFlags::DrawDisabled) );
1753 if( pItem->mnId == mnCurPageId )
1755 // SetCurPageId will change to an enabled page
1756 SetCurPageId( mnCurPageId );
1758 else if ( IsUpdateMode() )
1759 Invalidate();
1763 sal_uInt16 TabControl::GetPageCount() const
1765 return (sal_uInt16)mpTabCtrlData->maItemList.size();
1768 sal_uInt16 TabControl::GetPageId( sal_uInt16 nPos ) const
1770 if( size_t(nPos) < mpTabCtrlData->maItemList.size() )
1771 return mpTabCtrlData->maItemList[ nPos ].mnId;
1772 return 0;
1775 sal_uInt16 TabControl::GetPagePos( sal_uInt16 nPageId ) const
1777 for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
1778 it != mpTabCtrlData->maItemList.end(); ++it )
1780 if ( it->mnId == nPageId )
1781 return (sal_uInt16)(it - mpTabCtrlData->maItemList.begin());
1784 return TAB_PAGE_NOTFOUND;
1787 sal_uInt16 TabControl::GetPageId( const Point& rPos ) const
1789 for( size_t i = 0; i < mpTabCtrlData->maItemList.size(); ++i )
1791 if ( const_cast<TabControl*>(this)->ImplGetTabRect( static_cast<sal_uInt16>(i) ).IsInside( rPos ) )
1792 return mpTabCtrlData->maItemList[ i ].mnId;
1795 return 0;
1798 sal_uInt16 TabControl::GetPageId( const TabPage& rPage ) const
1800 for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
1801 it != mpTabCtrlData->maItemList.end(); ++it )
1803 if ( it->mpTabPage == &rPage )
1804 return it->mnId;
1807 return 0;
1810 sal_uInt16 TabControl::GetPageId( const OString& rName ) const
1812 for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
1813 it != mpTabCtrlData->maItemList.end(); ++it )
1815 if ( it->maTabName == rName )
1816 return it->mnId;
1819 return 0;
1822 void TabControl::SetCurPageId( sal_uInt16 nPageId )
1824 sal_uInt16 nPos = GetPagePos( nPageId );
1825 while( nPos != TAB_PAGE_NOTFOUND &&
1826 ! mpTabCtrlData->maItemList[nPos].mbEnabled )
1828 nPos++;
1829 if( size_t(nPos) >= mpTabCtrlData->maItemList.size() )
1830 nPos = 0;
1831 if( mpTabCtrlData->maItemList[nPos].mnId == nPageId )
1832 break;
1835 if( nPos != TAB_PAGE_NOTFOUND )
1837 nPageId = mpTabCtrlData->maItemList[nPos].mnId;
1838 if ( nPageId == mnCurPageId )
1840 if ( mnActPageId )
1841 mnActPageId = nPageId;
1842 return;
1845 if ( mnActPageId )
1846 mnActPageId = nPageId;
1847 else
1849 mbFormat = true;
1850 sal_uInt16 nOldId = mnCurPageId;
1851 mnCurPageId = nPageId;
1852 ImplChangeTabPage( nPageId, nOldId );
1857 sal_uInt16 TabControl::GetCurPageId() const
1859 if ( mnActPageId )
1860 return mnActPageId;
1861 else
1862 return mnCurPageId;
1865 void TabControl::SelectTabPage( sal_uInt16 nPageId )
1867 if ( nPageId && (nPageId != mnCurPageId) )
1869 ImplFreeLayoutData();
1871 CallEventListeners( VCLEVENT_TABPAGE_DEACTIVATE, reinterpret_cast<void*>(mnCurPageId) );
1872 if ( DeactivatePage() )
1874 mnActPageId = nPageId;
1875 ActivatePage();
1876 // Page could have been switched by the Activate handler
1877 nPageId = mnActPageId;
1878 mnActPageId = 0;
1879 SetCurPageId( nPageId );
1880 if( mpTabCtrlData->mpListBox )
1881 mpTabCtrlData->mpListBox->SelectEntryPos( GetPagePos( nPageId ) );
1882 CallEventListeners( VCLEVENT_TABPAGE_ACTIVATE, reinterpret_cast<void*>(nPageId) );
1887 void TabControl::SetTabPage( sal_uInt16 nPageId, TabPage* pTabPage )
1889 ImplTabItem* pItem = ImplGetItem( nPageId );
1891 if ( pItem && (pItem->mpTabPage.get() != pTabPage) )
1893 if ( pTabPage )
1895 SAL_WARN_IF( pTabPage->IsVisible() && !isLayoutEnabled(pTabPage), "vcl",
1896 "TabControl::SetTabPage() - Non-Layout Enabled Page is visible" );
1898 if ( IsDefaultSize() )
1899 SetTabPageSizePixel( pTabPage->GetSizePixel() );
1901 // only set here, so that Resize does not reposition TabPage
1902 pItem->mpTabPage = pTabPage;
1903 queue_resize();
1904 if ( pItem->mnId == mnCurPageId )
1905 ImplChangeTabPage( pItem->mnId, 0 );
1907 else
1909 pItem->mpTabPage = nullptr;
1910 queue_resize();
1915 TabPage* TabControl::GetTabPage( sal_uInt16 nPageId ) const
1917 ImplTabItem* pItem = ImplGetItem( nPageId );
1919 if ( pItem )
1920 return pItem->mpTabPage;
1921 else
1922 return nullptr;
1925 void TabControl::SetPageText( sal_uInt16 nPageId, const OUString& rText )
1927 ImplTabItem* pItem = ImplGetItem( nPageId );
1929 if ( pItem && pItem->maText != rText )
1931 pItem->maText = rText;
1932 mbFormat = true;
1933 if( mpTabCtrlData->mpListBox )
1935 sal_uInt16 nPos = GetPagePos( nPageId );
1936 mpTabCtrlData->mpListBox->RemoveEntry( nPos );
1937 mpTabCtrlData->mpListBox->InsertEntry( rText, nPos );
1939 if ( IsUpdateMode() )
1940 Invalidate();
1941 ImplFreeLayoutData();
1942 CallEventListeners( VCLEVENT_TABPAGE_PAGETEXTCHANGED, reinterpret_cast<void*>(nPageId) );
1946 OUString const & TabControl::GetPageText( sal_uInt16 nPageId ) const
1948 ImplTabItem* pItem = ImplGetItem( nPageId );
1950 assert( pItem );
1952 return pItem->maText;
1955 void TabControl::SetHelpText( sal_uInt16 nPageId, const OUString& rText )
1957 ImplTabItem* pItem = ImplGetItem( nPageId );
1959 assert( pItem );
1961 pItem->maHelpText = rText;
1964 const OUString& TabControl::GetHelpText( sal_uInt16 nPageId ) const
1966 ImplTabItem* pItem = ImplGetItem( nPageId );
1968 assert( pItem );
1970 if ( pItem->maHelpText.isEmpty() && !pItem->maHelpId.isEmpty() )
1972 Help* pHelp = Application::GetHelp();
1973 if ( pHelp )
1974 pItem->maHelpText = pHelp->GetHelpText( OStringToOUString( pItem->maHelpId, RTL_TEXTENCODING_UTF8 ), this );
1976 return pItem->maHelpText;
1979 void TabControl::SetHelpId( sal_uInt16 nPageId, const OString& rId ) const
1981 ImplTabItem* pItem = ImplGetItem( nPageId );
1983 if ( pItem )
1984 pItem->maHelpId = rId;
1987 OString TabControl::GetHelpId( sal_uInt16 nPageId ) const
1989 ImplTabItem* pItem = ImplGetItem( nPageId );
1991 if (pItem)
1992 return pItem->maHelpId;
1994 return OString();
1997 void TabControl::SetPageName( sal_uInt16 nPageId, const OString& rName ) const
1999 ImplTabItem* pItem = ImplGetItem( nPageId );
2001 if ( pItem )
2002 pItem->maTabName = rName;
2005 OString TabControl::GetPageName( sal_uInt16 nPageId ) const
2007 ImplTabItem* pItem = ImplGetItem( nPageId );
2009 if (pItem)
2010 return pItem->maTabName;
2012 return OString();
2015 void TabControl::SetPageImage( sal_uInt16 i_nPageId, const Image& i_rImage )
2017 ImplTabItem* pItem = ImplGetItem( i_nPageId );
2019 if ( pItem )
2021 pItem->maTabImage = i_rImage;
2022 mbFormat = true;
2023 if ( IsUpdateMode() )
2024 Invalidate();
2028 Rectangle TabControl::GetCharacterBounds( sal_uInt16 nPageId, long nIndex ) const
2030 Rectangle aRet;
2032 if( !HasLayoutData() || ! mpTabCtrlData->maLayoutPageIdToLine.size() )
2033 FillLayoutData();
2035 if( HasLayoutData() )
2037 std::unordered_map< int, int >::const_iterator it = mpTabCtrlData->maLayoutPageIdToLine.find( (int)nPageId );
2038 if( it != mpTabCtrlData->maLayoutPageIdToLine.end() )
2040 Pair aPair = mpControlData->mpLayoutData->GetLineStartEnd( it->second );
2041 if( (aPair.B() - aPair.A()) >= nIndex )
2042 aRet = mpControlData->mpLayoutData->GetCharacterBounds( aPair.A() + nIndex );
2046 return aRet;
2049 long TabControl::GetIndexForPoint( const Point& rPoint, sal_uInt16& rPageId ) const
2051 long nRet = -1;
2053 if( !HasLayoutData() || ! mpTabCtrlData->maLayoutPageIdToLine.size() )
2054 FillLayoutData();
2056 if( HasLayoutData() )
2058 int nIndex = mpControlData->mpLayoutData->GetIndexForPoint( rPoint );
2059 if( nIndex != -1 )
2061 // what line (->pageid) is this index in ?
2062 int nLines = mpControlData->mpLayoutData->GetLineCount();
2063 int nLine = -1;
2064 while( ++nLine < nLines )
2066 Pair aPair = mpControlData->mpLayoutData->GetLineStartEnd( nLine );
2067 if( aPair.A() <= nIndex && aPair.B() >= nIndex )
2069 nRet = nIndex - aPair.A();
2070 rPageId = (sal_uInt16)mpTabCtrlData->maLayoutLineToPageId[ nLine ];
2071 break;
2077 return nRet;
2080 void TabControl::FillLayoutData() const
2082 mpTabCtrlData->maLayoutLineToPageId.clear();
2083 mpTabCtrlData->maLayoutPageIdToLine.clear();
2084 const_cast<TabControl*>(this)->Invalidate();
2087 Rectangle TabControl::GetTabBounds( sal_uInt16 nPageId ) const
2089 Rectangle aRet;
2091 ImplTabItem* pItem = ImplGetItem( nPageId );
2092 if(pItem)
2093 aRet = pItem->maRect;
2095 return aRet;
2098 void TabControl::SetItemsOffset( const Point& rOffs )
2100 if( mpTabCtrlData )
2101 mpTabCtrlData->maItemsOffset = rOffs;
2104 Point TabControl::GetItemsOffset() const
2106 if( mpTabCtrlData )
2107 return mpTabCtrlData->maItemsOffset;
2108 else
2109 return Point();
2112 Size TabControl::calculateRequisition() const
2114 Size aOptimalPageSize(0, 0);
2116 sal_uInt16 nOrigPageId = GetCurPageId();
2117 for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
2118 it != mpTabCtrlData->maItemList.end(); ++it )
2120 const TabPage *pPage = it->mpTabPage;
2121 //it's a real nuisance if the page is not inserted yet :-(
2122 //We need to force all tabs to exist to get overall optimal size for dialog
2123 if (!pPage)
2125 TabControl *pThis = const_cast<TabControl*>(this);
2126 pThis->SetCurPageId(it->mnId);
2127 pThis->ActivatePage();
2128 pPage = it->mpTabPage;
2131 if (!pPage)
2132 continue;
2134 Size aPageSize(VclContainer::getLayoutRequisition(*pPage));
2136 if (aPageSize.Width() > aOptimalPageSize.Width())
2137 aOptimalPageSize.Width() = aPageSize.Width();
2138 if (aPageSize.Height() > aOptimalPageSize.Height())
2139 aOptimalPageSize.Height() = aPageSize.Height();
2142 //fdo#61940 If we were forced to activate pages in order to on-demand
2143 //create them to get their optimal size, then switch back to the original
2144 //page and re-activate it
2145 if (nOrigPageId != GetCurPageId())
2147 TabControl *pThis = const_cast<TabControl*>(this);
2148 pThis->SetCurPageId(nOrigPageId);
2149 pThis->ActivatePage();
2152 long nTabLabelsBottom = 0, nTabLabelsRight = 0;
2153 for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
2154 it != mpTabCtrlData->maItemList.end(); ++it )
2156 TabControl* pThis = const_cast<TabControl*>(this);
2158 sal_uInt16 nPos = it - mpTabCtrlData->maItemList.begin();
2159 Rectangle aTabRect = pThis->ImplGetTabRect(nPos, aOptimalPageSize.Width(), LONG_MAX);
2160 if (aTabRect.Bottom() > nTabLabelsBottom)
2161 nTabLabelsBottom = aTabRect.Bottom();
2162 if (aTabRect.Right() > nTabLabelsRight)
2163 nTabLabelsRight = aTabRect.Right();
2166 Size aOptimalSize(aOptimalPageSize);
2167 aOptimalSize.Height() += nTabLabelsBottom;
2168 aOptimalSize.Width() = std::max(nTabLabelsRight, aOptimalSize.Width());
2170 aOptimalSize.Width() += TAB_OFFSET * 2;
2171 aOptimalSize.Height() += TAB_OFFSET * 2;
2173 return aOptimalSize;
2176 Size TabControl::GetOptimalSize() const
2178 return calculateRequisition();
2181 void TabControl::queue_resize(StateChangedType eReason)
2183 mbLayoutDirty = true;
2184 Window::queue_resize(eReason);
2187 std::vector<sal_uInt16> TabControl::GetPageIDs() const
2189 std::vector<sal_uInt16> aIDs;
2190 for (auto itr = mpTabCtrlData->maItemList.begin(), itrEnd = mpTabCtrlData->maItemList.end();
2191 itr != itrEnd; ++itr)
2193 aIDs.push_back(itr->mnId);
2196 return aIDs;
2199 FactoryFunction TabControl::GetUITestFactory() const
2201 return TabControlUIObject::create;
2204 VCL_BUILDER_FACTORY(NotebookbarTabControl);
2206 sal_uInt16 NotebookbarTabControl::m_nHeaderHeight = 0;
2208 NotebookbarTabControl::NotebookbarTabControl(vcl::Window* pParent)
2209 : TabControl(pParent, WB_STDTABCONTROL)
2210 , bLastContextWasSupported(true)
2211 , eLastContext(vcl::EnumContext::Context::Context_Any)
2213 LanguageTag aLocale( Application::GetSettings().GetUILanguageTag());
2214 ResMgr* pResMgr = ResMgr::SearchCreateResMgr( "vcl", aLocale );
2216 Bitmap aBitmap;
2217 if( pResMgr )
2218 aBitmap = Bitmap( ResId( SV_RESID_BITMAP_NOTEBOOKBAR, *pResMgr ) );
2220 InsertPage(1, "");
2221 SetPageImage(1, Image(aBitmap));
2224 void NotebookbarTabControl::SetContext( vcl::EnumContext::Context eContext )
2226 if (eLastContext != eContext)
2228 bool bHandled = false;
2230 for (int nChild = 0; nChild < GetChildCount(); ++nChild)
2232 TabPage* pPage = static_cast<TabPage*>(GetChild(nChild));
2234 if (pPage->HasContext(eContext) || pPage->HasContext(vcl::EnumContext::Context::Context_Any))
2235 EnablePage(nChild + 2);
2236 else
2237 EnablePage(nChild + 2, false);
2239 if (!bHandled && bLastContextWasSupported
2240 && pPage->HasContext(vcl::EnumContext::Context::Context_Default))
2242 SetCurPageId(nChild + 2);
2245 if (pPage->HasContext(eContext) && eContext != vcl::EnumContext::Context::Context_Any)
2247 SetCurPageId(nChild + 2);
2248 bHandled = true;
2249 bLastContextWasSupported = true;
2253 if (!bHandled)
2254 bLastContextWasSupported = false;
2255 eLastContext = eContext;
2259 void NotebookbarTabControl::SetIconClickHdl( Link<NotebookBar*, void> aHdl )
2261 m_aIconClickHdl = aHdl;
2264 sal_uInt16 NotebookbarTabControl::GetPageId( const Point& rPos ) const
2266 for( size_t i = 0; i < mpTabCtrlData->maItemList.size(); ++i )
2268 if ( const_cast<NotebookbarTabControl*>(this)->ImplGetTabRect( static_cast<sal_uInt16>(i) ).IsInside( rPos ) )
2269 if ( mpTabCtrlData->maItemList[ i ].mbEnabled )
2270 return mpTabCtrlData->maItemList[ i ].mnId;
2273 return 0;
2276 void NotebookbarTabControl::SelectTabPage( sal_uInt16 nPageId )
2278 if ( nPageId == 1 )
2279 m_aIconClickHdl.Call( static_cast<NotebookBar*>(GetParent()) );
2280 else
2281 TabControl::SelectTabPage( nPageId );
2284 void NotebookbarTabControl::SetCurPageId( sal_uInt16 nPageId )
2286 if ( nPageId != 1 )
2287 TabControl::SetCurPageId( nPageId );
2290 sal_uInt16 NotebookbarTabControl::GetHeaderHeight()
2292 return m_nHeaderHeight;
2295 bool NotebookbarTabControl::ImplPlaceTabs( long nWidth )
2297 if ( nWidth <= 0 )
2298 return false;
2299 if ( mpTabCtrlData->maItemList.empty() )
2300 return false;
2302 long nMaxWidth = nWidth;
2304 const long nOffsetX = 2 + GetItemsOffset().X();
2305 const long nOffsetY = 2 + GetItemsOffset().Y();
2307 //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
2308 //of ugly bare tabs on lines of their own
2310 //collect widths
2311 std::vector<sal_Int32> aWidths;
2312 for( std::vector<ImplTabItem>::iterator it = mpTabCtrlData->maItemList.begin();
2313 it != mpTabCtrlData->maItemList.end(); ++it )
2315 aWidths.push_back(ImplGetItemSize( &(*it), nMaxWidth ).Width());
2318 //aBreakIndexes will contain the indexes of the last tab on each row
2319 std::deque<size_t> aBreakIndexes(MinimumRaggednessWrap::GetEndOfLineIndexes(aWidths, nMaxWidth - nOffsetX - 2));
2321 if ( (mnMaxPageWidth > 0) && (mnMaxPageWidth < nMaxWidth) )
2322 nMaxWidth = mnMaxPageWidth;
2323 nMaxWidth -= GetItemsOffset().X();
2325 long nX = nOffsetX;
2326 long nY = nOffsetY;
2328 sal_uInt16 nLines = 0;
2329 sal_uInt16 nCurLine = 0;
2331 long nLineWidthAry[100];
2332 sal_uInt16 nLinePosAry[101];
2333 nLineWidthAry[0] = 0;
2334 nLinePosAry[0] = 0;
2336 size_t nIndex = 0;
2337 sal_uInt16 nPos = 0;
2338 sal_uInt16 nHiddenWidth = 0;
2340 for( std::vector<ImplTabItem>::iterator it = mpTabCtrlData->maItemList.begin();
2341 it != mpTabCtrlData->maItemList.end(); ++it, ++nIndex )
2343 Size aSize = ImplGetItemSize( &(*it), nMaxWidth );
2345 bool bNewLine = false;
2346 if (!aBreakIndexes.empty() && nIndex > aBreakIndexes.front())
2348 aBreakIndexes.pop_front();
2349 bNewLine = true;
2352 if ( bNewLine && (nWidth > 2+nOffsetX) )
2354 if ( nLines == 99 )
2355 break;
2357 nX = nOffsetX;
2358 nY += aSize.Height();
2359 nLines++;
2360 nLineWidthAry[nLines] = 0;
2361 nLinePosAry[nLines] = nPos;
2364 Rectangle aNewRect( Point( nX, nY ), aSize );
2365 if ( mbSmallInvalidate && (it->maRect != aNewRect) )
2366 mbSmallInvalidate = false;
2368 // don't show empty space when tab is hidden, move next tabs to the left
2369 if ( it->mpTabPage && !it->mpTabPage->HasContext(vcl::EnumContext::Context_Any) )
2371 aNewRect.setX(aNewRect.getX() - nHiddenWidth);
2372 nHiddenWidth += aNewRect.getWidth();
2375 it->maRect = aNewRect;
2376 it->mnLine = nLines;
2377 it->mbFullVisible = true;
2379 nLineWidthAry[nLines] += aSize.Width();
2380 nX += aSize.Width();
2382 if ( it->mnId == mnCurPageId )
2383 nCurLine = nLines;
2385 nPos++;
2388 if ( nLines )
2389 { // two or more lines
2390 long nLineHeightAry[100];
2391 long nIH = mpTabCtrlData->maItemList[0].maRect.Bottom()-2;
2393 for ( sal_uInt16 i = 0; i < nLines+1; i++ )
2395 if ( i <= nCurLine )
2396 nLineHeightAry[i] = nIH*(nLines-(nCurLine-i)) + GetItemsOffset().Y();
2397 else
2398 nLineHeightAry[i] = nIH*(i-nCurLine-1) + GetItemsOffset().Y();
2401 nLinePosAry[nLines+1] = (sal_uInt16)mpTabCtrlData->maItemList.size();
2403 long nDX = 0;
2404 long nModDX = 0;
2405 long nIDX = 0;
2407 sal_uInt16 i = 0;
2408 sal_uInt16 n = 0;
2409 for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
2410 it != mpTabCtrlData->maItemList.end(); ++it )
2412 if ( i == nLinePosAry[n] )
2414 if ( n == nLines+1 )
2415 break;
2417 nIDX = 0;
2418 if( nLinePosAry[n+1]-i > 0 )
2420 nDX = ( nWidth - nOffsetX - nLineWidthAry[n] ) / ( nLinePosAry[n+1] - i );
2421 nModDX = ( nWidth - nOffsetX - nLineWidthAry[n] ) % ( nLinePosAry[n+1] - i );
2423 else
2425 // FIXME: this is a case of tabctrl way too small
2426 nDX = 0;
2427 nModDX = 0;
2429 n++;
2432 it->maRect.Left() += nIDX;
2433 it->maRect.Right() += nIDX + nDX;
2434 it->maRect.Top() = nLineHeightAry[n-1];
2435 it->maRect.Bottom() = nLineHeightAry[n-1] + nIH;
2436 nIDX += nDX;
2438 if ( nModDX )
2440 nIDX++;
2441 it->maRect.Right()++;
2442 nModDX--;
2445 i++;
2448 else
2449 { // only one line
2450 if(ImplGetSVData()->maNWFData.mbCenteredTabs)
2452 int nRightSpace = nMaxWidth;//space left on the right by the tabs
2453 for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
2454 it != mpTabCtrlData->maItemList.end(); ++it )
2456 nRightSpace -= it->maRect.Right()-it->maRect.Left();
2458 for( std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
2459 it != mpTabCtrlData->maItemList.end(); ++it )
2461 it->maRect.Left() += nRightSpace / 2;
2462 it->maRect.Right() += nRightSpace / 2;
2467 return true;
2470 void NotebookbarTabControl::ImplPaint(vcl::RenderContext& rRenderContext, const Rectangle& rRect)
2472 HideFocus();
2474 // reformat if needed
2475 Rectangle aRect = ImplGetTabRect(TAB_PAGERECT);
2477 // find current item
2478 ImplTabItem* pCurItem = nullptr;
2479 for (std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin();
2480 it != mpTabCtrlData->maItemList.end(); ++it )
2482 if (it->mnId == mnCurPageId)
2484 pCurItem = &(*it);
2485 break;
2489 // Draw the TabPage border
2490 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2491 Rectangle aCurRect;
2492 aRect.Left() -= TAB_OFFSET;
2493 aRect.Top() -= TAB_OFFSET;
2494 aRect.Right() += TAB_OFFSET;
2495 aRect.Bottom() += TAB_OFFSET;
2497 // if we have an invisible tabpage or no tabpage at all the tabpage rect should be
2498 // increased to avoid round corners that might be drawn by a theme
2499 // in this case we're only interested in the top border of the tabpage because the tabitems are used
2500 // standalone (eg impress)
2501 bool bNoTabPage = false;
2502 TabPage* pCurPage = pCurItem ? pCurItem->mpTabPage.get() : nullptr;
2503 if (!pCurPage || !pCurPage->IsVisible())
2505 bNoTabPage = true;
2506 aRect.Left() -= 10;
2507 aRect.Right() += 10;
2510 if (rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire))
2512 const ImplControlValue aControlValue;
2514 ControlState nState = ControlState::ENABLED;
2515 if (!IsEnabled())
2516 nState &= ~ControlState::ENABLED;
2517 if (HasFocus())
2518 nState |= ControlState::FOCUSED;
2520 vcl::Region aClipRgn(rRenderContext.GetActiveClipRegion());
2521 aClipRgn.Intersect(aRect);
2522 if (!rRect.IsEmpty())
2523 aClipRgn.Intersect(rRect);
2525 if (!aClipRgn.IsEmpty())
2527 rRenderContext.DrawNativeControl(ControlType::TabPane, ControlPart::Entire,
2528 aRect, nState, aControlValue, OUString());
2531 if (rRenderContext.IsNativeControlSupported(ControlType::TabHeader, ControlPart::Entire))
2533 Rectangle aHeaderRect(aRect.Left(), 0, aRect.Right(), aRect.Top());
2535 aClipRgn = rRenderContext.GetActiveClipRegion();
2536 aClipRgn.Intersect(aHeaderRect);
2537 if (!rRect.IsEmpty())
2538 aClipRgn.Intersect(rRect);
2540 if (!aClipRgn.IsEmpty())
2542 rRenderContext.DrawNativeControl(ControlType::TabHeader, ControlPart::Entire,
2543 aHeaderRect, nState, aControlValue, OUString());
2547 else
2549 long nTopOff = 1;
2550 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
2551 rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
2552 else
2553 rRenderContext.SetLineColor(Color(COL_BLACK));
2554 if (pCurItem && !pCurItem->maRect.IsEmpty())
2556 aCurRect = pCurItem->maRect;
2557 rRenderContext.DrawLine(aRect.TopLeft(), Point(aCurRect.Left() - 2, aRect.Top()));
2558 if (aCurRect.Right() + 1 < aRect.Right())
2560 rRenderContext.DrawLine(Point(aCurRect.Right(), aRect.Top()), aRect.TopRight());
2562 else
2564 nTopOff = 0;
2567 else
2568 rRenderContext.DrawLine(aRect.TopLeft(), aRect.TopRight());
2570 rRenderContext.DrawLine(aRect.TopLeft(), aRect.BottomLeft());
2572 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
2574 // if we have not tab page the bottom line of the tab page
2575 // directly touches the tab items, so choose a color that fits seamlessly
2576 if (bNoTabPage)
2577 rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
2578 else
2579 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
2580 rRenderContext.DrawLine(Point(1, aRect.Bottom() - 1), Point(aRect.Right() - 1, aRect.Bottom() - 1));
2581 rRenderContext.DrawLine(Point(aRect.Right() - 1, aRect.Top() + nTopOff), Point(aRect.Right() - 1, aRect.Bottom() - 1));
2582 if (bNoTabPage)
2583 rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
2584 else
2585 rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
2586 rRenderContext.DrawLine(Point(0, aRect.Bottom()), Point(aRect.Right(), aRect.Bottom()));
2587 rRenderContext.DrawLine(Point(aRect.Right(), aRect.Top() + nTopOff), Point(aRect.Right(), aRect.Bottom()));
2589 else
2591 rRenderContext.DrawLine(aRect.TopRight(), aRect.BottomRight());
2592 rRenderContext.DrawLine(aRect.BottomLeft(), aRect.BottomRight());
2596 if (!mpTabCtrlData->maItemList.empty() && mpTabCtrlData->mpListBox == nullptr)
2598 // Some native toolkits (GTK+) draw tabs right-to-left, with an
2599 // overlap between adjacent tabs
2600 bool bDrawTabsRTL = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::TabsDrawRtl);
2601 ImplTabItem* pFirstTab = nullptr;
2602 ImplTabItem* pLastTab = nullptr;
2603 size_t idx;
2605 // Event though there is a tab overlap with GTK+, the first tab is not
2606 // overlapped on the left side. Other toolkits ignore this option.
2607 if (bDrawTabsRTL)
2609 pFirstTab = mpTabCtrlData->maItemList.data();
2610 pLastTab = pFirstTab + mpTabCtrlData->maItemList.size();
2611 idx = mpTabCtrlData->maItemList.size() - 1;
2613 else
2615 pLastTab = mpTabCtrlData->maItemList.data();
2616 pFirstTab = pLastTab + mpTabCtrlData->maItemList.size();
2617 idx = 0;
2620 while (idx < mpTabCtrlData->maItemList.size())
2622 ImplTabItem* pItem = &mpTabCtrlData->maItemList[idx];
2624 if ((pItem != pCurItem) && (pItem->mbEnabled))
2626 vcl::Region aClipRgn(rRenderContext.GetActiveClipRegion());
2627 aClipRgn.Intersect(pItem->maRect);
2628 if (!rRect.IsEmpty())
2629 aClipRgn.Intersect(rRect);
2630 if (!aClipRgn.IsEmpty())
2632 ImplDrawItem(rRenderContext, pItem, aCurRect, false/*bLayout*/,
2633 pItem == pFirstTab, pItem == pLastTab);
2637 if (bDrawTabsRTL)
2638 idx--;
2639 else
2640 idx++;
2643 if (pCurItem)
2645 vcl::Region aClipRgn(rRenderContext.GetActiveClipRegion());
2646 aClipRgn.Intersect(pCurItem->maRect);
2647 if (!rRect.IsEmpty())
2648 aClipRgn.Intersect(rRect);
2649 if (!aClipRgn.IsEmpty())
2651 ImplDrawItem(rRenderContext, pCurItem, aCurRect,
2652 pCurItem == pFirstTab, pCurItem == pLastTab, true);
2657 if (HasFocus())
2658 ImplShowFocus();
2660 mbSmallInvalidate = true;
2663 Size NotebookbarTabControl::calculateRequisition() const
2665 Size aOptimalPageSize(0, 0);
2667 sal_uInt16 nOrigPageId = GetCurPageId();
2668 for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
2669 it != mpTabCtrlData->maItemList.end(); ++it )
2671 const TabPage *pPage = it->mpTabPage;
2672 //it's a real nuisance if the page is not inserted yet :-(
2673 //We need to force all tabs to exist to get overall optimal size for dialog
2674 if (!pPage)
2676 NotebookbarTabControl *pThis = const_cast<NotebookbarTabControl*>(this);
2677 pThis->SetCurPageId(it->mnId);
2678 pThis->ActivatePage();
2679 pPage = it->mpTabPage;
2682 if (!pPage)
2683 continue;
2685 Size aPageSize(VclContainer::getLayoutRequisition(*pPage));
2687 if (aPageSize.Width() > aOptimalPageSize.Width())
2688 aOptimalPageSize.Width() = aPageSize.Width();
2689 if (aPageSize.Height() > aOptimalPageSize.Height())
2690 aOptimalPageSize.Height() = aPageSize.Height();
2693 //fdo#61940 If we were forced to activate pages in order to on-demand
2694 //create them to get their optimal size, then switch back to the original
2695 //page and re-activate it
2696 if (nOrigPageId != GetCurPageId())
2698 NotebookbarTabControl *pThis = const_cast<NotebookbarTabControl*>(this);
2699 pThis->SetCurPageId(nOrigPageId);
2700 pThis->ActivatePage();
2703 long nTabLabelsBottom = 0, nTabLabelsRight = 0;
2704 for( std::vector< ImplTabItem >::const_iterator it = mpTabCtrlData->maItemList.begin();
2705 it != mpTabCtrlData->maItemList.end(); ++it )
2707 NotebookbarTabControl* pThis = const_cast<NotebookbarTabControl*>(this);
2709 sal_uInt16 nPos = it - mpTabCtrlData->maItemList.begin();
2710 Rectangle aTabRect = pThis->ImplGetTabRect(nPos, aOptimalPageSize.Width(), LONG_MAX);
2711 if (aTabRect.Bottom() > nTabLabelsBottom)
2713 nTabLabelsBottom = aTabRect.Bottom();
2714 m_nHeaderHeight = aTabRect.Bottom();
2716 if (aTabRect.Right() > nTabLabelsRight)
2717 nTabLabelsRight = aTabRect.Right();
2720 Size aOptimalSize(aOptimalPageSize);
2721 aOptimalSize.Height() += nTabLabelsBottom;
2722 aOptimalSize.Width() = std::max(nTabLabelsRight, aOptimalSize.Width());
2724 aOptimalSize.Width() += TAB_OFFSET * 2;
2725 aOptimalSize.Height() += TAB_OFFSET * 2;
2727 return aOptimalSize;
2730 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */