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