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