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