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/toolkit/button.hxx>
29 #include <vcl/tabpage.hxx>
30 #include <vcl/tabctrl.hxx>
31 #include <vcl/toolkit/controllayout.hxx>
32 #include <vcl/layout.hxx>
33 #include <vcl/toolkit/lstbox.hxx>
34 #include <vcl/settings.hxx>
35 #include <vcl/uitest/uiobject.hxx>
36 #include <bitmaps.hlst>
37 #include <tools/json_writer.hxx>
39 #include <controldata.hxx>
44 #include <unordered_map>
47 class ImplTabItem final
52 VclPtr
<TabPage
> mpTabPage
;
54 OUString maFormatText
;
56 OUString maAccessibleName
;
57 OUString maAccessibleDescription
;
59 tools::Rectangle maRect
;
62 bool m_bEnabled
; ///< the tab / page is selectable
63 bool m_bVisible
; ///< the tab / page can be visible
66 ImplTabItem(sal_uInt16 nId
);
68 sal_uInt16
id() const { return m_nId
; }
71 ImplTabItem::ImplTabItem(sal_uInt16 nId
)
74 , mbFullVisible(false)
80 struct ImplTabCtrlData
82 std::unordered_map
< int, int > maLayoutPageIdToLine
;
83 std::unordered_map
< int, int > maLayoutLineToPageId
;
84 std::vector
< ImplTabItem
> maItemList
;
85 VclPtr
<ListBox
> mpListBox
;
88 // for the Tab positions
89 #define TAB_PAGERECT 0xFFFF
91 void TabControl::ImplInit( vcl::Window
* pParent
, WinBits nStyle
)
95 if ( !(nStyle
& WB_NOTABSTOP
) )
97 if ( !(nStyle
& WB_NOGROUP
) )
99 if ( !(nStyle
& WB_NODIALOGCONTROL
) )
100 nStyle
|= WB_DIALOGCONTROL
;
102 Control::ImplInit( pParent
, nStyle
, nullptr );
109 mbRestoreHelpId
= false;
110 mbSmallInvalidate
= false;
111 mpTabCtrlData
.reset(new ImplTabCtrlData
);
112 mpTabCtrlData
->mpListBox
= nullptr;
114 ImplInitSettings( true );
116 if( nStyle
& WB_DROPDOWN
)
118 mpTabCtrlData
->mpListBox
= VclPtr
<ListBox
>::Create( this, WB_DROPDOWN
);
119 mpTabCtrlData
->mpListBox
->SetPosSizePixel( Point( 0, 0 ), Size( 200, 20 ) );
120 mpTabCtrlData
->mpListBox
->SetSelectHdl( LINK( this, TabControl
, ImplListBoxSelectHdl
) );
121 mpTabCtrlData
->mpListBox
->Show();
124 // if the tabcontrol is drawn (ie filled) by a native widget, make sure all controls will have transparent background
125 // otherwise they will paint with a wrong background
126 if( IsNativeControlSupported(ControlType::TabPane
, ControlPart::Entire
) )
127 EnableChildTransparentMode();
129 if (pParent
&& pParent
->IsDialog())
130 pParent
->AddChildEventListener( LINK( this, TabControl
, ImplWindowEventListener
) );
133 const vcl::Font
& TabControl::GetCanonicalFont( const StyleSettings
& _rStyle
) const
135 return _rStyle
.GetTabFont();
138 const Color
& TabControl::GetCanonicalTextColor( const StyleSettings
& _rStyle
) const
140 return _rStyle
.GetTabTextColor();
143 void TabControl::ImplInitSettings( bool bBackground
)
145 Control::ImplInitSettings();
150 vcl::Window
* pParent
= GetParent();
151 if ( !IsControlBackground() &&
152 (pParent
->IsChildTransparentModeEnabled()
153 || IsNativeControlSupported(ControlType::TabPane
, ControlPart::Entire
)
154 || IsNativeControlSupported(ControlType::TabItem
, ControlPart::Entire
) ) )
157 // set transparent mode for NWF tabcontrols to have
158 // the background always cleared properly
159 EnableChildTransparentMode();
160 SetParentClipMode( ParentClipMode::NoClip
);
161 SetPaintTransparent( true );
163 ImplGetWindowImpl()->mbUseNativeFocus
= ImplGetSVData()->maNWFData
.mbNoFocusRects
;
167 EnableChildTransparentMode( false );
169 SetPaintTransparent( false );
171 if ( IsControlBackground() )
172 SetBackground( GetControlBackground() );
174 SetBackground( pParent
->GetBackground() );
178 void TabControl::ImplFreeLayoutData()
180 if( HasLayoutData() )
182 ImplClearLayoutData();
183 mpTabCtrlData
->maLayoutPageIdToLine
.clear();
184 mpTabCtrlData
->maLayoutLineToPageId
.clear();
188 TabControl::TabControl( vcl::Window
* pParent
, WinBits nStyle
) :
189 Control( WindowType::TABCONTROL
)
191 ImplInit( pParent
, nStyle
);
192 SAL_INFO( "vcl", "*** TABCONTROL no notabs? " << (( GetStyle() & WB_NOBORDER
) ? "true" : "false") );
195 TabControl::~TabControl()
200 void TabControl::dispose()
202 Window
*pParent
= GetParent();
203 if (pParent
&& pParent
->IsDialog())
204 GetParent()->RemoveChildEventListener( LINK( this, TabControl
, ImplWindowEventListener
) );
206 ImplFreeLayoutData();
208 // delete TabCtrl data
210 mpTabCtrlData
->mpListBox
.disposeAndClear();
211 mpTabCtrlData
.reset();
215 ImplTabItem
* TabControl::ImplGetItem( sal_uInt16 nId
) const
217 for (auto & item
: mpTabCtrlData
->maItemList
)
219 if (item
.id() == nId
)
226 Size
TabControl::ImplGetItemSize( ImplTabItem
* pItem
, tools::Long nMaxWidth
)
228 pItem
->maFormatText
= pItem
->maText
;
229 Size
aSize( GetCtrlTextWidth( pItem
->maFormatText
), GetTextHeight() );
230 Size
aImageSize( 0, 0 );
231 if( !!pItem
->maTabImage
)
233 aImageSize
= pItem
->maTabImage
.GetSizePixel();
234 if( !pItem
->maFormatText
.isEmpty() )
235 aImageSize
.AdjustWidth(GetTextHeight()/4 );
237 aSize
.AdjustWidth(aImageSize
.Width() );
238 if( aImageSize
.Height() > aSize
.Height() )
239 aSize
.setHeight( aImageSize
.Height() );
241 aSize
.AdjustWidth(TAB_TABOFFSET_X
*2 );
242 aSize
.AdjustHeight(TAB_TABOFFSET_Y
*2 );
244 tools::Rectangle
aCtrlRegion( Point( 0, 0 ), aSize
);
245 tools::Rectangle aBoundingRgn
, aContentRgn
;
246 const TabitemValue
aControlValue(tools::Rectangle(TAB_TABOFFSET_X
, TAB_TABOFFSET_Y
,
247 aSize
.Width() - TAB_TABOFFSET_X
* 2,
248 aSize
.Height() - TAB_TABOFFSET_Y
* 2));
249 if(GetNativeControlRegion( ControlType::TabItem
, ControlPart::Entire
, aCtrlRegion
,
250 ControlState::ENABLED
, aControlValue
,
251 aBoundingRgn
, aContentRgn
) )
253 return aContentRgn
.GetSize();
256 // For languages with short names (e.g. Chinese), because the space is
257 // normally only one pixel per char
258 if ( pItem
->maFormatText
.getLength() < TAB_EXTRASPACE_X
)
259 aSize
.AdjustWidth(TAB_EXTRASPACE_X
-pItem
->maFormatText
.getLength() );
261 // shorten Text if needed
262 if ( aSize
.Width()+4 >= nMaxWidth
)
264 OUString
aAppendStr("...");
265 pItem
->maFormatText
+= aAppendStr
;
268 if (pItem
->maFormatText
.getLength() > aAppendStr
.getLength())
269 pItem
->maFormatText
= pItem
->maFormatText
.replaceAt( pItem
->maFormatText
.getLength()-aAppendStr
.getLength()-1, 1, "" );
270 aSize
.setWidth( GetCtrlTextWidth( pItem
->maFormatText
) );
271 aSize
.AdjustWidth(aImageSize
.Width() );
272 aSize
.AdjustWidth(TAB_TABOFFSET_X
*2 );
274 while ( (aSize
.Width()+4 >= nMaxWidth
) && (pItem
->maFormatText
.getLength() > aAppendStr
.getLength()) );
275 if ( aSize
.Width()+4 >= nMaxWidth
)
277 pItem
->maFormatText
= ".";
282 if( pItem
->maFormatText
.isEmpty() )
284 if( aSize
.Height() < aImageSize
.Height()+4 ) //leave space for focus rect
285 aSize
.setHeight( aImageSize
.Height()+4 );
291 // Feel free to move this to some more general place for reuse
292 // http://en.wikipedia.org/wiki/Word_wrap#Minimum_raggedness
293 // Mostly based on Alexey Frunze's nifty example at
294 // http://stackoverflow.com/questions/9071205/balanced-word-wrap-minimum-raggedness-in-php
295 namespace MinimumRaggednessWrap
297 static std::deque
<size_t> GetEndOfLineIndexes(const std::vector
<sal_Int32
>& rWidthsOf
, sal_Int32 nLineWidth
)
301 size_t nWidthsCount
= rWidthsOf
.size();
302 std::vector
<sal_Int32
> aCosts(nWidthsCount
* nWidthsCount
);
304 // cost function c(i, j) that computes the cost of a line consisting of
305 // the words Word[i] to Word[j]
306 for (size_t i
= 0; i
< nWidthsCount
; ++i
)
308 for (size_t j
= 0; j
< nWidthsCount
; ++j
)
312 sal_Int32 c
= nLineWidth
- (j
- i
);
313 for (size_t k
= i
; k
<= j
; ++k
)
315 c
= (c
>= 0) ? c
* c
: SAL_MAX_INT32
;
316 aCosts
[j
* nWidthsCount
+ i
] = c
;
320 aCosts
[j
* nWidthsCount
+ i
] = SAL_MAX_INT32
;
325 std::vector
<sal_Int32
> aFunction(nWidthsCount
);
326 std::vector
<sal_Int32
> aWrapPoints(nWidthsCount
);
328 // f(j) in aFunction[], collect wrap points in aWrapPoints[]
329 for (size_t j
= 0; j
< nWidthsCount
; ++j
)
331 aFunction
[j
] = aCosts
[j
* nWidthsCount
];
332 if (aFunction
[j
] == SAL_MAX_INT32
)
334 for (size_t k
= 0; k
< j
; ++k
)
337 if (aFunction
[k
] == SAL_MAX_INT32
|| aCosts
[j
* nWidthsCount
+ k
+ 1] == SAL_MAX_INT32
)
340 s
= aFunction
[k
] + aCosts
[j
* nWidthsCount
+ k
+ 1];
341 if (aFunction
[j
] > s
)
344 aWrapPoints
[j
] = k
+ 1;
350 std::deque
<size_t> aSolution
;
353 if (aFunction
[nWidthsCount
- 1] == SAL_MAX_INT32
)
357 size_t j
= nWidthsCount
- 1;
360 aSolution
.push_front(j
);
363 j
= aWrapPoints
[j
] - 1;
370 static void lcl_AdjustSingleLineTabs(tools::Long nMaxWidth
, ImplTabCtrlData
*pTabCtrlData
)
372 if (!ImplGetSVData()->maNWFData
.mbCenteredTabs
)
375 int nRightSpace
= nMaxWidth
; // space left on the right by the tabs
376 for (auto const& item
: pTabCtrlData
->maItemList
)
378 if (!item
.m_bVisible
)
380 nRightSpace
-= item
.maRect
.Right() - item
.maRect
.Left();
384 for (auto& item
: pTabCtrlData
->maItemList
)
386 if (!item
.m_bVisible
)
388 item
.maRect
.AdjustLeft(nRightSpace
);
389 item
.maRect
.AdjustRight(nRightSpace
);
393 bool TabControl::ImplPlaceTabs( tools::Long nWidth
)
397 if ( mpTabCtrlData
->maItemList
.empty() )
400 tools::Long nMaxWidth
= nWidth
;
402 const tools::Long nOffsetX
= 2;
403 const tools::Long nOffsetY
= 2;
405 //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
406 //of ugly bare tabs on lines of their own
409 std::vector
<sal_Int32
> aWidths
;
410 for (auto & item
: mpTabCtrlData
->maItemList
)
412 if (!item
.m_bVisible
)
414 aWidths
.push_back(ImplGetItemSize(&item
, nMaxWidth
).Width());
417 //aBreakIndexes will contain the indexes of the last tab on each row
418 std::deque
<size_t> aBreakIndexes(MinimumRaggednessWrap::GetEndOfLineIndexes(aWidths
, nMaxWidth
- nOffsetX
- 2));
420 tools::Long nX
= nOffsetX
;
421 tools::Long nY
= nOffsetY
;
423 sal_uInt16 nLines
= 0;
424 sal_uInt16 nCurLine
= 0;
426 tools::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 tools::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
));
492 nLineHeightAry
[i
] = nIH
*(i
-nCurLine
-1);
495 nLinePosAry
[nLines
+1] = static_cast<sal_uInt16
>(mpTabCtrlData
->maItemList
.size());
498 tools::Long nModDX
= 0;
499 tools::Long nIDX
= 0;
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
, tools::Long nWidth
, tools::Long nHeight
)
553 Size aWinSize
= Control::GetOutputSizePixel();
555 nWidth
= aWinSize
.Width();
557 nHeight
= aWinSize
.Height();
559 if ( mpTabCtrlData
->maItemList
.empty() )
561 tools::Long nW
= nWidth
-TAB_OFFSET
*2;
562 tools::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 tools::Long nW
= nWidth
-TAB_OFFSET
*2;
581 tools::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
, tools::Long nWidth
, tools::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() );
684 if ( pOldPage
&& pOldPage
->HasChildPathFocus() )
686 vcl::Window
* pFirstChild
= pPage
->ImplGetDlgWindow( 0, GetDlgWindowType::First
);
688 pFirstChild
->ImplControlFocus( GetFocusFlags::Init
);
697 // Invalidate the same region that will be send to NWF
698 // to always allow for bitmap caching
699 // see Window::DrawNativeControl()
700 if( IsNativeControlSupported( ControlType::TabPane
, ControlPart::Entire
) )
702 aRect
.AdjustLeft( -(TAB_OFFSET
) );
703 aRect
.AdjustTop( -(TAB_OFFSET
) );
704 aRect
.AdjustRight(TAB_OFFSET
);
705 aRect
.AdjustBottom(TAB_OFFSET
);
711 bool TabControl::ImplPosCurTabPage()
713 // resize/position current TabPage
714 ImplTabItem
* pItem
= ImplGetItem( GetCurPageId() );
715 if ( pItem
&& pItem
->mpTabPage
)
717 if ( GetStyle() & WB_NOBORDER
)
719 tools::Rectangle
aRectNoTab(Point(0, 0), GetSizePixel());
720 pItem
->mpTabPage
->SetPosSizePixel( aRectNoTab
.TopLeft(), aRectNoTab
.GetSize() );
723 tools::Rectangle aRect
= ImplGetTabRect( TAB_PAGERECT
);
724 pItem
->mpTabPage
->SetPosSizePixel( aRect
.TopLeft(), aRect
.GetSize() );
731 void TabControl::ImplActivateTabPage( bool bNext
)
733 sal_uInt16 nCurPos
= GetPagePos( GetCurPageId() );
736 nCurPos
= (nCurPos
+ 1) % GetPageCount();
740 nCurPos
= GetPageCount()-1;
745 SelectTabPage( GetPageId( nCurPos
) );
748 void TabControl::ImplShowFocus()
750 if ( !GetPageCount() || mpTabCtrlData
->mpListBox
)
753 sal_uInt16 nCurPos
= GetPagePos( mnCurPageId
);
754 tools::Rectangle aRect
= ImplGetTabRect( nCurPos
);
755 const ImplTabItem
& rItem
= mpTabCtrlData
->maItemList
[ nCurPos
];
756 Size aTabSize
= aRect
.GetSize();
757 Size
aImageSize( 0, 0 );
758 tools::Long nTextHeight
= GetTextHeight();
759 tools::Long nTextWidth
= GetCtrlTextWidth( rItem
.maFormatText
);
762 if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::Mono
) )
767 if( !! rItem
.maTabImage
)
769 aImageSize
= rItem
.maTabImage
.GetSizePixel();
770 if( !rItem
.maFormatText
.isEmpty() )
771 aImageSize
.AdjustWidth(GetTextHeight()/4 );
774 if( !rItem
.maFormatText
.isEmpty() )
776 // show focus around text
777 aRect
.SetLeft( aRect
.Left()+aImageSize
.Width()+((aTabSize
.Width()-nTextWidth
-aImageSize
.Width())/2)-nOff
-1-1 );
778 aRect
.SetTop( aRect
.Top()+((aTabSize
.Height()-nTextHeight
)/2)-1-1 );
779 aRect
.SetRight( aRect
.Left()+nTextWidth
+2 );
780 aRect
.SetBottom( aRect
.Top()+nTextHeight
+2 );
784 // show focus around image
785 tools::Long nXPos
= aRect
.Left()+((aTabSize
.Width()-nTextWidth
-aImageSize
.Width())/2)-nOff
-1;
786 tools::Long nYPos
= aRect
.Top();
787 if( aImageSize
.Height() < aRect
.GetHeight() )
788 nYPos
+= (aRect
.GetHeight() - aImageSize
.Height())/2;
790 aRect
.SetLeft( nXPos
- 2 );
791 aRect
.SetTop( nYPos
- 2 );
792 aRect
.SetRight( aRect
.Left() + aImageSize
.Width() + 4 );
793 aRect
.SetBottom( aRect
.Top() + aImageSize
.Height() + 4 );
798 void TabControl::ImplDrawItem(vcl::RenderContext
& rRenderContext
, ImplTabItem
const * pItem
, const tools::Rectangle
& rCurRect
,
799 bool bFirstInGroup
, bool bLastInGroup
)
801 if (!pItem
->m_bVisible
|| pItem
->maRect
.IsEmpty())
804 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
805 tools::Rectangle aRect
= pItem
->maRect
;
806 tools::Long nLeftBottom
= aRect
.Bottom();
807 tools::Long nRightBottom
= aRect
.Bottom();
808 bool bLeftBorder
= true;
809 bool bRightBorder
= true;
811 bool bNativeOK
= false;
813 sal_uInt16 nOff2
= 0;
814 sal_uInt16 nOff3
= 0;
816 if (!(rStyleSettings
.GetOptions() & StyleSettingsOptions::Mono
))
821 // if this is the active Page, we have to draw a little more
822 if (pItem
->id() == mnCurPageId
)
825 if (!ImplGetSVData()->maNWFData
.mbNoActiveTabTextRaise
)
830 Point aLeftTestPos
= aRect
.BottomLeft();
831 Point aRightTestPos
= aRect
.BottomRight();
832 if (aLeftTestPos
.Y() == rCurRect
.Bottom())
834 aLeftTestPos
.AdjustX( -2 );
835 if (rCurRect
.IsInside(aLeftTestPos
))
837 aRightTestPos
.AdjustX(2 );
838 if (rCurRect
.IsInside(aRightTestPos
))
839 bRightBorder
= false;
843 if (rCurRect
.IsInside(aLeftTestPos
))
845 if (rCurRect
.IsInside(aRightTestPos
))
850 ControlState nState
= ControlState::NONE
;
852 if (pItem
->id() == mnCurPageId
)
854 nState
|= ControlState::SELECTED
;
855 // only the selected item can be focused
857 nState
|= ControlState::FOCUSED
;
860 nState
|= ControlState::ENABLED
;
861 if (IsMouseOver() && pItem
->maRect
.IsInside(GetPointerPosPixel()))
863 nState
|= ControlState::ROLLOVER
;
864 for (auto const& item
: mpTabCtrlData
->maItemList
)
865 if ((&item
!= pItem
) && item
.m_bVisible
&& item
.maRect
.IsInside(GetPointerPosPixel()))
867 nState
&= ~ControlState::ROLLOVER
; // avoid multiple highlighted tabs
870 assert(nState
& ControlState::ROLLOVER
);
873 bNativeOK
= rRenderContext
.IsNativeControlSupported(ControlType::TabItem
, ControlPart::Entire
);
876 TabitemValue
tiValue(tools::Rectangle(pItem
->maRect
.Left() + TAB_TABOFFSET_X
,
877 pItem
->maRect
.Top() + TAB_TABOFFSET_Y
,
878 pItem
->maRect
.Right() - TAB_TABOFFSET_X
,
879 pItem
->maRect
.Bottom() - TAB_TABOFFSET_Y
));
880 if (pItem
->maRect
.Left() < 5)
881 tiValue
.mnAlignment
|= TabitemFlags::LeftAligned
;
882 if (pItem
->maRect
.Right() > mnLastWidth
- 5)
883 tiValue
.mnAlignment
|= TabitemFlags::RightAligned
;
885 tiValue
.mnAlignment
|= TabitemFlags::FirstInGroup
;
887 tiValue
.mnAlignment
|= TabitemFlags::LastInGroup
;
889 tools::Rectangle
aCtrlRegion( pItem
->maRect
);
890 aCtrlRegion
.AdjustBottom(TabPaneValue::m_nOverlap
);
891 bNativeOK
= rRenderContext
.DrawNativeControl(ControlType::TabItem
, ControlPart::Entire
,
892 aCtrlRegion
, nState
, tiValue
, OUString() );
897 if (!(rStyleSettings
.GetOptions() & StyleSettingsOptions::Mono
))
899 rRenderContext
.SetLineColor(rStyleSettings
.GetLightColor());
900 rRenderContext
.DrawPixel(Point(aRect
.Left() + 1 - nOff2
, aRect
.Top() + 1 - nOff2
)); // diagonally indented top-left pixel
903 rRenderContext
.DrawLine(Point(aRect
.Left() - nOff2
, aRect
.Top() + 2 - nOff2
),
904 Point(aRect
.Left() - nOff2
, nLeftBottom
- 1));
906 rRenderContext
.DrawLine(Point(aRect
.Left() + 2 - nOff2
, aRect
.Top() - nOff2
), // top line starting 2px from left border
907 Point(aRect
.Right() + nOff2
- 3, aRect
.Top() - nOff2
)); // ending 3px from right border
911 rRenderContext
.SetLineColor(rStyleSettings
.GetShadowColor());
912 rRenderContext
.DrawLine(Point(aRect
.Right() + nOff2
- 2, aRect
.Top() + 1 - nOff2
),
913 Point(aRect
.Right() + nOff2
- 2, nRightBottom
- 1));
915 rRenderContext
.SetLineColor(rStyleSettings
.GetDarkShadowColor());
916 rRenderContext
.DrawLine(Point(aRect
.Right() + nOff2
- 1, aRect
.Top() + 3 - nOff2
),
917 Point(aRect
.Right() + nOff2
- 1, nRightBottom
- 1));
922 rRenderContext
.SetLineColor(COL_BLACK
);
923 rRenderContext
.DrawPixel(Point(aRect
.Left() + 1 - nOff2
, aRect
.Top() + 1 - nOff2
));
924 rRenderContext
.DrawPixel(Point(aRect
.Right() + nOff2
- 2, aRect
.Top() + 1 - nOff2
));
927 rRenderContext
.DrawLine(Point(aRect
.Left() - nOff2
, aRect
.Top() + 2 - nOff2
),
928 Point(aRect
.Left() - nOff2
, nLeftBottom
- 1));
930 rRenderContext
.DrawLine(Point(aRect
.Left() + 2 - nOff2
, aRect
.Top() - nOff2
),
931 Point(aRect
.Right() - 3, aRect
.Top() - nOff2
));
934 rRenderContext
.DrawLine(Point(aRect
.Right() + nOff2
- 1, aRect
.Top() + 2 - nOff2
),
935 Point(aRect
.Right() + nOff2
- 1, nRightBottom
- 1));
940 // set font accordingly, current item is painted bold
941 // we set the font attributes always before drawing to be re-entrant (DrawNativeControl may trigger additional paints)
942 vcl::Font
aFont(rRenderContext
.GetFont());
943 aFont
.SetTransparent(true);
944 rRenderContext
.SetFont(aFont
);
946 Size aTabSize
= aRect
.GetSize();
947 Size
aImageSize(0, 0);
948 tools::Long nTextHeight
= rRenderContext
.GetTextHeight();
949 tools::Long nTextWidth
= rRenderContext
.GetCtrlTextWidth(pItem
->maFormatText
);
950 if (!!pItem
->maTabImage
)
952 aImageSize
= pItem
->maTabImage
.GetSizePixel();
953 if (!pItem
->maFormatText
.isEmpty())
954 aImageSize
.AdjustWidth(GetTextHeight() / 4 );
956 tools::Long nXPos
= aRect
.Left() + ((aTabSize
.Width() - nTextWidth
- aImageSize
.Width()) / 2) - nOff
- nOff3
;
957 tools::Long nYPos
= aRect
.Top() + ((aTabSize
.Height() - nTextHeight
) / 2) - nOff3
;
958 if (!pItem
->maFormatText
.isEmpty())
960 DrawTextFlags nStyle
= DrawTextFlags::Mnemonic
;
961 if (!pItem
->m_bEnabled
)
962 nStyle
|= DrawTextFlags::Disable
;
964 Color
aColor(rStyleSettings
.GetTabTextColor());
965 if (nState
& ControlState::SELECTED
)
966 aColor
= rStyleSettings
.GetTabHighlightTextColor();
967 else if (nState
& ControlState::ROLLOVER
)
968 aColor
= rStyleSettings
.GetTabRolloverTextColor();
970 Color
aOldColor(rRenderContext
.GetTextColor());
971 rRenderContext
.SetTextColor(aColor
);
973 const tools::Rectangle
aOutRect(nXPos
+ aImageSize
.Width(), nYPos
,
974 nXPos
+ aImageSize
.Width() + nTextWidth
, nYPos
+ nTextHeight
);
975 DrawControlText(rRenderContext
, aOutRect
, pItem
->maFormatText
, nStyle
,
978 rRenderContext
.SetTextColor(aOldColor
);
981 if (!!pItem
->maTabImage
)
983 Point
aImgTL( nXPos
, aRect
.Top() );
984 if (aImageSize
.Height() < aRect
.GetHeight())
985 aImgTL
.AdjustY((aRect
.GetHeight() - aImageSize
.Height()) / 2 );
986 rRenderContext
.DrawImage(aImgTL
, pItem
->maTabImage
, pItem
->m_bEnabled
? DrawImageFlags::NONE
: DrawImageFlags::Disable
);
990 bool TabControl::ImplHandleKeyEvent( const KeyEvent
& rKeyEvent
)
994 if ( GetPageCount() > 1 )
996 vcl::KeyCode aKeyCode
= rKeyEvent
.GetKeyCode();
997 sal_uInt16 nKeyCode
= aKeyCode
.GetCode();
999 if ( aKeyCode
.IsMod1() )
1001 if ( aKeyCode
.IsShift() || (nKeyCode
== KEY_PAGEUP
) )
1003 if ( (nKeyCode
== KEY_TAB
) || (nKeyCode
== KEY_PAGEUP
) )
1005 ImplActivateTabPage( false );
1011 if ( (nKeyCode
== KEY_TAB
) || (nKeyCode
== KEY_PAGEDOWN
) )
1013 ImplActivateTabPage( true );
1023 IMPL_LINK_NOARG(TabControl
, ImplListBoxSelectHdl
, ListBox
&, void)
1025 SelectTabPage( GetPageId( mpTabCtrlData
->mpListBox
->GetSelectedEntryPos() ) );
1028 IMPL_LINK( TabControl
, ImplWindowEventListener
, VclWindowEvent
&, rEvent
, void )
1030 if ( rEvent
.GetId() == VclEventId::WindowKeyInput
)
1032 // Do not handle events from TabControl or its children, which is done in Notify(), where the events can be consumed.
1033 if ( !IsWindowOrChild( rEvent
.GetWindow() ) )
1035 KeyEvent
* pKeyEvent
= static_cast< KeyEvent
* >(rEvent
.GetData());
1036 ImplHandleKeyEvent( *pKeyEvent
);
1041 void TabControl::MouseButtonDown( const MouseEvent
& rMEvt
)
1043 if (mpTabCtrlData
->mpListBox
|| !rMEvt
.IsLeft())
1046 ImplTabItem
*pItem
= ImplGetItem(rMEvt
.GetPosPixel());
1047 if (pItem
&& pItem
->m_bEnabled
)
1048 SelectTabPage(pItem
->id());
1051 void TabControl::KeyInput( const KeyEvent
& rKEvt
)
1053 if( mpTabCtrlData
->mpListBox
)
1054 mpTabCtrlData
->mpListBox
->KeyInput( rKEvt
);
1055 else if ( GetPageCount() > 1 )
1057 vcl::KeyCode aKeyCode
= rKEvt
.GetKeyCode();
1058 sal_uInt16 nKeyCode
= aKeyCode
.GetCode();
1060 if ( (nKeyCode
== KEY_LEFT
) || (nKeyCode
== KEY_RIGHT
) )
1062 bool bNext
= (nKeyCode
== KEY_RIGHT
);
1063 ImplActivateTabPage( bNext
);
1067 Control::KeyInput( rKEvt
);
1070 static bool lcl_canPaint(const vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rDrawRect
,
1071 const tools::Rectangle
& rItemRect
)
1073 vcl::Region
aClipRgn(rRenderContext
.GetActiveClipRegion());
1074 aClipRgn
.Intersect(rItemRect
);
1075 if (!rDrawRect
.IsEmpty())
1076 aClipRgn
.Intersect(rDrawRect
);
1077 return !aClipRgn
.IsEmpty();
1080 void TabControl::Paint( vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
1082 if (GetStyle() & WB_NOBORDER
)
1085 Control::Paint(rRenderContext
, rRect
);
1089 // reformat if needed
1090 tools::Rectangle aRect
= ImplGetTabRect(TAB_PAGERECT
);
1092 // find current item
1093 ImplTabItem
* pCurItem
= nullptr;
1094 for (auto & item
: mpTabCtrlData
->maItemList
)
1096 if (item
.id() == mnCurPageId
)
1103 // Draw the TabPage border
1104 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
1105 tools::Rectangle aCurRect
;
1106 aRect
.AdjustLeft( -(TAB_OFFSET
) );
1107 aRect
.AdjustTop( -(TAB_OFFSET
) );
1108 aRect
.AdjustRight(TAB_OFFSET
);
1109 aRect
.AdjustBottom(TAB_OFFSET
);
1111 // if we have an invisible tabpage or no tabpage at all the tabpage rect should be
1112 // increased to avoid round corners that might be drawn by a theme
1113 // in this case we're only interested in the top border of the tabpage because the tabitems are used
1114 // standalone (eg impress)
1115 bool bNoTabPage
= false;
1116 TabPage
* pCurPage
= pCurItem
? pCurItem
->mpTabPage
.get() : nullptr;
1117 if (!pCurPage
|| !pCurPage
->IsVisible())
1120 aRect
.AdjustLeft( -10 );
1121 aRect
.AdjustRight(10 );
1124 if (rRenderContext
.IsNativeControlSupported(ControlType::TabPane
, ControlPart::Entire
))
1126 const bool bPaneWithHeader
= rRenderContext
.IsNativeControlSupported(ControlType::TabPane
, ControlPart::TabPaneWithHeader
);
1127 tools::Rectangle
aHeaderRect(aRect
.Left(), 0, aRect
.Right(), aRect
.Top());
1128 if (bPaneWithHeader
)
1131 if (mpTabCtrlData
->maItemList
.size())
1133 tools::Long nRight
= 0;
1134 for (const auto &item
: mpTabCtrlData
->maItemList
)
1135 if (item
.m_bVisible
)
1136 nRight
= item
.maRect
.Right();
1138 aHeaderRect
.SetRight(nRight
);
1141 const TabPaneValue
aTabPaneValue(aHeaderRect
, pCurItem
? pCurItem
->maRect
: tools::Rectangle());
1143 ControlState nState
= ControlState::ENABLED
;
1145 nState
&= ~ControlState::ENABLED
;
1147 nState
|= ControlState::FOCUSED
;
1149 if (lcl_canPaint(rRenderContext
, rRect
, aRect
))
1150 rRenderContext
.DrawNativeControl(ControlType::TabPane
, ControlPart::Entire
,
1151 aRect
, nState
, aTabPaneValue
, OUString());
1153 if (!bPaneWithHeader
&& rRenderContext
.IsNativeControlSupported(ControlType::TabHeader
, ControlPart::Entire
)
1154 && lcl_canPaint(rRenderContext
, rRect
, aHeaderRect
))
1155 rRenderContext
.DrawNativeControl(ControlType::TabHeader
, ControlPart::Entire
,
1156 aHeaderRect
, nState
, aTabPaneValue
, OUString());
1160 tools::Long nTopOff
= 1;
1161 if (!(rStyleSettings
.GetOptions() & StyleSettingsOptions::Mono
))
1162 rRenderContext
.SetLineColor(rStyleSettings
.GetLightColor());
1164 rRenderContext
.SetLineColor(COL_BLACK
);
1165 if (pCurItem
&& !pCurItem
->maRect
.IsEmpty())
1167 aCurRect
= pCurItem
->maRect
;
1168 rRenderContext
.DrawLine(aRect
.TopLeft(), Point(aCurRect
.Left() - 2, aRect
.Top()));
1169 if (aCurRect
.Right() + 1 < aRect
.Right())
1171 rRenderContext
.DrawLine(Point(aCurRect
.Right(), aRect
.Top()), aRect
.TopRight());
1179 rRenderContext
.DrawLine(aRect
.TopLeft(), aRect
.TopRight());
1181 rRenderContext
.DrawLine(aRect
.TopLeft(), aRect
.BottomLeft());
1183 if (!(rStyleSettings
.GetOptions() & StyleSettingsOptions::Mono
))
1185 // if we have not tab page the bottom line of the tab page
1186 // directly touches the tab items, so choose a color that fits seamlessly
1188 rRenderContext
.SetLineColor(rStyleSettings
.GetDialogColor());
1190 rRenderContext
.SetLineColor(rStyleSettings
.GetShadowColor());
1191 rRenderContext
.DrawLine(Point(1, aRect
.Bottom() - 1), Point(aRect
.Right() - 1, aRect
.Bottom() - 1));
1192 rRenderContext
.DrawLine(Point(aRect
.Right() - 1, aRect
.Top() + nTopOff
), Point(aRect
.Right() - 1, aRect
.Bottom() - 1));
1194 rRenderContext
.SetLineColor(rStyleSettings
.GetDialogColor());
1196 rRenderContext
.SetLineColor(rStyleSettings
.GetDarkShadowColor());
1197 rRenderContext
.DrawLine(Point(0, aRect
.Bottom()), Point(aRect
.Right(), aRect
.Bottom()));
1198 rRenderContext
.DrawLine(Point(aRect
.Right(), aRect
.Top() + nTopOff
), Point(aRect
.Right(), aRect
.Bottom()));
1202 rRenderContext
.DrawLine(aRect
.TopRight(), aRect
.BottomRight());
1203 rRenderContext
.DrawLine(aRect
.BottomLeft(), aRect
.BottomRight());
1207 if (!mpTabCtrlData
->maItemList
.empty() && mpTabCtrlData
->mpListBox
== nullptr)
1209 // Some native toolkits (GTK+) draw tabs right-to-left, with an
1210 // overlap between adjacent tabs
1211 bool bDrawTabsRTL
= rRenderContext
.IsNativeControlSupported(ControlType::TabItem
, ControlPart::TabsDrawRtl
);
1212 ImplTabItem
* pFirstTab
= nullptr;
1213 ImplTabItem
* pLastTab
= nullptr;
1216 // Even though there is a tab overlap with GTK+, the first tab is not
1217 // overlapped on the left side. Other toolkits ignore this option.
1220 pFirstTab
= mpTabCtrlData
->maItemList
.data();
1221 pLastTab
= pFirstTab
+ mpTabCtrlData
->maItemList
.size() - 1;
1222 idx
= mpTabCtrlData
->maItemList
.size() - 1;
1226 pLastTab
= mpTabCtrlData
->maItemList
.data();
1227 pFirstTab
= pLastTab
+ mpTabCtrlData
->maItemList
.size() - 1;
1231 while (idx
< mpTabCtrlData
->maItemList
.size())
1233 ImplTabItem
* pItem
= &mpTabCtrlData
->maItemList
[idx
];
1235 if (pItem
!= pCurItem
&& pItem
->m_bVisible
&& lcl_canPaint(rRenderContext
, rRect
, pItem
->maRect
))
1236 ImplDrawItem(rRenderContext
, pItem
, aCurRect
, pItem
== pFirstTab
, pItem
== pLastTab
);
1244 if (pCurItem
&& lcl_canPaint(rRenderContext
, rRect
, pCurItem
->maRect
))
1245 ImplDrawItem(rRenderContext
, pCurItem
, aCurRect
, pCurItem
== pFirstTab
, pCurItem
== pLastTab
);
1251 mbSmallInvalidate
= true;
1254 void TabControl::setAllocation(const Size
&rAllocation
)
1256 ImplFreeLayoutData();
1258 if ( !IsReallyShown() )
1261 if( mpTabCtrlData
->mpListBox
)
1263 // get the listbox' preferred size
1264 Size
aTabCtrlSize( GetSizePixel() );
1265 tools::Long nPrefWidth
= mpTabCtrlData
->mpListBox
->get_preferred_size().Width();
1266 if( nPrefWidth
> aTabCtrlSize
.Width() )
1267 nPrefWidth
= aTabCtrlSize
.Width();
1268 Size
aNewSize( nPrefWidth
, LogicToPixel( Size( 12, 12 ), MapMode( MapUnit::MapAppFont
) ).Height() );
1269 Point
aNewPos( (aTabCtrlSize
.Width() - nPrefWidth
) / 2, 0 );
1270 mpTabCtrlData
->mpListBox
->SetPosSizePixel( aNewPos
, aNewSize
);
1275 // resize/position active TabPage
1276 bool bTabPage
= ImplPosCurTabPage();
1278 // check what needs to be invalidated
1279 Size aNewSize
= rAllocation
;
1280 tools::Long nNewWidth
= aNewSize
.Width();
1281 for (auto const& item
: mpTabCtrlData
->maItemList
)
1283 if (!item
.m_bVisible
)
1285 if (!item
.mbFullVisible
|| (item
.maRect
.Right()-2 >= nNewWidth
))
1287 mbSmallInvalidate
= false;
1292 if ( mbSmallInvalidate
)
1294 tools::Rectangle aRect
= ImplGetTabRect( TAB_PAGERECT
);
1295 aRect
.AdjustLeft( -(TAB_OFFSET
+TAB_BORDER_LEFT
) );
1296 aRect
.AdjustTop( -(TAB_OFFSET
+TAB_BORDER_TOP
) );
1297 aRect
.AdjustRight(TAB_OFFSET
+TAB_BORDER_RIGHT
);
1298 aRect
.AdjustBottom(TAB_OFFSET
+TAB_BORDER_BOTTOM
);
1300 Invalidate( aRect
, InvalidateFlags::NoChildren
);
1302 Invalidate( aRect
);
1308 Invalidate( InvalidateFlags::NoChildren
);
1313 mbLayoutDirty
= false;
1316 void TabControl::SetPosSizePixel(const Point
& rNewPos
, const Size
& rNewSize
)
1318 Window::SetPosSizePixel(rNewPos
, rNewSize
);
1319 //if size changed, TabControl::Resize got called already
1321 setAllocation(rNewSize
);
1324 void TabControl::SetSizePixel(const Size
& rNewSize
)
1326 Window::SetSizePixel(rNewSize
);
1327 //if size changed, TabControl::Resize got called already
1329 setAllocation(rNewSize
);
1332 void TabControl::SetPosPixel(const Point
& rPos
)
1334 Window::SetPosPixel(rPos
);
1336 setAllocation(GetOutputSizePixel());
1339 void TabControl::Resize()
1341 setAllocation(Control::GetOutputSizePixel());
1344 void TabControl::GetFocus()
1346 if( ! mpTabCtrlData
->mpListBox
)
1349 SetInputContext( InputContext( GetFont() ) );
1353 if( mpTabCtrlData
->mpListBox
->IsReallyVisible() )
1354 mpTabCtrlData
->mpListBox
->GrabFocus();
1356 Control::GetFocus();
1359 void TabControl::LoseFocus()
1361 if( mpTabCtrlData
&& ! mpTabCtrlData
->mpListBox
)
1363 Control::LoseFocus();
1366 void TabControl::RequestHelp( const HelpEvent
& rHEvt
)
1368 sal_uInt16 nItemId
= rHEvt
.KeyboardActivated() ? mnCurPageId
: GetPageId( ScreenToOutputPixel( rHEvt
.GetMousePosPixel() ) );
1372 if ( rHEvt
.GetMode() & HelpEventMode::BALLOON
)
1374 OUString aStr
= GetHelpText( nItemId
);
1375 if ( !aStr
.isEmpty() )
1377 tools::Rectangle aItemRect
= ImplGetTabRect( GetPagePos( nItemId
) );
1378 Point aPt
= OutputToScreenPixel( aItemRect
.TopLeft() );
1379 aItemRect
.SetLeft( aPt
.X() );
1380 aItemRect
.SetTop( aPt
.Y() );
1381 aPt
= OutputToScreenPixel( aItemRect
.BottomRight() );
1382 aItemRect
.SetRight( aPt
.X() );
1383 aItemRect
.SetBottom( aPt
.Y() );
1384 Help::ShowBalloon( this, aItemRect
.Center(), aItemRect
, aStr
);
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 tools::Rectangle aItemRect
= ImplGetTabRect( GetPagePos( nItemId
) );
1397 Point aPt
= OutputToScreenPixel( aItemRect
.TopLeft() );
1398 aItemRect
.SetLeft( aPt
.X() );
1399 aItemRect
.SetTop( aPt
.Y() );
1400 aPt
= OutputToScreenPixel( aItemRect
.BottomRight() );
1401 aItemRect
.SetRight( aPt
.X() );
1402 aItemRect
.SetBottom( 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 tools::Rectangle aItemRect
= ImplGetTabRect( GetPagePos( nItemId
) );
1422 Point aPt
= OutputToScreenPixel( aItemRect
.TopLeft() );
1423 aItemRect
.SetLeft( aPt
.X() );
1424 aItemRect
.SetTop( aPt
.Y() );
1425 aPt
= OutputToScreenPixel( aItemRect
.BottomRight() );
1426 aItemRect
.SetRight( aPt
.X() );
1427 aItemRect
.SetBottom( 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 (auto const& item
: mpTabCtrlData
->maItemList
)
1459 aMenu
->InsertItem(item
.id(), item
.maText
, MenuItemBits::CHECKABLE
| MenuItemBits::RADIOCHECK
);
1460 if (item
.id() == mnCurPageId
)
1461 aMenu
->CheckItem(item
.id());
1462 aMenu
->SetHelpId(item
.id(), OString());
1465 sal_uInt16 nId
= aMenu
->Execute( this, aMenuPos
);
1466 if ( nId
&& (nId
!= mnCurPageId
) )
1467 SelectTabPage( nId
);
1472 Control::Command( rCEvt
);
1475 void TabControl::StateChanged( StateChangedType nType
)
1477 Control::StateChanged( nType
);
1479 if ( nType
== StateChangedType::InitShow
)
1481 ImplPosCurTabPage();
1482 if( mpTabCtrlData
->mpListBox
)
1485 else if ( nType
== StateChangedType::UpdateMode
)
1487 if ( IsUpdateMode() )
1490 else if ( (nType
== StateChangedType::Zoom
) ||
1491 (nType
== StateChangedType::ControlFont
) )
1493 ImplInitSettings( false );
1496 else if ( nType
== StateChangedType::ControlForeground
)
1498 ImplInitSettings( false );
1501 else if ( nType
== StateChangedType::ControlBackground
)
1503 ImplInitSettings( true );
1508 void TabControl::DataChanged( const DataChangedEvent
& rDCEvt
)
1510 Control::DataChanged( rDCEvt
);
1512 if ( (rDCEvt
.GetType() == DataChangedEventType::FONTS
) ||
1513 (rDCEvt
.GetType() == DataChangedEventType::FONTSUBSTITUTION
) ||
1514 ((rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) &&
1515 (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
)) )
1517 ImplInitSettings( true );
1522 ImplTabItem
* TabControl::ImplGetItem(const Point
& rPt
) const
1524 ImplTabItem
* pFoundItem
= nullptr;
1526 for (auto & item
: mpTabCtrlData
->maItemList
)
1528 if (item
.m_bVisible
&& item
.maRect
.IsInside(rPt
))
1535 // assure that only one tab is highlighted at a time
1536 assert(nFound
<= 1);
1537 return nFound
== 1 ? pFoundItem
: nullptr;
1540 bool TabControl::PreNotify( NotifyEvent
& rNEvt
)
1542 if( rNEvt
.GetType() == MouseNotifyEvent::MOUSEMOVE
)
1544 const MouseEvent
* pMouseEvt
= rNEvt
.GetMouseEvent();
1545 if( pMouseEvt
&& !pMouseEvt
->GetButtons() && !pMouseEvt
->IsSynthetic() && !pMouseEvt
->IsModifierChanged() )
1547 // trigger redraw if mouse over state has changed
1548 if( IsNativeControlSupported(ControlType::TabItem
, ControlPart::Entire
) )
1550 ImplTabItem
*pItem
= ImplGetItem(GetPointerPosPixel());
1551 ImplTabItem
*pLastItem
= ImplGetItem(GetLastPointerPosPixel());
1552 if ((pItem
!= pLastItem
) || pMouseEvt
->IsLeaveWindow() || pMouseEvt
->IsEnterWindow())
1554 vcl::Region aClipRgn
;
1557 // allow for slightly bigger tabitems
1559 // TODO: query for the correct sizes
1560 tools::Rectangle
aRect(pLastItem
->maRect
);
1561 aRect
.AdjustLeft( -2 );
1562 aRect
.AdjustRight(2 );
1563 aRect
.AdjustTop( -3 );
1564 aClipRgn
.Union( aRect
);
1569 // allow for slightly bigger tabitems
1571 // TODO: query for the correct sizes
1572 tools::Rectangle
aRect(pItem
->maRect
);
1573 aRect
.AdjustLeft( -2 );
1574 aRect
.AdjustRight(2 );
1575 aRect
.AdjustTop( -3 );
1576 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
.AdjustWidth(TAB_OFFSET
*2 );
1615 tools::Rectangle aRect
= ImplGetTabRect( TAB_PAGERECT
,
1616 aNewSize
.Width(), aNewSize
.Height() );
1617 aNewSize
.AdjustHeight(aRect
.Top()+TAB_OFFSET
);
1618 Window::SetOutputSizePixel( aNewSize
);
1621 void TabControl::InsertPage( sal_uInt16 nPageId
, const OUString
& rText
,
1624 SAL_WARN_IF( !nPageId
, "vcl", "TabControl::InsertPage(): PageId == 0" );
1625 SAL_WARN_IF( GetPagePos( nPageId
) != TAB_PAGE_NOTFOUND
, "vcl",
1626 "TabControl::InsertPage(): PageId already exists" );
1628 // insert new page item
1629 ImplTabItem
* pItem
= nullptr;
1630 if( nPos
== TAB_APPEND
|| size_t(nPos
) >= mpTabCtrlData
->maItemList
.size() )
1632 mpTabCtrlData
->maItemList
.emplace_back(nPageId
);
1633 pItem
= &mpTabCtrlData
->maItemList
.back();
1634 if( mpTabCtrlData
->mpListBox
)
1635 mpTabCtrlData
->mpListBox
->InsertEntry( rText
);
1639 std::vector
< ImplTabItem
>::iterator new_it
=
1640 mpTabCtrlData
->maItemList
.emplace(mpTabCtrlData
->maItemList
.begin() + nPos
, nPageId
);
1642 if( mpTabCtrlData
->mpListBox
)
1643 mpTabCtrlData
->mpListBox
->InsertEntry( rText
, nPos
);
1645 if( mpTabCtrlData
->mpListBox
)
1648 mpTabCtrlData
->mpListBox
->SelectEntryPos( 0 );
1649 mpTabCtrlData
->mpListBox
->SetDropDownLineCount( mpTabCtrlData
->mpListBox
->GetEntryCount() );
1652 // set current page id
1654 mnCurPageId
= nPageId
;
1656 // init new page item
1657 pItem
->maText
= rText
;
1658 pItem
->mbFullVisible
= false;
1661 if ( IsUpdateMode() )
1664 ImplFreeLayoutData();
1665 if( mpTabCtrlData
->mpListBox
) // reposition/resize listbox
1668 CallEventListeners( VclEventId::TabpageInserted
, reinterpret_cast<void*>(nPageId
) );
1671 void TabControl::RemovePage( sal_uInt16 nPageId
)
1673 sal_uInt16 nPos
= GetPagePos( nPageId
);
1675 // does the item exist ?
1676 if ( nPos
== TAB_PAGE_NOTFOUND
)
1680 std::vector
< ImplTabItem
>::iterator it
= mpTabCtrlData
->maItemList
.begin() + nPos
;
1681 bool bIsCurrentPage
= (it
->id() == mnCurPageId
);
1682 mpTabCtrlData
->maItemList
.erase( it
);
1683 if( mpTabCtrlData
->mpListBox
)
1685 mpTabCtrlData
->mpListBox
->RemoveEntry( nPos
);
1686 mpTabCtrlData
->mpListBox
->SetDropDownLineCount( mpTabCtrlData
->mpListBox
->GetEntryCount() );
1689 // If current page is removed, then first page gets the current page
1690 if ( bIsCurrentPage
)
1694 if( ! mpTabCtrlData
->maItemList
.empty() )
1696 // don't do this by simply setting mnCurPageId to pFirstItem->id()
1697 // this leaves a lot of stuff (such trivia as _showing_ the new current page) undone
1698 // instead, call SetCurPageId
1699 // without this, the next (outside) call to SetCurPageId with the id of the first page
1700 // will result in doing nothing (as we assume that nothing changed, then), and the page
1701 // will never be shown.
1702 // 86875 - 05/11/2001 - frank.schoenheit@germany.sun.com
1704 SetCurPageId(mpTabCtrlData
->maItemList
[0].id());
1709 if ( IsUpdateMode() )
1712 ImplFreeLayoutData();
1714 CallEventListeners( VclEventId::TabpageRemoved
, reinterpret_cast<void*>(nPageId
) );
1717 void TabControl::SetPageEnabled( sal_uInt16 i_nPageId
, bool i_bEnable
)
1719 ImplTabItem
* pItem
= ImplGetItem( i_nPageId
);
1721 if (!pItem
|| pItem
->m_bEnabled
== i_bEnable
)
1724 pItem
->m_bEnabled
= i_bEnable
;
1725 if (!pItem
->m_bVisible
)
1729 if( mpTabCtrlData
->mpListBox
)
1730 mpTabCtrlData
->mpListBox
->SetEntryFlags( GetPagePos( i_nPageId
),
1731 i_bEnable
? ListBoxEntryFlags::NONE
: (ListBoxEntryFlags::DisableSelection
| ListBoxEntryFlags::DrawDisabled
) );
1733 // SetCurPageId will change to a valid page
1734 if (pItem
->id() == mnCurPageId
)
1735 SetCurPageId( mnCurPageId
);
1736 else if ( IsUpdateMode() )
1740 void TabControl::SetPageVisible( sal_uInt16 nPageId
, bool bVisible
)
1742 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1743 if (!pItem
|| pItem
->m_bVisible
== bVisible
)
1746 pItem
->m_bVisible
= bVisible
;
1749 if (pItem
->mbFullVisible
)
1750 mbSmallInvalidate
= false;
1751 pItem
->mbFullVisible
= false;
1752 pItem
->maRect
.SetEmpty();
1756 // SetCurPageId will change to a valid page
1757 if (pItem
->id() == mnCurPageId
)
1758 SetCurPageId(mnCurPageId
);
1759 else if (IsUpdateMode())
1763 sal_uInt16
TabControl::GetPageCount() const
1765 return static_cast<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
].id();
1775 sal_uInt16
TabControl::GetPagePos( sal_uInt16 nPageId
) const
1777 sal_uInt16 nPos
= 0;
1778 for (auto const& item
: mpTabCtrlData
->maItemList
)
1780 if (item
.id() == nPageId
)
1785 return TAB_PAGE_NOTFOUND
;
1788 sal_uInt16
TabControl::GetPageId( const Point
& rPos
) const
1790 Size winSize
= Control::GetOutputSizePixel();
1791 const auto &rList
= mpTabCtrlData
->maItemList
;
1792 const auto it
= std::find_if(rList
.begin(), rList
.end(), [&rPos
, &winSize
, this](const auto &item
) {
1793 return const_cast<TabControl
*>(this)->ImplGetTabRect(&item
, winSize
.Width(), winSize
.Height()).IsInside(rPos
); });
1794 return (it
!= rList
.end()) ? it
->id() : 0;
1797 sal_uInt16
TabControl::GetPageId( const OString
& rName
) const
1799 const auto &rList
= mpTabCtrlData
->maItemList
;
1800 const auto it
= std::find_if(rList
.begin(), rList
.end(), [&rName
](const auto &item
) {
1801 return item
.maTabName
== rName
; });
1802 return (it
!= rList
.end()) ? it
->id() : 0;
1805 void TabControl::SetCurPageId( sal_uInt16 nPageId
)
1807 sal_uInt16 nPos
= GetPagePos( nPageId
);
1808 while (nPos
!= TAB_PAGE_NOTFOUND
&& !mpTabCtrlData
->maItemList
[nPos
].m_bEnabled
)
1811 if( size_t(nPos
) >= mpTabCtrlData
->maItemList
.size() )
1813 if (mpTabCtrlData
->maItemList
[nPos
].id() == nPageId
)
1817 if( nPos
== TAB_PAGE_NOTFOUND
)
1820 nPageId
= mpTabCtrlData
->maItemList
[nPos
].id();
1821 if ( nPageId
== mnCurPageId
)
1824 mnActPageId
= nPageId
;
1829 mnActPageId
= nPageId
;
1833 sal_uInt16 nOldId
= mnCurPageId
;
1834 mnCurPageId
= nPageId
;
1835 ImplChangeTabPage( nPageId
, nOldId
);
1839 sal_uInt16
TabControl::GetCurPageId() const
1847 void TabControl::SelectTabPage( sal_uInt16 nPageId
)
1849 if ( !nPageId
|| (nPageId
== mnCurPageId
) )
1852 ImplFreeLayoutData();
1854 CallEventListeners( VclEventId::TabpageDeactivate
, reinterpret_cast<void*>(mnCurPageId
) );
1855 if ( DeactivatePage() )
1857 mnActPageId
= nPageId
;
1859 // Page could have been switched by the Activate handler
1860 nPageId
= mnActPageId
;
1862 SetCurPageId( nPageId
);
1863 if( mpTabCtrlData
->mpListBox
)
1864 mpTabCtrlData
->mpListBox
->SelectEntryPos( GetPagePos( nPageId
) );
1865 CallEventListeners( VclEventId::TabpageActivate
, reinterpret_cast<void*>(nPageId
) );
1869 void TabControl::SetTabPage( sal_uInt16 nPageId
, TabPage
* pTabPage
)
1871 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1873 if ( !pItem
|| (pItem
->mpTabPage
.get() == pTabPage
) )
1878 if ( IsDefaultSize() )
1879 SetTabPageSizePixel( pTabPage
->GetSizePixel() );
1881 // only set here, so that Resize does not reposition TabPage
1882 pItem
->mpTabPage
= pTabPage
;
1885 if (pItem
->id() == mnCurPageId
)
1886 ImplChangeTabPage(pItem
->id(), 0);
1890 pItem
->mpTabPage
= nullptr;
1895 TabPage
* TabControl::GetTabPage( sal_uInt16 nPageId
) const
1897 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1900 return pItem
->mpTabPage
;
1905 void TabControl::SetPageText( sal_uInt16 nPageId
, const OUString
& rText
)
1907 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1909 if ( !pItem
|| pItem
->maText
== rText
)
1912 pItem
->maText
= rText
;
1914 if( mpTabCtrlData
->mpListBox
)
1916 sal_uInt16 nPos
= GetPagePos( nPageId
);
1917 mpTabCtrlData
->mpListBox
->RemoveEntry( nPos
);
1918 mpTabCtrlData
->mpListBox
->InsertEntry( rText
, nPos
);
1920 if ( IsUpdateMode() )
1922 ImplFreeLayoutData();
1923 CallEventListeners( VclEventId::TabpagePageTextChanged
, reinterpret_cast<void*>(nPageId
) );
1926 OUString
const & TabControl::GetPageText( sal_uInt16 nPageId
) const
1928 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1932 return pItem
->maText
;
1935 void TabControl::SetHelpText( sal_uInt16 nPageId
, const OUString
& rText
)
1937 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1941 pItem
->maHelpText
= rText
;
1944 const OUString
& TabControl::GetHelpText( sal_uInt16 nPageId
) const
1946 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1948 return pItem
->maHelpText
;
1951 void TabControl::SetAccessibleName(sal_uInt16 nPageId
, const OUString
& rName
)
1953 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1955 pItem
->maAccessibleName
= rName
;
1958 OUString
TabControl::GetAccessibleName( sal_uInt16 nPageId
) const
1960 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1962 if (!pItem
->maAccessibleName
.isEmpty())
1963 return pItem
->maAccessibleName
;
1964 return OutputDevice::GetNonMnemonicString(pItem
->maText
);
1967 void TabControl::SetAccessibleDescription(sal_uInt16 nPageId
, const OUString
& rDesc
)
1969 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1971 pItem
->maAccessibleDescription
= rDesc
;
1974 OUString
TabControl::GetAccessibleDescription( sal_uInt16 nPageId
) const
1976 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1978 if (!pItem
->maAccessibleDescription
.isEmpty())
1979 return pItem
->maAccessibleDescription
;
1980 return pItem
->maHelpText
;
1983 void TabControl::SetPageName( sal_uInt16 nPageId
, const OString
& rName
) const
1985 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1988 pItem
->maTabName
= rName
;
1991 OString
TabControl::GetPageName( sal_uInt16 nPageId
) const
1993 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
1996 return pItem
->maTabName
;
2001 void TabControl::SetPageImage( sal_uInt16 i_nPageId
, const Image
& i_rImage
)
2003 ImplTabItem
* pItem
= ImplGetItem( i_nPageId
);
2007 pItem
->maTabImage
= i_rImage
;
2009 if ( IsUpdateMode() )
2014 tools::Rectangle
TabControl::GetCharacterBounds( sal_uInt16 nPageId
, tools::Long nIndex
) const
2016 tools::Rectangle aRet
;
2018 if( !HasLayoutData() || mpTabCtrlData
->maLayoutPageIdToLine
.empty() )
2021 if( HasLayoutData() )
2023 std::unordered_map
< int, int >::const_iterator it
= mpTabCtrlData
->maLayoutPageIdToLine
.find( static_cast<int>(nPageId
) );
2024 if( it
!= mpTabCtrlData
->maLayoutPageIdToLine
.end() )
2026 Pair aPair
= mpControlData
->mpLayoutData
->GetLineStartEnd( it
->second
);
2027 if( (aPair
.B() - aPair
.A()) >= nIndex
)
2028 aRet
= mpControlData
->mpLayoutData
->GetCharacterBounds( aPair
.A() + nIndex
);
2035 tools::Long
TabControl::GetIndexForPoint( const Point
& rPoint
, sal_uInt16
& rPageId
) const
2037 tools::Long nRet
= -1;
2039 if( !HasLayoutData() || mpTabCtrlData
->maLayoutPageIdToLine
.empty() )
2042 if( HasLayoutData() )
2044 int nIndex
= mpControlData
->mpLayoutData
->GetIndexForPoint( rPoint
);
2047 // what line (->pageid) is this index in ?
2048 int nLines
= mpControlData
->mpLayoutData
->GetLineCount();
2050 while( ++nLine
< nLines
)
2052 Pair aPair
= mpControlData
->mpLayoutData
->GetLineStartEnd( nLine
);
2053 if( aPair
.A() <= nIndex
&& aPair
.B() >= nIndex
)
2055 nRet
= nIndex
- aPair
.A();
2056 rPageId
= static_cast<sal_uInt16
>(mpTabCtrlData
->maLayoutLineToPageId
[ nLine
]);
2066 void TabControl::FillLayoutData() const
2068 mpTabCtrlData
->maLayoutLineToPageId
.clear();
2069 mpTabCtrlData
->maLayoutPageIdToLine
.clear();
2070 const_cast<TabControl
*>(this)->Invalidate();
2073 tools::Rectangle
TabControl::GetTabBounds( sal_uInt16 nPageId
) const
2075 tools::Rectangle aRet
;
2077 ImplTabItem
* pItem
= ImplGetItem( nPageId
);
2078 if (pItem
&& pItem
->m_bVisible
)
2079 aRet
= pItem
->maRect
;
2084 Size
TabControl::ImplCalculateRequisition(sal_uInt16
& nHeaderHeight
) const
2086 Size
aOptimalPageSize(0, 0);
2088 sal_uInt16 nOrigPageId
= GetCurPageId();
2089 for (auto const& item
: mpTabCtrlData
->maItemList
)
2091 const TabPage
*pPage
= item
.mpTabPage
;
2092 //it's a real nuisance if the page is not inserted yet :-(
2093 //We need to force all tabs to exist to get overall optimal size for dialog
2096 TabControl
*pThis
= const_cast<TabControl
*>(this);
2097 pThis
->SetCurPageId(item
.id());
2098 pThis
->ActivatePage();
2099 pPage
= item
.mpTabPage
;
2105 Size
aPageSize(VclContainer::getLayoutRequisition(*pPage
));
2107 if (aPageSize
.Width() > aOptimalPageSize
.Width())
2108 aOptimalPageSize
.setWidth( aPageSize
.Width() );
2109 if (aPageSize
.Height() > aOptimalPageSize
.Height())
2110 aOptimalPageSize
.setHeight( aPageSize
.Height() );
2113 //fdo#61940 If we were forced to activate pages in order to on-demand
2114 //create them to get their optimal size, then switch back to the original
2115 //page and re-activate it
2116 if (nOrigPageId
!= GetCurPageId())
2118 TabControl
*pThis
= const_cast<TabControl
*>(this);
2119 pThis
->SetCurPageId(nOrigPageId
);
2120 pThis
->ActivatePage();
2123 tools::Long nTabLabelsBottom
= 0, nTabLabelsRight
= 0;
2124 for (sal_uInt16
nPos(0), sizeList(static_cast <sal_uInt16
> (mpTabCtrlData
->maItemList
.size()));
2125 nPos
< sizeList
; ++nPos
)
2127 TabControl
* pThis
= const_cast<TabControl
*>(this);
2129 tools::Rectangle aTabRect
= pThis
->ImplGetTabRect(nPos
, aOptimalPageSize
.Width(), LONG_MAX
);
2130 if (aTabRect
.Bottom() > nTabLabelsBottom
)
2132 nTabLabelsBottom
= aTabRect
.Bottom();
2133 nHeaderHeight
= nTabLabelsBottom
;
2135 if (!aTabRect
.IsEmpty() && aTabRect
.Right() > nTabLabelsRight
)
2136 nTabLabelsRight
= aTabRect
.Right();
2139 Size
aOptimalSize(aOptimalPageSize
);
2140 aOptimalSize
.AdjustHeight(nTabLabelsBottom
);
2141 aOptimalSize
.setWidth( std::max(nTabLabelsRight
, aOptimalSize
.Width()) );
2143 aOptimalSize
.AdjustWidth(TAB_OFFSET
* 2 );
2144 aOptimalSize
.AdjustHeight(TAB_OFFSET
* 2 );
2146 return aOptimalSize
;
2149 Size
TabControl::calculateRequisition() const
2151 sal_uInt16 nHeaderHeight
;
2152 return ImplCalculateRequisition(nHeaderHeight
);
2155 Size
TabControl::GetOptimalSize() const
2157 return calculateRequisition();
2160 void TabControl::queue_resize(StateChangedType eReason
)
2162 mbLayoutDirty
= true;
2163 Window::queue_resize(eReason
);
2166 std::vector
<sal_uInt16
> TabControl::GetPageIDs() const
2168 std::vector
<sal_uInt16
> aIDs
;
2169 for (auto const& item
: mpTabCtrlData
->maItemList
)
2171 aIDs
.push_back(item
.id());
2177 FactoryFunction
TabControl::GetUITestFactory() const
2179 return TabControlUIObject::create
;
2182 void TabControl::DumpAsPropertyTree(tools::JsonWriter
& rJsonWriter
)
2184 Control::DumpAsPropertyTree(rJsonWriter
);
2186 auto tabsNode
= rJsonWriter
.startNode("tabs");
2187 for(auto id
: GetPageIDs())
2189 auto tabNode
= rJsonWriter
.startNode("");
2190 rJsonWriter
.put("text", GetPageText(id
));
2191 rJsonWriter
.put("id", id
);
2192 rJsonWriter
.put("name", GetPageName(id
));
2195 rJsonWriter
.put("selected", GetCurPageId());
2198 sal_uInt16
NotebookbarTabControlBase::m_nHeaderHeight
= 0;
2200 IMPL_LINK_NOARG(NotebookbarTabControlBase
, OpenMenu
, Button
*, void)
2202 m_aIconClickHdl
.Call(static_cast<NotebookBar
*>(GetParent()->GetParent()));
2205 NotebookbarTabControlBase::NotebookbarTabControlBase(vcl::Window
* pParent
)
2206 : TabControl(pParent
, WB_STDTABCONTROL
)
2207 , bLastContextWasSupported(true)
2208 , eLastContext(vcl::EnumContext::Context::Any
)
2210 m_pOpenMenu
= VclPtr
<PushButton
>::Create( this , WB_CENTER
| WB_VCENTER
);
2211 m_pOpenMenu
->SetClickHdl(LINK(this, NotebookbarTabControlBase
, OpenMenu
));
2212 m_pOpenMenu
->SetModeImage(Image(StockImage::Yes
, SV_RESID_BITMAP_NOTEBOOKBAR
));
2213 m_pOpenMenu
->SetSizePixel(m_pOpenMenu
->GetOptimalSize());
2214 m_pOpenMenu
->Show();
2217 NotebookbarTabControlBase::~NotebookbarTabControlBase()
2222 void NotebookbarTabControlBase::SetContext( vcl::EnumContext::Context eContext
)
2224 if (eLastContext
== eContext
)
2227 bool bHandled
= false;
2229 for (int nChild
= 0; nChild
< GetPageCount(); ++nChild
)
2231 sal_uInt16 nPageId
= TabControl::GetPageId(nChild
);
2232 TabPage
* pPage
= GetTabPage(nPageId
);
2236 SetPageVisible(nPageId
, pPage
->HasContext(eContext
) || pPage
->HasContext(vcl::EnumContext::Context::Any
));
2238 if (!bHandled
&& bLastContextWasSupported
2239 && pPage
->HasContext(vcl::EnumContext::Context::Default
))
2241 SetCurPageId(nPageId
);
2244 if (pPage
->HasContext(eContext
) && eContext
!= vcl::EnumContext::Context::Any
)
2246 SetCurPageId(nPageId
);
2248 bLastContextWasSupported
= true;
2254 bLastContextWasSupported
= false;
2255 eLastContext
= eContext
;
2258 void NotebookbarTabControlBase::dispose()
2260 m_pShortcuts
.disposeAndClear();
2261 m_pOpenMenu
.disposeAndClear();
2262 TabControl::dispose();
2265 void NotebookbarTabControlBase::SetToolBox( ToolBox
* pToolBox
)
2267 m_pShortcuts
.set( pToolBox
);
2270 void NotebookbarTabControlBase::SetIconClickHdl( Link
<NotebookBar
*, void> aHdl
)
2272 m_aIconClickHdl
= aHdl
;
2275 static bool lcl_isValidPage(const ImplTabItem
& rItem
, bool& bFound
)
2277 if (rItem
.m_bVisible
&& rItem
.m_bEnabled
)
2282 void NotebookbarTabControlBase::ImplActivateTabPage( bool bNext
)
2284 const sal_uInt16 nOldPos
= GetPagePos(GetCurPageId());
2285 bool bFound
= false;
2286 sal_Int32 nCurPos
= nOldPos
;
2290 for (nCurPos
++; nCurPos
< GetPageCount(); nCurPos
++)
2291 if (lcl_isValidPage(mpTabCtrlData
->maItemList
[nCurPos
], bFound
))
2296 for (nCurPos
--; nCurPos
>= 0; nCurPos
--)
2297 if (lcl_isValidPage(mpTabCtrlData
->maItemList
[nCurPos
], bFound
))
2303 SelectTabPage( TabControl::GetPageId( nCurPos
) );
2306 sal_uInt16
NotebookbarTabControlBase::GetHeaderHeight()
2308 return m_nHeaderHeight
;
2311 bool NotebookbarTabControlBase::ImplPlaceTabs( tools::Long nWidth
)
2315 if ( mpTabCtrlData
->maItemList
.empty() )
2317 if (!m_pOpenMenu
|| m_pOpenMenu
->isDisposed())
2320 const tools::Long nHamburgerWidth
= m_pOpenMenu
->GetSizePixel().Width();
2321 tools::Long nMaxWidth
= nWidth
- nHamburgerWidth
;
2322 tools::Long nShortcutsWidth
= m_pShortcuts
!= nullptr ? m_pShortcuts
->GetSizePixel().getWidth() + 1 : 0;
2323 tools::Long nFullWidth
= nShortcutsWidth
;
2325 const tools::Long nOffsetX
= 2 + nShortcutsWidth
;
2326 const tools::Long nOffsetY
= 2;
2328 //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
2329 //of ugly bare tabs on lines of their own
2331 for (auto & item
: mpTabCtrlData
->maItemList
)
2333 tools::Long nTabWidth
= 0;
2334 if (item
.m_bVisible
)
2336 nTabWidth
= ImplGetItemSize(&item
, nMaxWidth
).getWidth();
2337 if (!item
.maText
.isEmpty() && nTabWidth
< 100)
2340 nFullWidth
+= nTabWidth
;
2343 tools::Long nX
= nOffsetX
;
2344 tools::Long nY
= nOffsetY
;
2346 tools::Long nLineWidthAry
[100];
2347 nLineWidthAry
[0] = 0;
2349 for (auto & item
: mpTabCtrlData
->maItemList
)
2351 if (!item
.m_bVisible
)
2354 Size aSize
= ImplGetItemSize( &item
, nMaxWidth
);
2356 // set minimum tab size
2357 if( nFullWidth
< nMaxWidth
&& !item
.maText
.isEmpty() && aSize
.getWidth() < 100)
2358 aSize
.setWidth( 100 );
2360 if( !item
.maText
.isEmpty() && aSize
.getHeight() < 28 )
2361 aSize
.setHeight( 28 );
2363 tools::Rectangle
aNewRect( Point( nX
, nY
), aSize
);
2364 if ( mbSmallInvalidate
&& (item
.maRect
!= aNewRect
) )
2365 mbSmallInvalidate
= false;
2367 item
.maRect
= aNewRect
;
2369 item
.mbFullVisible
= true;
2371 nLineWidthAry
[0] += aSize
.Width();
2372 nX
+= aSize
.Width();
2375 // we always have only one line of tabs
2376 lcl_AdjustSingleLineTabs(nMaxWidth
, mpTabCtrlData
.get());
2378 // position the shortcutbox
2381 tools::Long nPosY
= (m_nHeaderHeight
- m_pShortcuts
->GetSizePixel().getHeight()) / 2;
2382 m_pShortcuts
->SetPosPixel(Point(0, nPosY
));
2385 tools::Long nPosY
= (m_nHeaderHeight
- m_pOpenMenu
->GetSizePixel().getHeight()) / 2;
2386 // position the menu
2387 m_pOpenMenu
->SetPosPixel(Point(nWidth
- nHamburgerWidth
, nPosY
));
2392 Size
NotebookbarTabControlBase::calculateRequisition() const
2394 return TabControl::ImplCalculateRequisition(m_nHeaderHeight
);
2397 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */