1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
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>
38 #include "controldata.hxx"
42 #include <unordered_map>
48 VclPtr
<TabPage
> mpTabPage
;
50 OUString maFormatText
;
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
)
82 if ( !(nStyle
& WB_NOTABSTOP
) )
84 if ( !(nStyle
& WB_NOGROUP
) )
86 if ( !(nStyle
& WB_NODIALOGCONTROL
) )
87 nStyle
|= WB_DIALOGCONTROL
;
89 Control::ImplInit( pParent
, nStyle
, nullptr );
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
);
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 );
151 ImplGetWindowImpl()->mbUseNativeFocus
= ImplGetSVData()->maNWFData
.mbNoFocusRects
;
155 EnableChildTransparentMode( false );
157 SetPaintTransparent( false );
159 if ( IsControlBackground() )
160 SetBackground( GetControlBackground() );
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()
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
199 mpTabCtrlData
->mpListBox
.disposeAndClear();
200 delete mpTabCtrlData
;
201 mpTabCtrlData
= nullptr;
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
)
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
= ".";
272 if( pItem
->maFormatText
.isEmpty() )
274 if( aSize
.Height() < aImageSize
.Height()+4 ) //leave space for focus rect
275 aSize
.Height() = aImageSize
.Height()+4;
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
)
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
)
302 sal_Int32 c
= nLineWidth
- (j
- i
);
303 for (size_t k
= i
; k
<= j
; ++k
)
305 c
= (c
>= 0) ? c
* c
: SAL_MAX_INT32
;
306 aCosts
[j
* nWidthsCount
+ i
] = c
;
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
)
327 if (aFunction
[k
] == SAL_MAX_INT32
|| aCosts
[j
* nWidthsCount
+ k
+ 1] == SAL_MAX_INT32
)
330 s
= aFunction
[k
] + aCosts
[j
* nWidthsCount
+ k
+ 1];
331 if (aFunction
[j
] > s
)
334 aWrapPoints
[j
] = k
+ 1;
340 std::deque
<size_t> aSolution
;
343 if (aFunction
[nWidthsCount
- 1] == SAL_MAX_INT32
)
347 size_t j
= nWidthsCount
- 1;
350 aSolution
.push_front(j
);
353 j
= aWrapPoints
[j
] - 1;
360 bool TabControl::ImplPlaceTabs( long nWidth
)
364 if ( mpTabCtrlData
->maItemList
.empty() )
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
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();
393 sal_uInt16 nLines
= 0;
394 sal_uInt16 nCurLine
= 0;
396 long nLineWidthAry
[100];
397 sal_uInt16 nLinePosAry
[101];
398 nLineWidthAry
[0] = 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();
416 if ( bNewLine
&& (nWidth
> 2+nOffsetX
) )
422 nY
+= aSize
.Height();
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
;
433 it
->mbFullVisible
= true;
435 nLineWidthAry
[nLines
] += aSize
.Width();
438 if ( it
->mnId
== mnCurPageId
)
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
++ )
452 nLineHeightAry
[i
] = nIH
*(nLines
-(nCurLine
-i
)) + GetItemsOffset().Y();
454 nLineHeightAry
[i
] = nIH
*(i
-nCurLine
-1) + GetItemsOffset().Y();
457 nLinePosAry
[nLines
+1] = (sal_uInt16
)mpTabCtrlData
->maItemList
.size();
465 for( std::vector
< ImplTabItem
>::iterator it
= mpTabCtrlData
->maItemList
.begin();
466 it
!= mpTabCtrlData
->maItemList
.end(); ++it
)
468 if ( i
== nLinePosAry
[n
] )
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
);
481 // FIXME: this is a case of tabctrl way too small
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
;
497 it
->maRect
.Right()++;
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;
526 Rectangle
TabControl::ImplGetTabRect( sal_uInt16 nItemPos
, long nWidth
, long nHeight
)
528 Size aWinSize
= Control::GetOutputSizePixel();
530 nWidth
= aWinSize
.Width();
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
) )
543 if ( nItemPos
== TAB_PAGERECT
)
547 nLastPos
= GetPagePos( mnCurPageId
);
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
) )
562 if ( (nWidth
<= 0) || (nHeight
<= 0) )
565 if ( mbFormat
|| (mnLastWidth
!= nWidth
) || (mnLastHeight
!= nHeight
) )
567 vcl::Font
aFont( GetFont() );
568 aFont
.SetTransparent( true );
571 bool bRet
= ImplPlaceTabs( nWidth
);
575 mnLastWidth
= nWidth
;
576 mnLastHeight
= nHeight
;
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
) )
602 aRect
.Right() = Control::GetOutputSizePixel().Width();
610 nPos
= GetPagePos( nOldId
);
611 aRect
= ImplGetTabRect( nPos
);
619 if ( pOldPage
== pPage
)
622 Rectangle aRect
= ImplGetTabRect( TAB_PAGERECT
);
626 if ( mbRestoreHelpId
)
627 pCtrlParent
->SetHelpId( OString() );
628 pOldPage
->DeactivatePage();
633 if ( ( GetStyle() & WB_NOBORDER
) )
635 Rectangle
aRectNoTab(Point(0, 0), GetSizePixel());
636 pPage
->SetPosSizePixel( aRectNoTab
.TopLeft(), aRectNoTab
.GetSize() );
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();
652 if ( pOldPage
&& pOldPage
->HasChildPathFocus() )
655 vcl::Window
* pFirstChild
= pPage
->ImplGetDlgWindow( n
, GetDlgWindowType::First
);
657 pFirstChild
->ImplControlFocus( GetFocusFlags::Init
);
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
;
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() );
692 Rectangle aRect
= ImplGetTabRect( TAB_PAGERECT
);
693 pItem
->mpTabPage
->SetPosSizePixel( aRect
.TopLeft(), aRect
.GetSize() );
700 void TabControl::ImplActivateTabPage( bool bNext
)
702 sal_uInt16 nCurPos
= GetPagePos( GetCurPageId() );
705 nCurPos
= (nCurPos
+ 1) % GetPageCount();
709 nCurPos
= GetPageCount()-1;
714 SelectTabPage( GetPageId( nCurPos
) );
717 void TabControl::ImplShowFocus()
719 if ( !GetPageCount() || mpTabCtrlData
->mpListBox
)
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
);
731 if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::Mono
) )
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;
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;
767 void TabControl::ImplDrawItem(vcl::RenderContext
& rRenderContext
, ImplTabItem
* pItem
, const Rectangle
& rCurRect
,
768 bool bFirstInGroup
, bool bLastInGroup
, bool /* bIsCurrentItem */ )
770 if (pItem
->maRect
.IsEmpty())
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;
780 bool bNativeOK
= false;
782 sal_uInt16 nOff2
= 0;
783 sal_uInt16 nOff3
= 0;
785 if (!(rStyleSettings
.GetOptions() & StyleSettingsOptions::Mono
))
790 // if this is the active Page, we have to draw a little more
791 if (pItem
->mnId
== mnCurPageId
)
794 if (!ImplGetSVData()->maNWFData
.mbNoActiveTabTextRaise
)
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
))
806 aRightTestPos
.X() += 2;
807 if (rCurRect
.IsInside(aRightTestPos
))
808 bRightBorder
= false;
812 if (rCurRect
.IsInside(aLeftTestPos
))
814 if (rCurRect
.IsInside(aRightTestPos
))
819 ControlState nState
= ControlState::NONE
;
821 if (pItem
->mnId
== mnCurPageId
)
823 nState
|= ControlState::SELECTED
;
824 // only the selected item can be focussed
826 nState
|= ControlState::FOCUSED
;
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
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
;
855 tiValue
.mnAlignment
|= TabitemFlags::FirstInGroup
;
857 tiValue
.mnAlignment
|= TabitemFlags::LastInGroup
;
859 Rectangle
aCtrlRegion( pItem
->maRect
);
860 bNativeOK
= rRenderContext
.DrawNativeControl(ControlType::TabItem
, ControlPart::Entire
,
861 aCtrlRegion
, nState
, tiValue
, OUString() );
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
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
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));
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
));
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
));
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
,
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
)
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 );
980 if ( (nKeyCode
== KEY_TAB
) || (nKeyCode
== KEY_PAGEDOWN
) )
982 ImplActivateTabPage( true );
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
)
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
)
1068 // Draw the TabPage border
1069 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
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())
1086 aRect
.Right() += 10;
1089 if (rRenderContext
.IsNativeControlSupported(ControlType::TabPane
, ControlPart::Entire
))
1091 const ImplControlValue aControlValue
;
1093 ControlState nState
= ControlState::ENABLED
;
1095 nState
&= ~ControlState::ENABLED
;
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());
1129 if (!(rStyleSettings
.GetOptions() & StyleSettingsOptions::Mono
))
1130 rRenderContext
.SetLineColor(rStyleSettings
.GetLightColor());
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());
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
1156 rRenderContext
.SetLineColor(rStyleSettings
.GetDialogColor());
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));
1162 rRenderContext
.SetLineColor(rStyleSettings
.GetDialogColor());
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()));
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;
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.
1188 pFirstTab
= mpTabCtrlData
->maItemList
.data();
1189 pLastTab
= pFirstTab
+ mpTabCtrlData
->maItemList
.size();
1190 idx
= mpTabCtrlData
->maItemList
.size() - 1;
1194 pLastTab
= mpTabCtrlData
->maItemList
.data();
1195 pFirstTab
= pLastTab
+ mpTabCtrlData
->maItemList
.size();
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
);
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);
1239 mbSmallInvalidate
= true;
1242 void TabControl::setAllocation(const Size
&rAllocation
)
1244 ImplFreeLayoutData();
1246 if ( !IsReallyShown() )
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
);
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;
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
;
1288 Invalidate( aRect
, InvalidateFlags::NoChildren
);
1290 Invalidate( aRect
);
1296 Invalidate( InvalidateFlags::NoChildren
);
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
1309 setAllocation(rNewSize
);
1312 void TabControl::SetSizePixel(const Size
& rNewSize
)
1314 Window::SetSizePixel(rNewSize
);
1315 //if size changed, TabControl::Resize got called already
1317 setAllocation(rNewSize
);
1320 void TabControl::SetPosPixel(const Point
& rPos
)
1322 Window::SetPosPixel(rPos
);
1324 setAllocation(GetOutputSizePixel());
1327 void TabControl::Resize()
1329 setAllocation(Control::GetOutputSizePixel());
1332 void TabControl::GetFocus()
1334 if( ! mpTabCtrlData
->mpListBox
)
1337 SetInputContext( InputContext( GetFont() ) );
1341 if( mpTabCtrlData
->mpListBox
->IsReallyVisible() )
1342 mpTabCtrlData
->mpListBox
->GrabFocus();
1344 Control::GetFocus();
1347 void TabControl::LoseFocus()
1349 if( mpTabCtrlData
&& ! mpTabCtrlData
->mpListBox
)
1351 Control::LoseFocus();
1354 void TabControl::RequestHelp( const HelpEvent
& rHEvt
)
1356 sal_uInt16 nItemId
= rHEvt
.KeyboardActivated() ? mnCurPageId
: GetPageId( ScreenToOutputPixel( rHEvt
.GetMousePosPixel() ) );
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
);
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();
1384 pHelp
->Start( aHelpId
, this );
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
);
1408 Help::ShowQuickHelp( this, aItemRect
, rStr
);
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
);
1434 Control::RequestHelp( rHEvt
);
1437 void TabControl::Command( const CommandEvent
& rCEvt
)
1439 if( (mpTabCtrlData
->mpListBox
== nullptr) && (rCEvt
.GetCommand() == CommandEventId::ContextMenu
) && (GetPageCount() > 1) )
1443 if ( rCEvt
.IsMouseEvent() )
1445 aMenuPos
= rCEvt
.GetMousePosPixel();
1446 bMenu
= GetPageId( aMenuPos
) != 0;
1450 aMenuPos
= ImplGetTabRect( GetPagePos( mnCurPageId
) ).Center();
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
);
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
)
1486 else if ( nType
== StateChangedType::UpdateMode
)
1488 if ( IsUpdateMode() )
1491 else if ( (nType
== StateChangedType::Zoom
) ||
1492 (nType
== StateChangedType::ControlFont
) )
1494 ImplInitSettings( true, false, false );
1497 else if ( nType
== StateChangedType::ControlForeground
)
1499 ImplInitSettings( false, true, false );
1502 else if ( nType
== StateChangedType::ControlBackground
)
1504 ImplInitSettings( false, false, true );
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 );
1523 Rectangle
* TabControl::ImplFindPartRect( const Point
& rPt
)
1525 ImplTabItem
* pFoundItem
= nullptr;
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
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
;
1559 // allow for slightly bigger tabitems
1561 // TODO: query for the correct sizes
1562 Rectangle
aRect(*pLastRect
);
1566 aClipRgn
.Union( aRect
);
1570 // allow for slightly bigger tabitems
1572 // TODO: query for the correct sizes
1573 Rectangle
aRect(*pRect
);
1577 aClipRgn
.Union( aRect
);
1579 if( !aClipRgn
.IsEmpty() )
1580 Invalidate( aClipRgn
);
1586 return Control::PreNotify(rNEvt
);
1589 bool TabControl::EventNotify( NotifyEvent
& rNEvt
)
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
,
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
);
1645 std::vector
< ImplTabItem
>::iterator new_it
=
1646 mpTabCtrlData
->maItemList
.insert( mpTabCtrlData
->maItemList
.begin() + nPos
, ImplTabItem() );
1648 if( mpTabCtrlData
->mpListBox
)
1649 mpTabCtrlData
->mpListBox
->InsertEntry( rText
, nPos
);
1651 if( mpTabCtrlData
->mpListBox
)
1654 mpTabCtrlData
->mpListBox
->SelectEntryPos( 0 );
1655 mpTabCtrlData
->mpListBox
->SetDropDownLineCount( mpTabCtrlData
->mpListBox
->GetEntryCount() );
1658 // set current page id
1660 mnCurPageId
= nPageId
;
1662 // init new page item
1663 pItem
->mnId
= nPageId
;
1664 pItem
->mpTabPage
= nullptr;
1665 pItem
->maText
= rText
;
1666 pItem
->mbFullVisible
= false;
1669 if ( IsUpdateMode() )
1672 ImplFreeLayoutData();
1673 if( mpTabCtrlData
->mpListBox
) // reposition/resize listbox
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
)
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
)
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
);
1716 if ( IsUpdateMode() )
1719 ImplFreeLayoutData();
1721 CallEventListeners( VCLEVENT_TABPAGE_REMOVED
, reinterpret_cast<void*>(nPageId
) );
1725 void TabControl::Clear()
1728 mpTabCtrlData
->maItemList
.clear();
1730 if( mpTabCtrlData
->mpListBox
)
1731 mpTabCtrlData
->mpListBox
->Clear();
1733 ImplFreeLayoutData();
1736 if ( IsUpdateMode() )
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
;
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() )
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
;
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
;
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
)
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
)
1822 void TabControl::SetCurPageId( sal_uInt16 nPageId
)
1824 sal_uInt16 nPos
= GetPagePos( nPageId
);
1825 while( nPos
!= TAB_PAGE_NOTFOUND
&&
1826 ! mpTabCtrlData
->maItemList
[nPos
].mbEnabled
)
1829 if( size_t(nPos
) >= mpTabCtrlData
->maItemList
.size() )
1831 if( mpTabCtrlData
->maItemList
[nPos
].mnId
== nPageId
)
1835 if( nPos
!= TAB_PAGE_NOTFOUND
)
1837 nPageId
= mpTabCtrlData
->maItemList
[nPos
].mnId
;
1838 if ( nPageId
== mnCurPageId
)
1841 mnActPageId
= nPageId
;
1846 mnActPageId
= nPageId
;
1850 sal_uInt16 nOldId
= mnCurPageId
;
1851 mnCurPageId
= nPageId
;
1852 ImplChangeTabPage( nPageId
, nOldId
);
1857 sal_uInt16
TabControl::GetCurPageId() const
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
;
1876 // Page could have been switched by the Activate handler
1877 nPageId
= mnActPageId
;
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
) )
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
;
1904 if ( pItem
->mnId
== mnCurPageId
)
1905 ImplChangeTabPage( pItem
->mnId
, 0 );
1909 pItem
->mpTabPage
= nullptr;
1915 TabPage
* TabControl::GetTabPage( sal_uInt16 nPageId
) const
1917 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1920 return pItem
->mpTabPage
;
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
;
1933 if( mpTabCtrlData
->mpListBox
)
1935 sal_uInt16 nPos
= GetPagePos( nPageId
);
1936 mpTabCtrlData
->mpListBox
->RemoveEntry( nPos
);
1937 mpTabCtrlData
->mpListBox
->InsertEntry( rText
, nPos
);
1939 if ( IsUpdateMode() )
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
);
1952 return pItem
->maText
;
1955 void TabControl::SetHelpText( sal_uInt16 nPageId
, const OUString
& rText
)
1957 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1961 pItem
->maHelpText
= rText
;
1964 const OUString
& TabControl::GetHelpText( sal_uInt16 nPageId
) const
1966 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1970 if ( pItem
->maHelpText
.isEmpty() && !pItem
->maHelpId
.isEmpty() )
1972 Help
* pHelp
= Application::GetHelp();
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
);
1984 pItem
->maHelpId
= rId
;
1987 OString
TabControl::GetHelpId( sal_uInt16 nPageId
) const
1989 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1992 return pItem
->maHelpId
;
1997 void TabControl::SetPageName( sal_uInt16 nPageId
, const OString
& rName
) const
1999 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
2002 pItem
->maTabName
= rName
;
2005 OString
TabControl::GetPageName( sal_uInt16 nPageId
) const
2007 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
2010 return pItem
->maTabName
;
2015 void TabControl::SetPageImage( sal_uInt16 i_nPageId
, const Image
& i_rImage
)
2017 ImplTabItem
* pItem
= ImplGetItem( i_nPageId
);
2021 pItem
->maTabImage
= i_rImage
;
2023 if ( IsUpdateMode() )
2028 Rectangle
TabControl::GetCharacterBounds( sal_uInt16 nPageId
, long nIndex
) const
2032 if( !HasLayoutData() || ! mpTabCtrlData
->maLayoutPageIdToLine
.size() )
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
);
2049 long TabControl::GetIndexForPoint( const Point
& rPoint
, sal_uInt16
& rPageId
) const
2053 if( !HasLayoutData() || ! mpTabCtrlData
->maLayoutPageIdToLine
.size() )
2056 if( HasLayoutData() )
2058 int nIndex
= mpControlData
->mpLayoutData
->GetIndexForPoint( rPoint
);
2061 // what line (->pageid) is this index in ?
2062 int nLines
= mpControlData
->mpLayoutData
->GetLineCount();
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
];
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
2091 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
2093 aRet
= pItem
->maRect
;
2098 void TabControl::SetItemsOffset( const Point
& rOffs
)
2101 mpTabCtrlData
->maItemsOffset
= rOffs
;
2104 Point
TabControl::GetItemsOffset() const
2107 return mpTabCtrlData
->maItemsOffset
;
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
2125 TabControl
*pThis
= const_cast<TabControl
*>(this);
2126 pThis
->SetCurPageId(it
->mnId
);
2127 pThis
->ActivatePage();
2128 pPage
= it
->mpTabPage
;
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
);
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
);
2218 aBitmap
= Bitmap( ResId( SV_RESID_BITMAP_NOTEBOOKBAR
, *pResMgr
) );
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);
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);
2249 bLastContextWasSupported
= true;
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
;
2276 void NotebookbarTabControl::SelectTabPage( sal_uInt16 nPageId
)
2279 m_aIconClickHdl
.Call( static_cast<NotebookBar
*>(GetParent()) );
2281 TabControl::SelectTabPage( nPageId
);
2284 void NotebookbarTabControl::SetCurPageId( sal_uInt16 nPageId
)
2287 TabControl::SetCurPageId( nPageId
);
2290 sal_uInt16
NotebookbarTabControl::GetHeaderHeight()
2292 return m_nHeaderHeight
;
2295 bool NotebookbarTabControl::ImplPlaceTabs( long nWidth
)
2299 if ( mpTabCtrlData
->maItemList
.empty() )
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
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();
2328 sal_uInt16 nLines
= 0;
2329 sal_uInt16 nCurLine
= 0;
2331 long nLineWidthAry
[100];
2332 sal_uInt16 nLinePosAry
[101];
2333 nLineWidthAry
[0] = 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();
2352 if ( bNewLine
&& (nWidth
> 2+nOffsetX
) )
2358 nY
+= aSize
.Height();
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
)
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();
2398 nLineHeightAry
[i
] = nIH
*(i
-nCurLine
-1) + GetItemsOffset().Y();
2401 nLinePosAry
[nLines
+1] = (sal_uInt16
)mpTabCtrlData
->maItemList
.size();
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 )
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
);
2425 // FIXME: this is a case of tabctrl way too small
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
;
2441 it
->maRect
.Right()++;
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;
2470 void NotebookbarTabControl::ImplPaint(vcl::RenderContext
& rRenderContext
, const Rectangle
& rRect
)
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
)
2489 // Draw the TabPage border
2490 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
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())
2507 aRect
.Right() += 10;
2510 if (rRenderContext
.IsNativeControlSupported(ControlType::TabPane
, ControlPart::Entire
))
2512 const ImplControlValue aControlValue
;
2514 ControlState nState
= ControlState::ENABLED
;
2516 nState
&= ~ControlState::ENABLED
;
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());
2550 if (!(rStyleSettings
.GetOptions() & StyleSettingsOptions::Mono
))
2551 rRenderContext
.SetLineColor(rStyleSettings
.GetLightColor());
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());
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
2577 rRenderContext
.SetLineColor(rStyleSettings
.GetDialogColor());
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));
2583 rRenderContext
.SetLineColor(rStyleSettings
.GetDialogColor());
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()));
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;
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.
2609 pFirstTab
= mpTabCtrlData
->maItemList
.data();
2610 pLastTab
= pFirstTab
+ mpTabCtrlData
->maItemList
.size();
2611 idx
= mpTabCtrlData
->maItemList
.size() - 1;
2615 pLastTab
= mpTabCtrlData
->maItemList
.data();
2616 pFirstTab
= pLastTab
+ mpTabCtrlData
->maItemList
.size();
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
);
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);
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
2676 NotebookbarTabControl
*pThis
= const_cast<NotebookbarTabControl
*>(this);
2677 pThis
->SetCurPageId(it
->mnId
);
2678 pThis
->ActivatePage();
2679 pPage
= it
->mpTabPage
;
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: */