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