xref: /core/vcl/source/control/tabctrl.cxx (revision 374dd9fbd660e95a9e6dd09d121c3528c23020a7)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <vcl/help.hxx>
23 #include <vcl/layout.hxx>
24 #include <vcl/notebookbar/notebookbar.hxx>
25 #include <vcl/tabctrl.hxx>
26 #include <vcl/tabpage.hxx>
27 #include <vcl/toolbox.hxx>
28 #include <vcl/toolkit/button.hxx>
29 #include <vcl/mnemonic.hxx>
30 #include <vcl/toolkit/lstbox.hxx>
31 #include <vcl/uitest/uiobject.hxx>
32 
33 #include <bitmaps.hlst>
34 #include <svdata.hxx>
35 #include <window.h>
36 
37 #include <deque>
38 #include <unordered_map>
39 #include <vector>
40 
41 #define TAB_OFFSET          3
42 /// Space to the left and right of the tabitem
43 #define TAB_ITEM_OFFSET_X     10
44 /// Space to the top and bottom of the tabitem
45 #define TAB_ITEM_OFFSET_Y     3
46 #define TAB_EXTRASPACE_X    6
47 #define TAB_BORDER_LEFT     1
48 #define TAB_BORDER_TOP      1
49 #define TAB_BORDER_RIGHT    2
50 #define TAB_BORDER_BOTTOM   2
51 
52 class ImplTabItem final
53 {
54     sal_uInt16 m_nId;
55 
56 public:
57     VclPtr<TabPage>     mpTabPage;
58     OUString            maText;
59     OUString            maFormatText;
60     OUString            maHelpText;
61     OUString            maAccessibleName;
62     OUString            maAccessibleDescription;
63     OUString             maTabName;
64     tools::Rectangle    maRect;
65     sal_uInt16          mnLine;
66     bool                mbFullVisible;
67     bool                m_bEnabled; ///< the tab / page is selectable
68     bool                m_bVisible; ///< the tab / page can be visible
69     Image               maTabImage;
70 
71     ImplTabItem(sal_uInt16 nId);
72 
id() const73     sal_uInt16 id() const { return m_nId; }
74 };
75 
ImplTabItem(sal_uInt16 nId)76 ImplTabItem::ImplTabItem(sal_uInt16 nId)
77     : m_nId(nId)
78     , mnLine(0)
79     , mbFullVisible(false)
80     , m_bEnabled(true)
81     , m_bVisible(true)
82 {
83 }
84 
85 struct ImplTabCtrlData
86 {
87     std::vector< ImplTabItem >      maItemList;
88     VclPtr<ListBox>                 mpListBox;
89 };
90 
91 // for the Tab positions
92 #define TAB_PAGERECT        0xFFFF
93 
ImplInit(vcl::Window * pParent,WinBits nStyle)94 void TabControl::ImplInit( vcl::Window* pParent, WinBits nStyle )
95 {
96     mbLayoutDirty = true;
97 
98     if ( !(nStyle & WB_NOTABSTOP) )
99         nStyle |= WB_TABSTOP;
100     if ( !(nStyle & WB_NOGROUP) )
101         nStyle |= WB_GROUP;
102     if ( !(nStyle & WB_NODIALOGCONTROL) )
103         nStyle |= WB_DIALOGCONTROL;
104 
105     Control::ImplInit( pParent, nStyle, nullptr );
106 
107     mnLastWidth                 = 0;
108     mnLastHeight                = 0;
109     mnActPageId                 = 0;
110     mnCurPageId                 = 0;
111     mbFormat                    = true;
112     mbShowTabs                  = true;
113     mbRestoreHelpId             = false;
114     mbSmallInvalidate           = false;
115     mpTabCtrlData.reset(new ImplTabCtrlData);
116     mpTabCtrlData->mpListBox    = nullptr;
117 
118     ImplInitSettings( true );
119 
120     if( nStyle & WB_DROPDOWN )
121     {
122         mpTabCtrlData->mpListBox = VclPtr<ListBox>::Create( this, WB_DROPDOWN );
123         mpTabCtrlData->mpListBox->SetPosSizePixel( Point( 0, 0 ), Size( 200, 20 ) );
124         mpTabCtrlData->mpListBox->SetSelectHdl( LINK( this, TabControl, ImplListBoxSelectHdl ) );
125         mpTabCtrlData->mpListBox->Show();
126     }
127 
128     // if the tabcontrol is drawn (ie filled) by a native widget, make sure all controls will have transparent background
129     // otherwise they will paint with a wrong background
130     if( IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire) )
131         EnableChildTransparentMode();
132 
133     if (pParent && pParent->IsDialog())
134         pParent->AddChildEventListener( LINK( this, TabControl, ImplWindowEventListener ) );
135 }
136 
GetCanonicalFont(const StyleSettings & _rStyle) const137 const vcl::Font& TabControl::GetCanonicalFont( const StyleSettings& _rStyle ) const
138 {
139     return _rStyle.GetTabFont();
140 }
141 
GetCanonicalTextColor(const StyleSettings & _rStyle) const142 const Color& TabControl::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
143 {
144     return _rStyle.GetTabTextColor();
145 }
146 
ImplInitSettings(bool bBackground)147 void TabControl::ImplInitSettings( bool bBackground )
148 {
149     Control::ImplInitSettings();
150 
151     if ( !bBackground )
152         return;
153 
154     vcl::Window* pParent = GetParent();
155     if ( !IsControlBackground() &&
156         (pParent->IsChildTransparentModeEnabled()
157         || IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire)
158         || IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire) ) )
159 
160     {
161         // set transparent mode for NWF tabcontrols to have
162         // the background always cleared properly
163         EnableChildTransparentMode();
164         SetParentClipMode( ParentClipMode::NoClip );
165         SetPaintTransparent( true );
166         SetBackground();
167         ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
168 
169         return;
170     }
171 
172     EnableChildTransparentMode( false );
173     SetParentClipMode();
174     SetPaintTransparent( false );
175 
176     if ( IsControlBackground() )
177         SetBackground( GetControlBackground() );
178     else
179         SetBackground( pParent->GetBackground() );
180 }
181 
TabControl(vcl::Window * pParent,WinBits nStyle)182 TabControl::TabControl( vcl::Window* pParent, WinBits nStyle ) :
183     Control( WindowType::TABCONTROL )
184 {
185     ImplInit( pParent, nStyle );
186     SAL_INFO( "vcl", "*** TABCONTROL no notabs? " << (( GetStyle() & WB_NOBORDER ) ? "true" : "false") );
187 }
188 
~TabControl()189 TabControl::~TabControl()
190 {
191     disposeOnce();
192 }
193 
dispose()194 void TabControl::dispose()
195 {
196     Window *pParent = GetParent();
197     if (pParent && pParent->IsDialog())
198         GetParent()->RemoveChildEventListener( LINK( this, TabControl, ImplWindowEventListener ) );
199 
200     // delete TabCtrl data
201     if (mpTabCtrlData)
202         mpTabCtrlData->mpListBox.disposeAndClear();
203     mpTabCtrlData.reset();
204     Control::dispose();
205 }
206 
ImplGetItem(sal_uInt16 nId) const207 ImplTabItem* TabControl::ImplGetItem( sal_uInt16 nId ) const
208 {
209     for (auto & item : mpTabCtrlData->maItemList)
210     {
211         if (item.id() == nId)
212             return &item;
213     }
214 
215     return nullptr;
216 }
217 
ImplGetItemSize(ImplTabItem * pItem,tools::Long nMaxWidth)218 Size TabControl::ImplGetItemSize( ImplTabItem* pItem, tools::Long nMaxWidth )
219 {
220     pItem->maFormatText = pItem->maText;
221     Size aSize( GetOutDev()->GetCtrlTextWidth( pItem->maFormatText ), GetTextHeight() );
222     Size aImageSize( 0, 0 );
223     if( !!pItem->maTabImage )
224     {
225         aImageSize = pItem->maTabImage.GetSizePixel();
226         if( !pItem->maFormatText.isEmpty() )
227             aImageSize.AdjustWidth(GetTextHeight()/4 );
228     }
229     aSize.AdjustWidth(aImageSize.Width() );
230     if( aImageSize.Height() > aSize.Height() )
231         aSize.setHeight( aImageSize.Height() );
232 
233     aSize.AdjustWidth(TAB_ITEM_OFFSET_X*2 );
234     aSize.AdjustHeight(TAB_ITEM_OFFSET_Y*2 );
235 
236     tools::Rectangle aCtrlRegion( Point( 0, 0 ), aSize );
237     tools::Rectangle aBoundingRgn, aContentRgn;
238     const TabitemValue aControlValue(tools::Rectangle(TAB_ITEM_OFFSET_X, TAB_ITEM_OFFSET_Y,
239                                                       aSize.Width() - TAB_ITEM_OFFSET_X * 2,
240                                                       aSize.Height() - TAB_ITEM_OFFSET_Y * 2),
241                                      TabBarPosition::Top);
242     if(GetNativeControlRegion( ControlType::TabItem, ControlPart::Entire, aCtrlRegion,
243                                            ControlState::ENABLED, aControlValue,
244                                            aBoundingRgn, aContentRgn ) )
245     {
246         return aContentRgn.GetSize();
247     }
248 
249     // For languages with short names (e.g. Chinese), because the space is
250     // normally only one pixel per char
251     if ( pItem->maFormatText.getLength() < TAB_EXTRASPACE_X )
252         aSize.AdjustWidth(TAB_EXTRASPACE_X-pItem->maFormatText.getLength() );
253 
254     // shorten Text if needed
255     if ( aSize.Width()+4 >= nMaxWidth )
256     {
257         OUString aAppendStr(u"..."_ustr);
258         pItem->maFormatText += aAppendStr;
259         do
260         {
261             if (pItem->maFormatText.getLength() > aAppendStr.getLength())
262                 pItem->maFormatText = pItem->maFormatText.replaceAt( pItem->maFormatText.getLength()-aAppendStr.getLength()-1, 1, u"" );
263             aSize.setWidth( GetOutDev()->GetCtrlTextWidth( pItem->maFormatText ) );
264             aSize.AdjustWidth(aImageSize.Width() );
265             aSize.AdjustWidth(TAB_ITEM_OFFSET_X*2 );
266         }
267         while ( (aSize.Width()+4 >= nMaxWidth) && (pItem->maFormatText.getLength() > aAppendStr.getLength()) );
268         if ( aSize.Width()+4 >= nMaxWidth )
269         {
270             pItem->maFormatText = ".";
271             aSize.setWidth( 1 );
272         }
273     }
274 
275     if( pItem->maFormatText.isEmpty() )
276     {
277         if( aSize.Height() < aImageSize.Height()+4 ) //leave space for focus rect
278             aSize.setHeight( aImageSize.Height()+4 );
279     }
280 
281     return aSize;
282 }
283 
284 // Feel free to move this to some more general place for reuse
285 // http://en.wikipedia.org/wiki/Word_wrap#Minimum_raggedness
286 // Mostly based on Alexey Frunze's nifty example at
287 // http://stackoverflow.com/questions/9071205/balanced-word-wrap-minimum-raggedness-in-php
288 namespace MinimumRaggednessWrap
289 {
GetEndOfLineIndexes(const std::vector<sal_Int32> & rWidthsOf,sal_Int32 nLineWidth)290     static std::deque<size_t> GetEndOfLineIndexes(const std::vector<sal_Int32>& rWidthsOf, sal_Int32 nLineWidth)
291     {
292         ++nLineWidth;
293 
294         size_t nWidthsCount = rWidthsOf.size();
295         std::vector<sal_Int32> aCosts(nWidthsCount * nWidthsCount);
296 
297         // cost function c(i, j) that computes the cost of a line consisting of
298         // the words Word[i] to Word[j]
299         for (size_t i = 0; i < nWidthsCount; ++i)
300         {
301             for (size_t j = 0; j < nWidthsCount; ++j)
302             {
303                 if (j >= i)
304                 {
305                     sal_Int32 c = nLineWidth - (j - i);
306                     for (size_t k = i; k <= j; ++k)
307                         c -= rWidthsOf[k];
308                     c = (c >= 0) ? c * c : SAL_MAX_INT32;
309                     aCosts[j * nWidthsCount + i] = c;
310                 }
311                 else
312                 {
313                     aCosts[j * nWidthsCount + i] = SAL_MAX_INT32;
314                 }
315             }
316         }
317 
318         std::vector<sal_Int32> aFunction(nWidthsCount);
319         std::vector<sal_Int32> aWrapPoints(nWidthsCount);
320 
321         // f(j) in aFunction[], collect wrap points in aWrapPoints[]
322         for (size_t j = 0; j < nWidthsCount; ++j)
323         {
324             aFunction[j] = aCosts[j * nWidthsCount];
325             if (aFunction[j] == SAL_MAX_INT32)
326             {
327                 for (size_t k = 0; k < j; ++k)
328                 {
329                     sal_Int32 s;
330                     if (aFunction[k] == SAL_MAX_INT32 || aCosts[j * nWidthsCount + k + 1] == SAL_MAX_INT32)
331                         s = SAL_MAX_INT32;
332                     else
333                         s = aFunction[k] + aCosts[j * nWidthsCount + k + 1];
334                     if (aFunction[j] > s)
335                     {
336                         aFunction[j] = s;
337                         aWrapPoints[j] = k + 1;
338                     }
339                 }
340             }
341         }
342 
343         std::deque<size_t> aSolution;
344 
345         // no solution
346         if (aFunction[nWidthsCount - 1] == SAL_MAX_INT32)
347             return aSolution;
348 
349         // optimal solution
350         size_t j = nWidthsCount - 1;
351         while (true)
352         {
353             aSolution.push_front(j);
354             if (!aWrapPoints[j])
355                 break;
356             j = aWrapPoints[j] - 1;
357         }
358 
359         return aSolution;
360     }
361 };
362 
lcl_AdjustSingleLineTabs(tools::Long nMaxWidth,ImplTabCtrlData * pTabCtrlData)363 static void lcl_AdjustSingleLineTabs(tools::Long nMaxWidth, ImplTabCtrlData *pTabCtrlData)
364 {
365     if (!ImplGetSVData()->maNWFData.mbCenteredTabs)
366         return;
367 
368     int nRightSpace = nMaxWidth; // space left on the right by the tabs
369     for (auto const& item : pTabCtrlData->maItemList)
370     {
371         if (!item.m_bVisible)
372             continue;
373         nRightSpace -= item.maRect.GetWidth();
374     }
375     nRightSpace /= 2;
376 
377     for (auto& item : pTabCtrlData->maItemList)
378     {
379         if (!item.m_bVisible)
380             continue;
381         item.maRect.AdjustLeft(nRightSpace);
382         item.maRect.AdjustRight(nRightSpace);
383     }
384 }
385 
ImplPlaceTabs(tools::Long nWidth)386 bool TabControl::ImplPlaceTabs( tools::Long nWidth )
387 {
388     if ( nWidth <= 0 )
389         return false;
390     if ( mpTabCtrlData->maItemList.empty() )
391         return false;
392 
393     tools::Long nMaxWidth = nWidth;
394 
395     const tools::Long nOffsetX = 2;
396     const tools::Long nOffsetY = 2;
397 
398     //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
399     //of ugly bare tabs on lines of their own
400 
401     //collect widths
402     std::vector<sal_Int32> aWidths;
403     for (auto & item : mpTabCtrlData->maItemList)
404     {
405         if (!item.m_bVisible)
406             continue;
407         aWidths.push_back(ImplGetItemSize(&item, nMaxWidth).Width());
408     }
409 
410     //aBreakIndexes will contain the indexes of the last tab on each row
411     std::deque<size_t> aBreakIndexes(MinimumRaggednessWrap::GetEndOfLineIndexes(aWidths, nMaxWidth - nOffsetX - 2));
412 
413     tools::Long nX = nOffsetX;
414     tools::Long nY = nOffsetY;
415 
416     sal_uInt16 nLines = 0;
417     sal_uInt16 nCurLine = 0;
418 
419     tools::Long nLineWidthAry[100];
420     sal_uInt16 nLinePosAry[101];
421     nLineWidthAry[0] = 0;
422     nLinePosAry[0] = 0;
423 
424     size_t nIndex = 0;
425 
426     for (auto & item : mpTabCtrlData->maItemList)
427     {
428         if (!item.m_bVisible)
429             continue;
430 
431         Size aSize = ImplGetItemSize( &item, nMaxWidth );
432 
433         bool bNewLine = false;
434         if (!aBreakIndexes.empty() && nIndex > aBreakIndexes.front())
435         {
436             aBreakIndexes.pop_front();
437             bNewLine = true;
438         }
439 
440         if ( bNewLine && (nWidth > 2+nOffsetX) )
441         {
442             if ( nLines == 99 )
443                 break;
444 
445             nX = nOffsetX;
446             nY += aSize.Height();
447             nLines++;
448             nLineWidthAry[nLines] = 0;
449             nLinePosAry[nLines] = nIndex;
450         }
451 
452         tools::Rectangle aNewRect( Point( nX, nY ), aSize );
453         if ( mbSmallInvalidate && (item.maRect != aNewRect) )
454             mbSmallInvalidate = false;
455         item.maRect = aNewRect;
456         item.mnLine = nLines;
457         item.mbFullVisible = true;
458 
459         nLineWidthAry[nLines] += aSize.Width();
460         nX += aSize.Width();
461 
462         if (item.id() == mnCurPageId)
463             nCurLine = nLines;
464 
465         ++nIndex;
466     }
467 
468     if (nLines) // two or more lines
469     {
470         tools::Long nLineHeightAry[100];
471         tools::Long nIH = 0;
472         for (const auto& item : mpTabCtrlData->maItemList)
473         {
474             if (!item.m_bVisible)
475                 continue;
476             nIH = item.maRect.Bottom() - 1;
477             break;
478         }
479 
480         for ( sal_uInt16 i = 0; i < nLines+1; i++ )
481         {
482             if ( i <= nCurLine )
483                 nLineHeightAry[i] = nIH*(nLines-(nCurLine-i));
484             else
485                 nLineHeightAry[i] = nIH*(i-nCurLine-1);
486         }
487 
488         nLinePosAry[nLines+1] = static_cast<sal_uInt16>(mpTabCtrlData->maItemList.size());
489 
490         tools::Long nDX = 0;
491         tools::Long nModDX = 0;
492         tools::Long nIDX = 0;
493 
494         sal_uInt16 i = 0;
495         sal_uInt16 n = 0;
496 
497         for (auto & item : mpTabCtrlData->maItemList)
498         {
499             if (!item.m_bVisible)
500                 continue;
501 
502             if ( i == nLinePosAry[n] )
503             {
504                 if ( n == nLines+1 )
505                     break;
506 
507                 nIDX = 0;
508                 if( nLinePosAry[n+1]-i > 0 )
509                 {
510                     nDX = ( nWidth - nOffsetX - nLineWidthAry[n] ) / ( nLinePosAry[n+1] - i );
511                     nModDX = ( nWidth - nOffsetX - nLineWidthAry[n] ) % ( nLinePosAry[n+1] - i );
512                 }
513                 else
514                 {
515                     // FIXME: this is a case of tabctrl way too small
516                     nDX = 0;
517                     nModDX = 0;
518                 }
519                 n++;
520             }
521 
522             item.maRect.AdjustLeft(nIDX );
523             item.maRect.AdjustRight(nIDX + nDX );
524             item.maRect.SetTop( nLineHeightAry[n-1] );
525             item.maRect.SetBottom(nLineHeightAry[n-1] + nIH - 1);
526             nIDX += nDX;
527 
528             if ( nModDX )
529             {
530                 nIDX++;
531                 item.maRect.AdjustRight( 1 );
532                 nModDX--;
533             }
534 
535             i++;
536         }
537     }
538     else // only one line
539         lcl_AdjustSingleLineTabs(nMaxWidth, mpTabCtrlData.get());
540 
541     return true;
542 }
543 
ImplGetTabRect(sal_uInt16 nItemPos,tools::Long nWidth,tools::Long nHeight)544 tools::Rectangle TabControl::ImplGetTabRect( sal_uInt16 nItemPos, tools::Long nWidth, tools::Long nHeight )
545 {
546     Size aWinSize = Control::GetOutputSizePixel();
547     if ( nWidth < 0 )
548         nWidth = aWinSize.Width();
549     if ( nHeight < 0 )
550         nHeight = aWinSize.Height();
551 
552     if ( mpTabCtrlData->maItemList.empty() )
553     {
554         tools::Long nW = nWidth-TAB_OFFSET*2;
555         tools::Long nH = nHeight-TAB_OFFSET*2;
556         return (nW > 0 && nH > 0)
557             ? tools::Rectangle(Point(TAB_OFFSET, TAB_OFFSET), Size(nW, nH))
558             : tools::Rectangle();
559     }
560 
561     if ( nItemPos == TAB_PAGERECT )
562     {
563         sal_uInt16 nLastPos;
564         if ( mnCurPageId )
565             nLastPos = GetPagePos( mnCurPageId );
566         else
567             nLastPos = 0;
568 
569         tools::Rectangle aRect = ImplGetTabRect( nLastPos, nWidth, nHeight );
570         if (aRect.IsEmpty())
571             return aRect;
572 
573         // with show-tabs of true (the usual) the page rect is from under the
574         // visible tab to the bottom of the TabControl, otherwise it extends
575         // from the top of the TabControl
576         tools::Long nTabBottom = mbShowTabs ? aRect.Bottom() : 0;
577 
578         tools::Long nW = nWidth-TAB_OFFSET*2;
579         tools::Long nH = nHeight - nTabBottom - TAB_OFFSET*2;
580         return (nW > 0 && nH > 0)
581             ? tools::Rectangle( Point( TAB_OFFSET, nTabBottom + TAB_OFFSET ), Size( nW, nH ) )
582             : tools::Rectangle();
583     }
584 
585     ImplTabItem* const pItem = (nItemPos < mpTabCtrlData->maItemList.size())
586                                ? &mpTabCtrlData->maItemList[nItemPos] : nullptr;
587     return ImplGetTabRect(pItem, nWidth, nHeight);
588 }
589 
ImplGetTabRect(const ImplTabItem * pItem,tools::Long nWidth,tools::Long nHeight)590 tools::Rectangle TabControl::ImplGetTabRect(const ImplTabItem* pItem, tools::Long nWidth, tools::Long nHeight)
591 {
592     if ((nWidth <= 1) || (nHeight <= 0) || !pItem || !pItem->m_bVisible)
593         return tools::Rectangle();
594 
595     nWidth -= 1;
596 
597     if ( mbFormat || (mnLastWidth != nWidth) || (mnLastHeight != nHeight) )
598     {
599         vcl::Font aFont( GetFont() );
600         aFont.SetTransparent( true );
601         SetFont( aFont );
602 
603         bool bRet = ImplPlaceTabs( nWidth );
604         if ( !bRet )
605             return tools::Rectangle();
606 
607         mnLastWidth     = nWidth;
608         mnLastHeight    = nHeight;
609         mbFormat        = false;
610     }
611 
612     return pItem->maRect;
613 }
614 
ImplChangeTabPage(sal_uInt16 nId,sal_uInt16 nOldId)615 void TabControl::ImplChangeTabPage( sal_uInt16 nId, sal_uInt16 nOldId )
616 {
617     ImplTabItem*    pOldItem = ImplGetItem( nOldId );
618     ImplTabItem*    pItem = ImplGetItem( nId );
619     TabPage*        pOldPage = pOldItem ? pOldItem->mpTabPage.get() : nullptr;
620     TabPage*        pPage = pItem ? pItem->mpTabPage.get() : nullptr;
621     vcl::Window*    pCtrlParent = GetParent();
622 
623     if ( IsReallyVisible() && IsUpdateMode() )
624     {
625         sal_uInt16 nPos = GetPagePos( nId );
626         tools::Rectangle aRect = ImplGetTabRect( nPos );
627 
628         if ( !pOldItem || !pItem || (pOldItem->mnLine != pItem->mnLine) )
629         {
630             aRect.SetLeft( 0 );
631             aRect.SetTop( 0 );
632             aRect.SetRight( Control::GetOutputSizePixel().Width() );
633         }
634         else
635         {
636             aRect.AdjustLeft( -3 );
637             aRect.AdjustTop( -2 );
638             aRect.AdjustRight(3 );
639             Invalidate( aRect );
640             nPos = GetPagePos( nOldId );
641             aRect = ImplGetTabRect( nPos );
642             aRect.AdjustLeft( -3 );
643             aRect.AdjustTop( -2 );
644             aRect.AdjustRight(3 );
645         }
646         Invalidate( aRect );
647     }
648 
649     if ( pOldPage == pPage )
650         return;
651 
652     tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
653 
654     if ( pOldPage )
655     {
656         if ( mbRestoreHelpId )
657             pCtrlParent->SetHelpId({});
658     }
659 
660     if ( pPage )
661     {
662         if ( GetStyle() & WB_NOBORDER )
663         {
664             tools::Rectangle aRectNoTab(Point(0, 0), GetSizePixel());
665             pPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
666         }
667         else
668             pPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
669 
670         // activate page here so the controls can be switched
671         // also set the help id of the parent window to that of the tab page
672         if ( GetHelpId().isEmpty() )
673         {
674             mbRestoreHelpId = true;
675             pCtrlParent->SetHelpId( pPage->GetHelpId() );
676         }
677 
678         pPage->Show();
679 
680         if ( pOldPage && pOldPage->HasChildPathFocus() )
681         {
682             vcl::Window* pFirstChild = pPage->ImplGetDlgWindow( 0, GetDlgWindowType::First );
683             if ( pFirstChild )
684                 pFirstChild->ImplControlFocus( GetFocusFlags::Init );
685             else
686                 GrabFocus();
687         }
688     }
689 
690     if ( pOldPage )
691         pOldPage->Hide();
692 
693     // Invalidate the same region that will be send to NWF
694     // to always allow for bitmap caching
695     // see Window::DrawNativeControl()
696     if( IsNativeControlSupported( ControlType::TabPane, ControlPart::Entire ) )
697     {
698         aRect.AdjustLeft( -(TAB_OFFSET) );
699         aRect.AdjustTop( -(TAB_OFFSET) );
700         aRect.AdjustRight(TAB_OFFSET );
701         aRect.AdjustBottom(TAB_OFFSET );
702     }
703 
704     Invalidate( aRect );
705 }
706 
ImplPosCurTabPage()707 bool TabControl::ImplPosCurTabPage()
708 {
709     // resize/position current TabPage
710     ImplTabItem* pItem = ImplGetItem( GetCurPageId() );
711     if ( pItem && pItem->mpTabPage )
712     {
713         if (  GetStyle() & WB_NOBORDER )
714         {
715             tools::Rectangle aRectNoTab(Point(0, 0), GetSizePixel());
716             pItem->mpTabPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
717             return true;
718         }
719         tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
720         pItem->mpTabPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
721         return true;
722     }
723 
724     return false;
725 }
726 
ImplActivateTabPage(bool bNext)727 void TabControl::ImplActivateTabPage( bool bNext )
728 {
729     sal_uInt16 nCurPos = GetPagePos( GetCurPageId() );
730 
731     if ( bNext )
732         nCurPos = (nCurPos + 1) % GetPageCount();
733     else
734     {
735         if ( !nCurPos )
736             nCurPos = GetPageCount()-1;
737         else
738             nCurPos--;
739     }
740 
741     SelectTabPage( GetPageId( nCurPos ) );
742 }
743 
ImplShowFocus()744 void TabControl::ImplShowFocus()
745 {
746     if ( !GetPageCount() || mpTabCtrlData->mpListBox )
747         return;
748 
749     sal_uInt16                   nCurPos     = GetPagePos( mnCurPageId );
750     tools::Rectangle                aRect       = ImplGetTabRect( nCurPos );
751     const ImplTabItem&       rItem       = mpTabCtrlData->maItemList[ nCurPos ];
752     Size                     aTabSize    = aRect.GetSize();
753     Size aImageSize( 0, 0 );
754     tools::Long                     nTextHeight = GetTextHeight();
755     tools::Long                     nTextWidth  = GetOutDev()->GetCtrlTextWidth( rItem.maFormatText );
756     sal_uInt16                   nOff;
757 
758     if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::Mono) )
759         nOff = 1;
760     else
761         nOff = 0;
762 
763     if( !! rItem.maTabImage )
764     {
765         aImageSize = rItem.maTabImage.GetSizePixel();
766         if( !rItem.maFormatText.isEmpty() )
767             aImageSize.AdjustWidth(GetTextHeight()/4 );
768     }
769 
770     if( !rItem.maFormatText.isEmpty() )
771     {
772         // show focus around text
773         aRect.SetLeft( aRect.Left()+aImageSize.Width()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1-1 );
774         aRect.SetTop( aRect.Top()+((aTabSize.Height()-nTextHeight)/2)-1-1 );
775         aRect.SetRight( aRect.Left()+nTextWidth+2 );
776         aRect.SetBottom( aRect.Top()+nTextHeight+2 );
777     }
778     else
779     {
780         // show focus around image
781         tools::Long nXPos = aRect.Left()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1;
782         tools::Long nYPos = aRect.Top();
783         if( aImageSize.Height() < aRect.GetHeight() )
784             nYPos += (aRect.GetHeight() - aImageSize.Height())/2;
785 
786         aRect.SetLeft( nXPos - 2 );
787         aRect.SetTop( nYPos - 2 );
788         aRect.SetRight( aRect.Left() + aImageSize.Width() + 4 );
789         aRect.SetBottom( aRect.Top() + aImageSize.Height() + 4 );
790     }
791     ShowFocus( aRect );
792 }
793 
ImplDrawItem(vcl::RenderContext & rRenderContext,ImplTabItem const * pItem,const tools::Rectangle & rCurRect,bool bFirstInGroup,bool bLastInGroup)794 void TabControl::ImplDrawItem(vcl::RenderContext& rRenderContext, ImplTabItem const * pItem, const tools::Rectangle& rCurRect,
795                               bool bFirstInGroup, bool bLastInGroup )
796 {
797     if (!pItem->m_bVisible || pItem->maRect.IsEmpty())
798         return;
799 
800     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
801     tools::Rectangle aRect = pItem->maRect;
802     tools::Long nLeftBottom = aRect.Bottom();
803     tools::Long nRightBottom = aRect.Bottom();
804     bool bLeftBorder = true;
805     bool bRightBorder = true;
806     sal_uInt16 nOff;
807     bool bNativeOK = false;
808 
809     sal_uInt16 nOff2 = 0;
810     sal_uInt16 nOff3 = 0;
811 
812     if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
813         nOff = 1;
814     else
815         nOff = 0;
816 
817     // if this is the active Page, we have to draw a little more
818     if (pItem->id() == mnCurPageId)
819     {
820         nOff2 = 2;
821         if (!ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise)
822             nOff3 = 1;
823     }
824     else
825     {
826         Point aLeftTestPos = aRect.BottomLeft();
827         Point aRightTestPos = aRect.BottomRight();
828         if (aLeftTestPos.Y() == rCurRect.Bottom())
829         {
830             aLeftTestPos.AdjustX( -2 );
831             if (rCurRect.Contains(aLeftTestPos))
832                 bLeftBorder = false;
833             aRightTestPos.AdjustX(2 );
834             if (rCurRect.Contains(aRightTestPos))
835                 bRightBorder = false;
836         }
837         else
838         {
839             if (rCurRect.Contains(aLeftTestPos))
840                 nLeftBottom -= 2;
841             if (rCurRect.Contains(aRightTestPos))
842                 nRightBottom -= 2;
843         }
844     }
845 
846     ControlState nState = ControlState::NONE;
847 
848     if (pItem->id() == mnCurPageId)
849     {
850         nState |= ControlState::SELECTED;
851         // only the selected item can be focused
852         if (HasFocus())
853             nState |= ControlState::FOCUSED;
854     }
855     if (IsEnabled())
856         nState |= ControlState::ENABLED;
857     if (IsMouseOver() && pItem->maRect.Contains(GetPointerPosPixel()))
858     {
859         nState |= ControlState::ROLLOVER;
860         for (auto const& item : mpTabCtrlData->maItemList)
861             if ((&item != pItem) && item.m_bVisible && item.maRect.Contains(GetPointerPosPixel()))
862             {
863                 nState &= ~ControlState::ROLLOVER; // avoid multiple highlighted tabs
864                 break;
865             }
866         assert(nState & ControlState::ROLLOVER);
867     }
868 
869     bNativeOK = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire);
870     if ( bNativeOK )
871     {
872         TabitemValue tiValue(tools::Rectangle(pItem->maRect.Left() + TAB_ITEM_OFFSET_X,
873                                               pItem->maRect.Top() + TAB_ITEM_OFFSET_Y,
874                                               pItem->maRect.Right() - TAB_ITEM_OFFSET_X,
875                                               pItem->maRect.Bottom() - TAB_ITEM_OFFSET_Y),
876                              TabBarPosition::Top);
877         if (pItem->maRect.Left() < 5)
878             tiValue.mnAlignment |= TabitemFlags::LeftAligned;
879         if (pItem->maRect.Right() > mnLastWidth - 5)
880             tiValue.mnAlignment |= TabitemFlags::RightAligned;
881         if (bFirstInGroup)
882             tiValue.mnAlignment |= TabitemFlags::FirstInGroup;
883         if (bLastInGroup)
884             tiValue.mnAlignment |= TabitemFlags::LastInGroup;
885 
886         tools::Rectangle aCtrlRegion( pItem->maRect );
887         aCtrlRegion.AdjustBottom(TabPaneValue::m_nOverlap);
888         bNativeOK = rRenderContext.DrawNativeControl(ControlType::TabItem, ControlPart::Entire,
889                                                      aCtrlRegion, nState, tiValue, OUString() );
890     }
891 
892     if (!bNativeOK)
893     {
894         if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
895         {
896             rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
897             rRenderContext.DrawPixel(Point(aRect.Left() + 1 - nOff2, aRect.Top() + 1 - nOff2)); // diagonally indented top-left pixel
898             if (bLeftBorder)
899             {
900                 rRenderContext.DrawLine(Point(aRect.Left() - nOff2, aRect.Top() + 2 - nOff2),
901                                         Point(aRect.Left() - nOff2, nLeftBottom - 1));
902             }
903             rRenderContext.DrawLine(Point(aRect.Left() + 2 - nOff2, aRect.Top() - nOff2),   // top line starting 2px from left border
904                                     Point(aRect.Right() + nOff2 - 3, aRect.Top() - nOff2)); // ending 3px from right border
905 
906             if (bRightBorder)
907             {
908                 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
909                 rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 2, aRect.Top() + 1 - nOff2),
910                                         Point(aRect.Right() + nOff2 - 2, nRightBottom - 1));
911 
912                 rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
913                 rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 1, aRect.Top() + 3 - nOff2),
914                                         Point(aRect.Right() + nOff2 - 1, nRightBottom - 1));
915             }
916         }
917         else
918         {
919             rRenderContext.SetLineColor(COL_BLACK);
920             rRenderContext.DrawPixel(Point(aRect.Left() + 1 - nOff2, aRect.Top() + 1 - nOff2));
921             rRenderContext.DrawPixel(Point(aRect.Right() + nOff2 - 2, aRect.Top() + 1 - nOff2));
922             if (bLeftBorder)
923             {
924                 rRenderContext.DrawLine(Point(aRect.Left() - nOff2, aRect.Top() + 2 - nOff2),
925                                         Point(aRect.Left() - nOff2, nLeftBottom - 1));
926             }
927             rRenderContext.DrawLine(Point(aRect.Left() + 2 - nOff2, aRect.Top() - nOff2),
928                                     Point(aRect.Right() - 3, aRect.Top() - nOff2));
929             if (bRightBorder)
930             {
931                 rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 1, aRect.Top() + 2 - nOff2),
932                                         Point(aRect.Right() + nOff2 - 1, nRightBottom - 1));
933             }
934         }
935     }
936 
937     // set font accordingly, current item is painted bold
938     // we set the font attributes always before drawing to be re-entrant (DrawNativeControl may trigger additional paints)
939     vcl::Font aFont(rRenderContext.GetFont());
940     aFont.SetTransparent(true);
941     rRenderContext.SetFont(aFont);
942 
943     Size aTabSize = aRect.GetSize();
944     Size aImageSize(0, 0);
945     tools::Long nTextHeight = rRenderContext.GetTextHeight();
946     tools::Long nTextWidth = rRenderContext.GetCtrlTextWidth(pItem->maFormatText);
947     if (!!pItem->maTabImage)
948     {
949         aImageSize = pItem->maTabImage.GetSizePixel();
950         if (!pItem->maFormatText.isEmpty())
951             aImageSize.AdjustWidth(GetTextHeight() / 4 );
952     }
953     tools::Long nXPos = aRect.Left() + ((aTabSize.Width() - nTextWidth - aImageSize.Width()) / 2) - nOff - nOff3;
954     tools::Long nYPos = aRect.Top() + ((aTabSize.Height() - nTextHeight) / 2) - nOff3;
955     if (!pItem->maFormatText.isEmpty())
956     {
957         DrawTextFlags nStyle = DrawTextFlags::Mnemonic;
958         if (!pItem->m_bEnabled)
959             nStyle |= DrawTextFlags::Disable;
960 
961         Color aColor(rStyleSettings.GetTabTextColor());
962         if (nState & ControlState::SELECTED)
963             aColor = rStyleSettings.GetTabHighlightTextColor();
964         else if (nState & ControlState::ROLLOVER)
965             aColor = rStyleSettings.GetTabRolloverTextColor();
966 
967         Color aOldColor(rRenderContext.GetTextColor());
968         rRenderContext.SetTextColor(aColor);
969 
970         const tools::Rectangle aOutRect(nXPos + aImageSize.Width(), nYPos,
971                                  nXPos + aImageSize.Width() + nTextWidth, nYPos + nTextHeight);
972         DrawControlText(rRenderContext, aOutRect, pItem->maFormatText, nStyle,
973                         nullptr, nullptr);
974 
975         rRenderContext.SetTextColor(aOldColor);
976     }
977 
978     if (!!pItem->maTabImage)
979     {
980         Point aImgTL( nXPos, aRect.Top() );
981         if (aImageSize.Height() < aRect.GetHeight())
982             aImgTL.AdjustY((aRect.GetHeight() - aImageSize.Height()) / 2 );
983         rRenderContext.DrawImage(aImgTL, pItem->maTabImage, pItem->m_bEnabled ? DrawImageFlags::NONE : DrawImageFlags::Disable );
984     }
985 }
986 
ImplHandleKeyEvent(const KeyEvent & rKeyEvent)987 bool TabControl::ImplHandleKeyEvent( const KeyEvent& rKeyEvent )
988 {
989     bool bRet = false;
990 
991     if ( GetPageCount() > 1 )
992     {
993         vcl::KeyCode aKeyCode = rKeyEvent.GetKeyCode();
994         sal_uInt16 nKeyCode = aKeyCode.GetCode();
995 
996         if ( aKeyCode.IsMod1() )
997         {
998             if ( aKeyCode.IsShift() || (nKeyCode == KEY_PAGEUP) )
999             {
1000                 if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEUP) )
1001                 {
1002                     ImplActivateTabPage( false );
1003                     bRet = true;
1004                 }
1005             }
1006             else
1007             {
1008                 if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEDOWN) )
1009                 {
1010                     ImplActivateTabPage( true );
1011                     bRet = true;
1012                 }
1013             }
1014         }
1015     }
1016 
1017     return bRet;
1018 }
1019 
IMPL_LINK_NOARG(TabControl,ImplListBoxSelectHdl,ListBox &,void)1020 IMPL_LINK_NOARG(TabControl, ImplListBoxSelectHdl, ListBox&, void)
1021 {
1022     SelectTabPage( GetPageId( mpTabCtrlData->mpListBox->GetSelectedEntryPos() ) );
1023 }
1024 
IMPL_LINK(TabControl,ImplWindowEventListener,VclWindowEvent &,rEvent,void)1025 IMPL_LINK( TabControl, ImplWindowEventListener, VclWindowEvent&, rEvent, void )
1026 {
1027     if ( rEvent.GetId() == VclEventId::WindowKeyInput )
1028     {
1029         // Do not handle events from TabControl or its children, which is done in Notify(), where the events can be consumed.
1030         if ( !IsWindowOrChild( rEvent.GetWindow() ) )
1031         {
1032             KeyEvent* pKeyEvent = static_cast< KeyEvent* >(rEvent.GetData());
1033             ImplHandleKeyEvent( *pKeyEvent );
1034         }
1035     }
1036 }
1037 
MouseButtonDown(const MouseEvent & rMEvt)1038 void TabControl::MouseButtonDown( const MouseEvent& rMEvt )
1039 {
1040     if (mpTabCtrlData->mpListBox || !rMEvt.IsLeft())
1041         return;
1042 
1043     ImplTabItem *pItem = ImplGetItem(rMEvt.GetPosPixel());
1044     if (pItem && pItem->m_bEnabled)
1045         SelectTabPage(pItem->id());
1046 }
1047 
KeyInput(const KeyEvent & rKEvt)1048 void TabControl::KeyInput( const KeyEvent& rKEvt )
1049 {
1050     if( mpTabCtrlData->mpListBox )
1051         mpTabCtrlData->mpListBox->KeyInput( rKEvt );
1052     else if ( GetPageCount() > 1 )
1053     {
1054         vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1055         sal_uInt16  nKeyCode = aKeyCode.GetCode();
1056 
1057         if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_RIGHT) )
1058         {
1059             bool bNext = (nKeyCode == KEY_RIGHT);
1060             ImplActivateTabPage( bNext );
1061         }
1062     }
1063 
1064     Control::KeyInput( rKEvt );
1065 }
1066 
lcl_canPaint(const vcl::RenderContext & rRenderContext,const tools::Rectangle & rDrawRect,const tools::Rectangle & rItemRect)1067 static bool lcl_canPaint(const vcl::RenderContext& rRenderContext, const tools::Rectangle& rDrawRect,
1068                          const tools::Rectangle& rItemRect)
1069 {
1070     vcl::Region aClipRgn(rRenderContext.GetActiveClipRegion());
1071     aClipRgn.Intersect(rItemRect);
1072     if (!rDrawRect.IsEmpty())
1073         aClipRgn.Intersect(rDrawRect);
1074     return !aClipRgn.IsEmpty();
1075 }
1076 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle & rRect)1077 void TabControl::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
1078 {
1079     if (GetStyle() & WB_NOBORDER)
1080         return;
1081 
1082     Control::Paint(rRenderContext, rRect);
1083 
1084     HideFocus();
1085 
1086     // reformat if needed
1087     tools::Rectangle aRect = ImplGetTabRect(TAB_PAGERECT);
1088 
1089     // find current item
1090     ImplTabItem* pCurItem = nullptr;
1091     for (auto & item : mpTabCtrlData->maItemList)
1092     {
1093         if (item.id() == mnCurPageId)
1094         {
1095             pCurItem = &item;
1096             break;
1097         }
1098     }
1099 
1100     // Draw the TabPage border
1101     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1102     tools::Rectangle aCurRect;
1103     aRect.AdjustLeft( -(TAB_OFFSET) );
1104     aRect.AdjustTop( -(TAB_OFFSET) );
1105     aRect.AdjustRight(TAB_OFFSET );
1106     aRect.AdjustBottom(TAB_OFFSET );
1107 
1108     // if we have an invisible tabpage or no tabpage at all the tabpage rect should be
1109     // increased to avoid round corners that might be drawn by a theme
1110     // in this case we're only interested in the top border of the tabpage because the tabitems are used
1111     // standalone (eg impress)
1112     bool bNoTabPage = false;
1113     TabPage* pCurPage = pCurItem ? pCurItem->mpTabPage.get() : nullptr;
1114     if (!pCurPage || !pCurPage->IsVisible())
1115     {
1116         bNoTabPage = true;
1117         aRect.AdjustLeft( -10 );
1118         aRect.AdjustRight(10 );
1119     }
1120 
1121     if (rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire))
1122     {
1123         const bool bPaneWithHeader = mbShowTabs && rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::TabPaneWithHeader);
1124         tools::Rectangle aHeaderRect(aRect.Left(), 0, aRect.Right(), aRect.Top());
1125 
1126         if (!mpTabCtrlData->maItemList.empty())
1127         {
1128             tools::Long nLeft = LONG_MAX;
1129             tools::Long nRight = 0;
1130             for (const auto &item : mpTabCtrlData->maItemList)
1131             {
1132                 if (!item.m_bVisible)
1133                     continue;
1134                 nRight = std::max(nRight, item.maRect.Right());
1135                 nLeft = std::min(nLeft, item.maRect.Left());
1136             }
1137             aHeaderRect.SetLeft(nLeft);
1138             aHeaderRect.SetRight(nRight);
1139         }
1140 
1141         if (bPaneWithHeader)
1142             aRect.SetTop(0);
1143 
1144         const TabPaneValue aTabPaneValue(aHeaderRect, pCurItem ? pCurItem->maRect : tools::Rectangle());
1145 
1146         ControlState nState = ControlState::ENABLED;
1147         if (!IsEnabled())
1148             nState &= ~ControlState::ENABLED;
1149         if (HasFocus())
1150             nState |= ControlState::FOCUSED;
1151 
1152         if (lcl_canPaint(rRenderContext, rRect, aRect))
1153             rRenderContext.DrawNativeControl(ControlType::TabPane, ControlPart::Entire,
1154                                              aRect, nState, aTabPaneValue, OUString());
1155 
1156         if (!bPaneWithHeader && rRenderContext.IsNativeControlSupported(ControlType::TabHeader, ControlPart::Entire)
1157                 && lcl_canPaint(rRenderContext, rRect, aHeaderRect))
1158             rRenderContext.DrawNativeControl(ControlType::TabHeader, ControlPart::Entire,
1159                                              aHeaderRect, nState, aTabPaneValue, OUString());
1160     }
1161     else
1162     {
1163         tools::Long nTopOff = 1;
1164         if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
1165             rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
1166         else
1167             rRenderContext.SetLineColor(COL_BLACK);
1168         if (mbShowTabs && pCurItem && !pCurItem->maRect.IsEmpty())
1169         {
1170             aCurRect = pCurItem->maRect;
1171             rRenderContext.DrawLine(aRect.TopLeft(), Point(aCurRect.Left() - 2, aRect.Top()));
1172             if (aCurRect.Right() + 1 < aRect.Right())
1173             {
1174                 rRenderContext.DrawLine(Point(aCurRect.Right(), aRect.Top()), aRect.TopRight());
1175             }
1176             else
1177             {
1178                 nTopOff = 0;
1179             }
1180         }
1181         else
1182             rRenderContext.DrawLine(aRect.TopLeft(), aRect.TopRight());
1183 
1184         rRenderContext.DrawLine(aRect.TopLeft(), aRect.BottomLeft());
1185 
1186         if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
1187         {
1188             // if we have not tab page the bottom line of the tab page
1189             // directly touches the tab items, so choose a color that fits seamlessly
1190             if (bNoTabPage)
1191                 rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
1192             else
1193                 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
1194             rRenderContext.DrawLine(Point(1, aRect.Bottom() - 1), Point(aRect.Right() - 1, aRect.Bottom() - 1));
1195             rRenderContext.DrawLine(Point(aRect.Right() - 1, aRect.Top() + nTopOff), Point(aRect.Right() - 1, aRect.Bottom() - 1));
1196             if (bNoTabPage)
1197                 rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
1198             else
1199                 rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
1200             rRenderContext.DrawLine(Point(0, aRect.Bottom()), Point(aRect.Right(), aRect.Bottom()));
1201             rRenderContext.DrawLine(Point(aRect.Right(), aRect.Top() + nTopOff), Point(aRect.Right(), aRect.Bottom()));
1202         }
1203         else
1204         {
1205             rRenderContext.DrawLine(aRect.TopRight(), aRect.BottomRight());
1206             rRenderContext.DrawLine(aRect.BottomLeft(), aRect.BottomRight());
1207         }
1208     }
1209 
1210     const size_t nItemListSize = mpTabCtrlData->maItemList.size();
1211     if (mbShowTabs && nItemListSize > 0 && mpTabCtrlData->mpListBox == nullptr)
1212     {
1213         // Some native toolkits (GTK+) draw tabs right-to-left, with an
1214         // overlap between adjacent tabs
1215         bool bDrawTabsRTL = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::TabsDrawRtl);
1216         ImplTabItem* pFirstTab = nullptr;
1217         ImplTabItem* pLastTab = nullptr;
1218         size_t idx;
1219 
1220         // Even though there is a tab overlap with GTK+, the first tab is not
1221         // overlapped on the left side. Other toolkits ignore this option.
1222         if (bDrawTabsRTL)
1223         {
1224             pFirstTab = mpTabCtrlData->maItemList.data();
1225             pLastTab = pFirstTab + nItemListSize - 1;
1226             idx = nItemListSize - 1;
1227         }
1228         else
1229         {
1230             pLastTab = mpTabCtrlData->maItemList.data();
1231             pFirstTab = pLastTab + nItemListSize - 1;
1232             idx = 0;
1233         }
1234 
1235         while (idx < nItemListSize)
1236         {
1237             ImplTabItem* pItem = &mpTabCtrlData->maItemList[idx];
1238 
1239             if (pItem != pCurItem && pItem->m_bVisible && lcl_canPaint(rRenderContext, rRect, pItem->maRect))
1240                 ImplDrawItem(rRenderContext, pItem, aCurRect, pItem == pFirstTab, pItem == pLastTab);
1241 
1242             if (bDrawTabsRTL)
1243                 idx--;
1244             else
1245                 idx++;
1246         }
1247 
1248         if (pCurItem && lcl_canPaint(rRenderContext, rRect, pCurItem->maRect))
1249             ImplDrawItem(rRenderContext, pCurItem, aCurRect, pCurItem == pFirstTab, pCurItem == pLastTab);
1250     }
1251 
1252     if (HasFocus())
1253         ImplShowFocus();
1254 
1255     mbSmallInvalidate = true;
1256 }
1257 
setAllocation(const Size & rAllocation)1258 void TabControl::setAllocation(const Size &rAllocation)
1259 {
1260     if ( !IsReallyShown() )
1261         return;
1262 
1263     if( mpTabCtrlData->mpListBox )
1264     {
1265         // get the listbox' preferred size
1266         Size aTabCtrlSize( GetSizePixel() );
1267         tools::Long nPrefWidth = mpTabCtrlData->mpListBox->get_preferred_size().Width();
1268         if( nPrefWidth > aTabCtrlSize.Width() )
1269             nPrefWidth = aTabCtrlSize.Width();
1270         Size aNewSize( nPrefWidth, LogicToPixel( Size( 12, 12 ), MapMode( MapUnit::MapAppFont ) ).Height() );
1271         Point aNewPos( (aTabCtrlSize.Width() - nPrefWidth) / 2, 0 );
1272         mpTabCtrlData->mpListBox->SetPosSizePixel( aNewPos, aNewSize );
1273     }
1274 
1275     mbFormat = true;
1276 
1277     // resize/position active TabPage
1278     bool bTabPage = ImplPosCurTabPage();
1279 
1280     // check what needs to be invalidated
1281     Size aNewSize = rAllocation;
1282     tools::Long nNewWidth = aNewSize.Width();
1283     for (auto const& item : mpTabCtrlData->maItemList)
1284     {
1285         if (!item.m_bVisible)
1286             continue;
1287         if (!item.mbFullVisible || (item.maRect.Right()-2 >= nNewWidth))
1288         {
1289             mbSmallInvalidate = false;
1290             break;
1291         }
1292     }
1293 
1294     if ( mbSmallInvalidate )
1295     {
1296         tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
1297         aRect.AdjustLeft( -(TAB_OFFSET+TAB_BORDER_LEFT) );
1298         aRect.AdjustTop( -(TAB_OFFSET+TAB_BORDER_TOP) );
1299         aRect.AdjustRight(TAB_OFFSET+TAB_BORDER_RIGHT );
1300         aRect.AdjustBottom(TAB_OFFSET+TAB_BORDER_BOTTOM );
1301         if ( bTabPage )
1302             Invalidate( aRect, InvalidateFlags::NoChildren );
1303         else
1304             Invalidate( aRect );
1305 
1306     }
1307     else
1308     {
1309         if ( bTabPage )
1310             Invalidate( InvalidateFlags::NoChildren );
1311         else
1312             Invalidate();
1313     }
1314 
1315     mbLayoutDirty = false;
1316 }
1317 
SetPosSizePixel(const Point & rNewPos,const Size & rNewSize)1318 void TabControl::SetPosSizePixel(const Point& rNewPos, const Size& rNewSize)
1319 {
1320     Window::SetPosSizePixel(rNewPos, rNewSize);
1321     //if size changed, TabControl::Resize got called already
1322     if (mbLayoutDirty)
1323         setAllocation(rNewSize);
1324 }
1325 
SetSizePixel(const Size & rNewSize)1326 void TabControl::SetSizePixel(const Size& rNewSize)
1327 {
1328     Window::SetSizePixel(rNewSize);
1329     //if size changed, TabControl::Resize got called already
1330     if (mbLayoutDirty)
1331         setAllocation(rNewSize);
1332 }
1333 
SetPosPixel(const Point & rPos)1334 void TabControl::SetPosPixel(const Point& rPos)
1335 {
1336     Window::SetPosPixel(rPos);
1337     if (mbLayoutDirty)
1338         setAllocation(GetOutputSizePixel());
1339 }
1340 
Resize()1341 void TabControl::Resize()
1342 {
1343     setAllocation(Control::GetOutputSizePixel());
1344 }
1345 
GetFocus()1346 void TabControl::GetFocus()
1347 {
1348     if( ! mpTabCtrlData->mpListBox )
1349     {
1350         if (mbShowTabs)
1351         {
1352             ImplShowFocus();
1353             SetInputContext( InputContext( GetFont() ) );
1354         }
1355         else
1356         {
1357             // no tabs, focus first thing in current page
1358             ImplTabItem* pItem = ImplGetItem(GetCurPageId());
1359             if (pItem && pItem->mpTabPage)
1360             {
1361                 vcl::Window* pFirstChild = pItem->mpTabPage->ImplGetDlgWindow(0, GetDlgWindowType::First);
1362                 if ( pFirstChild )
1363                     pFirstChild->ImplControlFocus(GetFocusFlags::Init);
1364             }
1365         }
1366     }
1367     else
1368     {
1369         if( mpTabCtrlData->mpListBox->IsReallyVisible() )
1370             mpTabCtrlData->mpListBox->GrabFocus();
1371     }
1372 
1373     Control::GetFocus();
1374 }
1375 
LoseFocus()1376 void TabControl::LoseFocus()
1377 {
1378     if( mpTabCtrlData && ! mpTabCtrlData->mpListBox )
1379         HideFocus();
1380     Control::LoseFocus();
1381 }
1382 
RequestHelp(const HelpEvent & rHEvt)1383 void TabControl::RequestHelp( const HelpEvent& rHEvt )
1384 {
1385     sal_uInt16 nItemId = rHEvt.KeyboardActivated() ? mnCurPageId : GetPageId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
1386 
1387     if ( nItemId )
1388     {
1389         if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
1390         {
1391             OUString aStr = GetHelpText( nItemId );
1392             if ( !aStr.isEmpty() )
1393             {
1394                 tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1395                 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1396                 aItemRect.SetLeft( aPt.X() );
1397                 aItemRect.SetTop( aPt.Y() );
1398                 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1399                 aItemRect.SetRight( aPt.X() );
1400                 aItemRect.SetBottom( aPt.Y() );
1401                 Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
1402                 return;
1403             }
1404         }
1405 
1406         // for Quick or Ballon Help, we show the text, if it is cut
1407         if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
1408         {
1409             ImplTabItem* pItem = ImplGetItem( nItemId );
1410             const OUString& rStr = pItem->maText;
1411             if ( rStr != pItem->maFormatText )
1412             {
1413                 tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1414                 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1415                 aItemRect.SetLeft( aPt.X() );
1416                 aItemRect.SetTop( aPt.Y() );
1417                 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1418                 aItemRect.SetRight( aPt.X() );
1419                 aItemRect.SetBottom( aPt.Y() );
1420                 if ( !rStr.isEmpty() )
1421                 {
1422                     if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
1423                         Help::ShowBalloon( this, aItemRect.Center(), aItemRect, rStr );
1424                     else
1425                         Help::ShowQuickHelp( this, aItemRect, rStr );
1426                     return;
1427                 }
1428             }
1429         }
1430 
1431         if ( rHEvt.GetMode() & HelpEventMode::QUICK )
1432         {
1433             ImplTabItem* pItem = ImplGetItem( nItemId );
1434             const OUString& rHelpText = pItem->maHelpText;
1435             if (!rHelpText.isEmpty())
1436             {
1437                 tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1438                 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1439                 aItemRect.SetLeft( aPt.X() );
1440                 aItemRect.SetTop( aPt.Y() );
1441                 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1442                 aItemRect.SetRight( aPt.X() );
1443                 aItemRect.SetBottom( aPt.Y() );
1444                 Help::ShowQuickHelp( this, aItemRect, rHelpText );
1445                 return;
1446             }
1447         }
1448     }
1449 
1450     Control::RequestHelp( rHEvt );
1451 }
1452 
Command(const CommandEvent & rCEvt)1453 void TabControl::Command( const CommandEvent& rCEvt )
1454 {
1455     if( (mpTabCtrlData->mpListBox == nullptr) && (rCEvt.GetCommand() == CommandEventId::ContextMenu) && (GetPageCount() > 1) )
1456     {
1457         Point   aMenuPos;
1458         bool    bMenu;
1459         if ( rCEvt.IsMouseEvent() )
1460         {
1461             aMenuPos = rCEvt.GetMousePosPixel();
1462             bMenu = GetPageId( aMenuPos ) != 0;
1463         }
1464         else
1465         {
1466             aMenuPos = ImplGetTabRect( GetPagePos( mnCurPageId ) ).Center();
1467             bMenu = true;
1468         }
1469 
1470         if ( bMenu )
1471         {
1472             ScopedVclPtrInstance<PopupMenu> aMenu;
1473             for (auto const& item : mpTabCtrlData->maItemList)
1474             {
1475                 aMenu->InsertItem(item.id(), item.maText, MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK);
1476                 if (item.id() == mnCurPageId)
1477                     aMenu->CheckItem(item.id());
1478                 aMenu->SetHelpId(item.id(), {});
1479             }
1480 
1481             sal_uInt16 nId = aMenu->Execute( this, aMenuPos );
1482             if ( nId && (nId != mnCurPageId) )
1483                 SelectTabPage( nId );
1484             return;
1485         }
1486     }
1487 
1488     Control::Command( rCEvt );
1489 }
1490 
StateChanged(StateChangedType nType)1491 void TabControl::StateChanged( StateChangedType nType )
1492 {
1493     Control::StateChanged( nType );
1494 
1495     if ( nType == StateChangedType::InitShow )
1496     {
1497         ImplPosCurTabPage();
1498         if( mpTabCtrlData->mpListBox )
1499             Resize();
1500     }
1501     else if ( nType == StateChangedType::UpdateMode )
1502     {
1503         if ( IsUpdateMode() )
1504             Invalidate();
1505     }
1506     else if ( (nType == StateChangedType::Zoom)  ||
1507               (nType == StateChangedType::ControlFont) )
1508     {
1509         ImplInitSettings( false );
1510         Invalidate();
1511     }
1512     else if ( nType == StateChangedType::ControlForeground )
1513     {
1514         ImplInitSettings( false );
1515         Invalidate();
1516     }
1517     else if ( nType == StateChangedType::ControlBackground )
1518     {
1519         ImplInitSettings( true );
1520         Invalidate();
1521     }
1522 }
1523 
DataChanged(const DataChangedEvent & rDCEvt)1524 void TabControl::DataChanged( const DataChangedEvent& rDCEvt )
1525 {
1526     Control::DataChanged( rDCEvt );
1527 
1528     if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
1529          (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
1530          ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
1531           (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
1532     {
1533         ImplInitSettings( true );
1534         Invalidate();
1535     }
1536 }
1537 
ImplGetItem(const Point & rPt) const1538 ImplTabItem* TabControl::ImplGetItem(const Point& rPt) const
1539 {
1540     ImplTabItem* pFoundItem = nullptr;
1541     int nFound = 0;
1542     for (auto & item : mpTabCtrlData->maItemList)
1543     {
1544         if (item.m_bVisible && item.maRect.Contains(rPt))
1545         {
1546             nFound++;
1547             pFoundItem = &item;
1548         }
1549     }
1550 
1551     // assure that only one tab is highlighted at a time
1552     assert(nFound <= 1);
1553     return nFound == 1 ? pFoundItem : nullptr;
1554 }
1555 
PreNotify(NotifyEvent & rNEvt)1556 bool TabControl::PreNotify( NotifyEvent& rNEvt )
1557 {
1558     if( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
1559     {
1560         const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
1561         if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
1562         {
1563             // trigger redraw if mouse over state has changed
1564             if( IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire) )
1565             {
1566                 ImplTabItem *pItem = ImplGetItem(GetPointerPosPixel());
1567                 ImplTabItem *pLastItem = ImplGetItem(GetLastPointerPosPixel());
1568                 if ((pItem != pLastItem) || pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
1569                 {
1570                     vcl::Region aClipRgn;
1571                     if (pLastItem)
1572                     {
1573                         // allow for slightly bigger tabitems
1574                         // as used by gtk
1575                         // TODO: query for the correct sizes
1576                         tools::Rectangle aRect(pLastItem->maRect);
1577                         aRect.AdjustLeft( -2 );
1578                         aRect.AdjustRight(2 );
1579                         aRect.AdjustTop( -3 );
1580                         aClipRgn.Union( aRect );
1581                     }
1582 
1583                     if (pItem)
1584                     {
1585                         // allow for slightly bigger tabitems
1586                         // as used by gtk
1587                         // TODO: query for the correct sizes
1588                         tools::Rectangle aRect(pItem->maRect);
1589                         aRect.AdjustLeft( -2 );
1590                         aRect.AdjustRight(2 );
1591                         aRect.AdjustTop( -3 );
1592                         aClipRgn.Union( aRect );
1593                     }
1594 
1595                     if( !aClipRgn.IsEmpty() )
1596                         Invalidate( aClipRgn );
1597                 }
1598             }
1599         }
1600     }
1601 
1602     return Control::PreNotify(rNEvt);
1603 }
1604 
EventNotify(NotifyEvent & rNEvt)1605 bool TabControl::EventNotify( NotifyEvent& rNEvt )
1606 {
1607     bool bRet = false;
1608 
1609     if ( rNEvt.GetType() == NotifyEventType::KEYINPUT )
1610         bRet = ImplHandleKeyEvent( *rNEvt.GetKeyEvent() );
1611 
1612     return bRet || Control::EventNotify( rNEvt );
1613 }
1614 
ActivatePage()1615 void TabControl::ActivatePage()
1616 {
1617     maActivateHdl.Call( this );
1618 }
1619 
DeactivatePage()1620 bool TabControl::DeactivatePage()
1621 {
1622     return !maDeactivateHdl.IsSet() || maDeactivateHdl.Call( this );
1623 }
1624 
SetTabPageSizePixel(const Size & rSize)1625 void TabControl::SetTabPageSizePixel( const Size& rSize )
1626 {
1627     Size aNewSize( rSize );
1628     aNewSize.AdjustWidth(TAB_OFFSET*2 );
1629     tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT,
1630                                       aNewSize.Width(), aNewSize.Height() );
1631     aNewSize.AdjustHeight(aRect.Top()+TAB_OFFSET );
1632     Window::SetOutputSizePixel( aNewSize );
1633 }
1634 
InsertPage(sal_uInt16 nPageId,const OUString & rText,sal_uInt16 nPos)1635 void TabControl::InsertPage( sal_uInt16 nPageId, const OUString& rText,
1636                              sal_uInt16 nPos )
1637 {
1638     SAL_WARN_IF( !nPageId, "vcl", "TabControl::InsertPage(): PageId == 0" );
1639     SAL_WARN_IF( GetPagePos( nPageId ) != TAB_PAGE_NOTFOUND, "vcl",
1640                 "TabControl::InsertPage(): PageId already exists" );
1641 
1642     // insert new page item
1643     ImplTabItem* pItem = nullptr;
1644     if( nPos == TAB_APPEND || size_t(nPos) >= mpTabCtrlData->maItemList.size() )
1645     {
1646         mpTabCtrlData->maItemList.emplace_back(nPageId);
1647         pItem = &mpTabCtrlData->maItemList.back();
1648         if( mpTabCtrlData->mpListBox )
1649             mpTabCtrlData->mpListBox->InsertEntry( rText );
1650     }
1651     else
1652     {
1653         std::vector< ImplTabItem >::iterator new_it =
1654             mpTabCtrlData->maItemList.emplace(mpTabCtrlData->maItemList.begin() + nPos, nPageId);
1655         pItem = &(*new_it);
1656         if( mpTabCtrlData->mpListBox )
1657             mpTabCtrlData->mpListBox->InsertEntry( rText, nPos);
1658     }
1659     if( mpTabCtrlData->mpListBox )
1660     {
1661         if( ! mnCurPageId )
1662             mpTabCtrlData->mpListBox->SelectEntryPos( 0 );
1663         mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
1664     }
1665 
1666     // set current page id
1667     if ( !mnCurPageId )
1668         mnCurPageId = nPageId;
1669 
1670     // init new page item
1671     pItem->maText           = rText;
1672     pItem->mbFullVisible    = false;
1673 
1674     mbFormat = true;
1675     if ( IsUpdateMode() )
1676         Invalidate();
1677 
1678     if( mpTabCtrlData->mpListBox ) // reposition/resize listbox
1679         Resize();
1680 
1681     CallEventListeners( VclEventId::TabpageInserted, reinterpret_cast<void*>(nPageId) );
1682 }
1683 
RemovePage(sal_uInt16 nPageId)1684 void TabControl::RemovePage( sal_uInt16 nPageId )
1685 {
1686     sal_uInt16 nPos = GetPagePos( nPageId );
1687 
1688     // does the item exist ?
1689     if ( nPos == TAB_PAGE_NOTFOUND )
1690         return;
1691 
1692     //remove page item
1693     std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin() + nPos;
1694     bool bIsCurrentPage = (it->id() == mnCurPageId);
1695     mpTabCtrlData->maItemList.erase( it );
1696     if( mpTabCtrlData->mpListBox )
1697     {
1698         mpTabCtrlData->mpListBox->RemoveEntry( nPos );
1699         mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
1700     }
1701 
1702     // If current page is removed, then first page gets the current page
1703     if ( bIsCurrentPage  )
1704     {
1705         mnCurPageId = 0;
1706 
1707         if( ! mpTabCtrlData->maItemList.empty() )
1708         {
1709             // don't do this by simply setting mnCurPageId to pFirstItem->id()
1710             // this leaves a lot of stuff (such trivia as _showing_ the new current page) undone
1711             // instead, call SetCurPageId
1712             // without this, the next (outside) call to SetCurPageId with the id of the first page
1713             // will result in doing nothing (as we assume that nothing changed, then), and the page
1714             // will never be shown.
1715             // 86875 - 05/11/2001 - frank.schoenheit@germany.sun.com
1716 
1717             SetCurPageId(mpTabCtrlData->maItemList[0].id());
1718         }
1719     }
1720 
1721     mbFormat = true;
1722     if ( IsUpdateMode() )
1723         Invalidate();
1724 
1725     CallEventListeners( VclEventId::TabpageRemoved, reinterpret_cast<void*>(nPageId) );
1726 }
1727 
SetPageEnabled(sal_uInt16 i_nPageId,bool i_bEnable)1728 void TabControl::SetPageEnabled( sal_uInt16 i_nPageId, bool i_bEnable )
1729 {
1730     ImplTabItem* pItem = ImplGetItem( i_nPageId );
1731 
1732     if (!pItem || pItem->m_bEnabled == i_bEnable)
1733         return;
1734 
1735     pItem->m_bEnabled = i_bEnable;
1736     if (!pItem->m_bVisible)
1737         return;
1738 
1739     mbFormat = true;
1740     if( mpTabCtrlData->mpListBox )
1741         mpTabCtrlData->mpListBox->SetEntryFlags( GetPagePos( i_nPageId ),
1742                                                  i_bEnable ? ListBoxEntryFlags::NONE : (ListBoxEntryFlags::DisableSelection | ListBoxEntryFlags::DrawDisabled) );
1743 
1744     // SetCurPageId will change to a valid page
1745     if (pItem->id() == mnCurPageId)
1746         SetCurPageId( mnCurPageId );
1747     else if ( IsUpdateMode() )
1748         Invalidate();
1749 }
1750 
SetPageVisible(sal_uInt16 nPageId,bool bVisible)1751 void TabControl::SetPageVisible( sal_uInt16 nPageId, bool bVisible )
1752 {
1753     ImplTabItem* pItem = ImplGetItem( nPageId );
1754     if (!pItem || pItem->m_bVisible == bVisible)
1755         return;
1756 
1757     pItem->m_bVisible = bVisible;
1758     if (!bVisible)
1759     {
1760         if (pItem->mbFullVisible)
1761             mbSmallInvalidate = false;
1762         pItem->mbFullVisible = false;
1763         pItem->maRect.SetEmpty();
1764     }
1765     mbFormat = true;
1766 
1767     // SetCurPageId will change to a valid page
1768     if (pItem->id() == mnCurPageId)
1769         SetCurPageId(mnCurPageId);
1770     else if (IsUpdateMode())
1771         Invalidate();
1772 }
1773 
GetPageCount() const1774 sal_uInt16 TabControl::GetPageCount() const
1775 {
1776     return static_cast<sal_uInt16>(mpTabCtrlData->maItemList.size());
1777 }
1778 
GetPageId(sal_uInt16 nPos) const1779 sal_uInt16 TabControl::GetPageId( sal_uInt16 nPos ) const
1780 {
1781     if( size_t(nPos) < mpTabCtrlData->maItemList.size() )
1782         return mpTabCtrlData->maItemList[nPos].id();
1783     return 0;
1784 }
1785 
GetPagePos(sal_uInt16 nPageId) const1786 sal_uInt16 TabControl::GetPagePos( sal_uInt16 nPageId ) const
1787 {
1788     sal_uInt16 nPos = 0;
1789     for (auto const& item : mpTabCtrlData->maItemList)
1790     {
1791         if (item.id() == nPageId)
1792             return nPos;
1793         ++nPos;
1794     }
1795 
1796     return TAB_PAGE_NOTFOUND;
1797 }
1798 
GetPageId(const Point & rPos) const1799 sal_uInt16 TabControl::GetPageId( const Point& rPos ) const
1800 {
1801     Size winSize = Control::GetOutputSizePixel();
1802     const auto &rList = mpTabCtrlData->maItemList;
1803     const auto it = std::find_if(rList.begin(), rList.end(), [&rPos, &winSize, this](const auto &item) {
1804         return const_cast<TabControl*>(this)->ImplGetTabRect(&item, winSize.Width(), winSize.Height()).Contains(rPos); });
1805     return (it != rList.end()) ? it->id() : 0;
1806 }
1807 
GetPageId(const OUString & rName) const1808 sal_uInt16 TabControl::GetPageId( const OUString& rName ) const
1809 {
1810     const auto &rList = mpTabCtrlData->maItemList;
1811     const auto it = std::find_if(rList.begin(), rList.end(), [&rName](const auto &item) {
1812         return item.maTabName == rName; });
1813     return (it != rList.end()) ? it->id() : 0;
1814 }
1815 
SetCurPageId(sal_uInt16 nPageId)1816 void TabControl::SetCurPageId( sal_uInt16 nPageId )
1817 {
1818     sal_uInt16 nPos = GetPagePos( nPageId );
1819     while (nPos != TAB_PAGE_NOTFOUND && !mpTabCtrlData->maItemList[nPos].m_bEnabled)
1820     {
1821         nPos++;
1822         if( size_t(nPos) >= mpTabCtrlData->maItemList.size() )
1823             nPos = 0;
1824         if (mpTabCtrlData->maItemList[nPos].id() == nPageId)
1825             break;
1826     }
1827 
1828     if( nPos == TAB_PAGE_NOTFOUND )
1829         return;
1830 
1831     nPageId = mpTabCtrlData->maItemList[nPos].id();
1832     if ( nPageId == mnCurPageId )
1833     {
1834         if ( mnActPageId )
1835             mnActPageId = nPageId;
1836         return;
1837     }
1838 
1839     if ( mnActPageId )
1840         mnActPageId = nPageId;
1841     else
1842     {
1843         mbFormat = true;
1844         sal_uInt16 nOldId = mnCurPageId;
1845         mnCurPageId = nPageId;
1846         ImplChangeTabPage( nPageId, nOldId );
1847     }
1848 }
1849 
GetCurPageId() const1850 sal_uInt16 TabControl::GetCurPageId() const
1851 {
1852     if ( mnActPageId )
1853         return mnActPageId;
1854     else
1855         return mnCurPageId;
1856 }
1857 
SelectTabPage(sal_uInt16 nPageId)1858 void TabControl::SelectTabPage( sal_uInt16 nPageId )
1859 {
1860     if ( !nPageId || (nPageId == mnCurPageId) )
1861         return;
1862 
1863     CallEventListeners( VclEventId::TabpageDeactivate, reinterpret_cast<void*>(mnCurPageId) );
1864     if ( DeactivatePage() )
1865     {
1866         mnActPageId = nPageId;
1867         ActivatePage();
1868         // Page could have been switched by the Activate handler
1869         nPageId = mnActPageId;
1870         mnActPageId = 0;
1871         SetCurPageId( nPageId );
1872         if( mpTabCtrlData->mpListBox )
1873             mpTabCtrlData->mpListBox->SelectEntryPos( GetPagePos( nPageId ) );
1874         CallEventListeners( VclEventId::TabpageActivate, reinterpret_cast<void*>(nPageId) );
1875     }
1876 }
1877 
SetTabPage(sal_uInt16 nPageId,TabPage * pTabPage)1878 void TabControl::SetTabPage( sal_uInt16 nPageId, TabPage* pTabPage )
1879 {
1880     ImplTabItem* pItem = ImplGetItem( nPageId );
1881 
1882     if ( !pItem || (pItem->mpTabPage.get() == pTabPage) )
1883         return;
1884 
1885     if ( pTabPage )
1886     {
1887         if ( IsDefaultSize() )
1888             SetTabPageSizePixel( pTabPage->GetSizePixel() );
1889 
1890         // only set here, so that Resize does not reposition TabPage
1891         pItem->mpTabPage = pTabPage;
1892         queue_resize();
1893 
1894         if (pItem->id() == mnCurPageId)
1895             ImplChangeTabPage(pItem->id(), 0);
1896     }
1897     else
1898     {
1899         pItem->mpTabPage = nullptr;
1900         queue_resize();
1901     }
1902 }
1903 
GetTabPage(sal_uInt16 nPageId) const1904 TabPage* TabControl::GetTabPage( sal_uInt16 nPageId ) const
1905 {
1906     ImplTabItem* pItem = ImplGetItem( nPageId );
1907 
1908     if ( pItem )
1909         return pItem->mpTabPage;
1910     else
1911         return nullptr;
1912 }
1913 
SetPageText(sal_uInt16 nPageId,const OUString & rText)1914 void TabControl::SetPageText( sal_uInt16 nPageId, const OUString& rText )
1915 {
1916     ImplTabItem* pItem = ImplGetItem( nPageId );
1917 
1918     if ( !pItem || pItem->maText == rText )
1919         return;
1920 
1921     pItem->maText = rText;
1922     mbFormat = true;
1923     if( mpTabCtrlData->mpListBox )
1924     {
1925         sal_uInt16 nPos = GetPagePos( nPageId );
1926         mpTabCtrlData->mpListBox->RemoveEntry( nPos );
1927         mpTabCtrlData->mpListBox->InsertEntry( rText, nPos );
1928     }
1929     if ( IsUpdateMode() )
1930         Invalidate();
1931     CallEventListeners( VclEventId::TabpagePageTextChanged, reinterpret_cast<void*>(nPageId) );
1932 }
1933 
GetPageText(sal_uInt16 nPageId) const1934 OUString const & TabControl::GetPageText( sal_uInt16 nPageId ) const
1935 {
1936     ImplTabItem* pItem = ImplGetItem( nPageId );
1937 
1938     assert( pItem );
1939 
1940     return pItem->maText;
1941 }
1942 
SetHelpText(sal_uInt16 nPageId,const OUString & rText)1943 void TabControl::SetHelpText( sal_uInt16 nPageId, const OUString& rText )
1944 {
1945     ImplTabItem* pItem = ImplGetItem( nPageId );
1946 
1947     assert( pItem );
1948 
1949     pItem->maHelpText = rText;
1950 }
1951 
GetHelpText(sal_uInt16 nPageId) const1952 const OUString& TabControl::GetHelpText( sal_uInt16 nPageId ) const
1953 {
1954     ImplTabItem* pItem = ImplGetItem( nPageId );
1955     assert( pItem );
1956     return pItem->maHelpText;
1957 }
1958 
SetAccessibleName(sal_uInt16 nPageId,const OUString & rName)1959 void TabControl::SetAccessibleName(sal_uInt16 nPageId, const OUString& rName)
1960 {
1961     ImplTabItem* pItem = ImplGetItem( nPageId );
1962     assert( pItem );
1963     pItem->maAccessibleName = rName;
1964 }
1965 
GetAccessibleName(sal_uInt16 nPageId) const1966 OUString TabControl::GetAccessibleName( sal_uInt16 nPageId ) const
1967 {
1968     ImplTabItem* pItem = ImplGetItem( nPageId );
1969     assert( pItem );
1970     if (!pItem->maAccessibleName.isEmpty())
1971         return pItem->maAccessibleName;
1972     return removeMnemonicFromString(pItem->maText);
1973 }
1974 
SetAccessibleDescription(sal_uInt16 nPageId,const OUString & rDesc)1975 void TabControl::SetAccessibleDescription(sal_uInt16 nPageId, const OUString& rDesc)
1976 {
1977     ImplTabItem* pItem = ImplGetItem( nPageId );
1978     assert( pItem );
1979     pItem->maAccessibleDescription = rDesc;
1980 }
1981 
GetAccessibleDescription(sal_uInt16 nPageId) const1982 OUString TabControl::GetAccessibleDescription( sal_uInt16 nPageId ) const
1983 {
1984     ImplTabItem* pItem = ImplGetItem( nPageId );
1985     assert( pItem );
1986     if (!pItem->maAccessibleDescription.isEmpty())
1987         return pItem->maAccessibleDescription;
1988     return pItem->maHelpText;
1989 }
1990 
SetPageName(sal_uInt16 nPageId,const OUString & rName) const1991 void TabControl::SetPageName( sal_uInt16 nPageId, const OUString& rName ) const
1992 {
1993     ImplTabItem* pItem = ImplGetItem( nPageId );
1994 
1995     if ( pItem )
1996         pItem->maTabName = rName;
1997 }
1998 
GetPageName(sal_uInt16 nPageId) const1999 OUString TabControl::GetPageName( sal_uInt16 nPageId ) const
2000 {
2001     ImplTabItem* pItem = ImplGetItem( nPageId );
2002 
2003     if (pItem)
2004         return pItem->maTabName;
2005 
2006     return {};
2007 }
2008 
SetPageImage(sal_uInt16 i_nPageId,const Image & i_rImage)2009 void TabControl::SetPageImage( sal_uInt16 i_nPageId, const Image& i_rImage )
2010 {
2011     ImplTabItem* pItem = ImplGetItem( i_nPageId );
2012 
2013     if ( pItem )
2014     {
2015         pItem->maTabImage = i_rImage;
2016         mbFormat = true;
2017         if ( IsUpdateMode() )
2018             Invalidate();
2019     }
2020 }
2021 
GetTabBounds(sal_uInt16 nPageId) const2022 tools::Rectangle TabControl::GetTabBounds( sal_uInt16 nPageId ) const
2023 {
2024     tools::Rectangle aRet;
2025 
2026     ImplTabItem* pItem = ImplGetItem( nPageId );
2027     if (pItem && pItem->m_bVisible)
2028         aRet = pItem->maRect;
2029 
2030     return aRet;
2031 }
2032 
ImplCalculateRequisition(sal_uInt16 & nHeaderHeight) const2033 Size TabControl::ImplCalculateRequisition(sal_uInt16& nHeaderHeight) const
2034 {
2035     Size aOptimalPageSize(0, 0);
2036 
2037     sal_uInt16 nOrigPageId = GetCurPageId();
2038     for (auto const& item : mpTabCtrlData->maItemList)
2039     {
2040         const TabPage *pPage = item.mpTabPage;
2041         //it's a real nuisance if the page is not inserted yet :-(
2042         //We need to force all tabs to exist to get overall optimal size for dialog
2043         if (!pPage)
2044         {
2045             TabControl *pThis = const_cast<TabControl*>(this);
2046             pThis->SetCurPageId(item.id());
2047             pThis->ActivatePage();
2048             pPage = item.mpTabPage;
2049         }
2050 
2051         if (!pPage)
2052             continue;
2053 
2054         Size aPageSize(VclContainer::getLayoutRequisition(*pPage));
2055 
2056         if (aPageSize.Width() > aOptimalPageSize.Width())
2057             aOptimalPageSize.setWidth( aPageSize.Width() );
2058         if (aPageSize.Height() > aOptimalPageSize.Height())
2059             aOptimalPageSize.setHeight( aPageSize.Height() );
2060     }
2061 
2062     //fdo#61940 If we were forced to activate pages in order to on-demand
2063     //create them to get their optimal size, then switch back to the original
2064     //page and re-activate it
2065     if (nOrigPageId != GetCurPageId())
2066     {
2067         TabControl *pThis = const_cast<TabControl*>(this);
2068         pThis->SetCurPageId(nOrigPageId);
2069         pThis->ActivatePage();
2070     }
2071 
2072     tools::Long nTabLabelsBottom = 0, nTabLabelsRight = 0;
2073     if (mbShowTabs)
2074     {
2075         for (sal_uInt16 nPos(0), sizeList(static_cast <sal_uInt16> (mpTabCtrlData->maItemList.size()));
2076                 nPos < sizeList; ++nPos)
2077         {
2078             TabControl* pThis = const_cast<TabControl*>(this);
2079 
2080             tools::Rectangle aTabRect = pThis->ImplGetTabRect(nPos, aOptimalPageSize.Width(), LONG_MAX);
2081             if (aTabRect.Bottom() > nTabLabelsBottom)
2082             {
2083                 nTabLabelsBottom = aTabRect.Bottom();
2084                 nHeaderHeight = nTabLabelsBottom;
2085             }
2086             if (!aTabRect.IsEmpty() && aTabRect.Right() > nTabLabelsRight)
2087                 nTabLabelsRight = aTabRect.Right();
2088         }
2089     }
2090 
2091     Size aOptimalSize(aOptimalPageSize);
2092     aOptimalSize.AdjustHeight(nTabLabelsBottom );
2093     aOptimalSize.setWidth( std::max(nTabLabelsRight, aOptimalSize.Width()) );
2094 
2095     aOptimalSize.AdjustWidth(TAB_OFFSET * 2 );
2096     aOptimalSize.AdjustHeight(TAB_OFFSET * 2 );
2097 
2098     return aOptimalSize;
2099 }
2100 
calculateRequisition() const2101 Size TabControl::calculateRequisition() const
2102 {
2103     sal_uInt16 nHeaderHeight;
2104     return ImplCalculateRequisition(nHeaderHeight);
2105 }
2106 
GetOptimalSize() const2107 Size TabControl::GetOptimalSize() const
2108 {
2109     return calculateRequisition();
2110 }
2111 
queue_resize(StateChangedType eReason)2112 void TabControl::queue_resize(StateChangedType eReason)
2113 {
2114     mbLayoutDirty = true;
2115     Window::queue_resize(eReason);
2116 }
2117 
GetPageIDs() const2118 std::vector<sal_uInt16> TabControl::GetPageIDs() const
2119 {
2120     std::vector<sal_uInt16> aIDs;
2121     for (auto const& item : mpTabCtrlData->maItemList)
2122     {
2123         aIDs.push_back(item.id());
2124     }
2125 
2126     return aIDs;
2127 }
2128 
set_property(const OUString & rKey,const OUString & rValue)2129 bool TabControl::set_property(const OUString &rKey, const OUString &rValue)
2130 {
2131     if (rKey == "show-tabs")
2132     {
2133         mbShowTabs = toBool(rValue);
2134         queue_resize();
2135     }
2136     else
2137         return Control::set_property(rKey, rValue);
2138     return true;
2139 }
2140 
GetUITestFactory() const2141 FactoryFunction TabControl::GetUITestFactory() const
2142 {
2143     return TabControlUIObject::create;
2144 }
2145 
DumpAsPropertyTree(tools::JsonWriter & rJsonWriter)2146 void TabControl::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
2147 {
2148     rJsonWriter.put("id", get_id());
2149     rJsonWriter.put("type", "tabcontrol");
2150     rJsonWriter.put("selected", GetCurPageId());
2151 
2152     {
2153         auto childrenNode = rJsonWriter.startArray("children");
2154         for (auto id : GetPageIDs())
2155         {
2156             TabPage* pChild = GetTabPage(id);
2157 
2158             if (pChild)
2159             {
2160                 auto childNode = rJsonWriter.startStruct();
2161                 pChild->DumpAsPropertyTree(rJsonWriter);
2162 
2163                 if (!pChild->IsVisible())
2164                     rJsonWriter.put("hidden", true);
2165             }
2166         }
2167     }
2168     {
2169         auto tabsNode = rJsonWriter.startArray("tabs");
2170         for(auto id : GetPageIDs())
2171         {
2172             auto tabNode = rJsonWriter.startStruct();
2173             rJsonWriter.put("text", GetPageText(id));
2174             rJsonWriter.put("id", id);
2175             rJsonWriter.put("name", GetPageName(id));
2176         }
2177     }
2178 }
2179 
2180 sal_uInt16 NotebookbarTabControlBase::m_nHeaderHeight = 0;
2181 
IMPL_LINK_NOARG(NotebookbarTabControlBase,OpenMenu,Button *,void)2182 IMPL_LINK_NOARG(NotebookbarTabControlBase, OpenMenu, Button*, void)
2183 {
2184     m_aIconClickHdl.Call(static_cast<NotebookBar*>(GetParent()->GetParent()));
2185 }
2186 
NotebookbarTabControlBase(vcl::Window * pParent)2187 NotebookbarTabControlBase::NotebookbarTabControlBase(vcl::Window* pParent)
2188     : TabControl(pParent, WB_STDTABCONTROL)
2189     , bLastContextWasSupported(true)
2190     , eLastContext(vcl::EnumContext::Context::Any)
2191 {
2192     m_pOpenMenu = VclPtr<PushButton>::Create( this , WB_CENTER | WB_VCENTER );
2193     m_pOpenMenu->SetClickHdl(LINK(this, NotebookbarTabControlBase, OpenMenu));
2194     m_pOpenMenu->SetModeImage(Image(StockImage::Yes, SV_RESID_BITMAP_NOTEBOOKBAR));
2195     m_pOpenMenu->SetSizePixel(m_pOpenMenu->GetOptimalSize());
2196     m_pOpenMenu->Show();
2197 }
2198 
~NotebookbarTabControlBase()2199 NotebookbarTabControlBase::~NotebookbarTabControlBase()
2200 {
2201     disposeOnce();
2202 }
2203 
SetContext(vcl::EnumContext::Context eContext)2204 void NotebookbarTabControlBase::SetContext( vcl::EnumContext::Context eContext )
2205 {
2206     if (eLastContext == eContext)
2207         return;
2208 
2209     bool bHandled = false;
2210 
2211     TabPage* pPage = GetTabPage(mnCurPageId);
2212     // Try to stay on the current tab (unless the new context has a special tab)
2213     if (pPage && eLastContext != vcl::EnumContext::Context::Any
2214         && pPage->HasContext(vcl::EnumContext::Context::Any) && pPage->IsEnabled())
2215     {
2216         bHandled = true;
2217     }
2218 
2219     for (int nChild = 0; nChild < GetPageCount(); ++nChild)
2220     {
2221         sal_uInt16 nPageId = TabControl::GetPageId(nChild);
2222         pPage = GetTabPage(nPageId);
2223 
2224         if (!pPage)
2225             continue;
2226 
2227         SetPageVisible(nPageId, pPage->HasContext(eContext) || pPage->HasContext(vcl::EnumContext::Context::Any));
2228 
2229         if (eContext != vcl::EnumContext::Context::Any
2230             && (!bHandled || !pPage->HasContext(vcl::EnumContext::Context::Any))
2231             && pPage->HasContext(eContext))
2232         {
2233             SetCurPageId(nPageId);
2234             bHandled = true;
2235             bLastContextWasSupported = true;
2236         }
2237 
2238         if (!bHandled && bLastContextWasSupported
2239             && pPage->HasContext(vcl::EnumContext::Context::Default))
2240         {
2241             SetCurPageId(nPageId);
2242         }
2243     }
2244 
2245     if (!bHandled)
2246         bLastContextWasSupported = false;
2247     eLastContext = eContext;
2248 
2249     // tdf#152908 Tabbed compact toolbar does not repaint itself when tabs getting removed
2250     // For unknown reason this is needed by the tabbed compact toolbar for other than gtk
2251     // vcl backends.
2252     Resize();
2253 }
2254 
dispose()2255 void NotebookbarTabControlBase::dispose()
2256 {
2257     m_pShortcuts.disposeAndClear();
2258     m_pOpenMenu.disposeAndClear();
2259     TabControl::dispose();
2260 }
2261 
SetToolBox(ToolBox * pToolBox)2262 void NotebookbarTabControlBase::SetToolBox( ToolBox* pToolBox )
2263 {
2264     m_pShortcuts.reset( pToolBox );
2265 }
2266 
SetIconClickHdl(Link<NotebookBar *,void> aHdl)2267 void NotebookbarTabControlBase::SetIconClickHdl( Link<NotebookBar*, void> aHdl )
2268 {
2269     m_aIconClickHdl = aHdl;
2270 }
2271 
lcl_isValidPage(const ImplTabItem & rItem)2272 static bool lcl_isValidPage(const ImplTabItem& rItem)
2273 {
2274     return rItem.m_bVisible && rItem.m_bEnabled;
2275 }
2276 
ImplActivateTabPage(bool bNext)2277 void NotebookbarTabControlBase::ImplActivateTabPage( bool bNext )
2278 {
2279     sal_Int32 nCurPos = GetPagePos(GetCurPageId());
2280 
2281     if (bNext)
2282     {
2283         for (sal_Int32 nPos = nCurPos + 1; nPos < GetPageCount(); nPos++)
2284             if (lcl_isValidPage(mpTabCtrlData->maItemList[nPos]))
2285             {
2286                 nCurPos = nPos;
2287                 break;
2288             }
2289     }
2290     else
2291     {
2292         for (sal_Int32 nPos = nCurPos - 1; nPos >= 0; nPos--)
2293             if (lcl_isValidPage(mpTabCtrlData->maItemList[nPos]))
2294             {
2295                 nCurPos = nPos;
2296                 break;
2297             }
2298     }
2299 
2300     SelectTabPage( TabControl::GetPageId( nCurPos ) );
2301 }
2302 
ImplPlaceTabs(tools::Long nWidth)2303 bool NotebookbarTabControlBase::ImplPlaceTabs( tools::Long nWidth )
2304 {
2305     if ( nWidth <= 0 )
2306         return false;
2307     if ( mpTabCtrlData->maItemList.empty() )
2308         return false;
2309     if (!m_pOpenMenu || m_pOpenMenu->isDisposed())
2310         return false;
2311 
2312     const tools::Long nHamburgerWidth = m_pOpenMenu->GetSizePixel().Width();
2313     tools::Long nMaxWidth = nWidth - nHamburgerWidth;
2314     tools::Long nShortcutsWidth = m_pShortcuts != nullptr ? m_pShortcuts->GetSizePixel().getWidth() + 1 : 0;
2315     tools::Long nFullWidth = nShortcutsWidth;
2316 
2317     const tools::Long nOffsetX = 2 + nShortcutsWidth;
2318     const tools::Long nOffsetY = 2;
2319 
2320     //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
2321     //of ugly bare tabs on lines of their own
2322 
2323     for (auto & item : mpTabCtrlData->maItemList)
2324     {
2325         tools::Long nTabWidth = 0;
2326         if (item.m_bVisible)
2327         {
2328             nTabWidth = ImplGetItemSize(&item, nMaxWidth).getWidth();
2329             if (!item.maText.isEmpty() && nTabWidth < 100)
2330                 nTabWidth = 100;
2331         }
2332         nFullWidth += nTabWidth;
2333     }
2334 
2335     tools::Long nX = nOffsetX;
2336     tools::Long nY = nOffsetY;
2337 
2338     tools::Long nLineWidthAry[100];
2339     nLineWidthAry[0] = 0;
2340 
2341     for (auto & item : mpTabCtrlData->maItemList)
2342     {
2343         if (!item.m_bVisible)
2344             continue;
2345 
2346         Size aSize = ImplGetItemSize( &item, nMaxWidth );
2347 
2348         // set minimum tab size
2349         if( nFullWidth < nMaxWidth && !item.maText.isEmpty() && aSize.getWidth() < 100)
2350             aSize.setWidth( 100 );
2351 
2352         if( !item.maText.isEmpty() && aSize.getHeight() < 28 )
2353             aSize.setHeight( 28 );
2354 
2355         tools::Rectangle aNewRect( Point( nX, nY ), aSize );
2356         if ( mbSmallInvalidate && (item.maRect != aNewRect) )
2357             mbSmallInvalidate = false;
2358 
2359         item.maRect = aNewRect;
2360         item.mnLine = 0;
2361         item.mbFullVisible = true;
2362 
2363         nLineWidthAry[0] += aSize.Width();
2364         nX += aSize.Width();
2365     }
2366 
2367     // we always have only one line of tabs
2368     // tdf#127610 subtract width of shortcuts from width available for tab items
2369     lcl_AdjustSingleLineTabs(nMaxWidth - nShortcutsWidth, mpTabCtrlData.get());
2370 
2371     // position the shortcutbox
2372     if (m_pShortcuts)
2373     {
2374         tools::Long nPosY = (m_nHeaderHeight - m_pShortcuts->GetSizePixel().getHeight()) / 2;
2375         m_pShortcuts->SetPosPixel(Point(0, nPosY));
2376     }
2377 
2378     tools::Long nPosY = (m_nHeaderHeight - m_pOpenMenu->GetSizePixel().getHeight()) / 2;
2379     // position the menu
2380     m_pOpenMenu->SetPosPixel(Point(nWidth - nHamburgerWidth, nPosY));
2381 
2382     return true;
2383 }
2384 
calculateRequisition() const2385 Size NotebookbarTabControlBase::calculateRequisition() const
2386 {
2387     return TabControl::ImplCalculateRequisition(m_nHeaderHeight);
2388 }
2389 
GetOpenMenu()2390 Control* NotebookbarTabControlBase::GetOpenMenu()
2391 {
2392     return m_pOpenMenu;
2393 }
2394 
2395 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2396