59cba4298d
Use the Notebookbar menu button as parent for its menu. This also makes the button position be used as the reference. This allows to drop the now unused NotebookbarTabControlBase::GetHeaderHeight. Also, set the reference point to be at the bottom of the button, so the menu opens below it instead of approximately in the middle, which matches how other menu buttons behave. To test: Set "Tabbed" for the UI variant in "View" -> "User Interface", then click the "Hamburger menu" button in the notebookbar. Change-Id: I0717fad73ff7a42d2bdaaa53a73d055234003d3c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176927 Reviewed-by: Michael Weghorn <m.weghorn@posteo.de> Tested-by: Jenkins
2401 lines
75 KiB
C++
2401 lines
75 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <sal/config.h>
|
|
#include <sal/log.hxx>
|
|
|
|
#include <vcl/notebookbar/notebookbar.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/help.hxx>
|
|
#include <vcl/event.hxx>
|
|
#include <vcl/menu.hxx>
|
|
#include <vcl/toolkit/button.hxx>
|
|
#include <vcl/tabpage.hxx>
|
|
#include <vcl/tabctrl.hxx>
|
|
#include <vcl/toolbox.hxx>
|
|
#include <vcl/layout.hxx>
|
|
#include <vcl/mnemonic.hxx>
|
|
#include <vcl/toolkit/lstbox.hxx>
|
|
#include <vcl/settings.hxx>
|
|
#include <vcl/uitest/uiobject.hxx>
|
|
#include <bitmaps.hlst>
|
|
#include <tools/json_writer.hxx>
|
|
|
|
#include <svdata.hxx>
|
|
#include <window.h>
|
|
|
|
#include <deque>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#define TAB_OFFSET 3
|
|
/// Space to the left and right of the tabitem
|
|
#define TAB_ITEM_OFFSET_X 10
|
|
/// Space to the top and bottom of the tabitem
|
|
#define TAB_ITEM_OFFSET_Y 3
|
|
#define TAB_EXTRASPACE_X 6
|
|
#define TAB_BORDER_LEFT 1
|
|
#define TAB_BORDER_TOP 1
|
|
#define TAB_BORDER_RIGHT 2
|
|
#define TAB_BORDER_BOTTOM 2
|
|
|
|
class ImplTabItem final
|
|
{
|
|
sal_uInt16 m_nId;
|
|
|
|
public:
|
|
VclPtr<TabPage> mpTabPage;
|
|
OUString maText;
|
|
OUString maFormatText;
|
|
OUString maHelpText;
|
|
OUString maAccessibleName;
|
|
OUString maAccessibleDescription;
|
|
OUString maTabName;
|
|
tools::Rectangle maRect;
|
|
sal_uInt16 mnLine;
|
|
bool mbFullVisible;
|
|
bool m_bEnabled; ///< the tab / page is selectable
|
|
bool m_bVisible; ///< the tab / page can be visible
|
|
Image maTabImage;
|
|
|
|
ImplTabItem(sal_uInt16 nId);
|
|
|
|
sal_uInt16 id() const { return m_nId; }
|
|
};
|
|
|
|
ImplTabItem::ImplTabItem(sal_uInt16 nId)
|
|
: m_nId(nId)
|
|
, mnLine(0)
|
|
, mbFullVisible(false)
|
|
, m_bEnabled(true)
|
|
, m_bVisible(true)
|
|
{
|
|
}
|
|
|
|
struct ImplTabCtrlData
|
|
{
|
|
std::vector< ImplTabItem > maItemList;
|
|
VclPtr<ListBox> mpListBox;
|
|
};
|
|
|
|
// for the Tab positions
|
|
#define TAB_PAGERECT 0xFFFF
|
|
|
|
void TabControl::ImplInit( vcl::Window* pParent, WinBits nStyle )
|
|
{
|
|
mbLayoutDirty = true;
|
|
|
|
if ( !(nStyle & WB_NOTABSTOP) )
|
|
nStyle |= WB_TABSTOP;
|
|
if ( !(nStyle & WB_NOGROUP) )
|
|
nStyle |= WB_GROUP;
|
|
if ( !(nStyle & WB_NODIALOGCONTROL) )
|
|
nStyle |= WB_DIALOGCONTROL;
|
|
|
|
Control::ImplInit( pParent, nStyle, nullptr );
|
|
|
|
mnLastWidth = 0;
|
|
mnLastHeight = 0;
|
|
mnActPageId = 0;
|
|
mnCurPageId = 0;
|
|
mbFormat = true;
|
|
mbShowTabs = true;
|
|
mbRestoreHelpId = false;
|
|
mbSmallInvalidate = false;
|
|
mpTabCtrlData.reset(new ImplTabCtrlData);
|
|
mpTabCtrlData->mpListBox = nullptr;
|
|
|
|
ImplInitSettings( true );
|
|
|
|
if( nStyle & WB_DROPDOWN )
|
|
{
|
|
mpTabCtrlData->mpListBox = VclPtr<ListBox>::Create( this, WB_DROPDOWN );
|
|
mpTabCtrlData->mpListBox->SetPosSizePixel( Point( 0, 0 ), Size( 200, 20 ) );
|
|
mpTabCtrlData->mpListBox->SetSelectHdl( LINK( this, TabControl, ImplListBoxSelectHdl ) );
|
|
mpTabCtrlData->mpListBox->Show();
|
|
}
|
|
|
|
// if the tabcontrol is drawn (ie filled) by a native widget, make sure all controls will have transparent background
|
|
// otherwise they will paint with a wrong background
|
|
if( IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire) )
|
|
EnableChildTransparentMode();
|
|
|
|
if (pParent && pParent->IsDialog())
|
|
pParent->AddChildEventListener( LINK( this, TabControl, ImplWindowEventListener ) );
|
|
}
|
|
|
|
const vcl::Font& TabControl::GetCanonicalFont( const StyleSettings& _rStyle ) const
|
|
{
|
|
return _rStyle.GetTabFont();
|
|
}
|
|
|
|
const Color& TabControl::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
|
|
{
|
|
return _rStyle.GetTabTextColor();
|
|
}
|
|
|
|
void TabControl::ImplInitSettings( bool bBackground )
|
|
{
|
|
Control::ImplInitSettings();
|
|
|
|
if ( !bBackground )
|
|
return;
|
|
|
|
vcl::Window* pParent = GetParent();
|
|
if ( !IsControlBackground() &&
|
|
(pParent->IsChildTransparentModeEnabled()
|
|
|| IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire)
|
|
|| IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire) ) )
|
|
|
|
{
|
|
// set transparent mode for NWF tabcontrols to have
|
|
// the background always cleared properly
|
|
EnableChildTransparentMode();
|
|
SetParentClipMode( ParentClipMode::NoClip );
|
|
SetPaintTransparent( true );
|
|
SetBackground();
|
|
ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
|
|
}
|
|
else
|
|
{
|
|
EnableChildTransparentMode( false );
|
|
SetParentClipMode();
|
|
SetPaintTransparent( false );
|
|
|
|
if ( IsControlBackground() )
|
|
SetBackground( GetControlBackground() );
|
|
else
|
|
SetBackground( pParent->GetBackground() );
|
|
}
|
|
}
|
|
|
|
TabControl::TabControl( vcl::Window* pParent, WinBits nStyle ) :
|
|
Control( WindowType::TABCONTROL )
|
|
{
|
|
ImplInit( pParent, nStyle );
|
|
SAL_INFO( "vcl", "*** TABCONTROL no notabs? " << (( GetStyle() & WB_NOBORDER ) ? "true" : "false") );
|
|
}
|
|
|
|
TabControl::~TabControl()
|
|
{
|
|
disposeOnce();
|
|
}
|
|
|
|
void TabControl::dispose()
|
|
{
|
|
Window *pParent = GetParent();
|
|
if (pParent && pParent->IsDialog())
|
|
GetParent()->RemoveChildEventListener( LINK( this, TabControl, ImplWindowEventListener ) );
|
|
|
|
// delete TabCtrl data
|
|
if (mpTabCtrlData)
|
|
mpTabCtrlData->mpListBox.disposeAndClear();
|
|
mpTabCtrlData.reset();
|
|
Control::dispose();
|
|
}
|
|
|
|
ImplTabItem* TabControl::ImplGetItem( sal_uInt16 nId ) const
|
|
{
|
|
for (auto & item : mpTabCtrlData->maItemList)
|
|
{
|
|
if (item.id() == nId)
|
|
return &item;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Size TabControl::ImplGetItemSize( ImplTabItem* pItem, tools::Long nMaxWidth )
|
|
{
|
|
pItem->maFormatText = pItem->maText;
|
|
Size aSize( GetOutDev()->GetCtrlTextWidth( pItem->maFormatText ), GetTextHeight() );
|
|
Size aImageSize( 0, 0 );
|
|
if( !!pItem->maTabImage )
|
|
{
|
|
aImageSize = pItem->maTabImage.GetSizePixel();
|
|
if( !pItem->maFormatText.isEmpty() )
|
|
aImageSize.AdjustWidth(GetTextHeight()/4 );
|
|
}
|
|
aSize.AdjustWidth(aImageSize.Width() );
|
|
if( aImageSize.Height() > aSize.Height() )
|
|
aSize.setHeight( aImageSize.Height() );
|
|
|
|
aSize.AdjustWidth(TAB_ITEM_OFFSET_X*2 );
|
|
aSize.AdjustHeight(TAB_ITEM_OFFSET_Y*2 );
|
|
|
|
tools::Rectangle aCtrlRegion( Point( 0, 0 ), aSize );
|
|
tools::Rectangle aBoundingRgn, aContentRgn;
|
|
const TabitemValue aControlValue(tools::Rectangle(TAB_ITEM_OFFSET_X, TAB_ITEM_OFFSET_Y,
|
|
aSize.Width() - TAB_ITEM_OFFSET_X * 2,
|
|
aSize.Height() - TAB_ITEM_OFFSET_Y * 2),
|
|
TabBarPosition::Top);
|
|
if(GetNativeControlRegion( ControlType::TabItem, ControlPart::Entire, aCtrlRegion,
|
|
ControlState::ENABLED, aControlValue,
|
|
aBoundingRgn, aContentRgn ) )
|
|
{
|
|
return aContentRgn.GetSize();
|
|
}
|
|
|
|
// For languages with short names (e.g. Chinese), because the space is
|
|
// normally only one pixel per char
|
|
if ( pItem->maFormatText.getLength() < TAB_EXTRASPACE_X )
|
|
aSize.AdjustWidth(TAB_EXTRASPACE_X-pItem->maFormatText.getLength() );
|
|
|
|
// shorten Text if needed
|
|
if ( aSize.Width()+4 >= nMaxWidth )
|
|
{
|
|
OUString aAppendStr(u"..."_ustr);
|
|
pItem->maFormatText += aAppendStr;
|
|
do
|
|
{
|
|
if (pItem->maFormatText.getLength() > aAppendStr.getLength())
|
|
pItem->maFormatText = pItem->maFormatText.replaceAt( pItem->maFormatText.getLength()-aAppendStr.getLength()-1, 1, u"" );
|
|
aSize.setWidth( GetOutDev()->GetCtrlTextWidth( pItem->maFormatText ) );
|
|
aSize.AdjustWidth(aImageSize.Width() );
|
|
aSize.AdjustWidth(TAB_ITEM_OFFSET_X*2 );
|
|
}
|
|
while ( (aSize.Width()+4 >= nMaxWidth) && (pItem->maFormatText.getLength() > aAppendStr.getLength()) );
|
|
if ( aSize.Width()+4 >= nMaxWidth )
|
|
{
|
|
pItem->maFormatText = ".";
|
|
aSize.setWidth( 1 );
|
|
}
|
|
}
|
|
|
|
if( pItem->maFormatText.isEmpty() )
|
|
{
|
|
if( aSize.Height() < aImageSize.Height()+4 ) //leave space for focus rect
|
|
aSize.setHeight( aImageSize.Height()+4 );
|
|
}
|
|
|
|
return aSize;
|
|
}
|
|
|
|
// Feel free to move this to some more general place for reuse
|
|
// http://en.wikipedia.org/wiki/Word_wrap#Minimum_raggedness
|
|
// Mostly based on Alexey Frunze's nifty example at
|
|
// http://stackoverflow.com/questions/9071205/balanced-word-wrap-minimum-raggedness-in-php
|
|
namespace MinimumRaggednessWrap
|
|
{
|
|
static std::deque<size_t> GetEndOfLineIndexes(const std::vector<sal_Int32>& rWidthsOf, sal_Int32 nLineWidth)
|
|
{
|
|
++nLineWidth;
|
|
|
|
size_t nWidthsCount = rWidthsOf.size();
|
|
std::vector<sal_Int32> aCosts(nWidthsCount * nWidthsCount);
|
|
|
|
// cost function c(i, j) that computes the cost of a line consisting of
|
|
// the words Word[i] to Word[j]
|
|
for (size_t i = 0; i < nWidthsCount; ++i)
|
|
{
|
|
for (size_t j = 0; j < nWidthsCount; ++j)
|
|
{
|
|
if (j >= i)
|
|
{
|
|
sal_Int32 c = nLineWidth - (j - i);
|
|
for (size_t k = i; k <= j; ++k)
|
|
c -= rWidthsOf[k];
|
|
c = (c >= 0) ? c * c : SAL_MAX_INT32;
|
|
aCosts[j * nWidthsCount + i] = c;
|
|
}
|
|
else
|
|
{
|
|
aCosts[j * nWidthsCount + i] = SAL_MAX_INT32;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<sal_Int32> aFunction(nWidthsCount);
|
|
std::vector<sal_Int32> aWrapPoints(nWidthsCount);
|
|
|
|
// f(j) in aFunction[], collect wrap points in aWrapPoints[]
|
|
for (size_t j = 0; j < nWidthsCount; ++j)
|
|
{
|
|
aFunction[j] = aCosts[j * nWidthsCount];
|
|
if (aFunction[j] == SAL_MAX_INT32)
|
|
{
|
|
for (size_t k = 0; k < j; ++k)
|
|
{
|
|
sal_Int32 s;
|
|
if (aFunction[k] == SAL_MAX_INT32 || aCosts[j * nWidthsCount + k + 1] == SAL_MAX_INT32)
|
|
s = SAL_MAX_INT32;
|
|
else
|
|
s = aFunction[k] + aCosts[j * nWidthsCount + k + 1];
|
|
if (aFunction[j] > s)
|
|
{
|
|
aFunction[j] = s;
|
|
aWrapPoints[j] = k + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::deque<size_t> aSolution;
|
|
|
|
// no solution
|
|
if (aFunction[nWidthsCount - 1] == SAL_MAX_INT32)
|
|
return aSolution;
|
|
|
|
// optimal solution
|
|
size_t j = nWidthsCount - 1;
|
|
while (true)
|
|
{
|
|
aSolution.push_front(j);
|
|
if (!aWrapPoints[j])
|
|
break;
|
|
j = aWrapPoints[j] - 1;
|
|
}
|
|
|
|
return aSolution;
|
|
}
|
|
};
|
|
|
|
static void lcl_AdjustSingleLineTabs(tools::Long nMaxWidth, ImplTabCtrlData *pTabCtrlData)
|
|
{
|
|
if (!ImplGetSVData()->maNWFData.mbCenteredTabs)
|
|
return;
|
|
|
|
int nRightSpace = nMaxWidth; // space left on the right by the tabs
|
|
for (auto const& item : pTabCtrlData->maItemList)
|
|
{
|
|
if (!item.m_bVisible)
|
|
continue;
|
|
nRightSpace -= item.maRect.GetWidth();
|
|
}
|
|
nRightSpace /= 2;
|
|
|
|
for (auto& item : pTabCtrlData->maItemList)
|
|
{
|
|
if (!item.m_bVisible)
|
|
continue;
|
|
item.maRect.AdjustLeft(nRightSpace);
|
|
item.maRect.AdjustRight(nRightSpace);
|
|
}
|
|
}
|
|
|
|
bool TabControl::ImplPlaceTabs( tools::Long nWidth )
|
|
{
|
|
if ( nWidth <= 0 )
|
|
return false;
|
|
if ( mpTabCtrlData->maItemList.empty() )
|
|
return false;
|
|
|
|
tools::Long nMaxWidth = nWidth;
|
|
|
|
const tools::Long nOffsetX = 2;
|
|
const tools::Long nOffsetY = 2;
|
|
|
|
//fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
|
|
//of ugly bare tabs on lines of their own
|
|
|
|
//collect widths
|
|
std::vector<sal_Int32> aWidths;
|
|
for (auto & item : mpTabCtrlData->maItemList)
|
|
{
|
|
if (!item.m_bVisible)
|
|
continue;
|
|
aWidths.push_back(ImplGetItemSize(&item, nMaxWidth).Width());
|
|
}
|
|
|
|
//aBreakIndexes will contain the indexes of the last tab on each row
|
|
std::deque<size_t> aBreakIndexes(MinimumRaggednessWrap::GetEndOfLineIndexes(aWidths, nMaxWidth - nOffsetX - 2));
|
|
|
|
tools::Long nX = nOffsetX;
|
|
tools::Long nY = nOffsetY;
|
|
|
|
sal_uInt16 nLines = 0;
|
|
sal_uInt16 nCurLine = 0;
|
|
|
|
tools::Long nLineWidthAry[100];
|
|
sal_uInt16 nLinePosAry[101];
|
|
nLineWidthAry[0] = 0;
|
|
nLinePosAry[0] = 0;
|
|
|
|
size_t nIndex = 0;
|
|
|
|
for (auto & item : mpTabCtrlData->maItemList)
|
|
{
|
|
if (!item.m_bVisible)
|
|
continue;
|
|
|
|
Size aSize = ImplGetItemSize( &item, nMaxWidth );
|
|
|
|
bool bNewLine = false;
|
|
if (!aBreakIndexes.empty() && nIndex > aBreakIndexes.front())
|
|
{
|
|
aBreakIndexes.pop_front();
|
|
bNewLine = true;
|
|
}
|
|
|
|
if ( bNewLine && (nWidth > 2+nOffsetX) )
|
|
{
|
|
if ( nLines == 99 )
|
|
break;
|
|
|
|
nX = nOffsetX;
|
|
nY += aSize.Height();
|
|
nLines++;
|
|
nLineWidthAry[nLines] = 0;
|
|
nLinePosAry[nLines] = nIndex;
|
|
}
|
|
|
|
tools::Rectangle aNewRect( Point( nX, nY ), aSize );
|
|
if ( mbSmallInvalidate && (item.maRect != aNewRect) )
|
|
mbSmallInvalidate = false;
|
|
item.maRect = aNewRect;
|
|
item.mnLine = nLines;
|
|
item.mbFullVisible = true;
|
|
|
|
nLineWidthAry[nLines] += aSize.Width();
|
|
nX += aSize.Width();
|
|
|
|
if (item.id() == mnCurPageId)
|
|
nCurLine = nLines;
|
|
|
|
++nIndex;
|
|
}
|
|
|
|
if (nLines) // two or more lines
|
|
{
|
|
tools::Long nLineHeightAry[100];
|
|
tools::Long nIH = 0;
|
|
for (const auto& item : mpTabCtrlData->maItemList)
|
|
{
|
|
if (!item.m_bVisible)
|
|
continue;
|
|
nIH = item.maRect.Bottom() - 1;
|
|
break;
|
|
}
|
|
|
|
for ( sal_uInt16 i = 0; i < nLines+1; i++ )
|
|
{
|
|
if ( i <= nCurLine )
|
|
nLineHeightAry[i] = nIH*(nLines-(nCurLine-i));
|
|
else
|
|
nLineHeightAry[i] = nIH*(i-nCurLine-1);
|
|
}
|
|
|
|
nLinePosAry[nLines+1] = static_cast<sal_uInt16>(mpTabCtrlData->maItemList.size());
|
|
|
|
tools::Long nDX = 0;
|
|
tools::Long nModDX = 0;
|
|
tools::Long nIDX = 0;
|
|
|
|
sal_uInt16 i = 0;
|
|
sal_uInt16 n = 0;
|
|
|
|
for (auto & item : mpTabCtrlData->maItemList)
|
|
{
|
|
if (!item.m_bVisible)
|
|
continue;
|
|
|
|
if ( i == nLinePosAry[n] )
|
|
{
|
|
if ( n == nLines+1 )
|
|
break;
|
|
|
|
nIDX = 0;
|
|
if( nLinePosAry[n+1]-i > 0 )
|
|
{
|
|
nDX = ( nWidth - nOffsetX - nLineWidthAry[n] ) / ( nLinePosAry[n+1] - i );
|
|
nModDX = ( nWidth - nOffsetX - nLineWidthAry[n] ) % ( nLinePosAry[n+1] - i );
|
|
}
|
|
else
|
|
{
|
|
// FIXME: this is a case of tabctrl way too small
|
|
nDX = 0;
|
|
nModDX = 0;
|
|
}
|
|
n++;
|
|
}
|
|
|
|
item.maRect.AdjustLeft(nIDX );
|
|
item.maRect.AdjustRight(nIDX + nDX );
|
|
item.maRect.SetTop( nLineHeightAry[n-1] );
|
|
item.maRect.SetBottom(nLineHeightAry[n-1] + nIH - 1);
|
|
nIDX += nDX;
|
|
|
|
if ( nModDX )
|
|
{
|
|
nIDX++;
|
|
item.maRect.AdjustRight( 1 );
|
|
nModDX--;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
else // only one line
|
|
lcl_AdjustSingleLineTabs(nMaxWidth, mpTabCtrlData.get());
|
|
|
|
return true;
|
|
}
|
|
|
|
tools::Rectangle TabControl::ImplGetTabRect( sal_uInt16 nItemPos, tools::Long nWidth, tools::Long nHeight )
|
|
{
|
|
Size aWinSize = Control::GetOutputSizePixel();
|
|
if ( nWidth < 0 )
|
|
nWidth = aWinSize.Width();
|
|
if ( nHeight < 0 )
|
|
nHeight = aWinSize.Height();
|
|
|
|
if ( mpTabCtrlData->maItemList.empty() )
|
|
{
|
|
tools::Long nW = nWidth-TAB_OFFSET*2;
|
|
tools::Long nH = nHeight-TAB_OFFSET*2;
|
|
return (nW > 0 && nH > 0)
|
|
? tools::Rectangle(Point(TAB_OFFSET, TAB_OFFSET), Size(nW, nH))
|
|
: tools::Rectangle();
|
|
}
|
|
|
|
if ( nItemPos == TAB_PAGERECT )
|
|
{
|
|
sal_uInt16 nLastPos;
|
|
if ( mnCurPageId )
|
|
nLastPos = GetPagePos( mnCurPageId );
|
|
else
|
|
nLastPos = 0;
|
|
|
|
tools::Rectangle aRect = ImplGetTabRect( nLastPos, nWidth, nHeight );
|
|
if (aRect.IsEmpty())
|
|
return aRect;
|
|
|
|
// with show-tabs of true (the usual) the page rect is from under the
|
|
// visible tab to the bottom of the TabControl, otherwise it extends
|
|
// from the top of the TabControl
|
|
tools::Long nTabBottom = mbShowTabs ? aRect.Bottom() : 0;
|
|
|
|
tools::Long nW = nWidth-TAB_OFFSET*2;
|
|
tools::Long nH = nHeight - nTabBottom - TAB_OFFSET*2;
|
|
return (nW > 0 && nH > 0)
|
|
? tools::Rectangle( Point( TAB_OFFSET, nTabBottom + TAB_OFFSET ), Size( nW, nH ) )
|
|
: tools::Rectangle();
|
|
}
|
|
|
|
ImplTabItem* const pItem = (nItemPos < mpTabCtrlData->maItemList.size())
|
|
? &mpTabCtrlData->maItemList[nItemPos] : nullptr;
|
|
return ImplGetTabRect(pItem, nWidth, nHeight);
|
|
}
|
|
|
|
tools::Rectangle TabControl::ImplGetTabRect(const ImplTabItem* pItem, tools::Long nWidth, tools::Long nHeight)
|
|
{
|
|
if ((nWidth <= 1) || (nHeight <= 0) || !pItem || !pItem->m_bVisible)
|
|
return tools::Rectangle();
|
|
|
|
nWidth -= 1;
|
|
|
|
if ( mbFormat || (mnLastWidth != nWidth) || (mnLastHeight != nHeight) )
|
|
{
|
|
vcl::Font aFont( GetFont() );
|
|
aFont.SetTransparent( true );
|
|
SetFont( aFont );
|
|
|
|
bool bRet = ImplPlaceTabs( nWidth );
|
|
if ( !bRet )
|
|
return tools::Rectangle();
|
|
|
|
mnLastWidth = nWidth;
|
|
mnLastHeight = nHeight;
|
|
mbFormat = false;
|
|
}
|
|
|
|
return pItem->maRect;
|
|
}
|
|
|
|
void TabControl::ImplChangeTabPage( sal_uInt16 nId, sal_uInt16 nOldId )
|
|
{
|
|
ImplTabItem* pOldItem = ImplGetItem( nOldId );
|
|
ImplTabItem* pItem = ImplGetItem( nId );
|
|
TabPage* pOldPage = pOldItem ? pOldItem->mpTabPage.get() : nullptr;
|
|
TabPage* pPage = pItem ? pItem->mpTabPage.get() : nullptr;
|
|
vcl::Window* pCtrlParent = GetParent();
|
|
|
|
if ( IsReallyVisible() && IsUpdateMode() )
|
|
{
|
|
sal_uInt16 nPos = GetPagePos( nId );
|
|
tools::Rectangle aRect = ImplGetTabRect( nPos );
|
|
|
|
if ( !pOldItem || !pItem || (pOldItem->mnLine != pItem->mnLine) )
|
|
{
|
|
aRect.SetLeft( 0 );
|
|
aRect.SetTop( 0 );
|
|
aRect.SetRight( Control::GetOutputSizePixel().Width() );
|
|
}
|
|
else
|
|
{
|
|
aRect.AdjustLeft( -3 );
|
|
aRect.AdjustTop( -2 );
|
|
aRect.AdjustRight(3 );
|
|
Invalidate( aRect );
|
|
nPos = GetPagePos( nOldId );
|
|
aRect = ImplGetTabRect( nPos );
|
|
aRect.AdjustLeft( -3 );
|
|
aRect.AdjustTop( -2 );
|
|
aRect.AdjustRight(3 );
|
|
}
|
|
Invalidate( aRect );
|
|
}
|
|
|
|
if ( pOldPage == pPage )
|
|
return;
|
|
|
|
tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
|
|
|
|
if ( pOldPage )
|
|
{
|
|
if ( mbRestoreHelpId )
|
|
pCtrlParent->SetHelpId({});
|
|
}
|
|
|
|
if ( pPage )
|
|
{
|
|
if ( GetStyle() & WB_NOBORDER )
|
|
{
|
|
tools::Rectangle aRectNoTab(Point(0, 0), GetSizePixel());
|
|
pPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
|
|
}
|
|
else
|
|
pPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
|
|
|
|
// activate page here so the controls can be switched
|
|
// also set the help id of the parent window to that of the tab page
|
|
if ( GetHelpId().isEmpty() )
|
|
{
|
|
mbRestoreHelpId = true;
|
|
pCtrlParent->SetHelpId( pPage->GetHelpId() );
|
|
}
|
|
|
|
pPage->Show();
|
|
|
|
if ( pOldPage && pOldPage->HasChildPathFocus() )
|
|
{
|
|
vcl::Window* pFirstChild = pPage->ImplGetDlgWindow( 0, GetDlgWindowType::First );
|
|
if ( pFirstChild )
|
|
pFirstChild->ImplControlFocus( GetFocusFlags::Init );
|
|
else
|
|
GrabFocus();
|
|
}
|
|
}
|
|
|
|
if ( pOldPage )
|
|
pOldPage->Hide();
|
|
|
|
// Invalidate the same region that will be send to NWF
|
|
// to always allow for bitmap caching
|
|
// see Window::DrawNativeControl()
|
|
if( IsNativeControlSupported( ControlType::TabPane, ControlPart::Entire ) )
|
|
{
|
|
aRect.AdjustLeft( -(TAB_OFFSET) );
|
|
aRect.AdjustTop( -(TAB_OFFSET) );
|
|
aRect.AdjustRight(TAB_OFFSET );
|
|
aRect.AdjustBottom(TAB_OFFSET );
|
|
}
|
|
|
|
Invalidate( aRect );
|
|
}
|
|
|
|
bool TabControl::ImplPosCurTabPage()
|
|
{
|
|
// resize/position current TabPage
|
|
ImplTabItem* pItem = ImplGetItem( GetCurPageId() );
|
|
if ( pItem && pItem->mpTabPage )
|
|
{
|
|
if ( GetStyle() & WB_NOBORDER )
|
|
{
|
|
tools::Rectangle aRectNoTab(Point(0, 0), GetSizePixel());
|
|
pItem->mpTabPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
|
|
return true;
|
|
}
|
|
tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
|
|
pItem->mpTabPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void TabControl::ImplActivateTabPage( bool bNext )
|
|
{
|
|
sal_uInt16 nCurPos = GetPagePos( GetCurPageId() );
|
|
|
|
if ( bNext )
|
|
nCurPos = (nCurPos + 1) % GetPageCount();
|
|
else
|
|
{
|
|
if ( !nCurPos )
|
|
nCurPos = GetPageCount()-1;
|
|
else
|
|
nCurPos--;
|
|
}
|
|
|
|
SelectTabPage( GetPageId( nCurPos ) );
|
|
}
|
|
|
|
void TabControl::ImplShowFocus()
|
|
{
|
|
if ( !GetPageCount() || mpTabCtrlData->mpListBox )
|
|
return;
|
|
|
|
sal_uInt16 nCurPos = GetPagePos( mnCurPageId );
|
|
tools::Rectangle aRect = ImplGetTabRect( nCurPos );
|
|
const ImplTabItem& rItem = mpTabCtrlData->maItemList[ nCurPos ];
|
|
Size aTabSize = aRect.GetSize();
|
|
Size aImageSize( 0, 0 );
|
|
tools::Long nTextHeight = GetTextHeight();
|
|
tools::Long nTextWidth = GetOutDev()->GetCtrlTextWidth( rItem.maFormatText );
|
|
sal_uInt16 nOff;
|
|
|
|
if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::Mono) )
|
|
nOff = 1;
|
|
else
|
|
nOff = 0;
|
|
|
|
if( !! rItem.maTabImage )
|
|
{
|
|
aImageSize = rItem.maTabImage.GetSizePixel();
|
|
if( !rItem.maFormatText.isEmpty() )
|
|
aImageSize.AdjustWidth(GetTextHeight()/4 );
|
|
}
|
|
|
|
if( !rItem.maFormatText.isEmpty() )
|
|
{
|
|
// show focus around text
|
|
aRect.SetLeft( aRect.Left()+aImageSize.Width()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1-1 );
|
|
aRect.SetTop( aRect.Top()+((aTabSize.Height()-nTextHeight)/2)-1-1 );
|
|
aRect.SetRight( aRect.Left()+nTextWidth+2 );
|
|
aRect.SetBottom( aRect.Top()+nTextHeight+2 );
|
|
}
|
|
else
|
|
{
|
|
// show focus around image
|
|
tools::Long nXPos = aRect.Left()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1;
|
|
tools::Long nYPos = aRect.Top();
|
|
if( aImageSize.Height() < aRect.GetHeight() )
|
|
nYPos += (aRect.GetHeight() - aImageSize.Height())/2;
|
|
|
|
aRect.SetLeft( nXPos - 2 );
|
|
aRect.SetTop( nYPos - 2 );
|
|
aRect.SetRight( aRect.Left() + aImageSize.Width() + 4 );
|
|
aRect.SetBottom( aRect.Top() + aImageSize.Height() + 4 );
|
|
}
|
|
ShowFocus( aRect );
|
|
}
|
|
|
|
void TabControl::ImplDrawItem(vcl::RenderContext& rRenderContext, ImplTabItem const * pItem, const tools::Rectangle& rCurRect,
|
|
bool bFirstInGroup, bool bLastInGroup )
|
|
{
|
|
if (!pItem->m_bVisible || pItem->maRect.IsEmpty())
|
|
return;
|
|
|
|
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
tools::Rectangle aRect = pItem->maRect;
|
|
tools::Long nLeftBottom = aRect.Bottom();
|
|
tools::Long nRightBottom = aRect.Bottom();
|
|
bool bLeftBorder = true;
|
|
bool bRightBorder = true;
|
|
sal_uInt16 nOff;
|
|
bool bNativeOK = false;
|
|
|
|
sal_uInt16 nOff2 = 0;
|
|
sal_uInt16 nOff3 = 0;
|
|
|
|
if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
|
|
nOff = 1;
|
|
else
|
|
nOff = 0;
|
|
|
|
// if this is the active Page, we have to draw a little more
|
|
if (pItem->id() == mnCurPageId)
|
|
{
|
|
nOff2 = 2;
|
|
if (!ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise)
|
|
nOff3 = 1;
|
|
}
|
|
else
|
|
{
|
|
Point aLeftTestPos = aRect.BottomLeft();
|
|
Point aRightTestPos = aRect.BottomRight();
|
|
if (aLeftTestPos.Y() == rCurRect.Bottom())
|
|
{
|
|
aLeftTestPos.AdjustX( -2 );
|
|
if (rCurRect.Contains(aLeftTestPos))
|
|
bLeftBorder = false;
|
|
aRightTestPos.AdjustX(2 );
|
|
if (rCurRect.Contains(aRightTestPos))
|
|
bRightBorder = false;
|
|
}
|
|
else
|
|
{
|
|
if (rCurRect.Contains(aLeftTestPos))
|
|
nLeftBottom -= 2;
|
|
if (rCurRect.Contains(aRightTestPos))
|
|
nRightBottom -= 2;
|
|
}
|
|
}
|
|
|
|
ControlState nState = ControlState::NONE;
|
|
|
|
if (pItem->id() == mnCurPageId)
|
|
{
|
|
nState |= ControlState::SELECTED;
|
|
// only the selected item can be focused
|
|
if (HasFocus())
|
|
nState |= ControlState::FOCUSED;
|
|
}
|
|
if (IsEnabled())
|
|
nState |= ControlState::ENABLED;
|
|
if (IsMouseOver() && pItem->maRect.Contains(GetPointerPosPixel()))
|
|
{
|
|
nState |= ControlState::ROLLOVER;
|
|
for (auto const& item : mpTabCtrlData->maItemList)
|
|
if ((&item != pItem) && item.m_bVisible && item.maRect.Contains(GetPointerPosPixel()))
|
|
{
|
|
nState &= ~ControlState::ROLLOVER; // avoid multiple highlighted tabs
|
|
break;
|
|
}
|
|
assert(nState & ControlState::ROLLOVER);
|
|
}
|
|
|
|
bNativeOK = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire);
|
|
if ( bNativeOK )
|
|
{
|
|
TabitemValue tiValue(tools::Rectangle(pItem->maRect.Left() + TAB_ITEM_OFFSET_X,
|
|
pItem->maRect.Top() + TAB_ITEM_OFFSET_Y,
|
|
pItem->maRect.Right() - TAB_ITEM_OFFSET_X,
|
|
pItem->maRect.Bottom() - TAB_ITEM_OFFSET_Y),
|
|
TabBarPosition::Top);
|
|
if (pItem->maRect.Left() < 5)
|
|
tiValue.mnAlignment |= TabitemFlags::LeftAligned;
|
|
if (pItem->maRect.Right() > mnLastWidth - 5)
|
|
tiValue.mnAlignment |= TabitemFlags::RightAligned;
|
|
if (bFirstInGroup)
|
|
tiValue.mnAlignment |= TabitemFlags::FirstInGroup;
|
|
if (bLastInGroup)
|
|
tiValue.mnAlignment |= TabitemFlags::LastInGroup;
|
|
|
|
tools::Rectangle aCtrlRegion( pItem->maRect );
|
|
aCtrlRegion.AdjustBottom(TabPaneValue::m_nOverlap);
|
|
bNativeOK = rRenderContext.DrawNativeControl(ControlType::TabItem, ControlPart::Entire,
|
|
aCtrlRegion, nState, tiValue, OUString() );
|
|
}
|
|
|
|
if (!bNativeOK)
|
|
{
|
|
if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
|
|
{
|
|
rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
|
|
rRenderContext.DrawPixel(Point(aRect.Left() + 1 - nOff2, aRect.Top() + 1 - nOff2)); // diagonally indented top-left pixel
|
|
if (bLeftBorder)
|
|
{
|
|
rRenderContext.DrawLine(Point(aRect.Left() - nOff2, aRect.Top() + 2 - nOff2),
|
|
Point(aRect.Left() - nOff2, nLeftBottom - 1));
|
|
}
|
|
rRenderContext.DrawLine(Point(aRect.Left() + 2 - nOff2, aRect.Top() - nOff2), // top line starting 2px from left border
|
|
Point(aRect.Right() + nOff2 - 3, aRect.Top() - nOff2)); // ending 3px from right border
|
|
|
|
if (bRightBorder)
|
|
{
|
|
rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
|
|
rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 2, aRect.Top() + 1 - nOff2),
|
|
Point(aRect.Right() + nOff2 - 2, nRightBottom - 1));
|
|
|
|
rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
|
|
rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 1, aRect.Top() + 3 - nOff2),
|
|
Point(aRect.Right() + nOff2 - 1, nRightBottom - 1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rRenderContext.SetLineColor(COL_BLACK);
|
|
rRenderContext.DrawPixel(Point(aRect.Left() + 1 - nOff2, aRect.Top() + 1 - nOff2));
|
|
rRenderContext.DrawPixel(Point(aRect.Right() + nOff2 - 2, aRect.Top() + 1 - nOff2));
|
|
if (bLeftBorder)
|
|
{
|
|
rRenderContext.DrawLine(Point(aRect.Left() - nOff2, aRect.Top() + 2 - nOff2),
|
|
Point(aRect.Left() - nOff2, nLeftBottom - 1));
|
|
}
|
|
rRenderContext.DrawLine(Point(aRect.Left() + 2 - nOff2, aRect.Top() - nOff2),
|
|
Point(aRect.Right() - 3, aRect.Top() - nOff2));
|
|
if (bRightBorder)
|
|
{
|
|
rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 1, aRect.Top() + 2 - nOff2),
|
|
Point(aRect.Right() + nOff2 - 1, nRightBottom - 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
// set font accordingly, current item is painted bold
|
|
// we set the font attributes always before drawing to be re-entrant (DrawNativeControl may trigger additional paints)
|
|
vcl::Font aFont(rRenderContext.GetFont());
|
|
aFont.SetTransparent(true);
|
|
rRenderContext.SetFont(aFont);
|
|
|
|
Size aTabSize = aRect.GetSize();
|
|
Size aImageSize(0, 0);
|
|
tools::Long nTextHeight = rRenderContext.GetTextHeight();
|
|
tools::Long nTextWidth = rRenderContext.GetCtrlTextWidth(pItem->maFormatText);
|
|
if (!!pItem->maTabImage)
|
|
{
|
|
aImageSize = pItem->maTabImage.GetSizePixel();
|
|
if (!pItem->maFormatText.isEmpty())
|
|
aImageSize.AdjustWidth(GetTextHeight() / 4 );
|
|
}
|
|
tools::Long nXPos = aRect.Left() + ((aTabSize.Width() - nTextWidth - aImageSize.Width()) / 2) - nOff - nOff3;
|
|
tools::Long nYPos = aRect.Top() + ((aTabSize.Height() - nTextHeight) / 2) - nOff3;
|
|
if (!pItem->maFormatText.isEmpty())
|
|
{
|
|
DrawTextFlags nStyle = DrawTextFlags::Mnemonic;
|
|
if (!pItem->m_bEnabled)
|
|
nStyle |= DrawTextFlags::Disable;
|
|
|
|
Color aColor(rStyleSettings.GetTabTextColor());
|
|
if (nState & ControlState::SELECTED)
|
|
aColor = rStyleSettings.GetTabHighlightTextColor();
|
|
else if (nState & ControlState::ROLLOVER)
|
|
aColor = rStyleSettings.GetTabRolloverTextColor();
|
|
|
|
Color aOldColor(rRenderContext.GetTextColor());
|
|
rRenderContext.SetTextColor(aColor);
|
|
|
|
const tools::Rectangle aOutRect(nXPos + aImageSize.Width(), nYPos,
|
|
nXPos + aImageSize.Width() + nTextWidth, nYPos + nTextHeight);
|
|
DrawControlText(rRenderContext, aOutRect, pItem->maFormatText, nStyle,
|
|
nullptr, nullptr);
|
|
|
|
rRenderContext.SetTextColor(aOldColor);
|
|
}
|
|
|
|
if (!!pItem->maTabImage)
|
|
{
|
|
Point aImgTL( nXPos, aRect.Top() );
|
|
if (aImageSize.Height() < aRect.GetHeight())
|
|
aImgTL.AdjustY((aRect.GetHeight() - aImageSize.Height()) / 2 );
|
|
rRenderContext.DrawImage(aImgTL, pItem->maTabImage, pItem->m_bEnabled ? DrawImageFlags::NONE : DrawImageFlags::Disable );
|
|
}
|
|
}
|
|
|
|
bool TabControl::ImplHandleKeyEvent( const KeyEvent& rKeyEvent )
|
|
{
|
|
bool bRet = false;
|
|
|
|
if ( GetPageCount() > 1 )
|
|
{
|
|
vcl::KeyCode aKeyCode = rKeyEvent.GetKeyCode();
|
|
sal_uInt16 nKeyCode = aKeyCode.GetCode();
|
|
|
|
if ( aKeyCode.IsMod1() )
|
|
{
|
|
if ( aKeyCode.IsShift() || (nKeyCode == KEY_PAGEUP) )
|
|
{
|
|
if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEUP) )
|
|
{
|
|
ImplActivateTabPage( false );
|
|
bRet = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEDOWN) )
|
|
{
|
|
ImplActivateTabPage( true );
|
|
bRet = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
IMPL_LINK_NOARG(TabControl, ImplListBoxSelectHdl, ListBox&, void)
|
|
{
|
|
SelectTabPage( GetPageId( mpTabCtrlData->mpListBox->GetSelectedEntryPos() ) );
|
|
}
|
|
|
|
IMPL_LINK( TabControl, ImplWindowEventListener, VclWindowEvent&, rEvent, void )
|
|
{
|
|
if ( rEvent.GetId() == VclEventId::WindowKeyInput )
|
|
{
|
|
// Do not handle events from TabControl or its children, which is done in Notify(), where the events can be consumed.
|
|
if ( !IsWindowOrChild( rEvent.GetWindow() ) )
|
|
{
|
|
KeyEvent* pKeyEvent = static_cast< KeyEvent* >(rEvent.GetData());
|
|
ImplHandleKeyEvent( *pKeyEvent );
|
|
}
|
|
}
|
|
}
|
|
|
|
void TabControl::MouseButtonDown( const MouseEvent& rMEvt )
|
|
{
|
|
if (mpTabCtrlData->mpListBox || !rMEvt.IsLeft())
|
|
return;
|
|
|
|
ImplTabItem *pItem = ImplGetItem(rMEvt.GetPosPixel());
|
|
if (pItem && pItem->m_bEnabled)
|
|
SelectTabPage(pItem->id());
|
|
}
|
|
|
|
void TabControl::KeyInput( const KeyEvent& rKEvt )
|
|
{
|
|
if( mpTabCtrlData->mpListBox )
|
|
mpTabCtrlData->mpListBox->KeyInput( rKEvt );
|
|
else if ( GetPageCount() > 1 )
|
|
{
|
|
vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
|
|
sal_uInt16 nKeyCode = aKeyCode.GetCode();
|
|
|
|
if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_RIGHT) )
|
|
{
|
|
bool bNext = (nKeyCode == KEY_RIGHT);
|
|
ImplActivateTabPage( bNext );
|
|
}
|
|
}
|
|
|
|
Control::KeyInput( rKEvt );
|
|
}
|
|
|
|
static bool lcl_canPaint(const vcl::RenderContext& rRenderContext, const tools::Rectangle& rDrawRect,
|
|
const tools::Rectangle& rItemRect)
|
|
{
|
|
vcl::Region aClipRgn(rRenderContext.GetActiveClipRegion());
|
|
aClipRgn.Intersect(rItemRect);
|
|
if (!rDrawRect.IsEmpty())
|
|
aClipRgn.Intersect(rDrawRect);
|
|
return !aClipRgn.IsEmpty();
|
|
}
|
|
|
|
void TabControl::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
|
|
{
|
|
if (GetStyle() & WB_NOBORDER)
|
|
return;
|
|
|
|
Control::Paint(rRenderContext, rRect);
|
|
|
|
HideFocus();
|
|
|
|
// reformat if needed
|
|
tools::Rectangle aRect = ImplGetTabRect(TAB_PAGERECT);
|
|
|
|
// find current item
|
|
ImplTabItem* pCurItem = nullptr;
|
|
for (auto & item : mpTabCtrlData->maItemList)
|
|
{
|
|
if (item.id() == mnCurPageId)
|
|
{
|
|
pCurItem = &item;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Draw the TabPage border
|
|
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
tools::Rectangle aCurRect;
|
|
aRect.AdjustLeft( -(TAB_OFFSET) );
|
|
aRect.AdjustTop( -(TAB_OFFSET) );
|
|
aRect.AdjustRight(TAB_OFFSET );
|
|
aRect.AdjustBottom(TAB_OFFSET );
|
|
|
|
// if we have an invisible tabpage or no tabpage at all the tabpage rect should be
|
|
// increased to avoid round corners that might be drawn by a theme
|
|
// in this case we're only interested in the top border of the tabpage because the tabitems are used
|
|
// standalone (eg impress)
|
|
bool bNoTabPage = false;
|
|
TabPage* pCurPage = pCurItem ? pCurItem->mpTabPage.get() : nullptr;
|
|
if (!pCurPage || !pCurPage->IsVisible())
|
|
{
|
|
bNoTabPage = true;
|
|
aRect.AdjustLeft( -10 );
|
|
aRect.AdjustRight(10 );
|
|
}
|
|
|
|
if (rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire))
|
|
{
|
|
const bool bPaneWithHeader = mbShowTabs && rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::TabPaneWithHeader);
|
|
tools::Rectangle aHeaderRect(aRect.Left(), 0, aRect.Right(), aRect.Top());
|
|
|
|
if (!mpTabCtrlData->maItemList.empty())
|
|
{
|
|
tools::Long nLeft = LONG_MAX;
|
|
tools::Long nRight = 0;
|
|
for (const auto &item : mpTabCtrlData->maItemList)
|
|
{
|
|
if (!item.m_bVisible)
|
|
continue;
|
|
nRight = std::max(nRight, item.maRect.Right());
|
|
nLeft = std::min(nLeft, item.maRect.Left());
|
|
}
|
|
aHeaderRect.SetLeft(nLeft);
|
|
aHeaderRect.SetRight(nRight);
|
|
}
|
|
|
|
if (bPaneWithHeader)
|
|
aRect.SetTop(0);
|
|
|
|
const TabPaneValue aTabPaneValue(aHeaderRect, pCurItem ? pCurItem->maRect : tools::Rectangle());
|
|
|
|
ControlState nState = ControlState::ENABLED;
|
|
if (!IsEnabled())
|
|
nState &= ~ControlState::ENABLED;
|
|
if (HasFocus())
|
|
nState |= ControlState::FOCUSED;
|
|
|
|
if (lcl_canPaint(rRenderContext, rRect, aRect))
|
|
rRenderContext.DrawNativeControl(ControlType::TabPane, ControlPart::Entire,
|
|
aRect, nState, aTabPaneValue, OUString());
|
|
|
|
if (!bPaneWithHeader && rRenderContext.IsNativeControlSupported(ControlType::TabHeader, ControlPart::Entire)
|
|
&& lcl_canPaint(rRenderContext, rRect, aHeaderRect))
|
|
rRenderContext.DrawNativeControl(ControlType::TabHeader, ControlPart::Entire,
|
|
aHeaderRect, nState, aTabPaneValue, OUString());
|
|
}
|
|
else
|
|
{
|
|
tools::Long nTopOff = 1;
|
|
if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
|
|
rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
|
|
else
|
|
rRenderContext.SetLineColor(COL_BLACK);
|
|
if (mbShowTabs && pCurItem && !pCurItem->maRect.IsEmpty())
|
|
{
|
|
aCurRect = pCurItem->maRect;
|
|
rRenderContext.DrawLine(aRect.TopLeft(), Point(aCurRect.Left() - 2, aRect.Top()));
|
|
if (aCurRect.Right() + 1 < aRect.Right())
|
|
{
|
|
rRenderContext.DrawLine(Point(aCurRect.Right(), aRect.Top()), aRect.TopRight());
|
|
}
|
|
else
|
|
{
|
|
nTopOff = 0;
|
|
}
|
|
}
|
|
else
|
|
rRenderContext.DrawLine(aRect.TopLeft(), aRect.TopRight());
|
|
|
|
rRenderContext.DrawLine(aRect.TopLeft(), aRect.BottomLeft());
|
|
|
|
if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
|
|
{
|
|
// if we have not tab page the bottom line of the tab page
|
|
// directly touches the tab items, so choose a color that fits seamlessly
|
|
if (bNoTabPage)
|
|
rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
|
|
else
|
|
rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
|
|
rRenderContext.DrawLine(Point(1, aRect.Bottom() - 1), Point(aRect.Right() - 1, aRect.Bottom() - 1));
|
|
rRenderContext.DrawLine(Point(aRect.Right() - 1, aRect.Top() + nTopOff), Point(aRect.Right() - 1, aRect.Bottom() - 1));
|
|
if (bNoTabPage)
|
|
rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
|
|
else
|
|
rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
|
|
rRenderContext.DrawLine(Point(0, aRect.Bottom()), Point(aRect.Right(), aRect.Bottom()));
|
|
rRenderContext.DrawLine(Point(aRect.Right(), aRect.Top() + nTopOff), Point(aRect.Right(), aRect.Bottom()));
|
|
}
|
|
else
|
|
{
|
|
rRenderContext.DrawLine(aRect.TopRight(), aRect.BottomRight());
|
|
rRenderContext.DrawLine(aRect.BottomLeft(), aRect.BottomRight());
|
|
}
|
|
}
|
|
|
|
const size_t nItemListSize = mpTabCtrlData->maItemList.size();
|
|
if (mbShowTabs && nItemListSize > 0 && mpTabCtrlData->mpListBox == nullptr)
|
|
{
|
|
// Some native toolkits (GTK+) draw tabs right-to-left, with an
|
|
// overlap between adjacent tabs
|
|
bool bDrawTabsRTL = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::TabsDrawRtl);
|
|
ImplTabItem* pFirstTab = nullptr;
|
|
ImplTabItem* pLastTab = nullptr;
|
|
size_t idx;
|
|
|
|
// Even though there is a tab overlap with GTK+, the first tab is not
|
|
// overlapped on the left side. Other toolkits ignore this option.
|
|
if (bDrawTabsRTL)
|
|
{
|
|
pFirstTab = mpTabCtrlData->maItemList.data();
|
|
pLastTab = pFirstTab + nItemListSize - 1;
|
|
idx = nItemListSize - 1;
|
|
}
|
|
else
|
|
{
|
|
pLastTab = mpTabCtrlData->maItemList.data();
|
|
pFirstTab = pLastTab + nItemListSize - 1;
|
|
idx = 0;
|
|
}
|
|
|
|
while (idx < nItemListSize)
|
|
{
|
|
ImplTabItem* pItem = &mpTabCtrlData->maItemList[idx];
|
|
|
|
if (pItem != pCurItem && pItem->m_bVisible && lcl_canPaint(rRenderContext, rRect, pItem->maRect))
|
|
ImplDrawItem(rRenderContext, pItem, aCurRect, pItem == pFirstTab, pItem == pLastTab);
|
|
|
|
if (bDrawTabsRTL)
|
|
idx--;
|
|
else
|
|
idx++;
|
|
}
|
|
|
|
if (pCurItem && lcl_canPaint(rRenderContext, rRect, pCurItem->maRect))
|
|
ImplDrawItem(rRenderContext, pCurItem, aCurRect, pCurItem == pFirstTab, pCurItem == pLastTab);
|
|
}
|
|
|
|
if (HasFocus())
|
|
ImplShowFocus();
|
|
|
|
mbSmallInvalidate = true;
|
|
}
|
|
|
|
void TabControl::setAllocation(const Size &rAllocation)
|
|
{
|
|
if ( !IsReallyShown() )
|
|
return;
|
|
|
|
if( mpTabCtrlData->mpListBox )
|
|
{
|
|
// get the listbox' preferred size
|
|
Size aTabCtrlSize( GetSizePixel() );
|
|
tools::Long nPrefWidth = mpTabCtrlData->mpListBox->get_preferred_size().Width();
|
|
if( nPrefWidth > aTabCtrlSize.Width() )
|
|
nPrefWidth = aTabCtrlSize.Width();
|
|
Size aNewSize( nPrefWidth, LogicToPixel( Size( 12, 12 ), MapMode( MapUnit::MapAppFont ) ).Height() );
|
|
Point aNewPos( (aTabCtrlSize.Width() - nPrefWidth) / 2, 0 );
|
|
mpTabCtrlData->mpListBox->SetPosSizePixel( aNewPos, aNewSize );
|
|
}
|
|
|
|
mbFormat = true;
|
|
|
|
// resize/position active TabPage
|
|
bool bTabPage = ImplPosCurTabPage();
|
|
|
|
// check what needs to be invalidated
|
|
Size aNewSize = rAllocation;
|
|
tools::Long nNewWidth = aNewSize.Width();
|
|
for (auto const& item : mpTabCtrlData->maItemList)
|
|
{
|
|
if (!item.m_bVisible)
|
|
continue;
|
|
if (!item.mbFullVisible || (item.maRect.Right()-2 >= nNewWidth))
|
|
{
|
|
mbSmallInvalidate = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( mbSmallInvalidate )
|
|
{
|
|
tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT );
|
|
aRect.AdjustLeft( -(TAB_OFFSET+TAB_BORDER_LEFT) );
|
|
aRect.AdjustTop( -(TAB_OFFSET+TAB_BORDER_TOP) );
|
|
aRect.AdjustRight(TAB_OFFSET+TAB_BORDER_RIGHT );
|
|
aRect.AdjustBottom(TAB_OFFSET+TAB_BORDER_BOTTOM );
|
|
if ( bTabPage )
|
|
Invalidate( aRect, InvalidateFlags::NoChildren );
|
|
else
|
|
Invalidate( aRect );
|
|
|
|
}
|
|
else
|
|
{
|
|
if ( bTabPage )
|
|
Invalidate( InvalidateFlags::NoChildren );
|
|
else
|
|
Invalidate();
|
|
}
|
|
|
|
mbLayoutDirty = false;
|
|
}
|
|
|
|
void TabControl::SetPosSizePixel(const Point& rNewPos, const Size& rNewSize)
|
|
{
|
|
Window::SetPosSizePixel(rNewPos, rNewSize);
|
|
//if size changed, TabControl::Resize got called already
|
|
if (mbLayoutDirty)
|
|
setAllocation(rNewSize);
|
|
}
|
|
|
|
void TabControl::SetSizePixel(const Size& rNewSize)
|
|
{
|
|
Window::SetSizePixel(rNewSize);
|
|
//if size changed, TabControl::Resize got called already
|
|
if (mbLayoutDirty)
|
|
setAllocation(rNewSize);
|
|
}
|
|
|
|
void TabControl::SetPosPixel(const Point& rPos)
|
|
{
|
|
Window::SetPosPixel(rPos);
|
|
if (mbLayoutDirty)
|
|
setAllocation(GetOutputSizePixel());
|
|
}
|
|
|
|
void TabControl::Resize()
|
|
{
|
|
setAllocation(Control::GetOutputSizePixel());
|
|
}
|
|
|
|
void TabControl::GetFocus()
|
|
{
|
|
if( ! mpTabCtrlData->mpListBox )
|
|
{
|
|
if (mbShowTabs)
|
|
{
|
|
ImplShowFocus();
|
|
SetInputContext( InputContext( GetFont() ) );
|
|
}
|
|
else
|
|
{
|
|
// no tabs, focus first thing in current page
|
|
ImplTabItem* pItem = ImplGetItem(GetCurPageId());
|
|
if (pItem && pItem->mpTabPage)
|
|
{
|
|
vcl::Window* pFirstChild = pItem->mpTabPage->ImplGetDlgWindow(0, GetDlgWindowType::First);
|
|
if ( pFirstChild )
|
|
pFirstChild->ImplControlFocus(GetFocusFlags::Init);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( mpTabCtrlData->mpListBox->IsReallyVisible() )
|
|
mpTabCtrlData->mpListBox->GrabFocus();
|
|
}
|
|
|
|
Control::GetFocus();
|
|
}
|
|
|
|
void TabControl::LoseFocus()
|
|
{
|
|
if( mpTabCtrlData && ! mpTabCtrlData->mpListBox )
|
|
HideFocus();
|
|
Control::LoseFocus();
|
|
}
|
|
|
|
void TabControl::RequestHelp( const HelpEvent& rHEvt )
|
|
{
|
|
sal_uInt16 nItemId = rHEvt.KeyboardActivated() ? mnCurPageId : GetPageId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
|
|
|
|
if ( nItemId )
|
|
{
|
|
if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
|
|
{
|
|
OUString aStr = GetHelpText( nItemId );
|
|
if ( !aStr.isEmpty() )
|
|
{
|
|
tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
|
|
Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
|
|
aItemRect.SetLeft( aPt.X() );
|
|
aItemRect.SetTop( aPt.Y() );
|
|
aPt = OutputToScreenPixel( aItemRect.BottomRight() );
|
|
aItemRect.SetRight( aPt.X() );
|
|
aItemRect.SetBottom( aPt.Y() );
|
|
Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// for Quick or Ballon Help, we show the text, if it is cut
|
|
if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nItemId );
|
|
const OUString& rStr = pItem->maText;
|
|
if ( rStr != pItem->maFormatText )
|
|
{
|
|
tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
|
|
Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
|
|
aItemRect.SetLeft( aPt.X() );
|
|
aItemRect.SetTop( aPt.Y() );
|
|
aPt = OutputToScreenPixel( aItemRect.BottomRight() );
|
|
aItemRect.SetRight( aPt.X() );
|
|
aItemRect.SetBottom( aPt.Y() );
|
|
if ( !rStr.isEmpty() )
|
|
{
|
|
if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
|
|
Help::ShowBalloon( this, aItemRect.Center(), aItemRect, rStr );
|
|
else
|
|
Help::ShowQuickHelp( this, aItemRect, rStr );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( rHEvt.GetMode() & HelpEventMode::QUICK )
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nItemId );
|
|
const OUString& rHelpText = pItem->maHelpText;
|
|
if (!rHelpText.isEmpty())
|
|
{
|
|
tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
|
|
Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
|
|
aItemRect.SetLeft( aPt.X() );
|
|
aItemRect.SetTop( aPt.Y() );
|
|
aPt = OutputToScreenPixel( aItemRect.BottomRight() );
|
|
aItemRect.SetRight( aPt.X() );
|
|
aItemRect.SetBottom( aPt.Y() );
|
|
Help::ShowQuickHelp( this, aItemRect, rHelpText );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Control::RequestHelp( rHEvt );
|
|
}
|
|
|
|
void TabControl::Command( const CommandEvent& rCEvt )
|
|
{
|
|
if( (mpTabCtrlData->mpListBox == nullptr) && (rCEvt.GetCommand() == CommandEventId::ContextMenu) && (GetPageCount() > 1) )
|
|
{
|
|
Point aMenuPos;
|
|
bool bMenu;
|
|
if ( rCEvt.IsMouseEvent() )
|
|
{
|
|
aMenuPos = rCEvt.GetMousePosPixel();
|
|
bMenu = GetPageId( aMenuPos ) != 0;
|
|
}
|
|
else
|
|
{
|
|
aMenuPos = ImplGetTabRect( GetPagePos( mnCurPageId ) ).Center();
|
|
bMenu = true;
|
|
}
|
|
|
|
if ( bMenu )
|
|
{
|
|
ScopedVclPtrInstance<PopupMenu> aMenu;
|
|
for (auto const& item : mpTabCtrlData->maItemList)
|
|
{
|
|
aMenu->InsertItem(item.id(), item.maText, MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK);
|
|
if (item.id() == mnCurPageId)
|
|
aMenu->CheckItem(item.id());
|
|
aMenu->SetHelpId(item.id(), {});
|
|
}
|
|
|
|
sal_uInt16 nId = aMenu->Execute( this, aMenuPos );
|
|
if ( nId && (nId != mnCurPageId) )
|
|
SelectTabPage( nId );
|
|
return;
|
|
}
|
|
}
|
|
|
|
Control::Command( rCEvt );
|
|
}
|
|
|
|
void TabControl::StateChanged( StateChangedType nType )
|
|
{
|
|
Control::StateChanged( nType );
|
|
|
|
if ( nType == StateChangedType::InitShow )
|
|
{
|
|
ImplPosCurTabPage();
|
|
if( mpTabCtrlData->mpListBox )
|
|
Resize();
|
|
}
|
|
else if ( nType == StateChangedType::UpdateMode )
|
|
{
|
|
if ( IsUpdateMode() )
|
|
Invalidate();
|
|
}
|
|
else if ( (nType == StateChangedType::Zoom) ||
|
|
(nType == StateChangedType::ControlFont) )
|
|
{
|
|
ImplInitSettings( false );
|
|
Invalidate();
|
|
}
|
|
else if ( nType == StateChangedType::ControlForeground )
|
|
{
|
|
ImplInitSettings( false );
|
|
Invalidate();
|
|
}
|
|
else if ( nType == StateChangedType::ControlBackground )
|
|
{
|
|
ImplInitSettings( true );
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void TabControl::DataChanged( const DataChangedEvent& rDCEvt )
|
|
{
|
|
Control::DataChanged( rDCEvt );
|
|
|
|
if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
|
|
(rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
|
|
((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
|
|
(rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
|
|
{
|
|
ImplInitSettings( true );
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
ImplTabItem* TabControl::ImplGetItem(const Point& rPt) const
|
|
{
|
|
ImplTabItem* pFoundItem = nullptr;
|
|
int nFound = 0;
|
|
for (auto & item : mpTabCtrlData->maItemList)
|
|
{
|
|
if (item.m_bVisible && item.maRect.Contains(rPt))
|
|
{
|
|
nFound++;
|
|
pFoundItem = &item;
|
|
}
|
|
}
|
|
|
|
// assure that only one tab is highlighted at a time
|
|
assert(nFound <= 1);
|
|
return nFound == 1 ? pFoundItem : nullptr;
|
|
}
|
|
|
|
bool TabControl::PreNotify( NotifyEvent& rNEvt )
|
|
{
|
|
if( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
|
|
{
|
|
const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
|
|
if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
|
|
{
|
|
// trigger redraw if mouse over state has changed
|
|
if( IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire) )
|
|
{
|
|
ImplTabItem *pItem = ImplGetItem(GetPointerPosPixel());
|
|
ImplTabItem *pLastItem = ImplGetItem(GetLastPointerPosPixel());
|
|
if ((pItem != pLastItem) || pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
|
|
{
|
|
vcl::Region aClipRgn;
|
|
if (pLastItem)
|
|
{
|
|
// allow for slightly bigger tabitems
|
|
// as used by gtk
|
|
// TODO: query for the correct sizes
|
|
tools::Rectangle aRect(pLastItem->maRect);
|
|
aRect.AdjustLeft( -2 );
|
|
aRect.AdjustRight(2 );
|
|
aRect.AdjustTop( -3 );
|
|
aClipRgn.Union( aRect );
|
|
}
|
|
|
|
if (pItem)
|
|
{
|
|
// allow for slightly bigger tabitems
|
|
// as used by gtk
|
|
// TODO: query for the correct sizes
|
|
tools::Rectangle aRect(pItem->maRect);
|
|
aRect.AdjustLeft( -2 );
|
|
aRect.AdjustRight(2 );
|
|
aRect.AdjustTop( -3 );
|
|
aClipRgn.Union( aRect );
|
|
}
|
|
|
|
if( !aClipRgn.IsEmpty() )
|
|
Invalidate( aClipRgn );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Control::PreNotify(rNEvt);
|
|
}
|
|
|
|
bool TabControl::EventNotify( NotifyEvent& rNEvt )
|
|
{
|
|
bool bRet = false;
|
|
|
|
if ( rNEvt.GetType() == NotifyEventType::KEYINPUT )
|
|
bRet = ImplHandleKeyEvent( *rNEvt.GetKeyEvent() );
|
|
|
|
return bRet || Control::EventNotify( rNEvt );
|
|
}
|
|
|
|
void TabControl::ActivatePage()
|
|
{
|
|
maActivateHdl.Call( this );
|
|
}
|
|
|
|
bool TabControl::DeactivatePage()
|
|
{
|
|
return !maDeactivateHdl.IsSet() || maDeactivateHdl.Call( this );
|
|
}
|
|
|
|
void TabControl::SetTabPageSizePixel( const Size& rSize )
|
|
{
|
|
Size aNewSize( rSize );
|
|
aNewSize.AdjustWidth(TAB_OFFSET*2 );
|
|
tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT,
|
|
aNewSize.Width(), aNewSize.Height() );
|
|
aNewSize.AdjustHeight(aRect.Top()+TAB_OFFSET );
|
|
Window::SetOutputSizePixel( aNewSize );
|
|
}
|
|
|
|
void TabControl::InsertPage( sal_uInt16 nPageId, const OUString& rText,
|
|
sal_uInt16 nPos )
|
|
{
|
|
SAL_WARN_IF( !nPageId, "vcl", "TabControl::InsertPage(): PageId == 0" );
|
|
SAL_WARN_IF( GetPagePos( nPageId ) != TAB_PAGE_NOTFOUND, "vcl",
|
|
"TabControl::InsertPage(): PageId already exists" );
|
|
|
|
// insert new page item
|
|
ImplTabItem* pItem = nullptr;
|
|
if( nPos == TAB_APPEND || size_t(nPos) >= mpTabCtrlData->maItemList.size() )
|
|
{
|
|
mpTabCtrlData->maItemList.emplace_back(nPageId);
|
|
pItem = &mpTabCtrlData->maItemList.back();
|
|
if( mpTabCtrlData->mpListBox )
|
|
mpTabCtrlData->mpListBox->InsertEntry( rText );
|
|
}
|
|
else
|
|
{
|
|
std::vector< ImplTabItem >::iterator new_it =
|
|
mpTabCtrlData->maItemList.emplace(mpTabCtrlData->maItemList.begin() + nPos, nPageId);
|
|
pItem = &(*new_it);
|
|
if( mpTabCtrlData->mpListBox )
|
|
mpTabCtrlData->mpListBox->InsertEntry( rText, nPos);
|
|
}
|
|
if( mpTabCtrlData->mpListBox )
|
|
{
|
|
if( ! mnCurPageId )
|
|
mpTabCtrlData->mpListBox->SelectEntryPos( 0 );
|
|
mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
|
|
}
|
|
|
|
// set current page id
|
|
if ( !mnCurPageId )
|
|
mnCurPageId = nPageId;
|
|
|
|
// init new page item
|
|
pItem->maText = rText;
|
|
pItem->mbFullVisible = false;
|
|
|
|
mbFormat = true;
|
|
if ( IsUpdateMode() )
|
|
Invalidate();
|
|
|
|
if( mpTabCtrlData->mpListBox ) // reposition/resize listbox
|
|
Resize();
|
|
|
|
CallEventListeners( VclEventId::TabpageInserted, reinterpret_cast<void*>(nPageId) );
|
|
}
|
|
|
|
void TabControl::RemovePage( sal_uInt16 nPageId )
|
|
{
|
|
sal_uInt16 nPos = GetPagePos( nPageId );
|
|
|
|
// does the item exist ?
|
|
if ( nPos == TAB_PAGE_NOTFOUND )
|
|
return;
|
|
|
|
//remove page item
|
|
std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin() + nPos;
|
|
bool bIsCurrentPage = (it->id() == mnCurPageId);
|
|
mpTabCtrlData->maItemList.erase( it );
|
|
if( mpTabCtrlData->mpListBox )
|
|
{
|
|
mpTabCtrlData->mpListBox->RemoveEntry( nPos );
|
|
mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
|
|
}
|
|
|
|
// If current page is removed, then first page gets the current page
|
|
if ( bIsCurrentPage )
|
|
{
|
|
mnCurPageId = 0;
|
|
|
|
if( ! mpTabCtrlData->maItemList.empty() )
|
|
{
|
|
// don't do this by simply setting mnCurPageId to pFirstItem->id()
|
|
// this leaves a lot of stuff (such trivia as _showing_ the new current page) undone
|
|
// instead, call SetCurPageId
|
|
// without this, the next (outside) call to SetCurPageId with the id of the first page
|
|
// will result in doing nothing (as we assume that nothing changed, then), and the page
|
|
// will never be shown.
|
|
// 86875 - 05/11/2001 - frank.schoenheit@germany.sun.com
|
|
|
|
SetCurPageId(mpTabCtrlData->maItemList[0].id());
|
|
}
|
|
}
|
|
|
|
mbFormat = true;
|
|
if ( IsUpdateMode() )
|
|
Invalidate();
|
|
|
|
CallEventListeners( VclEventId::TabpageRemoved, reinterpret_cast<void*>(nPageId) );
|
|
}
|
|
|
|
void TabControl::SetPageEnabled( sal_uInt16 i_nPageId, bool i_bEnable )
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( i_nPageId );
|
|
|
|
if (!pItem || pItem->m_bEnabled == i_bEnable)
|
|
return;
|
|
|
|
pItem->m_bEnabled = i_bEnable;
|
|
if (!pItem->m_bVisible)
|
|
return;
|
|
|
|
mbFormat = true;
|
|
if( mpTabCtrlData->mpListBox )
|
|
mpTabCtrlData->mpListBox->SetEntryFlags( GetPagePos( i_nPageId ),
|
|
i_bEnable ? ListBoxEntryFlags::NONE : (ListBoxEntryFlags::DisableSelection | ListBoxEntryFlags::DrawDisabled) );
|
|
|
|
// SetCurPageId will change to a valid page
|
|
if (pItem->id() == mnCurPageId)
|
|
SetCurPageId( mnCurPageId );
|
|
else if ( IsUpdateMode() )
|
|
Invalidate();
|
|
}
|
|
|
|
void TabControl::SetPageVisible( sal_uInt16 nPageId, bool bVisible )
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
if (!pItem || pItem->m_bVisible == bVisible)
|
|
return;
|
|
|
|
pItem->m_bVisible = bVisible;
|
|
if (!bVisible)
|
|
{
|
|
if (pItem->mbFullVisible)
|
|
mbSmallInvalidate = false;
|
|
pItem->mbFullVisible = false;
|
|
pItem->maRect.SetEmpty();
|
|
}
|
|
mbFormat = true;
|
|
|
|
// SetCurPageId will change to a valid page
|
|
if (pItem->id() == mnCurPageId)
|
|
SetCurPageId(mnCurPageId);
|
|
else if (IsUpdateMode())
|
|
Invalidate();
|
|
}
|
|
|
|
sal_uInt16 TabControl::GetPageCount() const
|
|
{
|
|
return static_cast<sal_uInt16>(mpTabCtrlData->maItemList.size());
|
|
}
|
|
|
|
sal_uInt16 TabControl::GetPageId( sal_uInt16 nPos ) const
|
|
{
|
|
if( size_t(nPos) < mpTabCtrlData->maItemList.size() )
|
|
return mpTabCtrlData->maItemList[nPos].id();
|
|
return 0;
|
|
}
|
|
|
|
sal_uInt16 TabControl::GetPagePos( sal_uInt16 nPageId ) const
|
|
{
|
|
sal_uInt16 nPos = 0;
|
|
for (auto const& item : mpTabCtrlData->maItemList)
|
|
{
|
|
if (item.id() == nPageId)
|
|
return nPos;
|
|
++nPos;
|
|
}
|
|
|
|
return TAB_PAGE_NOTFOUND;
|
|
}
|
|
|
|
sal_uInt16 TabControl::GetPageId( const Point& rPos ) const
|
|
{
|
|
Size winSize = Control::GetOutputSizePixel();
|
|
const auto &rList = mpTabCtrlData->maItemList;
|
|
const auto it = std::find_if(rList.begin(), rList.end(), [&rPos, &winSize, this](const auto &item) {
|
|
return const_cast<TabControl*>(this)->ImplGetTabRect(&item, winSize.Width(), winSize.Height()).Contains(rPos); });
|
|
return (it != rList.end()) ? it->id() : 0;
|
|
}
|
|
|
|
sal_uInt16 TabControl::GetPageId( const OUString& rName ) const
|
|
{
|
|
const auto &rList = mpTabCtrlData->maItemList;
|
|
const auto it = std::find_if(rList.begin(), rList.end(), [&rName](const auto &item) {
|
|
return item.maTabName == rName; });
|
|
return (it != rList.end()) ? it->id() : 0;
|
|
}
|
|
|
|
void TabControl::SetCurPageId( sal_uInt16 nPageId )
|
|
{
|
|
sal_uInt16 nPos = GetPagePos( nPageId );
|
|
while (nPos != TAB_PAGE_NOTFOUND && !mpTabCtrlData->maItemList[nPos].m_bEnabled)
|
|
{
|
|
nPos++;
|
|
if( size_t(nPos) >= mpTabCtrlData->maItemList.size() )
|
|
nPos = 0;
|
|
if (mpTabCtrlData->maItemList[nPos].id() == nPageId)
|
|
break;
|
|
}
|
|
|
|
if( nPos == TAB_PAGE_NOTFOUND )
|
|
return;
|
|
|
|
nPageId = mpTabCtrlData->maItemList[nPos].id();
|
|
if ( nPageId == mnCurPageId )
|
|
{
|
|
if ( mnActPageId )
|
|
mnActPageId = nPageId;
|
|
return;
|
|
}
|
|
|
|
if ( mnActPageId )
|
|
mnActPageId = nPageId;
|
|
else
|
|
{
|
|
mbFormat = true;
|
|
sal_uInt16 nOldId = mnCurPageId;
|
|
mnCurPageId = nPageId;
|
|
ImplChangeTabPage( nPageId, nOldId );
|
|
}
|
|
}
|
|
|
|
sal_uInt16 TabControl::GetCurPageId() const
|
|
{
|
|
if ( mnActPageId )
|
|
return mnActPageId;
|
|
else
|
|
return mnCurPageId;
|
|
}
|
|
|
|
void TabControl::SelectTabPage( sal_uInt16 nPageId )
|
|
{
|
|
if ( !nPageId || (nPageId == mnCurPageId) )
|
|
return;
|
|
|
|
CallEventListeners( VclEventId::TabpageDeactivate, reinterpret_cast<void*>(mnCurPageId) );
|
|
if ( DeactivatePage() )
|
|
{
|
|
mnActPageId = nPageId;
|
|
ActivatePage();
|
|
// Page could have been switched by the Activate handler
|
|
nPageId = mnActPageId;
|
|
mnActPageId = 0;
|
|
SetCurPageId( nPageId );
|
|
if( mpTabCtrlData->mpListBox )
|
|
mpTabCtrlData->mpListBox->SelectEntryPos( GetPagePos( nPageId ) );
|
|
CallEventListeners( VclEventId::TabpageActivate, reinterpret_cast<void*>(nPageId) );
|
|
}
|
|
}
|
|
|
|
void TabControl::SetTabPage( sal_uInt16 nPageId, TabPage* pTabPage )
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
|
|
if ( !pItem || (pItem->mpTabPage.get() == pTabPage) )
|
|
return;
|
|
|
|
if ( pTabPage )
|
|
{
|
|
if ( IsDefaultSize() )
|
|
SetTabPageSizePixel( pTabPage->GetSizePixel() );
|
|
|
|
// only set here, so that Resize does not reposition TabPage
|
|
pItem->mpTabPage = pTabPage;
|
|
queue_resize();
|
|
|
|
if (pItem->id() == mnCurPageId)
|
|
ImplChangeTabPage(pItem->id(), 0);
|
|
}
|
|
else
|
|
{
|
|
pItem->mpTabPage = nullptr;
|
|
queue_resize();
|
|
}
|
|
}
|
|
|
|
TabPage* TabControl::GetTabPage( sal_uInt16 nPageId ) const
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
|
|
if ( pItem )
|
|
return pItem->mpTabPage;
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
void TabControl::SetPageText( sal_uInt16 nPageId, const OUString& rText )
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
|
|
if ( !pItem || pItem->maText == rText )
|
|
return;
|
|
|
|
pItem->maText = rText;
|
|
mbFormat = true;
|
|
if( mpTabCtrlData->mpListBox )
|
|
{
|
|
sal_uInt16 nPos = GetPagePos( nPageId );
|
|
mpTabCtrlData->mpListBox->RemoveEntry( nPos );
|
|
mpTabCtrlData->mpListBox->InsertEntry( rText, nPos );
|
|
}
|
|
if ( IsUpdateMode() )
|
|
Invalidate();
|
|
CallEventListeners( VclEventId::TabpagePageTextChanged, reinterpret_cast<void*>(nPageId) );
|
|
}
|
|
|
|
OUString const & TabControl::GetPageText( sal_uInt16 nPageId ) const
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
|
|
assert( pItem );
|
|
|
|
return pItem->maText;
|
|
}
|
|
|
|
void TabControl::SetHelpText( sal_uInt16 nPageId, const OUString& rText )
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
|
|
assert( pItem );
|
|
|
|
pItem->maHelpText = rText;
|
|
}
|
|
|
|
const OUString& TabControl::GetHelpText( sal_uInt16 nPageId ) const
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
assert( pItem );
|
|
return pItem->maHelpText;
|
|
}
|
|
|
|
void TabControl::SetAccessibleName(sal_uInt16 nPageId, const OUString& rName)
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
assert( pItem );
|
|
pItem->maAccessibleName = rName;
|
|
}
|
|
|
|
OUString TabControl::GetAccessibleName( sal_uInt16 nPageId ) const
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
assert( pItem );
|
|
if (!pItem->maAccessibleName.isEmpty())
|
|
return pItem->maAccessibleName;
|
|
return removeMnemonicFromString(pItem->maText);
|
|
}
|
|
|
|
void TabControl::SetAccessibleDescription(sal_uInt16 nPageId, const OUString& rDesc)
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
assert( pItem );
|
|
pItem->maAccessibleDescription = rDesc;
|
|
}
|
|
|
|
OUString TabControl::GetAccessibleDescription( sal_uInt16 nPageId ) const
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
assert( pItem );
|
|
if (!pItem->maAccessibleDescription.isEmpty())
|
|
return pItem->maAccessibleDescription;
|
|
return pItem->maHelpText;
|
|
}
|
|
|
|
void TabControl::SetPageName( sal_uInt16 nPageId, const OUString& rName ) const
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
|
|
if ( pItem )
|
|
pItem->maTabName = rName;
|
|
}
|
|
|
|
OUString TabControl::GetPageName( sal_uInt16 nPageId ) const
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
|
|
if (pItem)
|
|
return pItem->maTabName;
|
|
|
|
return {};
|
|
}
|
|
|
|
void TabControl::SetPageImage( sal_uInt16 i_nPageId, const Image& i_rImage )
|
|
{
|
|
ImplTabItem* pItem = ImplGetItem( i_nPageId );
|
|
|
|
if ( pItem )
|
|
{
|
|
pItem->maTabImage = i_rImage;
|
|
mbFormat = true;
|
|
if ( IsUpdateMode() )
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
tools::Rectangle TabControl::GetTabBounds( sal_uInt16 nPageId ) const
|
|
{
|
|
tools::Rectangle aRet;
|
|
|
|
ImplTabItem* pItem = ImplGetItem( nPageId );
|
|
if (pItem && pItem->m_bVisible)
|
|
aRet = pItem->maRect;
|
|
|
|
return aRet;
|
|
}
|
|
|
|
Size TabControl::ImplCalculateRequisition(sal_uInt16& nHeaderHeight) const
|
|
{
|
|
Size aOptimalPageSize(0, 0);
|
|
|
|
sal_uInt16 nOrigPageId = GetCurPageId();
|
|
for (auto const& item : mpTabCtrlData->maItemList)
|
|
{
|
|
const TabPage *pPage = item.mpTabPage;
|
|
//it's a real nuisance if the page is not inserted yet :-(
|
|
//We need to force all tabs to exist to get overall optimal size for dialog
|
|
if (!pPage)
|
|
{
|
|
TabControl *pThis = const_cast<TabControl*>(this);
|
|
pThis->SetCurPageId(item.id());
|
|
pThis->ActivatePage();
|
|
pPage = item.mpTabPage;
|
|
}
|
|
|
|
if (!pPage)
|
|
continue;
|
|
|
|
Size aPageSize(VclContainer::getLayoutRequisition(*pPage));
|
|
|
|
if (aPageSize.Width() > aOptimalPageSize.Width())
|
|
aOptimalPageSize.setWidth( aPageSize.Width() );
|
|
if (aPageSize.Height() > aOptimalPageSize.Height())
|
|
aOptimalPageSize.setHeight( aPageSize.Height() );
|
|
}
|
|
|
|
//fdo#61940 If we were forced to activate pages in order to on-demand
|
|
//create them to get their optimal size, then switch back to the original
|
|
//page and re-activate it
|
|
if (nOrigPageId != GetCurPageId())
|
|
{
|
|
TabControl *pThis = const_cast<TabControl*>(this);
|
|
pThis->SetCurPageId(nOrigPageId);
|
|
pThis->ActivatePage();
|
|
}
|
|
|
|
tools::Long nTabLabelsBottom = 0, nTabLabelsRight = 0;
|
|
if (mbShowTabs)
|
|
{
|
|
for (sal_uInt16 nPos(0), sizeList(static_cast <sal_uInt16> (mpTabCtrlData->maItemList.size()));
|
|
nPos < sizeList; ++nPos)
|
|
{
|
|
TabControl* pThis = const_cast<TabControl*>(this);
|
|
|
|
tools::Rectangle aTabRect = pThis->ImplGetTabRect(nPos, aOptimalPageSize.Width(), LONG_MAX);
|
|
if (aTabRect.Bottom() > nTabLabelsBottom)
|
|
{
|
|
nTabLabelsBottom = aTabRect.Bottom();
|
|
nHeaderHeight = nTabLabelsBottom;
|
|
}
|
|
if (!aTabRect.IsEmpty() && aTabRect.Right() > nTabLabelsRight)
|
|
nTabLabelsRight = aTabRect.Right();
|
|
}
|
|
}
|
|
|
|
Size aOptimalSize(aOptimalPageSize);
|
|
aOptimalSize.AdjustHeight(nTabLabelsBottom );
|
|
aOptimalSize.setWidth( std::max(nTabLabelsRight, aOptimalSize.Width()) );
|
|
|
|
aOptimalSize.AdjustWidth(TAB_OFFSET * 2 );
|
|
aOptimalSize.AdjustHeight(TAB_OFFSET * 2 );
|
|
|
|
return aOptimalSize;
|
|
}
|
|
|
|
Size TabControl::calculateRequisition() const
|
|
{
|
|
sal_uInt16 nHeaderHeight;
|
|
return ImplCalculateRequisition(nHeaderHeight);
|
|
}
|
|
|
|
Size TabControl::GetOptimalSize() const
|
|
{
|
|
return calculateRequisition();
|
|
}
|
|
|
|
void TabControl::queue_resize(StateChangedType eReason)
|
|
{
|
|
mbLayoutDirty = true;
|
|
Window::queue_resize(eReason);
|
|
}
|
|
|
|
std::vector<sal_uInt16> TabControl::GetPageIDs() const
|
|
{
|
|
std::vector<sal_uInt16> aIDs;
|
|
for (auto const& item : mpTabCtrlData->maItemList)
|
|
{
|
|
aIDs.push_back(item.id());
|
|
}
|
|
|
|
return aIDs;
|
|
}
|
|
|
|
bool TabControl::set_property(const OUString &rKey, const OUString &rValue)
|
|
{
|
|
if (rKey == "show-tabs")
|
|
{
|
|
mbShowTabs = toBool(rValue);
|
|
queue_resize();
|
|
}
|
|
else
|
|
return Control::set_property(rKey, rValue);
|
|
return true;
|
|
}
|
|
|
|
FactoryFunction TabControl::GetUITestFactory() const
|
|
{
|
|
return TabControlUIObject::create;
|
|
}
|
|
|
|
void TabControl::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
|
|
{
|
|
rJsonWriter.put("id", get_id());
|
|
rJsonWriter.put("type", "tabcontrol");
|
|
rJsonWriter.put("selected", GetCurPageId());
|
|
|
|
{
|
|
auto childrenNode = rJsonWriter.startArray("children");
|
|
for (auto id : GetPageIDs())
|
|
{
|
|
TabPage* pChild = GetTabPage(id);
|
|
|
|
if (pChild)
|
|
{
|
|
auto childNode = rJsonWriter.startStruct();
|
|
pChild->DumpAsPropertyTree(rJsonWriter);
|
|
|
|
if (!pChild->IsVisible())
|
|
rJsonWriter.put("hidden", true);
|
|
}
|
|
}
|
|
}
|
|
{
|
|
auto tabsNode = rJsonWriter.startArray("tabs");
|
|
for(auto id : GetPageIDs())
|
|
{
|
|
auto tabNode = rJsonWriter.startStruct();
|
|
rJsonWriter.put("text", GetPageText(id));
|
|
rJsonWriter.put("id", id);
|
|
rJsonWriter.put("name", GetPageName(id));
|
|
}
|
|
}
|
|
}
|
|
|
|
sal_uInt16 NotebookbarTabControlBase::m_nHeaderHeight = 0;
|
|
|
|
IMPL_LINK_NOARG(NotebookbarTabControlBase, OpenMenu, Button*, void)
|
|
{
|
|
m_aIconClickHdl.Call(static_cast<NotebookBar*>(GetParent()->GetParent()));
|
|
}
|
|
|
|
NotebookbarTabControlBase::NotebookbarTabControlBase(vcl::Window* pParent)
|
|
: TabControl(pParent, WB_STDTABCONTROL)
|
|
, bLastContextWasSupported(true)
|
|
, eLastContext(vcl::EnumContext::Context::Any)
|
|
{
|
|
m_pOpenMenu = VclPtr<PushButton>::Create( this , WB_CENTER | WB_VCENTER );
|
|
m_pOpenMenu->SetClickHdl(LINK(this, NotebookbarTabControlBase, OpenMenu));
|
|
m_pOpenMenu->SetModeImage(Image(StockImage::Yes, SV_RESID_BITMAP_NOTEBOOKBAR));
|
|
m_pOpenMenu->SetSizePixel(m_pOpenMenu->GetOptimalSize());
|
|
m_pOpenMenu->Show();
|
|
}
|
|
|
|
NotebookbarTabControlBase::~NotebookbarTabControlBase()
|
|
{
|
|
disposeOnce();
|
|
}
|
|
|
|
void NotebookbarTabControlBase::SetContext( vcl::EnumContext::Context eContext )
|
|
{
|
|
if (eLastContext == eContext)
|
|
return;
|
|
|
|
bool bHandled = false;
|
|
|
|
TabPage* pPage = GetTabPage(mnCurPageId);
|
|
// Try to stay on the current tab (unless the new context has a special tab)
|
|
if (pPage && eLastContext != vcl::EnumContext::Context::Any
|
|
&& pPage->HasContext(vcl::EnumContext::Context::Any) && pPage->IsEnabled())
|
|
{
|
|
bHandled = true;
|
|
}
|
|
|
|
for (int nChild = 0; nChild < GetPageCount(); ++nChild)
|
|
{
|
|
sal_uInt16 nPageId = TabControl::GetPageId(nChild);
|
|
pPage = GetTabPage(nPageId);
|
|
|
|
if (!pPage)
|
|
continue;
|
|
|
|
SetPageVisible(nPageId, pPage->HasContext(eContext) || pPage->HasContext(vcl::EnumContext::Context::Any));
|
|
|
|
if (eContext != vcl::EnumContext::Context::Any
|
|
&& (!bHandled || !pPage->HasContext(vcl::EnumContext::Context::Any))
|
|
&& pPage->HasContext(eContext))
|
|
{
|
|
SetCurPageId(nPageId);
|
|
bHandled = true;
|
|
bLastContextWasSupported = true;
|
|
}
|
|
|
|
if (!bHandled && bLastContextWasSupported
|
|
&& pPage->HasContext(vcl::EnumContext::Context::Default))
|
|
{
|
|
SetCurPageId(nPageId);
|
|
}
|
|
}
|
|
|
|
if (!bHandled)
|
|
bLastContextWasSupported = false;
|
|
eLastContext = eContext;
|
|
|
|
// tdf#152908 Tabbed compact toolbar does not repaint itself when tabs getting removed
|
|
// For unknown reason this is needed by the tabbed compact toolbar for other than gtk
|
|
// vcl backends.
|
|
Resize();
|
|
}
|
|
|
|
void NotebookbarTabControlBase::dispose()
|
|
{
|
|
m_pShortcuts.disposeAndClear();
|
|
m_pOpenMenu.disposeAndClear();
|
|
TabControl::dispose();
|
|
}
|
|
|
|
void NotebookbarTabControlBase::SetToolBox( ToolBox* pToolBox )
|
|
{
|
|
m_pShortcuts.set( pToolBox );
|
|
}
|
|
|
|
void NotebookbarTabControlBase::SetIconClickHdl( Link<NotebookBar*, void> aHdl )
|
|
{
|
|
m_aIconClickHdl = aHdl;
|
|
}
|
|
|
|
static bool lcl_isValidPage(const ImplTabItem& rItem)
|
|
{
|
|
return rItem.m_bVisible && rItem.m_bEnabled;
|
|
}
|
|
|
|
void NotebookbarTabControlBase::ImplActivateTabPage( bool bNext )
|
|
{
|
|
sal_Int32 nCurPos = GetPagePos(GetCurPageId());
|
|
|
|
if (bNext)
|
|
{
|
|
for (sal_Int32 nPos = nCurPos + 1; nPos < GetPageCount(); nPos++)
|
|
if (lcl_isValidPage(mpTabCtrlData->maItemList[nPos]))
|
|
{
|
|
nCurPos = nPos;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (sal_Int32 nPos = nCurPos - 1; nPos >= 0; nPos--)
|
|
if (lcl_isValidPage(mpTabCtrlData->maItemList[nPos]))
|
|
{
|
|
nCurPos = nPos;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SelectTabPage( TabControl::GetPageId( nCurPos ) );
|
|
}
|
|
|
|
bool NotebookbarTabControlBase::ImplPlaceTabs( tools::Long nWidth )
|
|
{
|
|
if ( nWidth <= 0 )
|
|
return false;
|
|
if ( mpTabCtrlData->maItemList.empty() )
|
|
return false;
|
|
if (!m_pOpenMenu || m_pOpenMenu->isDisposed())
|
|
return false;
|
|
|
|
const tools::Long nHamburgerWidth = m_pOpenMenu->GetSizePixel().Width();
|
|
tools::Long nMaxWidth = nWidth - nHamburgerWidth;
|
|
tools::Long nShortcutsWidth = m_pShortcuts != nullptr ? m_pShortcuts->GetSizePixel().getWidth() + 1 : 0;
|
|
tools::Long nFullWidth = nShortcutsWidth;
|
|
|
|
const tools::Long nOffsetX = 2 + nShortcutsWidth;
|
|
const tools::Long nOffsetY = 2;
|
|
|
|
//fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
|
|
//of ugly bare tabs on lines of their own
|
|
|
|
for (auto & item : mpTabCtrlData->maItemList)
|
|
{
|
|
tools::Long nTabWidth = 0;
|
|
if (item.m_bVisible)
|
|
{
|
|
nTabWidth = ImplGetItemSize(&item, nMaxWidth).getWidth();
|
|
if (!item.maText.isEmpty() && nTabWidth < 100)
|
|
nTabWidth = 100;
|
|
}
|
|
nFullWidth += nTabWidth;
|
|
}
|
|
|
|
tools::Long nX = nOffsetX;
|
|
tools::Long nY = nOffsetY;
|
|
|
|
tools::Long nLineWidthAry[100];
|
|
nLineWidthAry[0] = 0;
|
|
|
|
for (auto & item : mpTabCtrlData->maItemList)
|
|
{
|
|
if (!item.m_bVisible)
|
|
continue;
|
|
|
|
Size aSize = ImplGetItemSize( &item, nMaxWidth );
|
|
|
|
// set minimum tab size
|
|
if( nFullWidth < nMaxWidth && !item.maText.isEmpty() && aSize.getWidth() < 100)
|
|
aSize.setWidth( 100 );
|
|
|
|
if( !item.maText.isEmpty() && aSize.getHeight() < 28 )
|
|
aSize.setHeight( 28 );
|
|
|
|
tools::Rectangle aNewRect( Point( nX, nY ), aSize );
|
|
if ( mbSmallInvalidate && (item.maRect != aNewRect) )
|
|
mbSmallInvalidate = false;
|
|
|
|
item.maRect = aNewRect;
|
|
item.mnLine = 0;
|
|
item.mbFullVisible = true;
|
|
|
|
nLineWidthAry[0] += aSize.Width();
|
|
nX += aSize.Width();
|
|
}
|
|
|
|
// we always have only one line of tabs
|
|
// tdf#127610 subtract width of shortcuts from width available for tab items
|
|
lcl_AdjustSingleLineTabs(nMaxWidth - nShortcutsWidth, mpTabCtrlData.get());
|
|
|
|
// position the shortcutbox
|
|
if (m_pShortcuts)
|
|
{
|
|
tools::Long nPosY = (m_nHeaderHeight - m_pShortcuts->GetSizePixel().getHeight()) / 2;
|
|
m_pShortcuts->SetPosPixel(Point(0, nPosY));
|
|
}
|
|
|
|
tools::Long nPosY = (m_nHeaderHeight - m_pOpenMenu->GetSizePixel().getHeight()) / 2;
|
|
// position the menu
|
|
m_pOpenMenu->SetPosPixel(Point(nWidth - nHamburgerWidth, nPosY));
|
|
|
|
return true;
|
|
}
|
|
|
|
Size NotebookbarTabControlBase::calculateRequisition() const
|
|
{
|
|
return TabControl::ImplCalculateRequisition(m_nHeaderHeight);
|
|
}
|
|
|
|
Control* NotebookbarTabControlBase::GetOpenMenu()
|
|
{
|
|
return m_pOpenMenu;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|