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 <sal/config.h>
21 #include <sal/log.hxx>
23 #include <vcl/notebookbar.hxx>
24 #include <vcl/svapp.hxx>
25 #include <vcl/help.hxx>
26 #include <vcl/event.hxx>
27 #include <vcl/menu.hxx>
28 #include <vcl/button.hxx>
29 #include <vcl/tabpage.hxx>
30 #include <vcl/tabctrl.hxx>
31 #include <vcl/controllayout.hxx>
32 #include <vcl/layout.hxx>
33 #include <vcl/lstbox.hxx>
34 #include <vcl/settings.hxx>
35 #include <vcl/uitest/uiobject.hxx>
36 #include <bitmaps.hlst>
38 #include <controldata.hxx>
42 #include <unordered_map>
45 class ImplTabItem final
50 VclPtr
<TabPage
> mpTabPage
;
52 OUString maFormatText
;
55 tools::Rectangle maRect
;
58 bool m_bEnabled
; ///< the tab / page is selectable
59 bool m_bVisible
; ///< the tab / page can be visible
62 ImplTabItem(sal_uInt16 nId
);
64 sal_uInt16
id() const { return m_nId
; }
67 ImplTabItem::ImplTabItem(sal_uInt16 nId
)
70 , mbFullVisible(false)
76 struct ImplTabCtrlData
78 std::unordered_map
< int, int > maLayoutPageIdToLine
;
79 std::unordered_map
< int, int > maLayoutLineToPageId
;
80 Point maItemsOffset
; // offset of the tabitems
81 std::vector
< ImplTabItem
> maItemList
;
82 VclPtr
<ListBox
> mpListBox
;
85 // for the Tab positions
86 #define TAB_PAGERECT 0xFFFF
87 #define HAMBURGER_DIM 28
89 void TabControl::ImplInit( vcl::Window
* pParent
, WinBits nStyle
)
93 if ( !(nStyle
& WB_NOTABSTOP
) )
95 if ( !(nStyle
& WB_NOGROUP
) )
97 if ( !(nStyle
& WB_NODIALOGCONTROL
) )
98 nStyle
|= WB_DIALOGCONTROL
;
100 Control::ImplInit( pParent
, nStyle
, nullptr );
107 mbRestoreHelpId
= false;
108 mbSmallInvalidate
= false;
109 mpTabCtrlData
.reset(new ImplTabCtrlData
);
110 mpTabCtrlData
->mpListBox
= nullptr;
112 ImplInitSettings( true );
114 if( nStyle
& WB_DROPDOWN
)
116 mpTabCtrlData
->mpListBox
= VclPtr
<ListBox
>::Create( this, WB_DROPDOWN
);
117 mpTabCtrlData
->mpListBox
->SetPosSizePixel( Point( 0, 0 ), Size( 200, 20 ) );
118 mpTabCtrlData
->mpListBox
->SetSelectHdl( LINK( this, TabControl
, ImplListBoxSelectHdl
) );
119 mpTabCtrlData
->mpListBox
->Show();
122 // if the tabcontrol is drawn (ie filled) by a native widget, make sure all controls will have transparent background
123 // otherwise they will paint with a wrong background
124 if( IsNativeControlSupported(ControlType::TabPane
, ControlPart::Entire
) )
125 EnableChildTransparentMode();
127 if (pParent
&& pParent
->IsDialog())
128 pParent
->AddChildEventListener( LINK( this, TabControl
, ImplWindowEventListener
) );
131 const vcl::Font
& TabControl::GetCanonicalFont( const StyleSettings
& _rStyle
) const
133 return _rStyle
.GetTabFont();
136 const Color
& TabControl::GetCanonicalTextColor( const StyleSettings
& _rStyle
) const
138 return _rStyle
.GetTabTextColor();
141 void TabControl::ImplInitSettings( bool bBackground
)
143 Control::ImplInitSettings();
147 vcl::Window
* pParent
= GetParent();
148 if ( !IsControlBackground() &&
149 (pParent
->IsChildTransparentModeEnabled()
150 || IsNativeControlSupported(ControlType::TabPane
, ControlPart::Entire
)
151 || IsNativeControlSupported(ControlType::TabItem
, ControlPart::Entire
) ) )
154 // set transparent mode for NWF tabcontrols to have
155 // the background always cleared properly
156 EnableChildTransparentMode();
157 SetParentClipMode( ParentClipMode::NoClip
);
158 SetPaintTransparent( true );
160 ImplGetWindowImpl()->mbUseNativeFocus
= ImplGetSVData()->maNWFData
.mbNoFocusRects
;
164 EnableChildTransparentMode( false );
166 SetPaintTransparent( false );
168 if ( IsControlBackground() )
169 SetBackground( GetControlBackground() );
171 SetBackground( pParent
->GetBackground() );
176 void TabControl::ImplFreeLayoutData()
178 if( HasLayoutData() )
180 ImplClearLayoutData();
181 mpTabCtrlData
->maLayoutPageIdToLine
.clear();
182 mpTabCtrlData
->maLayoutLineToPageId
.clear();
186 TabControl::TabControl( vcl::Window
* pParent
, WinBits nStyle
) :
187 Control( WindowType::TABCONTROL
)
189 ImplInit( pParent
, nStyle
);
190 SAL_INFO( "vcl", "*** TABCONTROL no notabs? " << (( GetStyle() & WB_NOBORDER
) ? "true" : "false") );
193 TabControl::~TabControl()
198 void TabControl::dispose()
200 Window
*pParent
= GetParent();
201 if (pParent
&& pParent
->IsDialog())
202 GetParent()->RemoveChildEventListener( LINK( this, TabControl
, ImplWindowEventListener
) );
204 ImplFreeLayoutData();
206 // delete TabCtrl data
208 mpTabCtrlData
->mpListBox
.disposeAndClear();
209 mpTabCtrlData
.reset();
213 ImplTabItem
* TabControl::ImplGetItem( sal_uInt16 nId
) const
215 for (auto & item
: mpTabCtrlData
->maItemList
)
217 if (item
.id() == nId
)
224 Size
TabControl::ImplGetItemSize( ImplTabItem
* pItem
, long nMaxWidth
)
226 pItem
->maFormatText
= pItem
->maText
;
227 Size
aSize( GetCtrlTextWidth( pItem
->maFormatText
), GetTextHeight() );
228 Size
aImageSize( 0, 0 );
229 if( !!pItem
->maTabImage
)
231 aImageSize
= pItem
->maTabImage
.GetSizePixel();
232 if( !pItem
->maFormatText
.isEmpty() )
233 aImageSize
.AdjustWidth(GetTextHeight()/4 );
235 aSize
.AdjustWidth(aImageSize
.Width() );
236 if( aImageSize
.Height() > aSize
.Height() )
237 aSize
.setHeight( aImageSize
.Height() );
239 aSize
.AdjustWidth(TAB_TABOFFSET_X
*2 );
240 aSize
.AdjustHeight(TAB_TABOFFSET_Y
*2 );
242 tools::Rectangle
aCtrlRegion( Point( 0, 0 ), aSize
);
243 tools::Rectangle aBoundingRgn
, aContentRgn
;
244 const TabitemValue
aControlValue(tools::Rectangle(TAB_TABOFFSET_X
, TAB_TABOFFSET_Y
,
245 aSize
.Width() - TAB_TABOFFSET_X
* 2,
246 aSize
.Height() - TAB_TABOFFSET_Y
* 2));
247 if(GetNativeControlRegion( ControlType::TabItem
, ControlPart::Entire
, aCtrlRegion
,
248 ControlState::ENABLED
, aControlValue
,
249 aBoundingRgn
, aContentRgn
) )
251 return aContentRgn
.GetSize();
254 // For languages with short names (e.g. Chinese), because the space is
255 // normally only one pixel per char
256 if ( pItem
->maFormatText
.getLength() < TAB_EXTRASPACE_X
)
257 aSize
.AdjustWidth(TAB_EXTRASPACE_X
-pItem
->maFormatText
.getLength() );
259 // shorten Text if needed
260 if ( aSize
.Width()+4 >= nMaxWidth
)
262 OUString
aAppendStr("...");
263 pItem
->maFormatText
+= aAppendStr
;
266 if (pItem
->maFormatText
.getLength() > aAppendStr
.getLength())
267 pItem
->maFormatText
= pItem
->maFormatText
.replaceAt( pItem
->maFormatText
.getLength()-aAppendStr
.getLength()-1, 1, "" );
268 aSize
.setWidth( GetCtrlTextWidth( pItem
->maFormatText
) );
269 aSize
.AdjustWidth(aImageSize
.Width() );
270 aSize
.AdjustWidth(TAB_TABOFFSET_X
*2 );
272 while ( (aSize
.Width()+4 >= nMaxWidth
) && (pItem
->maFormatText
.getLength() > aAppendStr
.getLength()) );
273 if ( aSize
.Width()+4 >= nMaxWidth
)
275 pItem
->maFormatText
= ".";
280 if( pItem
->maFormatText
.isEmpty() )
282 if( aSize
.Height() < aImageSize
.Height()+4 ) //leave space for focus rect
283 aSize
.setHeight( aImageSize
.Height()+4 );
289 // Feel free to move this to some more general place for reuse
290 // http://en.wikipedia.org/wiki/Word_wrap#Minimum_raggedness
291 // Mostly based on Alexey Frunze's nifty example at
292 // http://stackoverflow.com/questions/9071205/balanced-word-wrap-minimum-raggedness-in-php
293 namespace MinimumRaggednessWrap
295 static std::deque
<size_t> GetEndOfLineIndexes(const std::vector
<sal_Int32
>& rWidthsOf
, sal_Int32 nLineWidth
)
299 size_t nWidthsCount
= rWidthsOf
.size();
300 std::vector
<sal_Int32
> aCosts(nWidthsCount
* nWidthsCount
);
302 // cost function c(i, j) that computes the cost of a line consisting of
303 // the words Word[i] to Word[j]
304 for (size_t i
= 0; i
< nWidthsCount
; ++i
)
306 for (size_t j
= 0; j
< nWidthsCount
; ++j
)
310 sal_Int32 c
= nLineWidth
- (j
- i
);
311 for (size_t k
= i
; k
<= j
; ++k
)
313 c
= (c
>= 0) ? c
* c
: SAL_MAX_INT32
;
314 aCosts
[j
* nWidthsCount
+ i
] = c
;
318 aCosts
[j
* nWidthsCount
+ i
] = SAL_MAX_INT32
;
323 std::vector
<sal_Int32
> aFunction(nWidthsCount
);
324 std::vector
<sal_Int32
> aWrapPoints(nWidthsCount
);
326 // f(j) in aFunction[], collect wrap points in aWrapPoints[]
327 for (size_t j
= 0; j
< nWidthsCount
; ++j
)
329 aFunction
[j
] = aCosts
[j
* nWidthsCount
];
330 if (aFunction
[j
] == SAL_MAX_INT32
)
332 for (size_t k
= 0; k
< j
; ++k
)
335 if (aFunction
[k
] == SAL_MAX_INT32
|| aCosts
[j
* nWidthsCount
+ k
+ 1] == SAL_MAX_INT32
)
338 s
= aFunction
[k
] + aCosts
[j
* nWidthsCount
+ k
+ 1];
339 if (aFunction
[j
] > s
)
342 aWrapPoints
[j
] = k
+ 1;
348 std::deque
<size_t> aSolution
;
351 if (aFunction
[nWidthsCount
- 1] == SAL_MAX_INT32
)
355 size_t j
= nWidthsCount
- 1;
358 aSolution
.push_front(j
);
361 j
= aWrapPoints
[j
] - 1;
368 static void lcl_AdjustSingleLineTabs(long nMaxWidth
, ImplTabCtrlData
*pTabCtrlData
)
370 if (!ImplGetSVData()->maNWFData
.mbCenteredTabs
)
373 int nRightSpace
= nMaxWidth
; // space left on the right by the tabs
374 for (auto const& item
: pTabCtrlData
->maItemList
)
376 if (!item
.m_bVisible
)
378 nRightSpace
-= item
.maRect
.Right() - item
.maRect
.Left();
382 for (auto& item
: pTabCtrlData
->maItemList
)
384 if (!item
.m_bVisible
)
386 item
.maRect
.AdjustLeft(nRightSpace
);
387 item
.maRect
.AdjustRight(nRightSpace
);
391 bool TabControl::ImplPlaceTabs( long nWidth
)
395 if ( mpTabCtrlData
->maItemList
.empty() )
398 long nMaxWidth
= nWidth
;
400 const long nOffsetX
= 2 + GetItemsOffset().X();
401 const long nOffsetY
= 2 + GetItemsOffset().Y();
403 //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
404 //of ugly bare tabs on lines of their own
407 std::vector
<sal_Int32
> aWidths
;
408 for (auto & item
: mpTabCtrlData
->maItemList
)
410 if (!item
.m_bVisible
)
412 aWidths
.push_back(ImplGetItemSize(&item
, nMaxWidth
).Width());
415 //aBreakIndexes will contain the indexes of the last tab on each row
416 std::deque
<size_t> aBreakIndexes(MinimumRaggednessWrap::GetEndOfLineIndexes(aWidths
, nMaxWidth
- nOffsetX
- 2));
418 nMaxWidth
-= GetItemsOffset().X();
423 sal_uInt16 nLines
= 0;
424 sal_uInt16 nCurLine
= 0;
426 long nLineWidthAry
[100];
427 sal_uInt16 nLinePosAry
[101];
428 nLineWidthAry
[0] = 0;
433 for (auto & item
: mpTabCtrlData
->maItemList
)
435 if (!item
.m_bVisible
)
438 Size aSize
= ImplGetItemSize( &item
, nMaxWidth
);
440 bool bNewLine
= false;
441 if (!aBreakIndexes
.empty() && nIndex
> aBreakIndexes
.front())
443 aBreakIndexes
.pop_front();
447 if ( bNewLine
&& (nWidth
> 2+nOffsetX
) )
453 nY
+= aSize
.Height();
455 nLineWidthAry
[nLines
] = 0;
456 nLinePosAry
[nLines
] = nIndex
;
459 tools::Rectangle
aNewRect( Point( nX
, nY
), aSize
);
460 if ( mbSmallInvalidate
&& (item
.maRect
!= aNewRect
) )
461 mbSmallInvalidate
= false;
462 item
.maRect
= aNewRect
;
463 item
.mnLine
= nLines
;
464 item
.mbFullVisible
= true;
466 nLineWidthAry
[nLines
] += aSize
.Width();
469 if (item
.id() == mnCurPageId
)
475 if (nLines
) // two or more lines
477 long nLineHeightAry
[100];
479 for (const auto& item
: mpTabCtrlData
->maItemList
)
481 if (!item
.m_bVisible
)
483 nIH
= item
.maRect
.Bottom() - 1;
487 for ( sal_uInt16 i
= 0; i
< nLines
+1; i
++ )
490 nLineHeightAry
[i
] = nIH
*(nLines
-(nCurLine
-i
)) + GetItemsOffset().Y();
492 nLineHeightAry
[i
] = nIH
*(i
-nCurLine
-1) + GetItemsOffset().Y();
495 nLinePosAry
[nLines
+1] = static_cast<sal_uInt16
>(mpTabCtrlData
->maItemList
.size());
504 for (auto & item
: mpTabCtrlData
->maItemList
)
506 if (!item
.m_bVisible
)
509 if ( i
== nLinePosAry
[n
] )
515 if( nLinePosAry
[n
+1]-i
> 0 )
517 nDX
= ( nWidth
- nOffsetX
- nLineWidthAry
[n
] ) / ( nLinePosAry
[n
+1] - i
);
518 nModDX
= ( nWidth
- nOffsetX
- nLineWidthAry
[n
] ) % ( nLinePosAry
[n
+1] - i
);
522 // FIXME: this is a case of tabctrl way too small
529 item
.maRect
.AdjustLeft(nIDX
);
530 item
.maRect
.AdjustRight(nIDX
+ nDX
);
531 item
.maRect
.SetTop( nLineHeightAry
[n
-1] );
532 item
.maRect
.SetBottom(nLineHeightAry
[n
-1] + nIH
- 1);
538 item
.maRect
.AdjustRight( 1 );
545 else // only one line
546 lcl_AdjustSingleLineTabs(nMaxWidth
, mpTabCtrlData
.get());
551 tools::Rectangle
TabControl::ImplGetTabRect( sal_uInt16 nItemPos
, long nWidth
, long nHeight
)
553 Size aWinSize
= Control::GetOutputSizePixel();
555 nWidth
= aWinSize
.Width();
557 nHeight
= aWinSize
.Height();
559 if ( mpTabCtrlData
->maItemList
.empty() )
561 long nW
= nWidth
-TAB_OFFSET
*2;
562 long nH
= nHeight
-TAB_OFFSET
*2;
563 return (nW
> 0 && nH
> 0)
564 ? tools::Rectangle(Point(TAB_OFFSET
, TAB_OFFSET
), Size(nW
, nH
))
565 : tools::Rectangle();
568 if ( nItemPos
== TAB_PAGERECT
)
572 nLastPos
= GetPagePos( mnCurPageId
);
576 tools::Rectangle aRect
= ImplGetTabRect( nLastPos
, nWidth
, nHeight
);
580 long nW
= nWidth
-TAB_OFFSET
*2;
581 long nH
= nHeight
-aRect
.Bottom()-TAB_OFFSET
*2;
582 return (nW
> 0 && nH
> 0)
583 ? tools::Rectangle( Point( TAB_OFFSET
, aRect
.Bottom()+TAB_OFFSET
), Size( nW
, nH
) )
584 : tools::Rectangle();
587 ImplTabItem
* const pItem
= (nItemPos
< mpTabCtrlData
->maItemList
.size())
588 ? &mpTabCtrlData
->maItemList
[nItemPos
] : nullptr;
589 return ImplGetTabRect(pItem
, nWidth
, nHeight
);
592 tools::Rectangle
TabControl::ImplGetTabRect(const ImplTabItem
* pItem
, long nWidth
, long nHeight
)
594 if ((nWidth
<= 1) || (nHeight
<= 0) || !pItem
|| !pItem
->m_bVisible
)
595 return tools::Rectangle();
599 if ( mbFormat
|| (mnLastWidth
!= nWidth
) || (mnLastHeight
!= nHeight
) )
601 vcl::Font
aFont( GetFont() );
602 aFont
.SetTransparent( true );
605 bool bRet
= ImplPlaceTabs( nWidth
);
607 return tools::Rectangle();
609 mnLastWidth
= nWidth
;
610 mnLastHeight
= nHeight
;
614 return pItem
->maRect
;
617 void TabControl::ImplChangeTabPage( sal_uInt16 nId
, sal_uInt16 nOldId
)
619 ImplFreeLayoutData();
621 ImplTabItem
* pOldItem
= ImplGetItem( nOldId
);
622 ImplTabItem
* pItem
= ImplGetItem( nId
);
623 TabPage
* pOldPage
= pOldItem
? pOldItem
->mpTabPage
.get() : nullptr;
624 TabPage
* pPage
= pItem
? pItem
->mpTabPage
.get() : nullptr;
625 vcl::Window
* pCtrlParent
= GetParent();
627 if ( IsReallyVisible() && IsUpdateMode() )
629 sal_uInt16 nPos
= GetPagePos( nId
);
630 tools::Rectangle aRect
= ImplGetTabRect( nPos
);
632 if ( !pOldItem
|| !pItem
|| (pOldItem
->mnLine
!= pItem
->mnLine
) )
636 aRect
.SetRight( Control::GetOutputSizePixel().Width() );
640 aRect
.AdjustLeft( -3 );
641 aRect
.AdjustTop( -2 );
642 aRect
.AdjustRight(3 );
644 nPos
= GetPagePos( nOldId
);
645 aRect
= ImplGetTabRect( nPos
);
646 aRect
.AdjustLeft( -3 );
647 aRect
.AdjustTop( -2 );
648 aRect
.AdjustRight(3 );
653 if ( pOldPage
== pPage
)
656 tools::Rectangle aRect
= ImplGetTabRect( TAB_PAGERECT
);
660 if ( mbRestoreHelpId
)
661 pCtrlParent
->SetHelpId( OString() );
666 if ( GetStyle() & WB_NOBORDER
)
668 tools::Rectangle
aRectNoTab(Point(0, 0), GetSizePixel());
669 pPage
->SetPosSizePixel( aRectNoTab
.TopLeft(), aRectNoTab
.GetSize() );
672 pPage
->SetPosSizePixel( aRect
.TopLeft(), aRect
.GetSize() );
674 // activate page here so the controls can be switched
675 // also set the help id of the parent window to that of the tab page
676 if ( GetHelpId().isEmpty() )
678 mbRestoreHelpId
= true;
679 pCtrlParent
->SetHelpId( pPage
->GetHelpId() );
682 pPage
->ActivatePage();
685 if ( pOldPage
&& pOldPage
->HasChildPathFocus() )
687 vcl::Window
* pFirstChild
= pPage
->ImplGetDlgWindow( 0, GetDlgWindowType::First
);
689 pFirstChild
->ImplControlFocus( GetFocusFlags::Init
);
698 // Invalidate the same region that will be send to NWF
699 // to always allow for bitmap caching
700 // see Window::DrawNativeControl()
701 if( IsNativeControlSupported( ControlType::TabPane
, ControlPart::Entire
) )
703 aRect
.AdjustLeft( -(TAB_OFFSET
) );
704 aRect
.AdjustTop( -(TAB_OFFSET
) );
705 aRect
.AdjustRight(TAB_OFFSET
);
706 aRect
.AdjustBottom(TAB_OFFSET
);
712 bool TabControl::ImplPosCurTabPage()
714 // resize/position current TabPage
715 ImplTabItem
* pItem
= ImplGetItem( GetCurPageId() );
716 if ( pItem
&& pItem
->mpTabPage
)
718 if ( GetStyle() & WB_NOBORDER
)
720 tools::Rectangle
aRectNoTab(Point(0, 0), GetSizePixel());
721 pItem
->mpTabPage
->SetPosSizePixel( aRectNoTab
.TopLeft(), aRectNoTab
.GetSize() );
724 tools::Rectangle aRect
= ImplGetTabRect( TAB_PAGERECT
);
725 pItem
->mpTabPage
->SetPosSizePixel( aRect
.TopLeft(), aRect
.GetSize() );
732 void TabControl::ImplActivateTabPage( bool bNext
)
734 sal_uInt16 nCurPos
= GetPagePos( GetCurPageId() );
737 nCurPos
= (nCurPos
+ 1) % GetPageCount();
741 nCurPos
= GetPageCount()-1;
746 SelectTabPage( GetPageId( nCurPos
) );
749 void TabControl::ImplShowFocus()
751 if ( !GetPageCount() || mpTabCtrlData
->mpListBox
)
754 sal_uInt16 nCurPos
= GetPagePos( mnCurPageId
);
755 tools::Rectangle aRect
= ImplGetTabRect( nCurPos
);
756 const ImplTabItem
& rItem
= mpTabCtrlData
->maItemList
[ nCurPos
];
757 Size aTabSize
= aRect
.GetSize();
758 Size
aImageSize( 0, 0 );
759 long nTextHeight
= GetTextHeight();
760 long nTextWidth
= GetCtrlTextWidth( rItem
.maFormatText
);
763 if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::Mono
) )
768 if( !! rItem
.maTabImage
)
770 aImageSize
= rItem
.maTabImage
.GetSizePixel();
771 if( !rItem
.maFormatText
.isEmpty() )
772 aImageSize
.AdjustWidth(GetTextHeight()/4 );
775 if( !rItem
.maFormatText
.isEmpty() )
777 // show focus around text
778 aRect
.SetLeft( aRect
.Left()+aImageSize
.Width()+((aTabSize
.Width()-nTextWidth
-aImageSize
.Width())/2)-nOff
-1-1 );
779 aRect
.SetTop( aRect
.Top()+((aTabSize
.Height()-nTextHeight
)/2)-1-1 );
780 aRect
.SetRight( aRect
.Left()+nTextWidth
+2 );
781 aRect
.SetBottom( aRect
.Top()+nTextHeight
+2 );
785 // show focus around image
786 long nXPos
= aRect
.Left()+((aTabSize
.Width()-nTextWidth
-aImageSize
.Width())/2)-nOff
-1;
787 long nYPos
= aRect
.Top();
788 if( aImageSize
.Height() < aRect
.GetHeight() )
789 nYPos
+= (aRect
.GetHeight() - aImageSize
.Height())/2;
791 aRect
.SetLeft( nXPos
- 2 );
792 aRect
.SetTop( nYPos
- 2 );
793 aRect
.SetRight( aRect
.Left() + aImageSize
.Width() + 4 );
794 aRect
.SetBottom( aRect
.Top() + aImageSize
.Height() + 4 );
799 void TabControl::ImplDrawItem(vcl::RenderContext
& rRenderContext
, ImplTabItem
const * pItem
, const tools::Rectangle
& rCurRect
,
800 bool bFirstInGroup
, bool bLastInGroup
)
802 if (!pItem
->m_bVisible
|| pItem
->maRect
.IsEmpty())
805 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
806 tools::Rectangle aRect
= pItem
->maRect
;
807 long nLeftBottom
= aRect
.Bottom();
808 long nRightBottom
= aRect
.Bottom();
809 bool bLeftBorder
= true;
810 bool bRightBorder
= true;
812 bool bNativeOK
= false;
814 sal_uInt16 nOff2
= 0;
815 sal_uInt16 nOff3
= 0;
817 if (!(rStyleSettings
.GetOptions() & StyleSettingsOptions::Mono
))
822 // if this is the active Page, we have to draw a little more
823 if (pItem
->id() == mnCurPageId
)
826 if (!ImplGetSVData()->maNWFData
.mbNoActiveTabTextRaise
)
831 Point aLeftTestPos
= aRect
.BottomLeft();
832 Point aRightTestPos
= aRect
.BottomRight();
833 if (aLeftTestPos
.Y() == rCurRect
.Bottom())
835 aLeftTestPos
.AdjustX( -2 );
836 if (rCurRect
.IsInside(aLeftTestPos
))
838 aRightTestPos
.AdjustX(2 );
839 if (rCurRect
.IsInside(aRightTestPos
))
840 bRightBorder
= false;
844 if (rCurRect
.IsInside(aLeftTestPos
))
846 if (rCurRect
.IsInside(aRightTestPos
))
851 ControlState nState
= ControlState::NONE
;
853 if (pItem
->id() == mnCurPageId
)
855 nState
|= ControlState::SELECTED
;
856 // only the selected item can be focused
858 nState
|= ControlState::FOCUSED
;
861 nState
|= ControlState::ENABLED
;
862 if (IsMouseOver() && pItem
->maRect
.IsInside(GetPointerPosPixel()))
864 nState
|= ControlState::ROLLOVER
;
865 for (auto const& item
: mpTabCtrlData
->maItemList
)
866 if ((&item
!= pItem
) && item
.m_bVisible
&& item
.maRect
.IsInside(GetPointerPosPixel()))
868 nState
&= ~ControlState::ROLLOVER
; // avoid multiple highlighted tabs
871 assert(nState
& ControlState::ROLLOVER
);
874 bNativeOK
= rRenderContext
.IsNativeControlSupported(ControlType::TabItem
, ControlPart::Entire
);
877 TabitemValue
tiValue(tools::Rectangle(pItem
->maRect
.Left() + TAB_TABOFFSET_X
,
878 pItem
->maRect
.Top() + TAB_TABOFFSET_Y
,
879 pItem
->maRect
.Right() - TAB_TABOFFSET_X
,
880 pItem
->maRect
.Bottom() - TAB_TABOFFSET_Y
));
881 if (pItem
->maRect
.Left() < 5)
882 tiValue
.mnAlignment
|= TabitemFlags::LeftAligned
;
883 if (pItem
->maRect
.Right() > mnLastWidth
- 5)
884 tiValue
.mnAlignment
|= TabitemFlags::RightAligned
;
886 tiValue
.mnAlignment
|= TabitemFlags::FirstInGroup
;
888 tiValue
.mnAlignment
|= TabitemFlags::LastInGroup
;
890 tools::Rectangle
aCtrlRegion( pItem
->maRect
);
891 aCtrlRegion
.AdjustBottom(TabPaneValue::m_nOverlap
);
892 bNativeOK
= rRenderContext
.DrawNativeControl(ControlType::TabItem
, ControlPart::Entire
,
893 aCtrlRegion
, nState
, tiValue
, OUString() );
898 if (!(rStyleSettings
.GetOptions() & StyleSettingsOptions::Mono
))
900 rRenderContext
.SetLineColor(rStyleSettings
.GetLightColor());
901 rRenderContext
.DrawPixel(Point(aRect
.Left() + 1 - nOff2
, aRect
.Top() + 1 - nOff2
)); // diagonally indented top-left pixel
904 rRenderContext
.DrawLine(Point(aRect
.Left() - nOff2
, aRect
.Top() + 2 - nOff2
),
905 Point(aRect
.Left() - nOff2
, nLeftBottom
- 1));
907 rRenderContext
.DrawLine(Point(aRect
.Left() + 2 - nOff2
, aRect
.Top() - nOff2
), // top line starting 2px from left border
908 Point(aRect
.Right() + nOff2
- 3, aRect
.Top() - nOff2
)); // ending 3px from right border
912 rRenderContext
.SetLineColor(rStyleSettings
.GetShadowColor());
913 rRenderContext
.DrawLine(Point(aRect
.Right() + nOff2
- 2, aRect
.Top() + 1 - nOff2
),
914 Point(aRect
.Right() + nOff2
- 2, nRightBottom
- 1));
916 rRenderContext
.SetLineColor(rStyleSettings
.GetDarkShadowColor());
917 rRenderContext
.DrawLine(Point(aRect
.Right() + nOff2
- 1, aRect
.Top() + 3 - nOff2
),
918 Point(aRect
.Right() + nOff2
- 1, nRightBottom
- 1));
923 rRenderContext
.SetLineColor(COL_BLACK
);
924 rRenderContext
.DrawPixel(Point(aRect
.Left() + 1 - nOff2
, aRect
.Top() + 1 - nOff2
));
925 rRenderContext
.DrawPixel(Point(aRect
.Right() + nOff2
- 2, aRect
.Top() + 1 - nOff2
));
928 rRenderContext
.DrawLine(Point(aRect
.Left() - nOff2
, aRect
.Top() + 2 - nOff2
),
929 Point(aRect
.Left() - nOff2
, nLeftBottom
- 1));
931 rRenderContext
.DrawLine(Point(aRect
.Left() + 2 - nOff2
, aRect
.Top() - nOff2
),
932 Point(aRect
.Right() - 3, aRect
.Top() - nOff2
));
935 rRenderContext
.DrawLine(Point(aRect
.Right() + nOff2
- 1, aRect
.Top() + 2 - nOff2
),
936 Point(aRect
.Right() + nOff2
- 1, nRightBottom
- 1));
941 // set font accordingly, current item is painted bold
942 // we set the font attributes always before drawing to be re-entrant (DrawNativeControl may trigger additional paints)
943 vcl::Font
aFont(rRenderContext
.GetFont());
944 aFont
.SetTransparent(true);
945 rRenderContext
.SetFont(aFont
);
947 Size aTabSize
= aRect
.GetSize();
948 Size
aImageSize(0, 0);
949 long nTextHeight
= rRenderContext
.GetTextHeight();
950 long nTextWidth
= rRenderContext
.GetCtrlTextWidth(pItem
->maFormatText
);
951 if (!!pItem
->maTabImage
)
953 aImageSize
= pItem
->maTabImage
.GetSizePixel();
954 if (!pItem
->maFormatText
.isEmpty())
955 aImageSize
.AdjustWidth(GetTextHeight() / 4 );
957 long nXPos
= aRect
.Left() + ((aTabSize
.Width() - nTextWidth
- aImageSize
.Width()) / 2) - nOff
- nOff3
;
958 long nYPos
= aRect
.Top() + ((aTabSize
.Height() - nTextHeight
) / 2) - nOff3
;
959 if (!pItem
->maFormatText
.isEmpty())
961 DrawTextFlags nStyle
= DrawTextFlags::Mnemonic
;
962 if (!pItem
->m_bEnabled
)
963 nStyle
|= DrawTextFlags::Disable
;
965 Color
aColor(rStyleSettings
.GetTabTextColor());
966 if (nState
& ControlState::SELECTED
)
967 aColor
= rStyleSettings
.GetTabHighlightTextColor();
968 else if (nState
& ControlState::ROLLOVER
)
969 aColor
= rStyleSettings
.GetTabRolloverTextColor();
971 Color
aOldColor(rRenderContext
.GetTextColor());
972 rRenderContext
.SetTextColor(aColor
);
974 const tools::Rectangle
aOutRect(nXPos
+ aImageSize
.Width(), nYPos
,
975 nXPos
+ aImageSize
.Width() + nTextWidth
, nYPos
+ nTextHeight
);
976 DrawControlText(rRenderContext
, aOutRect
, pItem
->maFormatText
, nStyle
,
979 rRenderContext
.SetTextColor(aOldColor
);
982 if (!!pItem
->maTabImage
)
984 Point
aImgTL( nXPos
, aRect
.Top() );
985 if (aImageSize
.Height() < aRect
.GetHeight())
986 aImgTL
.AdjustY((aRect
.GetHeight() - aImageSize
.Height()) / 2 );
987 rRenderContext
.DrawImage(aImgTL
, pItem
->maTabImage
, pItem
->m_bEnabled
? DrawImageFlags::NONE
: DrawImageFlags::Disable
);
991 bool TabControl::ImplHandleKeyEvent( const KeyEvent
& rKeyEvent
)
995 if ( GetPageCount() > 1 )
997 vcl::KeyCode aKeyCode
= rKeyEvent
.GetKeyCode();
998 sal_uInt16 nKeyCode
= aKeyCode
.GetCode();
1000 if ( aKeyCode
.IsMod1() )
1002 if ( aKeyCode
.IsShift() || (nKeyCode
== KEY_PAGEUP
) )
1004 if ( (nKeyCode
== KEY_TAB
) || (nKeyCode
== KEY_PAGEUP
) )
1006 ImplActivateTabPage( false );
1012 if ( (nKeyCode
== KEY_TAB
) || (nKeyCode
== KEY_PAGEDOWN
) )
1014 ImplActivateTabPage( true );
1024 IMPL_LINK_NOARG(TabControl
, ImplListBoxSelectHdl
, ListBox
&, void)
1026 SelectTabPage( GetPageId( mpTabCtrlData
->mpListBox
->GetSelectedEntryPos() ) );
1029 IMPL_LINK( TabControl
, ImplWindowEventListener
, VclWindowEvent
&, rEvent
, void )
1031 if ( rEvent
.GetId() == VclEventId::WindowKeyInput
)
1033 // Do not handle events from TabControl or its children, which is done in Notify(), where the events can be consumed.
1034 if ( !IsWindowOrChild( rEvent
.GetWindow() ) )
1036 KeyEvent
* pKeyEvent
= static_cast< KeyEvent
* >(rEvent
.GetData());
1037 ImplHandleKeyEvent( *pKeyEvent
);
1042 void TabControl::MouseButtonDown( const MouseEvent
& rMEvt
)
1044 if (mpTabCtrlData
->mpListBox
.get() != nullptr || !rMEvt
.IsLeft())
1047 ImplTabItem
*pItem
= ImplGetItem(rMEvt
.GetPosPixel());
1048 if (pItem
&& pItem
->m_bEnabled
)
1049 SelectTabPage(pItem
->id());
1052 void TabControl::KeyInput( const KeyEvent
& rKEvt
)
1054 if( mpTabCtrlData
->mpListBox
)
1055 mpTabCtrlData
->mpListBox
->KeyInput( rKEvt
);
1056 else if ( GetPageCount() > 1 )
1058 vcl::KeyCode aKeyCode
= rKEvt
.GetKeyCode();
1059 sal_uInt16 nKeyCode
= aKeyCode
.GetCode();
1061 if ( (nKeyCode
== KEY_LEFT
) || (nKeyCode
== KEY_RIGHT
) )
1063 bool bNext
= (nKeyCode
== KEY_RIGHT
);
1064 ImplActivateTabPage( bNext
);
1068 Control::KeyInput( rKEvt
);
1071 static bool lcl_canPaint(const vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rDrawRect
,
1072 const tools::Rectangle
& rItemRect
)
1074 vcl::Region
aClipRgn(rRenderContext
.GetActiveClipRegion());
1075 aClipRgn
.Intersect(rItemRect
);
1076 if (!rDrawRect
.IsEmpty())
1077 aClipRgn
.Intersect(rDrawRect
);
1078 return !aClipRgn
.IsEmpty();
1081 void TabControl::Paint( vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
1083 if (GetStyle() & WB_NOBORDER
)
1086 Control::Paint(rRenderContext
, rRect
);
1090 // reformat if needed
1091 tools::Rectangle aRect
= ImplGetTabRect(TAB_PAGERECT
);
1093 // find current item
1094 ImplTabItem
* pCurItem
= nullptr;
1095 for (auto & item
: mpTabCtrlData
->maItemList
)
1097 if (item
.id() == mnCurPageId
)
1104 // Draw the TabPage border
1105 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
1106 tools::Rectangle aCurRect
;
1107 aRect
.AdjustLeft( -(TAB_OFFSET
) );
1108 aRect
.AdjustTop( -(TAB_OFFSET
) );
1109 aRect
.AdjustRight(TAB_OFFSET
);
1110 aRect
.AdjustBottom(TAB_OFFSET
);
1112 // if we have an invisible tabpage or no tabpage at all the tabpage rect should be
1113 // increased to avoid round corners that might be drawn by a theme
1114 // in this case we're only interested in the top border of the tabpage because the tabitems are used
1115 // standalone (eg impress)
1116 bool bNoTabPage
= false;
1117 TabPage
* pCurPage
= pCurItem
? pCurItem
->mpTabPage
.get() : nullptr;
1118 if (!pCurPage
|| !pCurPage
->IsVisible())
1121 aRect
.AdjustLeft( -10 );
1122 aRect
.AdjustRight(10 );
1125 if (rRenderContext
.IsNativeControlSupported(ControlType::TabPane
, ControlPart::Entire
))
1127 const bool bPaneWithHeader
= rRenderContext
.IsNativeControlSupported(ControlType::TabPane
, ControlPart::TabPaneWithHeader
);
1128 tools::Rectangle
aHeaderRect(aRect
.Left(), 0, aRect
.Right(), aRect
.Top());
1129 if (bPaneWithHeader
)
1132 if (mpTabCtrlData
->maItemList
.size())
1135 for (const auto &item
: mpTabCtrlData
->maItemList
)
1136 if (item
.m_bVisible
)
1137 nRight
= item
.maRect
.Right();
1139 aHeaderRect
.SetRight(nRight
);
1142 const TabPaneValue
aTabPaneValue(aHeaderRect
, pCurItem
? pCurItem
->maRect
: tools::Rectangle());
1144 ControlState nState
= ControlState::ENABLED
;
1146 nState
&= ~ControlState::ENABLED
;
1148 nState
|= ControlState::FOCUSED
;
1150 if (lcl_canPaint(rRenderContext
, rRect
, aRect
))
1151 rRenderContext
.DrawNativeControl(ControlType::TabPane
, ControlPart::Entire
,
1152 aRect
, nState
, aTabPaneValue
, OUString());
1154 if (!bPaneWithHeader
&& rRenderContext
.IsNativeControlSupported(ControlType::TabHeader
, ControlPart::Entire
)
1155 && lcl_canPaint(rRenderContext
, rRect
, aHeaderRect
))
1156 rRenderContext
.DrawNativeControl(ControlType::TabHeader
, ControlPart::Entire
,
1157 aHeaderRect
, nState
, aTabPaneValue
, OUString());
1162 if (!(rStyleSettings
.GetOptions() & StyleSettingsOptions::Mono
))
1163 rRenderContext
.SetLineColor(rStyleSettings
.GetLightColor());
1165 rRenderContext
.SetLineColor(COL_BLACK
);
1166 if (pCurItem
&& !pCurItem
->maRect
.IsEmpty())
1168 aCurRect
= pCurItem
->maRect
;
1169 rRenderContext
.DrawLine(aRect
.TopLeft(), Point(aCurRect
.Left() - 2, aRect
.Top()));
1170 if (aCurRect
.Right() + 1 < aRect
.Right())
1172 rRenderContext
.DrawLine(Point(aCurRect
.Right(), aRect
.Top()), aRect
.TopRight());
1180 rRenderContext
.DrawLine(aRect
.TopLeft(), aRect
.TopRight());
1182 rRenderContext
.DrawLine(aRect
.TopLeft(), aRect
.BottomLeft());
1184 if (!(rStyleSettings
.GetOptions() & StyleSettingsOptions::Mono
))
1186 // if we have not tab page the bottom line of the tab page
1187 // directly touches the tab items, so choose a color that fits seamlessly
1189 rRenderContext
.SetLineColor(rStyleSettings
.GetDialogColor());
1191 rRenderContext
.SetLineColor(rStyleSettings
.GetShadowColor());
1192 rRenderContext
.DrawLine(Point(1, aRect
.Bottom() - 1), Point(aRect
.Right() - 1, aRect
.Bottom() - 1));
1193 rRenderContext
.DrawLine(Point(aRect
.Right() - 1, aRect
.Top() + nTopOff
), Point(aRect
.Right() - 1, aRect
.Bottom() - 1));
1195 rRenderContext
.SetLineColor(rStyleSettings
.GetDialogColor());
1197 rRenderContext
.SetLineColor(rStyleSettings
.GetDarkShadowColor());
1198 rRenderContext
.DrawLine(Point(0, aRect
.Bottom()), Point(aRect
.Right(), aRect
.Bottom()));
1199 rRenderContext
.DrawLine(Point(aRect
.Right(), aRect
.Top() + nTopOff
), Point(aRect
.Right(), aRect
.Bottom()));
1203 rRenderContext
.DrawLine(aRect
.TopRight(), aRect
.BottomRight());
1204 rRenderContext
.DrawLine(aRect
.BottomLeft(), aRect
.BottomRight());
1208 if (!mpTabCtrlData
->maItemList
.empty() && mpTabCtrlData
->mpListBox
== nullptr)
1210 // Some native toolkits (GTK+) draw tabs right-to-left, with an
1211 // overlap between adjacent tabs
1212 bool bDrawTabsRTL
= rRenderContext
.IsNativeControlSupported(ControlType::TabItem
, ControlPart::TabsDrawRtl
);
1213 ImplTabItem
* pFirstTab
= nullptr;
1214 ImplTabItem
* pLastTab
= nullptr;
1217 // Even though there is a tab overlap with GTK+, the first tab is not
1218 // overlapped on the left side. Other toolkits ignore this option.
1221 pFirstTab
= mpTabCtrlData
->maItemList
.data();
1222 pLastTab
= pFirstTab
+ mpTabCtrlData
->maItemList
.size() - 1;
1223 idx
= mpTabCtrlData
->maItemList
.size() - 1;
1227 pLastTab
= mpTabCtrlData
->maItemList
.data();
1228 pFirstTab
= pLastTab
+ mpTabCtrlData
->maItemList
.size() - 1;
1232 while (idx
< mpTabCtrlData
->maItemList
.size())
1234 ImplTabItem
* pItem
= &mpTabCtrlData
->maItemList
[idx
];
1236 if (pItem
!= pCurItem
&& pItem
->m_bVisible
&& lcl_canPaint(rRenderContext
, rRect
, pItem
->maRect
))
1237 ImplDrawItem(rRenderContext
, pItem
, aCurRect
, pItem
== pFirstTab
, pItem
== pLastTab
);
1245 if (pCurItem
&& lcl_canPaint(rRenderContext
, rRect
, pCurItem
->maRect
))
1246 ImplDrawItem(rRenderContext
, pCurItem
, aCurRect
, pCurItem
== pFirstTab
, pCurItem
== pLastTab
);
1252 mbSmallInvalidate
= true;
1255 void TabControl::setAllocation(const Size
&rAllocation
)
1257 ImplFreeLayoutData();
1259 if ( !IsReallyShown() )
1262 if( mpTabCtrlData
->mpListBox
)
1264 // get the listbox' preferred size
1265 Size
aTabCtrlSize( GetSizePixel() );
1266 long nPrefWidth
= mpTabCtrlData
->mpListBox
->get_preferred_size().Width();
1267 if( nPrefWidth
> aTabCtrlSize
.Width() )
1268 nPrefWidth
= aTabCtrlSize
.Width();
1269 Size
aNewSize( nPrefWidth
, LogicToPixel( Size( 12, 12 ), MapMode( MapUnit::MapAppFont
) ).Height() );
1270 Point
aNewPos( (aTabCtrlSize
.Width() - nPrefWidth
) / 2, 0 );
1271 mpTabCtrlData
->mpListBox
->SetPosSizePixel( aNewPos
, aNewSize
);
1276 // resize/position active TabPage
1277 bool bTabPage
= ImplPosCurTabPage();
1279 // check what needs to be invalidated
1280 Size aNewSize
= rAllocation
;
1281 long nNewWidth
= aNewSize
.Width();
1282 for (auto const& item
: mpTabCtrlData
->maItemList
)
1284 if (!item
.m_bVisible
)
1286 if (!item
.mbFullVisible
|| (item
.maRect
.Right()-2 >= nNewWidth
))
1288 mbSmallInvalidate
= false;
1293 if ( mbSmallInvalidate
)
1295 tools::Rectangle aRect
= ImplGetTabRect( TAB_PAGERECT
);
1296 aRect
.AdjustLeft( -(TAB_OFFSET
+TAB_BORDER_LEFT
) );
1297 aRect
.AdjustTop( -(TAB_OFFSET
+TAB_BORDER_TOP
) );
1298 aRect
.AdjustRight(TAB_OFFSET
+TAB_BORDER_RIGHT
);
1299 aRect
.AdjustBottom(TAB_OFFSET
+TAB_BORDER_BOTTOM
);
1301 Invalidate( aRect
, InvalidateFlags::NoChildren
);
1303 Invalidate( aRect
);
1309 Invalidate( InvalidateFlags::NoChildren
);
1314 mbLayoutDirty
= false;
1317 void TabControl::SetPosSizePixel(const Point
& rNewPos
, const Size
& rNewSize
)
1319 Window::SetPosSizePixel(rNewPos
, rNewSize
);
1320 //if size changed, TabControl::Resize got called already
1322 setAllocation(rNewSize
);
1325 void TabControl::SetSizePixel(const Size
& rNewSize
)
1327 Window::SetSizePixel(rNewSize
);
1328 //if size changed, TabControl::Resize got called already
1330 setAllocation(rNewSize
);
1333 void TabControl::SetPosPixel(const Point
& rPos
)
1335 Window::SetPosPixel(rPos
);
1337 setAllocation(GetOutputSizePixel());
1340 void TabControl::Resize()
1342 setAllocation(Control::GetOutputSizePixel());
1345 void TabControl::GetFocus()
1347 if( ! mpTabCtrlData
->mpListBox
)
1350 SetInputContext( InputContext( GetFont() ) );
1354 if( mpTabCtrlData
->mpListBox
->IsReallyVisible() )
1355 mpTabCtrlData
->mpListBox
->GrabFocus();
1357 Control::GetFocus();
1360 void TabControl::LoseFocus()
1362 if( mpTabCtrlData
&& ! mpTabCtrlData
->mpListBox
)
1364 Control::LoseFocus();
1367 void TabControl::RequestHelp( const HelpEvent
& rHEvt
)
1369 sal_uInt16 nItemId
= rHEvt
.KeyboardActivated() ? mnCurPageId
: GetPageId( ScreenToOutputPixel( rHEvt
.GetMousePosPixel() ) );
1373 if ( rHEvt
.GetMode() & HelpEventMode::BALLOON
)
1375 OUString aStr
= GetHelpText( nItemId
);
1376 if ( !aStr
.isEmpty() )
1378 tools::Rectangle aItemRect
= ImplGetTabRect( GetPagePos( nItemId
) );
1379 Point aPt
= OutputToScreenPixel( aItemRect
.TopLeft() );
1380 aItemRect
.SetLeft( aPt
.X() );
1381 aItemRect
.SetTop( aPt
.Y() );
1382 aPt
= OutputToScreenPixel( aItemRect
.BottomRight() );
1383 aItemRect
.SetRight( aPt
.X() );
1384 aItemRect
.SetBottom( aPt
.Y() );
1385 Help::ShowBalloon( this, aItemRect
.Center(), aItemRect
, aStr
);
1390 // for Quick or Ballon Help, we show the text, if it is cut
1391 if ( rHEvt
.GetMode() & (HelpEventMode::QUICK
| HelpEventMode::BALLOON
) )
1393 ImplTabItem
* pItem
= ImplGetItem( nItemId
);
1394 const OUString
& rStr
= pItem
->maText
;
1395 if ( rStr
!= pItem
->maFormatText
)
1397 tools::Rectangle aItemRect
= ImplGetTabRect( GetPagePos( nItemId
) );
1398 Point aPt
= OutputToScreenPixel( aItemRect
.TopLeft() );
1399 aItemRect
.SetLeft( aPt
.X() );
1400 aItemRect
.SetTop( aPt
.Y() );
1401 aPt
= OutputToScreenPixel( aItemRect
.BottomRight() );
1402 aItemRect
.SetRight( aPt
.X() );
1403 aItemRect
.SetBottom( aPt
.Y() );
1404 if ( !rStr
.isEmpty() )
1406 if ( rHEvt
.GetMode() & HelpEventMode::BALLOON
)
1407 Help::ShowBalloon( this, aItemRect
.Center(), aItemRect
, rStr
);
1409 Help::ShowQuickHelp( this, aItemRect
, rStr
);
1415 if ( rHEvt
.GetMode() & HelpEventMode::QUICK
)
1417 ImplTabItem
* pItem
= ImplGetItem( nItemId
);
1418 const OUString
& rHelpText
= pItem
->maHelpText
;
1419 // show tooltip if not text but image is set and helptext is available
1420 if ( !rHelpText
.isEmpty() && pItem
->maText
.isEmpty() && !!pItem
->maTabImage
)
1422 tools::Rectangle aItemRect
= ImplGetTabRect( GetPagePos( nItemId
) );
1423 Point aPt
= OutputToScreenPixel( aItemRect
.TopLeft() );
1424 aItemRect
.SetLeft( aPt
.X() );
1425 aItemRect
.SetTop( aPt
.Y() );
1426 aPt
= OutputToScreenPixel( aItemRect
.BottomRight() );
1427 aItemRect
.SetRight( aPt
.X() );
1428 aItemRect
.SetBottom( aPt
.Y() );
1429 Help::ShowQuickHelp( this, aItemRect
, rHelpText
);
1435 Control::RequestHelp( rHEvt
);
1438 void TabControl::Command( const CommandEvent
& rCEvt
)
1440 if( (mpTabCtrlData
->mpListBox
== nullptr) && (rCEvt
.GetCommand() == CommandEventId::ContextMenu
) && (GetPageCount() > 1) )
1444 if ( rCEvt
.IsMouseEvent() )
1446 aMenuPos
= rCEvt
.GetMousePosPixel();
1447 bMenu
= GetPageId( aMenuPos
) != 0;
1451 aMenuPos
= ImplGetTabRect( GetPagePos( mnCurPageId
) ).Center();
1457 ScopedVclPtrInstance
<PopupMenu
> aMenu
;
1458 for (auto const& item
: mpTabCtrlData
->maItemList
)
1460 aMenu
->InsertItem(item
.id(), item
.maText
, MenuItemBits::CHECKABLE
| MenuItemBits::RADIOCHECK
);
1461 if (item
.id() == mnCurPageId
)
1462 aMenu
->CheckItem(item
.id());
1463 aMenu
->SetHelpId(item
.id(), OString());
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( false );
1497 else if ( nType
== StateChangedType::ControlForeground
)
1499 ImplInitSettings( false );
1502 else if ( nType
== StateChangedType::ControlBackground
)
1504 ImplInitSettings( 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 );
1523 ImplTabItem
* TabControl::ImplGetItem(const Point
& rPt
) const
1525 ImplTabItem
* pFoundItem
= nullptr;
1527 for (auto & item
: mpTabCtrlData
->maItemList
)
1529 if (item
.m_bVisible
&& item
.maRect
.IsInside(rPt
))
1536 // assure that only one tab is highlighted at a time
1537 assert(nFound
<= 1);
1538 return nFound
== 1 ? pFoundItem
: 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 ImplTabItem
*pItem
= ImplGetItem(GetPointerPosPixel());
1553 ImplTabItem
*pLastItem
= ImplGetItem(GetLastPointerPosPixel());
1554 if ((pItem
!= pLastItem
) || pMouseEvt
->IsLeaveWindow() || pMouseEvt
->IsEnterWindow())
1556 vcl::Region aClipRgn
;
1559 // allow for slightly bigger tabitems
1561 // TODO: query for the correct sizes
1562 tools::Rectangle
aRect(pLastItem
->maRect
);
1563 aRect
.AdjustLeft( -2 );
1564 aRect
.AdjustRight(2 );
1565 aRect
.AdjustTop( -3 );
1566 aClipRgn
.Union( aRect
);
1571 // allow for slightly bigger tabitems
1573 // TODO: query for the correct sizes
1574 tools::Rectangle
aRect(pItem
->maRect
);
1575 aRect
.AdjustLeft( -2 );
1576 aRect
.AdjustRight(2 );
1577 aRect
.AdjustTop( -3 );
1578 aClipRgn
.Union( aRect
);
1581 if( !aClipRgn
.IsEmpty() )
1582 Invalidate( aClipRgn
);
1588 return Control::PreNotify(rNEvt
);
1591 bool TabControl::EventNotify( NotifyEvent
& rNEvt
)
1595 if ( rNEvt
.GetType() == MouseNotifyEvent::KEYINPUT
)
1596 bRet
= ImplHandleKeyEvent( *rNEvt
.GetKeyEvent() );
1598 return bRet
|| Control::EventNotify( rNEvt
);
1601 void TabControl::ActivatePage()
1603 maActivateHdl
.Call( this );
1606 bool TabControl::DeactivatePage()
1608 return !maDeactivateHdl
.IsSet() || maDeactivateHdl
.Call( this );
1611 void TabControl::SetTabPageSizePixel( const Size
& rSize
)
1613 ImplFreeLayoutData();
1615 Size
aNewSize( rSize
);
1616 aNewSize
.AdjustWidth(TAB_OFFSET
*2 );
1617 tools::Rectangle aRect
= ImplGetTabRect( TAB_PAGERECT
,
1618 aNewSize
.Width(), aNewSize
.Height() );
1619 aNewSize
.AdjustHeight(aRect
.Top()+TAB_OFFSET
);
1620 Window::SetOutputSizePixel( aNewSize
);
1623 Size
TabControl::GetTabPageSizePixel() const
1625 tools::Rectangle aRect
= const_cast<TabControl
*>(this)->ImplGetTabRect( TAB_PAGERECT
);
1626 return aRect
.GetSize();
1629 void TabControl::InsertPage( sal_uInt16 nPageId
, const OUString
& rText
,
1632 SAL_WARN_IF( !nPageId
, "vcl", "TabControl::InsertPage(): PageId == 0" );
1633 SAL_WARN_IF( GetPagePos( nPageId
) != TAB_PAGE_NOTFOUND
, "vcl",
1634 "TabControl::InsertPage(): PageId already exists" );
1636 // insert new page item
1637 ImplTabItem
* pItem
= nullptr;
1638 if( nPos
== TAB_APPEND
|| size_t(nPos
) >= mpTabCtrlData
->maItemList
.size() )
1640 mpTabCtrlData
->maItemList
.emplace_back(nPageId
);
1641 pItem
= &mpTabCtrlData
->maItemList
.back();
1642 if( mpTabCtrlData
->mpListBox
)
1643 mpTabCtrlData
->mpListBox
->InsertEntry( rText
);
1647 std::vector
< ImplTabItem
>::iterator new_it
=
1648 mpTabCtrlData
->maItemList
.emplace(mpTabCtrlData
->maItemList
.begin() + nPos
, nPageId
);
1650 if( mpTabCtrlData
->mpListBox
)
1651 mpTabCtrlData
->mpListBox
->InsertEntry( rText
, nPos
);
1653 if( mpTabCtrlData
->mpListBox
)
1656 mpTabCtrlData
->mpListBox
->SelectEntryPos( 0 );
1657 mpTabCtrlData
->mpListBox
->SetDropDownLineCount( mpTabCtrlData
->mpListBox
->GetEntryCount() );
1660 // set current page id
1662 mnCurPageId
= nPageId
;
1664 // init new page item
1665 pItem
->maText
= rText
;
1666 pItem
->mbFullVisible
= false;
1669 if ( IsUpdateMode() )
1672 ImplFreeLayoutData();
1673 if( mpTabCtrlData
->mpListBox
) // reposition/resize listbox
1676 CallEventListeners( VclEventId::TabpageInserted
, 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
->id() == 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, then 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->id()
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].id());
1716 if ( IsUpdateMode() )
1719 ImplFreeLayoutData();
1721 CallEventListeners( VclEventId::TabpageRemoved
, 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( VclEventId::TabpageRemovedAll
);
1742 void TabControl::SetPageEnabled( sal_uInt16 i_nPageId
, bool i_bEnable
)
1744 ImplTabItem
* pItem
= ImplGetItem( i_nPageId
);
1746 if (pItem
&& pItem
->m_bEnabled
!= i_bEnable
)
1748 pItem
->m_bEnabled
= i_bEnable
;
1749 if (!pItem
->m_bVisible
)
1753 if( mpTabCtrlData
->mpListBox
)
1754 mpTabCtrlData
->mpListBox
->SetEntryFlags( GetPagePos( i_nPageId
),
1755 i_bEnable
? ListBoxEntryFlags::NONE
: (ListBoxEntryFlags::DisableSelection
| ListBoxEntryFlags::DrawDisabled
) );
1757 // SetCurPageId will change to a valid page
1758 if (pItem
->id() == mnCurPageId
)
1759 SetCurPageId( mnCurPageId
);
1760 else if ( IsUpdateMode() )
1765 void TabControl::SetPageVisible( sal_uInt16 nPageId
, bool bVisible
)
1767 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1768 if (!pItem
|| pItem
->m_bVisible
== bVisible
)
1771 pItem
->m_bVisible
= bVisible
;
1774 if (pItem
->mbFullVisible
)
1775 mbSmallInvalidate
= false;
1776 pItem
->mbFullVisible
= false;
1777 pItem
->maRect
.SetEmpty();
1781 // SetCurPageId will change to a valid page
1782 if (pItem
->id() == mnCurPageId
)
1783 SetCurPageId(mnCurPageId
);
1784 else if (IsUpdateMode())
1788 sal_uInt16
TabControl::GetPageCount() const
1790 return static_cast<sal_uInt16
>(mpTabCtrlData
->maItemList
.size());
1793 sal_uInt16
TabControl::GetPageId( sal_uInt16 nPos
) const
1795 if( size_t(nPos
) < mpTabCtrlData
->maItemList
.size() )
1796 return mpTabCtrlData
->maItemList
[nPos
].id();
1800 sal_uInt16
TabControl::GetPagePos( sal_uInt16 nPageId
) const
1802 sal_uInt16 nPos
= 0;
1803 for (auto const& item
: mpTabCtrlData
->maItemList
)
1805 if (item
.id() == nPageId
)
1810 return TAB_PAGE_NOTFOUND
;
1813 sal_uInt16
TabControl::GetPageId( const Point
& rPos
) const
1815 const auto &rList
= mpTabCtrlData
->maItemList
;
1816 const auto it
= std::find_if(rList
.begin(), rList
.end(), [&rPos
, this](const auto &item
) {
1817 return const_cast<TabControl
*>(this)->ImplGetTabRect(&item
).IsInside(rPos
); });
1818 return (it
!= rList
.end()) ? it
->id() : 0;
1821 sal_uInt16
TabControl::GetPageId( const OString
& rName
) const
1823 const auto &rList
= mpTabCtrlData
->maItemList
;
1824 const auto it
= std::find_if(rList
.begin(), rList
.end(), [&rName
](const auto &item
) {
1825 return item
.maTabName
== rName
; });
1826 return (it
!= rList
.end()) ? it
->id() : 0;
1829 void TabControl::SetCurPageId( sal_uInt16 nPageId
)
1831 sal_uInt16 nPos
= GetPagePos( nPageId
);
1832 while (nPos
!= TAB_PAGE_NOTFOUND
&& !mpTabCtrlData
->maItemList
[nPos
].m_bEnabled
)
1835 if( size_t(nPos
) >= mpTabCtrlData
->maItemList
.size() )
1837 if (mpTabCtrlData
->maItemList
[nPos
].id() == nPageId
)
1841 if( nPos
!= TAB_PAGE_NOTFOUND
)
1843 nPageId
= mpTabCtrlData
->maItemList
[nPos
].id();
1844 if ( nPageId
== mnCurPageId
)
1847 mnActPageId
= nPageId
;
1852 mnActPageId
= nPageId
;
1856 sal_uInt16 nOldId
= mnCurPageId
;
1857 mnCurPageId
= nPageId
;
1858 ImplChangeTabPage( nPageId
, nOldId
);
1863 sal_uInt16
TabControl::GetCurPageId() const
1871 void TabControl::SelectTabPage( sal_uInt16 nPageId
)
1873 if ( nPageId
&& (nPageId
!= mnCurPageId
) )
1875 ImplFreeLayoutData();
1877 CallEventListeners( VclEventId::TabpageDeactivate
, reinterpret_cast<void*>(mnCurPageId
) );
1878 if ( DeactivatePage() )
1880 mnActPageId
= nPageId
;
1882 // Page could have been switched by the Activate handler
1883 nPageId
= mnActPageId
;
1885 SetCurPageId( nPageId
);
1886 if( mpTabCtrlData
->mpListBox
)
1887 mpTabCtrlData
->mpListBox
->SelectEntryPos( GetPagePos( nPageId
) );
1888 CallEventListeners( VclEventId::TabpageActivate
, reinterpret_cast<void*>(nPageId
) );
1893 void TabControl::SetTabPage( sal_uInt16 nPageId
, TabPage
* pTabPage
)
1895 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1897 if ( pItem
&& (pItem
->mpTabPage
.get() != pTabPage
) )
1901 if ( IsDefaultSize() )
1902 SetTabPageSizePixel( pTabPage
->GetSizePixel() );
1904 // only set here, so that Resize does not reposition TabPage
1905 pItem
->mpTabPage
= pTabPage
;
1908 if (pItem
->id() == mnCurPageId
)
1909 ImplChangeTabPage(pItem
->id(), 0);
1913 pItem
->mpTabPage
= nullptr;
1919 TabPage
* TabControl::GetTabPage( sal_uInt16 nPageId
) const
1921 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1924 return pItem
->mpTabPage
;
1929 void TabControl::SetPageText( sal_uInt16 nPageId
, const OUString
& rText
)
1931 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1933 if ( pItem
&& pItem
->maText
!= rText
)
1935 pItem
->maText
= rText
;
1937 if( mpTabCtrlData
->mpListBox
)
1939 sal_uInt16 nPos
= GetPagePos( nPageId
);
1940 mpTabCtrlData
->mpListBox
->RemoveEntry( nPos
);
1941 mpTabCtrlData
->mpListBox
->InsertEntry( rText
, nPos
);
1943 if ( IsUpdateMode() )
1945 ImplFreeLayoutData();
1946 CallEventListeners( VclEventId::TabpagePageTextChanged
, reinterpret_cast<void*>(nPageId
) );
1950 OUString
const & TabControl::GetPageText( sal_uInt16 nPageId
) const
1952 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1956 return pItem
->maText
;
1959 void TabControl::SetHelpText( sal_uInt16 nPageId
, const OUString
& rText
)
1961 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1965 pItem
->maHelpText
= rText
;
1968 const OUString
& TabControl::GetHelpText( sal_uInt16 nPageId
) const
1970 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1972 return pItem
->maHelpText
;
1975 void TabControl::SetPageName( sal_uInt16 nPageId
, const OString
& rName
) const
1977 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1980 pItem
->maTabName
= rName
;
1983 OString
TabControl::GetPageName( sal_uInt16 nPageId
) const
1985 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1988 return pItem
->maTabName
;
1993 void TabControl::SetPageImage( sal_uInt16 i_nPageId
, const Image
& i_rImage
)
1995 ImplTabItem
* pItem
= ImplGetItem( i_nPageId
);
1999 pItem
->maTabImage
= i_rImage
;
2001 if ( IsUpdateMode() )
2006 tools::Rectangle
TabControl::GetCharacterBounds( sal_uInt16 nPageId
, long nIndex
) const
2008 tools::Rectangle aRet
;
2010 if( !HasLayoutData() || mpTabCtrlData
->maLayoutPageIdToLine
.empty() )
2013 if( HasLayoutData() )
2015 std::unordered_map
< int, int >::const_iterator it
= mpTabCtrlData
->maLayoutPageIdToLine
.find( static_cast<int>(nPageId
) );
2016 if( it
!= mpTabCtrlData
->maLayoutPageIdToLine
.end() )
2018 Pair aPair
= mpControlData
->mpLayoutData
->GetLineStartEnd( it
->second
);
2019 if( (aPair
.B() - aPair
.A()) >= nIndex
)
2020 aRet
= mpControlData
->mpLayoutData
->GetCharacterBounds( aPair
.A() + nIndex
);
2027 long TabControl::GetIndexForPoint( const Point
& rPoint
, sal_uInt16
& rPageId
) const
2031 if( !HasLayoutData() || mpTabCtrlData
->maLayoutPageIdToLine
.empty() )
2034 if( HasLayoutData() )
2036 int nIndex
= mpControlData
->mpLayoutData
->GetIndexForPoint( rPoint
);
2039 // what line (->pageid) is this index in ?
2040 int nLines
= mpControlData
->mpLayoutData
->GetLineCount();
2042 while( ++nLine
< nLines
)
2044 Pair aPair
= mpControlData
->mpLayoutData
->GetLineStartEnd( nLine
);
2045 if( aPair
.A() <= nIndex
&& aPair
.B() >= nIndex
)
2047 nRet
= nIndex
- aPair
.A();
2048 rPageId
= static_cast<sal_uInt16
>(mpTabCtrlData
->maLayoutLineToPageId
[ nLine
]);
2058 void TabControl::FillLayoutData() const
2060 mpTabCtrlData
->maLayoutLineToPageId
.clear();
2061 mpTabCtrlData
->maLayoutPageIdToLine
.clear();
2062 const_cast<TabControl
*>(this)->Invalidate();
2065 tools::Rectangle
TabControl::GetTabBounds( sal_uInt16 nPageId
) const
2067 tools::Rectangle aRet
;
2069 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
2070 if (pItem
&& pItem
->m_bVisible
)
2071 aRet
= pItem
->maRect
;
2076 void TabControl::SetItemsOffset( const Point
& rOffs
)
2079 mpTabCtrlData
->maItemsOffset
= rOffs
;
2082 Point
TabControl::GetItemsOffset() const
2085 return mpTabCtrlData
->maItemsOffset
;
2090 Size
TabControl::ImplCalculateRequisition(sal_uInt16
& nHeaderHeight
) const
2092 Size
aOptimalPageSize(0, 0);
2094 sal_uInt16 nOrigPageId
= GetCurPageId();
2095 for (auto const& item
: mpTabCtrlData
->maItemList
)
2097 const TabPage
*pPage
= item
.mpTabPage
;
2098 //it's a real nuisance if the page is not inserted yet :-(
2099 //We need to force all tabs to exist to get overall optimal size for dialog
2102 TabControl
*pThis
= const_cast<TabControl
*>(this);
2103 pThis
->SetCurPageId(item
.id());
2104 pThis
->ActivatePage();
2105 pPage
= item
.mpTabPage
;
2111 Size
aPageSize(VclContainer::getLayoutRequisition(*pPage
));
2113 if (aPageSize
.Width() > aOptimalPageSize
.Width())
2114 aOptimalPageSize
.setWidth( aPageSize
.Width() );
2115 if (aPageSize
.Height() > aOptimalPageSize
.Height())
2116 aOptimalPageSize
.setHeight( aPageSize
.Height() );
2119 //fdo#61940 If we were forced to activate pages in order to on-demand
2120 //create them to get their optimal size, then switch back to the original
2121 //page and re-activate it
2122 if (nOrigPageId
!= GetCurPageId())
2124 TabControl
*pThis
= const_cast<TabControl
*>(this);
2125 pThis
->SetCurPageId(nOrigPageId
);
2126 pThis
->ActivatePage();
2129 long nTabLabelsBottom
= 0, nTabLabelsRight
= 0;
2130 for (sal_uInt16
nPos(0), sizeList(static_cast <sal_uInt16
> (mpTabCtrlData
->maItemList
.size()));
2131 nPos
< sizeList
; ++nPos
)
2133 TabControl
* pThis
= const_cast<TabControl
*>(this);
2135 tools::Rectangle aTabRect
= pThis
->ImplGetTabRect(nPos
, aOptimalPageSize
.Width(), LONG_MAX
);
2136 if (aTabRect
.Bottom() > nTabLabelsBottom
)
2138 nTabLabelsBottom
= aTabRect
.Bottom();
2139 nHeaderHeight
= nTabLabelsBottom
;
2141 if (!aTabRect
.IsEmpty() && aTabRect
.Right() > nTabLabelsRight
)
2142 nTabLabelsRight
= aTabRect
.Right();
2145 Size
aOptimalSize(aOptimalPageSize
);
2146 aOptimalSize
.AdjustHeight(nTabLabelsBottom
);
2147 aOptimalSize
.setWidth( std::max(nTabLabelsRight
, aOptimalSize
.Width()) );
2149 aOptimalSize
.AdjustWidth(TAB_OFFSET
* 2 );
2150 aOptimalSize
.AdjustHeight(TAB_OFFSET
* 2 );
2152 return aOptimalSize
;
2155 Size
TabControl::calculateRequisition() const
2157 sal_uInt16 nHeaderHeight
;
2158 return ImplCalculateRequisition(nHeaderHeight
);
2161 Size
TabControl::GetOptimalSize() const
2163 return calculateRequisition();
2166 void TabControl::queue_resize(StateChangedType eReason
)
2168 mbLayoutDirty
= true;
2169 Window::queue_resize(eReason
);
2172 std::vector
<sal_uInt16
> TabControl::GetPageIDs() const
2174 std::vector
<sal_uInt16
> aIDs
;
2175 for (auto const& item
: mpTabCtrlData
->maItemList
)
2177 aIDs
.push_back(item
.id());
2183 FactoryFunction
TabControl::GetUITestFactory() const
2185 return TabControlUIObject::create
;
2188 sal_uInt16
NotebookbarTabControlBase::m_nHeaderHeight
= 0;
2190 IMPL_LINK_NOARG(NotebookbarTabControlBase
, OpenMenu
, Button
*, void)
2192 m_aIconClickHdl
.Call(static_cast<NotebookBar
*>(GetParent()->GetParent()));
2195 NotebookbarTabControlBase::NotebookbarTabControlBase(vcl::Window
* pParent
)
2196 : TabControl(pParent
, WB_STDTABCONTROL
)
2197 , bLastContextWasSupported(true)
2198 , eLastContext(vcl::EnumContext::Context::Any
)
2200 m_pOpenMenu
= VclPtr
<PushButton
>::Create( this , WB_CENTER
| WB_VCENTER
);
2201 m_pOpenMenu
->SetSizePixel(Size(HAMBURGER_DIM
, HAMBURGER_DIM
));
2202 m_pOpenMenu
->SetClickHdl(LINK(this, NotebookbarTabControlBase
, OpenMenu
));
2203 m_pOpenMenu
->SetModeImage(Image(StockImage::Yes
, SV_RESID_BITMAP_NOTEBOOKBAR
));
2204 m_pOpenMenu
->Show();
2207 NotebookbarTabControlBase::~NotebookbarTabControlBase()
2212 void NotebookbarTabControlBase::SetContext( vcl::EnumContext::Context eContext
)
2214 if (eLastContext
!= eContext
)
2216 bool bHandled
= false;
2218 for (int nChild
= 0; nChild
< GetPageCount(); ++nChild
)
2220 sal_uInt16 nPageId
= TabControl::GetPageId(nChild
);
2221 TabPage
* pPage
= GetTabPage(nPageId
);
2225 SetPageVisible(nPageId
, pPage
->HasContext(eContext
) || pPage
->HasContext(vcl::EnumContext::Context::Any
));
2227 if (!bHandled
&& bLastContextWasSupported
2228 && pPage
->HasContext(vcl::EnumContext::Context::Default
))
2230 SetCurPageId(nPageId
);
2233 if (pPage
->HasContext(eContext
) && eContext
!= vcl::EnumContext::Context::Any
)
2235 SetCurPageId(nPageId
);
2237 bLastContextWasSupported
= true;
2243 bLastContextWasSupported
= false;
2244 eLastContext
= eContext
;
2248 void NotebookbarTabControlBase::dispose()
2250 m_pShortcuts
.disposeAndClear();
2251 m_pOpenMenu
.disposeAndClear();
2252 TabControl::dispose();
2255 void NotebookbarTabControlBase::SetToolBox( ToolBox
* pToolBox
)
2257 m_pShortcuts
.set( pToolBox
);
2260 void NotebookbarTabControlBase::SetIconClickHdl( Link
<NotebookBar
*, void> aHdl
)
2262 m_aIconClickHdl
= aHdl
;
2265 static bool lcl_isValidPage(const ImplTabItem
& rItem
, bool& bFound
)
2267 if (rItem
.m_bVisible
&& rItem
.m_bEnabled
)
2272 void NotebookbarTabControlBase::ImplActivateTabPage( bool bNext
)
2274 const sal_uInt16 nOldPos
= GetPagePos(GetCurPageId());
2275 bool bFound
= false;
2276 sal_Int32 nCurPos
= nOldPos
;
2280 for (nCurPos
++; nCurPos
< GetPageCount(); nCurPos
++)
2281 if (lcl_isValidPage(mpTabCtrlData
->maItemList
[nCurPos
], bFound
))
2286 for (nCurPos
--; nCurPos
>= 0; nCurPos
--)
2287 if (lcl_isValidPage(mpTabCtrlData
->maItemList
[nCurPos
], bFound
))
2293 SelectTabPage( TabControl::GetPageId( nCurPos
) );
2296 sal_uInt16
NotebookbarTabControlBase::GetHeaderHeight()
2298 return m_nHeaderHeight
;
2301 bool NotebookbarTabControlBase::ImplPlaceTabs( long nWidth
)
2305 if ( mpTabCtrlData
->maItemList
.empty() )
2307 if (!m_pOpenMenu
|| m_pOpenMenu
->isDisposed())
2310 long nMaxWidth
= nWidth
- HAMBURGER_DIM
;
2311 long nShortcutsWidth
= m_pShortcuts
!= nullptr ? m_pShortcuts
->GetSizePixel().getWidth() + 1 : 0;
2312 long nFullWidth
= nShortcutsWidth
;
2314 const long nOffsetX
= 2 + GetItemsOffset().X() + nShortcutsWidth
;
2315 const long nOffsetY
= 2 + GetItemsOffset().Y();
2317 //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
2318 //of ugly bare tabs on lines of their own
2320 for (auto & item
: mpTabCtrlData
->maItemList
)
2323 if (item
.m_bVisible
)
2325 nTabWidth
= ImplGetItemSize(&item
, nMaxWidth
).getWidth();
2326 if (!item
.maText
.isEmpty() && nTabWidth
< 100)
2329 nFullWidth
+= nTabWidth
;
2332 nMaxWidth
-= GetItemsOffset().X();
2337 long nLineWidthAry
[100];
2338 nLineWidthAry
[0] = 0;
2340 for (auto & item
: mpTabCtrlData
->maItemList
)
2342 if (!item
.m_bVisible
)
2345 Size aSize
= ImplGetItemSize( &item
, nMaxWidth
);
2347 // set minimum tab size
2348 if( nFullWidth
< nMaxWidth
&& !item
.maText
.isEmpty() && aSize
.getWidth() < 100)
2349 aSize
.setWidth( 100 );
2351 if( !item
.maText
.isEmpty() && aSize
.getHeight() < 28 )
2352 aSize
.setHeight( 28 );
2354 tools::Rectangle
aNewRect( Point( nX
, nY
), aSize
);
2355 if ( mbSmallInvalidate
&& (item
.maRect
!= aNewRect
) )
2356 mbSmallInvalidate
= false;
2358 item
.maRect
= aNewRect
;
2360 item
.mbFullVisible
= true;
2362 nLineWidthAry
[0] += aSize
.Width();
2363 nX
+= aSize
.Width();
2366 // we always have only one line of tabs
2367 lcl_AdjustSingleLineTabs(nMaxWidth
, mpTabCtrlData
.get());
2369 // position the shortcutbox
2371 m_pShortcuts
->SetPosPixel(Point(0, 0));
2373 // position the menu
2374 m_pOpenMenu
->SetPosPixel(Point(nWidth
- HAMBURGER_DIM
, 0));
2379 Size
NotebookbarTabControlBase::calculateRequisition() const
2381 return TabControl::ImplCalculateRequisition(m_nHeaderHeight
);
2384 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */