Files
libreoffice/vcl/source/window/layout.cxx
Stephan Bergmann f853ec317f Extend loplugin:external to warn about classes
...following up on 314f15bff0 "Extend
loplugin:external to warn about enums".

Cases where free functions were moved into an unnamed namespace along with a
class, to not break ADL, are in:

  filter/source/svg/svgexport.cxx
  sc/source/filter/excel/xelink.cxx
  sc/source/filter/excel/xilink.cxx
  svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx

All other free functions mentioning moved classes appear to be harmless and not
give rise to (silent, even) ADL breakage.  (One remaining TODO in
compilerplugins/clang/external.cxx is that derived classes are not covered by
computeAffectedTypes, even though they could also be affected by ADL-breakage---
but don't seem to be in any acutal case across the code base.)

For friend declarations using elaborate type specifiers, like

  class C1 {};
  class C2 { friend class C1; };

* If C2 (but not C1) is moved into an unnamed namespace, the friend declaration
must be changed to not use an elaborate type specifier (i.e., "friend C1;"; see
C++17 [namespace.memdef]/3: "If the name in a friend declaration is neither
qualified nor a template-id and the declaration is a function or an
elaborated-type-specifier, the lookup to determine whether the entity has been
previously declared shall not consider any scopes outside the innermost
enclosing namespace.")

* If C1 (but not C2) is moved into an unnamed namespace, the friend declaration
must be changed too, see <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71882>
"elaborated-type-specifier friend not looked up in unnamed namespace".

Apart from that, to keep changes simple and mostly mechanical (which should help
avoid regressions), out-of-line definitions of class members have been left in
the enclosing (named) namespace.  But explicit specializations of class
templates had to be moved into the unnamed namespace to appease
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92598> "explicit specialization of
template from unnamed namespace using unqualified-id in enclosing namespace".

Also, accompanying declarations (of e.g. typedefs or static variables) that
could arguably be moved into the unnamed namespace too have been left alone.

And in some cases, mention of affected types in blacklists in other loplugins
needed to be adapted.

And sc/qa/unit/mark_test.cxx uses a hack of including other .cxx, one of which
is sc/source/core/data/segmenttree.cxx where e.g. ScFlatUInt16SegmentsImpl is
not moved into an unnamed namespace (because it is declared in
sc/inc/segmenttree.hxx), but its base ScFlatSegmentsImpl is.  GCC warns about
such combinations with enabled-by-default -Wsubobject-linkage, but "The compiler
doesn’t give this warning for types defined in the main .C file, as those are
unlikely to have multiple definitions."
(<https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/Warning-Options.html>)  The
warned-about classes also don't have multiple definitions in the given test, so
disable the warning when including the .cxx.

Change-Id: Ib694094c0d8168be68f8fe90dfd0acbb66a3f1e4
Reviewed-on: https://gerrit.libreoffice.org/83239
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2019-11-22 12:57:32 +01:00

2681 lines
86 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/.
*/
#include <com/sun/star/accessibility/AccessibleRole.hpp>
#include <o3tl/enumarray.hxx>
#include <o3tl/enumrange.hxx>
#include <vcl/decoview.hxx>
#include <vcl/dialog.hxx>
#include <vcl/layout.hxx>
#include <vcl/stdtext.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <messagedialog.hxx>
#include <window.h>
#include <boost/multi_array.hpp>
#include <boost/property_tree/ptree.hpp>
#include <vcl/vclmedit.hxx>
#include <sal/log.hxx>
VclContainer::VclContainer(vcl::Window *pParent, WinBits nStyle)
: Window(WindowType::CONTAINER)
, m_bLayoutDirty(true)
{
ImplInit(pParent, nStyle, nullptr);
EnableChildTransparentMode();
SetPaintTransparent(true);
SetBackground();
}
sal_uInt16 VclContainer::getDefaultAccessibleRole() const
{
return css::accessibility::AccessibleRole::PANEL;
}
Size VclContainer::GetOptimalSize() const
{
return calculateRequisition();
}
void VclContainer::setLayoutPosSize(vcl::Window &rWindow, const Point &rPos, const Size &rSize)
{
sal_Int32 nBorderWidth = rWindow.get_border_width();
sal_Int32 nLeft = rWindow.get_margin_left() + nBorderWidth;
sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth;
sal_Int32 nRight = rWindow.get_margin_right() + nBorderWidth;
sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth;
Point aPos(rPos.X() + nLeft, rPos.Y() + nTop);
Size aSize(rSize.Width() - nLeft - nRight, rSize.Height() - nTop - nBottom);
rWindow.SetPosSizePixel(aPos, aSize);
}
void VclContainer::setLayoutAllocation(vcl::Window &rChild, const Point &rAllocPos, const Size &rChildAlloc)
{
VclAlign eHalign = rChild.get_halign();
VclAlign eValign = rChild.get_valign();
//typical case
if (eHalign == VclAlign::Fill && eValign == VclAlign::Fill)
{
setLayoutPosSize(rChild, rAllocPos, rChildAlloc);
return;
}
Point aChildPos(rAllocPos);
Size aChildSize(rChildAlloc);
Size aChildPreferredSize(getLayoutRequisition(rChild));
switch (eHalign)
{
case VclAlign::Fill:
break;
case VclAlign::Start:
if (aChildPreferredSize.Width() < rChildAlloc.Width())
aChildSize.setWidth( aChildPreferredSize.Width() );
break;
case VclAlign::End:
if (aChildPreferredSize.Width() < rChildAlloc.Width())
aChildSize.setWidth( aChildPreferredSize.Width() );
aChildPos.AdjustX(rChildAlloc.Width() );
aChildPos.AdjustX( -(aChildSize.Width()) );
break;
case VclAlign::Center:
if (aChildPreferredSize.Width() < aChildSize.Width())
aChildSize.setWidth( aChildPreferredSize.Width() );
aChildPos.AdjustX((rChildAlloc.Width() - aChildSize.Width()) / 2 );
break;
}
switch (eValign)
{
case VclAlign::Fill:
break;
case VclAlign::Start:
if (aChildPreferredSize.Height() < rChildAlloc.Height())
aChildSize.setHeight( aChildPreferredSize.Height() );
break;
case VclAlign::End:
if (aChildPreferredSize.Height() < rChildAlloc.Height())
aChildSize.setHeight( aChildPreferredSize.Height() );
aChildPos.AdjustY(rChildAlloc.Height() );
aChildPos.AdjustY( -(aChildSize.Height()) );
break;
case VclAlign::Center:
if (aChildPreferredSize.Height() < aChildSize.Height())
aChildSize.setHeight( aChildPreferredSize.Height() );
aChildPos.AdjustY((rChildAlloc.Height() - aChildSize.Height()) / 2 );
break;
}
setLayoutPosSize(rChild, aChildPos, aChildSize);
}
namespace
{
Size subtractBorder(const vcl::Window &rWindow, const Size& rSize)
{
sal_Int32 nBorderWidth = rWindow.get_border_width();
sal_Int32 nLeft = rWindow.get_margin_left() + nBorderWidth;
sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth;
sal_Int32 nRight = rWindow.get_margin_right() + nBorderWidth;
sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth;
Size aSize(rSize);
return Size(aSize.Width() + nLeft + nRight, aSize.Height() + nTop + nBottom);
}
}
Size VclContainer::getLayoutRequisition(const vcl::Window &rWindow)
{
return subtractBorder(rWindow, rWindow.get_preferred_size());
}
void VclContainer::SetPosSizePixel(const Point& rAllocPos, const Size& rAllocation)
{
bool bSizeChanged = rAllocation != GetOutputSizePixel();
Window::SetPosSizePixel(rAllocPos, rAllocation);
if (m_bLayoutDirty || bSizeChanged)
{
m_bLayoutDirty = false;
setAllocation(rAllocation);
}
}
void VclContainer::SetPosPixel(const Point& rAllocPos)
{
Point aAllocPos = rAllocPos;
sal_Int32 nBorderWidth = get_border_width();
aAllocPos.AdjustX(nBorderWidth + get_margin_left() );
aAllocPos.AdjustY(nBorderWidth + get_margin_top() );
if (aAllocPos != GetPosPixel())
Window::SetPosPixel(aAllocPos);
}
void VclContainer::SetSizePixel(const Size& rAllocation)
{
Size aAllocation = rAllocation;
sal_Int32 nBorderWidth = get_border_width();
aAllocation.AdjustWidth( -(nBorderWidth*2 + get_margin_left() + get_margin_right()) );
aAllocation.AdjustHeight( -(nBorderWidth*2 + get_margin_top() + get_margin_bottom()) );
bool bSizeChanged = aAllocation != GetSizePixel();
if (bSizeChanged)
Window::SetSizePixel(aAllocation);
if (m_bLayoutDirty || bSizeChanged)
{
m_bLayoutDirty = false;
setAllocation(aAllocation);
}
}
void VclContainer::queue_resize(StateChangedType eReason)
{
m_bLayoutDirty = true;
Window::queue_resize(eReason);
}
// support for screenshot context menu
void VclContainer::Command(const CommandEvent& rCEvt)
{
if (CommandEventId::ContextMenu == rCEvt.GetCommand())
{
auto pParent = GetParent();
if (pParent)
{
CommandEvent aCEvt(rCEvt.GetMousePosPixel() + GetPosPixel(), rCEvt.GetCommand(), rCEvt.IsMouseEvent(), rCEvt.GetEventData());
pParent->Command(aCEvt);
return;
}
}
// call parent (do not consume)
Window::Command(rCEvt);
}
void VclBox::accumulateMaxes(const Size &rChildSize, Size &rSize) const
{
long nSecondaryChildDimension = getSecondaryDimension(rChildSize);
long nSecondaryBoxDimension = getSecondaryDimension(rSize);
setSecondaryDimension(rSize, std::max(nSecondaryChildDimension, nSecondaryBoxDimension));
long nPrimaryChildDimension = getPrimaryDimension(rChildSize);
long nPrimaryBoxDimension = getPrimaryDimension(rSize);
if (m_bHomogeneous)
setPrimaryDimension(rSize, std::max(nPrimaryBoxDimension, nPrimaryChildDimension));
else
setPrimaryDimension(rSize, nPrimaryBoxDimension + nPrimaryChildDimension);
}
Size VclBox::calculateRequisition() const
{
sal_uInt16 nVisibleChildren = 0;
Size aSize;
for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
++nVisibleChildren;
Size aChildSize = getLayoutRequisition(*pChild);
long nPrimaryDimension = getPrimaryDimension(aChildSize);
nPrimaryDimension += pChild->get_padding() * 2;
setPrimaryDimension(aChildSize, nPrimaryDimension);
accumulateMaxes(aChildSize, aSize);
}
return finalizeMaxes(aSize, nVisibleChildren);
}
void VclBox::setAllocation(const Size &rAllocation)
{
sal_uInt16 nVisibleChildren = 0, nExpandChildren = 0;
for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
++nVisibleChildren;
bool bExpand = getPrimaryDimensionChildExpand(*pChild);
if (bExpand)
++nExpandChildren;
}
if (!nVisibleChildren)
return;
long nAllocPrimaryDimension = getPrimaryDimension(rAllocation);
long nHomogeneousDimension = 0, nExtraSpace = 0;
if (m_bHomogeneous)
{
nHomogeneousDimension = (nAllocPrimaryDimension -
(nVisibleChildren - 1) * m_nSpacing) / nVisibleChildren;
}
else if (nExpandChildren)
{
Size aRequisition = calculateRequisition();
nExtraSpace = (getPrimaryDimension(rAllocation) - getPrimaryDimension(aRequisition)) / nExpandChildren;
}
//Split into those we pack from the start onwards, and those we pack from the end backwards
o3tl::enumarray<VclPackType,std::vector<vcl::Window*>> aWindows;
for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
VclPackType ePacking = pChild->get_pack_type();
aWindows[ePacking].push_back(pChild);
}
//See VclBuilder::sortIntoBestTabTraversalOrder for why they are in visual
//order under the parent which requires us to reverse them here to
//pack from the end back
std::reverse(aWindows[VclPackType::End].begin(),aWindows[VclPackType::End].end());
for (VclPackType ePackType : o3tl::enumrange<VclPackType>())
{
Point aPos(0, 0);
if (ePackType == VclPackType::End)
{
long nPrimaryCoordinate = getPrimaryCoordinate(aPos);
setPrimaryCoordinate(aPos, nPrimaryCoordinate + nAllocPrimaryDimension);
}
for (auto const& window : aWindows[ePackType])
{
vcl::Window *pChild = window;
long nPadding = pChild->get_padding();
Size aBoxSize;
if (m_bHomogeneous)
setPrimaryDimension(aBoxSize, nHomogeneousDimension);
else
{
aBoxSize = getLayoutRequisition(*pChild);
long nPrimaryDimension = getPrimaryDimension(aBoxSize);
nPrimaryDimension += nPadding * 2;
if (getPrimaryDimensionChildExpand(*pChild))
nPrimaryDimension += nExtraSpace;
setPrimaryDimension(aBoxSize, nPrimaryDimension);
}
setSecondaryDimension(aBoxSize, getSecondaryDimension(rAllocation));
Point aChildPos(aPos);
Size aChildSize(aBoxSize);
long nPrimaryCoordinate = getPrimaryCoordinate(aPos);
bool bFill = pChild->get_fill();
if (bFill)
{
setPrimaryDimension(aChildSize, std::max(static_cast<long>(1),
getPrimaryDimension(aBoxSize) - nPadding * 2));
setPrimaryCoordinate(aChildPos, nPrimaryCoordinate + nPadding);
}
else
{
setPrimaryDimension(aChildSize,
getPrimaryDimension(getLayoutRequisition(*pChild)));
setPrimaryCoordinate(aChildPos, nPrimaryCoordinate +
(getPrimaryDimension(aBoxSize) - getPrimaryDimension(aChildSize)) / 2);
}
long nDiff = getPrimaryDimension(aBoxSize) + m_nSpacing;
if (ePackType == VclPackType::Start)
setPrimaryCoordinate(aPos, nPrimaryCoordinate + nDiff);
else
{
setPrimaryCoordinate(aPos, nPrimaryCoordinate - nDiff);
setPrimaryCoordinate(aChildPos, getPrimaryCoordinate(aChildPos) -
getPrimaryDimension(aBoxSize));
}
setLayoutAllocation(*pChild, aChildPos, aChildSize);
}
}
}
bool VclBox::set_property(const OString &rKey, const OUString &rValue)
{
if (rKey == "spacing")
set_spacing(rValue.toInt32());
else if (rKey == "homogeneous")
set_homogeneous(toBool(rValue));
else
return VclContainer::set_property(rKey, rValue);
return true;
}
boost::property_tree::ptree VclBox::DumpAsPropertyTree()
{
boost::property_tree::ptree aTree(VclContainer::DumpAsPropertyTree());
aTree.put("vertical", m_bVerticalContainer);
return aTree;
}
sal_uInt16 VclBox::getDefaultAccessibleRole() const
{
#if defined(_WIN32)
//fdo#74284 call Boxes Panels, keep then as "Filler" under
//at least Linux seeing as that's what Gtk does for GtkBoxes
return css::accessibility::AccessibleRole::PANEL;
#else
return css::accessibility::AccessibleRole::FILLER;
#endif
}
#define DEFAULT_CHILD_MIN_WIDTH 85
#define DEFAULT_CHILD_MIN_HEIGHT 27
Size VclBox::finalizeMaxes(const Size &rSize, sal_uInt16 nVisibleChildren) const
{
Size aRet;
if (nVisibleChildren)
{
long nPrimaryDimension = getPrimaryDimension(rSize);
if (m_bHomogeneous)
nPrimaryDimension *= nVisibleChildren;
setPrimaryDimension(aRet, nPrimaryDimension + m_nSpacing * (nVisibleChildren-1));
setSecondaryDimension(aRet, getSecondaryDimension(rSize));
}
return aRet;
}
Size VclButtonBox::addReqGroups(const VclButtonBox::Requisition &rReq) const
{
Size aRet;
long nMainGroupDimension = getPrimaryDimension(rReq.m_aMainGroupSize);
long nSubGroupDimension = getPrimaryDimension(rReq.m_aSubGroupSize);
setPrimaryDimension(aRet, nMainGroupDimension + nSubGroupDimension);
setSecondaryDimension(aRet,
std::max(getSecondaryDimension(rReq.m_aMainGroupSize),
getSecondaryDimension(rReq.m_aSubGroupSize)));
return aRet;
}
static long getMaxNonOutlier(const std::vector<long> &rG, long nAvgDimension)
{
long nMaxDimensionNonOutlier = 0;
for (auto const& nPrimaryChildDimension : rG)
{
if (nPrimaryChildDimension < nAvgDimension * 1.5)
{
nMaxDimensionNonOutlier = std::max(nPrimaryChildDimension,
nMaxDimensionNonOutlier);
}
}
return nMaxDimensionNonOutlier;
}
static std::vector<long> setButtonSizes(const std::vector<long> &rG,
const std::vector<bool> &rNonHomogeneous,
long nAvgDimension, long nMaxNonOutlier, long nMinWidth)
{
std::vector<long> aVec;
//set everything < 1.5 times the average to the same width, leave the
//outliers un-touched
std::vector<bool>::const_iterator aJ = rNonHomogeneous.begin();
auto nNonOutlierWidth = std::max(nMaxNonOutlier, nMinWidth);
for (auto const& nPrimaryChildDimension : rG)
{
bool bNonHomogeneous = *aJ;
if (!bNonHomogeneous && nPrimaryChildDimension < nAvgDimension * 1.5)
{
aVec.push_back(nNonOutlierWidth);
}
else
{
aVec.push_back(std::max(nPrimaryChildDimension, nMinWidth));
}
++aJ;
}
return aVec;
}
VclButtonBox::Requisition VclButtonBox::calculatePrimarySecondaryRequisitions() const
{
Requisition aReq;
Size aMainGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme
Size aSubGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme
long nMinMainGroupPrimary = getPrimaryDimension(aMainGroupSize);
long nMinSubGroupPrimary = getPrimaryDimension(aSubGroupSize);
long nMainGroupSecondary = getSecondaryDimension(aMainGroupSize);
long nSubGroupSecondary = getSecondaryDimension(aSubGroupSize);
bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center);
std::vector<long> aMainGroupSizes;
std::vector<bool> aMainGroupNonHomogeneous;
std::vector<long> aSubGroupSizes;
std::vector<bool> aSubGroupNonHomogeneous;
for (const vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
Size aChildSize = getLayoutRequisition(*pChild);
if (bIgnoreSecondaryPacking || !pChild->get_secondary())
{
//set the max secondary dimension
nMainGroupSecondary = std::max(nMainGroupSecondary, getSecondaryDimension(aChildSize));
//collect the primary dimensions
aMainGroupSizes.push_back(getPrimaryDimension(aChildSize));
aMainGroupNonHomogeneous.push_back(pChild->get_non_homogeneous());
}
else
{
nSubGroupSecondary = std::max(nSubGroupSecondary, getSecondaryDimension(aChildSize));
aSubGroupSizes.push_back(getPrimaryDimension(aChildSize));
aSubGroupNonHomogeneous.push_back(pChild->get_non_homogeneous());
}
}
if (m_bHomogeneous)
{
long nMaxMainDimension = aMainGroupSizes.empty() ? 0 :
*std::max_element(aMainGroupSizes.begin(), aMainGroupSizes.end());
nMaxMainDimension = std::max(nMaxMainDimension, nMinMainGroupPrimary);
long nMaxSubDimension = aSubGroupSizes.empty() ? 0 :
*std::max_element(aSubGroupSizes.begin(), aSubGroupSizes.end());
nMaxSubDimension = std::max(nMaxSubDimension, nMinSubGroupPrimary);
long nMaxDimension = std::max(nMaxMainDimension, nMaxSubDimension);
aReq.m_aMainGroupDimensions.resize(aMainGroupSizes.size(), nMaxDimension);
aReq.m_aSubGroupDimensions.resize(aSubGroupSizes.size(), nMaxDimension);
}
else
{
//Ideally set everything to the same size, but find outlier widgets
//that are way wider than the average and leave them
//at their natural size and set the remainder to share the
//max size of the remaining members of the buttonbox
long nAccDimension = std::accumulate(aMainGroupSizes.begin(),
aMainGroupSizes.end(), 0);
nAccDimension = std::accumulate(aSubGroupSizes.begin(),
aSubGroupSizes.end(), nAccDimension);
size_t nTotalSize = aMainGroupSizes.size() + aSubGroupSizes.size();
long nAvgDimension = nTotalSize ? nAccDimension / nTotalSize : 0;
long nMaxMainNonOutlier = getMaxNonOutlier(aMainGroupSizes,
nAvgDimension);
long nMaxSubNonOutlier = getMaxNonOutlier(aSubGroupSizes,
nAvgDimension);
long nMaxNonOutlier = std::max(nMaxMainNonOutlier, nMaxSubNonOutlier);
aReq.m_aMainGroupDimensions = setButtonSizes(aMainGroupSizes,
aMainGroupNonHomogeneous,
nAvgDimension, nMaxNonOutlier, nMinMainGroupPrimary);
aReq.m_aSubGroupDimensions = setButtonSizes(aSubGroupSizes,
aSubGroupNonHomogeneous,
nAvgDimension, nMaxNonOutlier, nMinSubGroupPrimary);
}
if (!aReq.m_aMainGroupDimensions.empty())
{
setSecondaryDimension(aReq.m_aMainGroupSize, nMainGroupSecondary);
setPrimaryDimension(aReq.m_aMainGroupSize,
std::accumulate(aReq.m_aMainGroupDimensions.begin(),
aReq.m_aMainGroupDimensions.end(), 0));
}
if (!aReq.m_aSubGroupDimensions.empty())
{
setSecondaryDimension(aReq.m_aSubGroupSize, nSubGroupSecondary);
setPrimaryDimension(aReq.m_aSubGroupSize,
std::accumulate(aReq.m_aSubGroupDimensions.begin(),
aReq.m_aSubGroupDimensions.end(), 0));
}
return aReq;
}
Size VclButtonBox::addSpacing(const Size &rSize, sal_uInt16 nVisibleChildren) const
{
Size aRet;
if (nVisibleChildren)
{
long nPrimaryDimension = getPrimaryDimension(rSize);
setPrimaryDimension(aRet,
nPrimaryDimension + m_nSpacing * (nVisibleChildren-1));
setSecondaryDimension(aRet, getSecondaryDimension(rSize));
}
return aRet;
}
Size VclButtonBox::calculateRequisition() const
{
Requisition aReq(calculatePrimarySecondaryRequisitions());
sal_uInt16 nVisibleChildren = aReq.m_aMainGroupDimensions.size() +
aReq.m_aSubGroupDimensions.size();
return addSpacing(addReqGroups(aReq), nVisibleChildren);
}
bool VclButtonBox::set_property(const OString &rKey, const OUString &rValue)
{
if (rKey == "layout-style")
{
VclButtonBoxStyle eStyle = VclButtonBoxStyle::Default;
if (rValue == "spread")
eStyle = VclButtonBoxStyle::Spread;
else if (rValue == "edge")
eStyle = VclButtonBoxStyle::Edge;
else if (rValue == "start")
eStyle = VclButtonBoxStyle::Start;
else if (rValue == "end")
eStyle = VclButtonBoxStyle::End;
else if (rValue == "center")
eStyle = VclButtonBoxStyle::Center;
else
{
SAL_WARN("vcl.layout", "unknown layout style " << rValue);
}
m_eLayoutStyle = eStyle;
}
else
return VclBox::set_property(rKey, rValue);
return true;
}
void VclButtonBox::setAllocation(const Size &rAllocation)
{
Requisition aReq(calculatePrimarySecondaryRequisitions());
if (aReq.m_aMainGroupDimensions.empty() && aReq.m_aSubGroupDimensions.empty())
return;
long nAllocPrimaryDimension = getPrimaryDimension(rAllocation);
Point aMainGroupPos, aOtherGroupPos;
int nSpacing = m_nSpacing;
//To-Do, other layout styles
switch (m_eLayoutStyle)
{
case VclButtonBoxStyle::Start:
if (!aReq.m_aSubGroupDimensions.empty())
{
long nOtherPrimaryDimension = getPrimaryDimension(
addSpacing(aReq.m_aSubGroupSize, aReq.m_aSubGroupDimensions.size()));
setPrimaryCoordinate(aOtherGroupPos,
nAllocPrimaryDimension - nOtherPrimaryDimension);
}
break;
case VclButtonBoxStyle::Spread:
if (!aReq.m_aMainGroupDimensions.empty())
{
long nMainPrimaryDimension = getPrimaryDimension(
addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension;
nExtraSpace += (aReq.m_aMainGroupDimensions.size()-1) * nSpacing;
nSpacing = nExtraSpace/(aReq.m_aMainGroupDimensions.size()+1);
setPrimaryCoordinate(aMainGroupPos, nSpacing);
}
break;
case VclButtonBoxStyle::Center:
if (!aReq.m_aMainGroupDimensions.empty())
{
long nMainPrimaryDimension = getPrimaryDimension(
addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension;
setPrimaryCoordinate(aMainGroupPos, nExtraSpace/2);
}
break;
default:
SAL_WARN("vcl.layout", "todo unimplemented layout style");
[[fallthrough]];
case VclButtonBoxStyle::Default:
case VclButtonBoxStyle::End:
if (!aReq.m_aMainGroupDimensions.empty())
{
long nMainPrimaryDimension = getPrimaryDimension(
addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
setPrimaryCoordinate(aMainGroupPos,
nAllocPrimaryDimension - nMainPrimaryDimension);
}
break;
}
Size aChildSize;
setSecondaryDimension(aChildSize, getSecondaryDimension(rAllocation));
std::vector<long>::const_iterator aPrimaryI = aReq.m_aMainGroupDimensions.begin();
std::vector<long>::const_iterator aSecondaryI = aReq.m_aSubGroupDimensions.begin();
bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center);
for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
if (bIgnoreSecondaryPacking || !pChild->get_secondary())
{
long nMainGroupPrimaryDimension = *aPrimaryI++;
setPrimaryDimension(aChildSize, nMainGroupPrimaryDimension);
setLayoutAllocation(*pChild, aMainGroupPos, aChildSize);
long nPrimaryCoordinate = getPrimaryCoordinate(aMainGroupPos);
setPrimaryCoordinate(aMainGroupPos, nPrimaryCoordinate + nMainGroupPrimaryDimension + nSpacing);
}
else
{
long nSubGroupPrimaryDimension = *aSecondaryI++;
setPrimaryDimension(aChildSize, nSubGroupPrimaryDimension);
setLayoutAllocation(*pChild, aOtherGroupPos, aChildSize);
long nPrimaryCoordinate = getPrimaryCoordinate(aOtherGroupPos);
setPrimaryCoordinate(aOtherGroupPos, nPrimaryCoordinate + nSubGroupPrimaryDimension + nSpacing);
}
}
}
namespace {
struct ButtonOrder
{
const char* m_aType;
int const m_nPriority;
};
}
static int getButtonPriority(const OString &rType)
{
static const size_t N_TYPES = 6;
static const ButtonOrder aDiscardCancelSave[N_TYPES] =
{
{ "/discard", 0 },
{ "/cancel", 1 },
{ "/no", 2 },
{ "/save", 3 },
{ "/yes", 3 },
{ "/ok", 3 }
};
static const ButtonOrder aSaveDiscardCancel[N_TYPES] =
{
{ "/save", 0 },
{ "/yes", 0 },
{ "/ok", 0 },
{ "/discard", 1 },
{ "/no", 1 },
{ "/cancel", 2 }
};
const ButtonOrder* pOrder = &aDiscardCancelSave[0];
const OUString &rEnv = Application::GetDesktopEnvironment();
if (rEnv.equalsIgnoreAsciiCase("windows") ||
rEnv.equalsIgnoreAsciiCase("tde") ||
rEnv.startsWithIgnoreAsciiCase("kde"))
{
pOrder = &aSaveDiscardCancel[0];
}
for (size_t i = 0; i < N_TYPES; ++i, ++pOrder)
{
if (rType.endsWith(pOrder->m_aType))
return pOrder->m_nPriority;
}
return -1;
}
namespace {
class sortButtons
{
bool const m_bVerticalContainer;
public:
explicit sortButtons(bool bVerticalContainer)
: m_bVerticalContainer(bVerticalContainer)
{
}
bool operator()(const vcl::Window *pA, const vcl::Window *pB) const;
};
}
bool sortButtons::operator()(const vcl::Window *pA, const vcl::Window *pB) const
{
//sort into two groups of pack start and pack end
VclPackType ePackA = pA->get_pack_type();
VclPackType ePackB = pB->get_pack_type();
if (ePackA < ePackB)
return true;
if (ePackA > ePackB)
return false;
bool bPackA = pA->get_secondary();
bool bPackB = pB->get_secondary();
if (!m_bVerticalContainer)
{
//for horizontal boxes group secondaries before primaries
if (bPackA > bPackB)
return true;
if (bPackA < bPackB)
return false;
}
else
{
//for vertical boxes group secondaries after primaries
if (bPackA < bPackB)
return true;
if (bPackA > bPackB)
return false;
}
//now order within groups according to platform rules
return getButtonPriority(pA->GetHelpId()) < getButtonPriority(pB->GetHelpId());
}
void VclButtonBox::sort_native_button_order()
{
std::vector<vcl::Window*> aChilds;
for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
aChilds.push_back(pChild);
}
//sort child order within parent so that we match the platform
//button order
std::stable_sort(aChilds.begin(), aChilds.end(), sortButtons(m_bVerticalContainer));
BuilderUtils::reorderWithinParent(aChilds, true);
}
namespace {
struct GridEntry
{
VclPtr<vcl::Window> pChild;
sal_Int32 nSpanWidth;
sal_Int32 nSpanHeight;
int x;
int y;
GridEntry()
: pChild(nullptr)
, nSpanWidth(0)
, nSpanHeight(0)
, x(-1)
, y(-1)
{
}
};
}
typedef boost::multi_array<GridEntry, 2> array_type;
static array_type assembleGrid(const VclGrid &rGrid);
static bool isNullGrid(const array_type& A);
static void calcMaxs(const array_type &A, std::vector<VclGrid::Value> &rWidths, std::vector<VclGrid::Value> &rHeights);
array_type assembleGrid(const VclGrid &rGrid)
{
array_type A;
for (vcl::Window* pChild = rGrid.GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
sal_Int32 nLeftAttach = std::max<sal_Int32>(pChild->get_grid_left_attach(), 0);
sal_Int32 nWidth = pChild->get_grid_width();
sal_Int32 nMaxXPos = nLeftAttach+nWidth-1;
sal_Int32 nTopAttach = std::max<sal_Int32>(pChild->get_grid_top_attach(), 0);
sal_Int32 nHeight = pChild->get_grid_height();
sal_Int32 nMaxYPos = nTopAttach+nHeight-1;
sal_Int32 nCurrentMaxXPos = A.shape()[0]-1;
sal_Int32 nCurrentMaxYPos = A.shape()[1]-1;
if (nMaxXPos > nCurrentMaxXPos || nMaxYPos > nCurrentMaxYPos)
{
nCurrentMaxXPos = std::max(nMaxXPos, nCurrentMaxXPos);
nCurrentMaxYPos = std::max(nMaxYPos, nCurrentMaxYPos);
A.resize(boost::extents[nCurrentMaxXPos+1][nCurrentMaxYPos+1]);
}
GridEntry &rEntry = A[nLeftAttach][nTopAttach];
rEntry.pChild = pChild;
rEntry.nSpanWidth = nWidth;
rEntry.nSpanHeight = nHeight;
rEntry.x = nLeftAttach;
rEntry.y = nTopAttach;
for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
{
for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
{
GridEntry &rSpan = A[nLeftAttach+nSpanX][nTopAttach+nSpanY];
rSpan.x = nLeftAttach;
rSpan.y = nTopAttach;
}
}
}
//see if we have any empty rows/cols
sal_Int32 nMaxX = A.shape()[0];
sal_Int32 nMaxY = A.shape()[1];
std::vector<bool> aNonEmptyCols(nMaxX);
std::vector<bool> aNonEmptyRows(nMaxY);
for (sal_Int32 x = 0; x < nMaxX; ++x)
{
for (sal_Int32 y = 0; y < nMaxY; ++y)
{
const GridEntry &rEntry = A[x][y];
const vcl::Window *pChild = rEntry.pChild;
if (pChild && pChild->IsVisible())
{
aNonEmptyCols[x] = true;
if (rGrid.get_column_homogeneous())
{
for (sal_Int32 nSpanX = 1; nSpanX < rEntry.nSpanWidth; ++nSpanX)
aNonEmptyCols[x+nSpanX] = true;
}
aNonEmptyRows[y] = true;
if (rGrid.get_row_homogeneous())
{
for (sal_Int32 nSpanY = 1; nSpanY < rEntry.nSpanHeight; ++nSpanY)
aNonEmptyRows[y+nSpanY] = true;
}
}
}
}
if (!rGrid.get_column_homogeneous())
{
//reduce the spans of elements that span empty columns
for (sal_Int32 x = 0; x < nMaxX; ++x)
{
std::set<GridEntry*> candidates;
for (sal_Int32 y = 0; y < nMaxY; ++y)
{
if (aNonEmptyCols[x])
continue;
GridEntry &rSpan = A[x][y];
//cell x/y is spanned by the widget at cell rSpan.x/rSpan.y,
//just points back to itself if there's no cell spanning
if ((rSpan.x == -1) || (rSpan.y == -1))
{
//there is no entry for this cell, i.e. this is a cell
//with no widget in it, or spanned by any other widget
continue;
}
GridEntry &rEntry = A[rSpan.x][rSpan.y];
candidates.insert(&rEntry);
}
for (auto const& candidate : candidates)
{
GridEntry *pEntry = candidate;
--pEntry->nSpanWidth;
}
}
}
if (!rGrid.get_row_homogeneous())
{
//reduce the spans of elements that span empty rows
for (sal_Int32 y = 0; y < nMaxY; ++y)
{
std::set<GridEntry*> candidates;
for (sal_Int32 x = 0; x < nMaxX; ++x)
{
if (aNonEmptyRows[y])
continue;
GridEntry &rSpan = A[x][y];
//cell x/y is spanned by the widget at cell rSpan.x/rSpan.y,
//just points back to itself if there's no cell spanning
if ((rSpan.x == -1) || (rSpan.y == -1))
{
//there is no entry for this cell, i.e. this is a cell
//with no widget in it, or spanned by any other widget
continue;
}
GridEntry &rEntry = A[rSpan.x][rSpan.y];
candidates.insert(&rEntry);
}
for (auto const& candidate : candidates)
{
GridEntry *pEntry = candidate;
--pEntry->nSpanHeight;
}
}
}
sal_Int32 nNonEmptyCols = std::count(aNonEmptyCols.begin(), aNonEmptyCols.end(), true);
sal_Int32 nNonEmptyRows = std::count(aNonEmptyRows.begin(), aNonEmptyRows.end(), true);
//make new grid without empty rows and columns
array_type B(boost::extents[nNonEmptyCols][nNonEmptyRows]);
for (sal_Int32 x = 0, x2 = 0; x < nMaxX; ++x)
{
if (!aNonEmptyCols[x])
continue;
for (sal_Int32 y = 0, y2 = 0; y < nMaxY; ++y)
{
if (!aNonEmptyRows[y])
continue;
GridEntry &rEntry = A[x][y];
B[x2][y2++] = rEntry;
}
++x2;
}
return B;
}
static bool isNullGrid(const array_type &A)
{
sal_Int32 nMaxX = A.shape()[0];
sal_Int32 nMaxY = A.shape()[1];
return !nMaxX || !nMaxY;
}
static void calcMaxs(const array_type &A, std::vector<VclGrid::Value> &rWidths, std::vector<VclGrid::Value> &rHeights)
{
sal_Int32 nMaxX = A.shape()[0];
sal_Int32 nMaxY = A.shape()[1];
rWidths.resize(nMaxX);
rHeights.resize(nMaxY);
//first use the non spanning entries to set default width/heights
for (sal_Int32 x = 0; x < nMaxX; ++x)
{
for (sal_Int32 y = 0; y < nMaxY; ++y)
{
const GridEntry &rEntry = A[x][y];
const vcl::Window *pChild = rEntry.pChild;
if (!pChild || !pChild->IsVisible())
continue;
sal_Int32 nWidth = rEntry.nSpanWidth;
sal_Int32 nHeight = rEntry.nSpanHeight;
for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
rWidths[x+nSpanX].m_bExpand |= pChild->get_hexpand();
for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
rHeights[y+nSpanY].m_bExpand |= pChild->get_vexpand();
if (nWidth == 1 || nHeight == 1)
{
Size aChildSize = VclContainer::getLayoutRequisition(*pChild);
if (nWidth == 1)
rWidths[x].m_nValue = std::max(rWidths[x].m_nValue, aChildSize.Width());
if (nHeight == 1)
rHeights[y].m_nValue = std::max(rHeights[y].m_nValue, aChildSize.Height());
}
}
}
//now use the spanning entries and split any extra sizes across expanding rows/cols
//where possible
for (sal_Int32 x = 0; x < nMaxX; ++x)
{
for (sal_Int32 y = 0; y < nMaxY; ++y)
{
const GridEntry &rEntry = A[x][y];
const vcl::Window *pChild = rEntry.pChild;
if (!pChild || !pChild->IsVisible())
continue;
sal_Int32 nWidth = rEntry.nSpanWidth;
sal_Int32 nHeight = rEntry.nSpanHeight;
if (nWidth == 1 && nHeight == 1)
continue;
Size aChildSize = VclContainer::getLayoutRequisition(*pChild);
if (nWidth > 1)
{
sal_Int32 nExistingWidth = 0;
for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
nExistingWidth += rWidths[x+nSpanX].m_nValue;
sal_Int32 nExtraWidth = aChildSize.Width() - nExistingWidth;
if (nExtraWidth > 0)
{
bool bForceExpandAll = false;
sal_Int32 nExpandables = 0;
for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
if (rWidths[x+nSpanX].m_bExpand)
++nExpandables;
if (nExpandables == 0)
{
nExpandables = nWidth;
bForceExpandAll = true;
}
for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
{
if (rWidths[x+nSpanX].m_bExpand || bForceExpandAll)
rWidths[x+nSpanX].m_nValue += nExtraWidth/nExpandables;
}
}
}
if (nHeight > 1)
{
sal_Int32 nExistingHeight = 0;
for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
nExistingHeight += rHeights[y+nSpanY].m_nValue;
sal_Int32 nExtraHeight = aChildSize.Height() - nExistingHeight;
if (nExtraHeight > 0)
{
bool bForceExpandAll = false;
sal_Int32 nExpandables = 0;
for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
if (rHeights[y+nSpanY].m_bExpand)
++nExpandables;
if (nExpandables == 0)
{
nExpandables = nHeight;
bForceExpandAll = true;
}
for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
{
if (rHeights[y+nSpanY].m_bExpand || bForceExpandAll)
rHeights[y+nSpanY].m_nValue += nExtraHeight/nExpandables;
}
}
}
}
}
}
static bool compareValues(const VclGrid::Value &i, const VclGrid::Value &j)
{
return i.m_nValue < j.m_nValue;
}
static VclGrid::Value accumulateValues(const VclGrid::Value &i, const VclGrid::Value &j)
{
VclGrid::Value aRet;
aRet.m_nValue = i.m_nValue + j.m_nValue;
aRet.m_bExpand = i.m_bExpand || j.m_bExpand;
return aRet;
}
Size VclGrid::calculateRequisition() const
{
return calculateRequisitionForSpacings(get_row_spacing(), get_column_spacing());
}
Size VclGrid::calculateRequisitionForSpacings(sal_Int32 nRowSpacing, sal_Int32 nColSpacing) const
{
array_type A = assembleGrid(*this);
if (isNullGrid(A))
return Size();
std::vector<Value> aWidths;
std::vector<Value> aHeights;
calcMaxs(A, aWidths, aHeights);
long nTotalWidth = 0;
if (get_column_homogeneous())
{
nTotalWidth = std::max_element(aWidths.begin(), aWidths.end(), compareValues)->m_nValue;
nTotalWidth *= aWidths.size();
}
else
{
nTotalWidth = std::accumulate(aWidths.begin(), aWidths.end(), Value(), accumulateValues).m_nValue;
}
nTotalWidth += nColSpacing * (aWidths.size()-1);
long nTotalHeight = 0;
if (get_row_homogeneous())
{
nTotalHeight = std::max_element(aHeights.begin(), aHeights.end(), compareValues)->m_nValue;
nTotalHeight *= aHeights.size();
}
else
{
nTotalHeight = std::accumulate(aHeights.begin(), aHeights.end(), Value(), accumulateValues).m_nValue;
}
nTotalHeight += nRowSpacing * (aHeights.size()-1);
return Size(nTotalWidth, nTotalHeight);
}
void VclGrid::setAllocation(const Size& rAllocation)
{
array_type A = assembleGrid(*this);
if (isNullGrid(A))
return;
sal_Int32 nMaxX = A.shape()[0];
sal_Int32 nMaxY = A.shape()[1];
Size aRequisition;
std::vector<Value> aWidths(nMaxX);
std::vector<Value> aHeights(nMaxY);
if (!get_column_homogeneous() || !get_row_homogeneous())
{
aRequisition = calculateRequisition();
calcMaxs(A, aWidths, aHeights);
}
sal_Int32 nColSpacing(get_column_spacing());
sal_Int32 nRowSpacing(get_row_spacing());
long nAvailableWidth = rAllocation.Width();
if (nMaxX)
nAvailableWidth -= nColSpacing * (nMaxX - 1);
if (get_column_homogeneous())
{
for (sal_Int32 x = 0; x < nMaxX; ++x)
aWidths[x].m_nValue = nAvailableWidth/nMaxX;
}
else if (rAllocation.Width() != aRequisition.Width())
{
sal_Int32 nExpandables = 0;
for (sal_Int32 x = 0; x < nMaxX; ++x)
if (aWidths[x].m_bExpand)
++nExpandables;
long nExtraWidthForExpanders = nExpandables ? (rAllocation.Width() - aRequisition.Width()) / nExpandables : 0;
//We don't fit and there is no volunteer to be shrunk
if (!nExpandables && rAllocation.Width() < aRequisition.Width())
{
//first reduce spacing
while (nColSpacing)
{
nColSpacing /= 2;
aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing);
if (aRequisition.Width() <= rAllocation.Width())
break;
}
//share out the remaining pain to everyone
long nExtraWidth = (rAllocation.Width() - aRequisition.Width()) / nMaxX;
for (sal_Int32 x = 0; x < nMaxX; ++x)
aWidths[x].m_nValue += nExtraWidth;
}
if (nExtraWidthForExpanders)
{
for (sal_Int32 x = 0; x < nMaxX; ++x)
if (aWidths[x].m_bExpand)
aWidths[x].m_nValue += nExtraWidthForExpanders;
}
}
long nAvailableHeight = rAllocation.Height();
if (nMaxY)
nAvailableHeight -= nRowSpacing * (nMaxY - 1);
if (get_row_homogeneous())
{
for (sal_Int32 y = 0; y < nMaxY; ++y)
aHeights[y].m_nValue = nAvailableHeight/nMaxY;
}
else if (rAllocation.Height() != aRequisition.Height())
{
sal_Int32 nExpandables = 0;
for (sal_Int32 y = 0; y < nMaxY; ++y)
if (aHeights[y].m_bExpand)
++nExpandables;
long nExtraHeightForExpanders = nExpandables ? (rAllocation.Height() - aRequisition.Height()) / nExpandables : 0;
//We don't fit and there is no volunteer to be shrunk
if (!nExpandables && rAllocation.Height() < aRequisition.Height())
{
//first reduce spacing
while (nRowSpacing)
{
nRowSpacing /= 2;
aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing);
if (aRequisition.Height() <= rAllocation.Height())
break;
}
//share out the remaining pain to everyone
long nExtraHeight = (rAllocation.Height() - aRequisition.Height()) / nMaxY;
for (sal_Int32 y = 0; y < nMaxY; ++y)
aHeights[y].m_nValue += nExtraHeight;
}
if (nExtraHeightForExpanders)
{
for (sal_Int32 y = 0; y < nMaxY; ++y)
if (aHeights[y].m_bExpand)
aHeights[y].m_nValue += nExtraHeightForExpanders;
}
}
Point aAllocPos(0, 0);
for (sal_Int32 x = 0; x < nMaxX; ++x)
{
for (sal_Int32 y = 0; y < nMaxY; ++y)
{
GridEntry &rEntry = A[x][y];
vcl::Window *pChild = rEntry.pChild;
if (pChild)
{
Size aChildAlloc(0, 0);
sal_Int32 nWidth = rEntry.nSpanWidth;
for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
aChildAlloc.AdjustWidth(aWidths[x+nSpanX].m_nValue );
aChildAlloc.AdjustWidth(nColSpacing*(nWidth-1) );
sal_Int32 nHeight = rEntry.nSpanHeight;
for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
aChildAlloc.AdjustHeight(aHeights[y+nSpanY].m_nValue );
aChildAlloc.AdjustHeight(nRowSpacing*(nHeight-1) );
setLayoutAllocation(*pChild, aAllocPos, aChildAlloc);
}
aAllocPos.AdjustY(aHeights[y].m_nValue + nRowSpacing );
}
aAllocPos.AdjustX(aWidths[x].m_nValue + nColSpacing );
aAllocPos.setY( 0 );
}
}
boost::property_tree::ptree VclGrid::DumpAsPropertyTree()
{
boost::property_tree::ptree aTree(VclContainer::DumpAsPropertyTree());
aTree.put("type", "grid");
return aTree;
}
bool toBool(const OUString &rValue)
{
return (!rValue.isEmpty() && (rValue[0] == 't' || rValue[0] == 'T' || rValue[0] == '1'));
}
bool VclGrid::set_property(const OString &rKey, const OUString &rValue)
{
if (rKey == "row-spacing")
set_row_spacing(rValue.toInt32());
else if (rKey == "column-spacing")
set_column_spacing(rValue.toInt32());
else if (rKey == "row-homogeneous")
m_bRowHomogeneous = toBool(rValue);
else if (rKey == "column-homogeneous")
m_bColumnHomogeneous = toBool(rValue);
else if (rKey == "n-rows")
/*nothing to do*/;
else
return VclContainer::set_property(rKey, rValue);
return true;
}
const vcl::Window *VclBin::get_child() const
{
const WindowImpl* pWindowImpl = ImplGetWindowImpl();
return pWindowImpl->mpFirstChild;
}
vcl::Window *VclBin::get_child()
{
return const_cast<vcl::Window*>(const_cast<const VclBin*>(this)->get_child());
}
Size VclBin::calculateRequisition() const
{
const vcl::Window *pChild = get_child();
if (pChild && pChild->IsVisible())
return getLayoutRequisition(*pChild);
return Size(0, 0);
}
void VclBin::setAllocation(const Size &rAllocation)
{
vcl::Window *pChild = get_child();
if (pChild && pChild->IsVisible())
setLayoutAllocation(*pChild, Point(0, 0), rAllocation);
}
VclFrame::~VclFrame()
{
disposeOnce();
}
void VclFrame::dispose()
{
m_pLabel.clear();
VclBin::dispose();
}
//To-Do, hook a DecorationView into VclFrame ?
Size VclFrame::calculateRequisition() const
{
Size aRet(0, 0);
const vcl::Window *pChild = get_child();
const vcl::Window *pLabel = get_label_widget();
if (pChild && pChild->IsVisible())
aRet = getLayoutRequisition(*pChild);
if (pLabel && pLabel->IsVisible())
{
Size aLabelSize = getLayoutRequisition(*pLabel);
aRet.AdjustHeight(aLabelSize.Height() );
aRet.setWidth( std::max(aLabelSize.Width(), aRet.Width()) );
}
return aRet;
}
void VclFrame::setAllocation(const Size &rAllocation)
{
//SetBackground( Color(0xFF, 0x00, 0xFF) );
Size aAllocation(rAllocation);
Point aChildPos;
vcl::Window *pChild = get_child();
vcl::Window *pLabel = get_label_widget();
if (pLabel && pLabel->IsVisible())
{
Size aLabelSize = getLayoutRequisition(*pLabel);
aLabelSize.setHeight( std::min(aLabelSize.Height(), aAllocation.Height()) );
aLabelSize.setWidth( std::min(aLabelSize.Width(), aAllocation.Width()) );
setLayoutAllocation(*pLabel, aChildPos, aLabelSize);
aAllocation.AdjustHeight( -(aLabelSize.Height()) );
aChildPos.AdjustY(aLabelSize.Height() );
}
if (pChild && pChild->IsVisible())
setLayoutAllocation(*pChild, aChildPos, aAllocation);
}
IMPL_LINK(VclFrame, WindowEventListener, VclWindowEvent&, rEvent, void)
{
if (rEvent.GetId() == VclEventId::ObjectDying)
designate_label(nullptr);
}
void VclFrame::designate_label(vcl::Window *pWindow)
{
assert(!pWindow || pWindow->GetParent() == this);
if (m_pLabel)
m_pLabel->RemoveEventListener(LINK(this, VclFrame, WindowEventListener));
m_pLabel = pWindow;
if (m_pLabel)
m_pLabel->AddEventListener(LINK(this, VclFrame, WindowEventListener));
}
const vcl::Window *VclFrame::get_label_widget() const
{
assert(GetChildCount() == 2);
if (m_pLabel)
return m_pLabel;
//The label widget is normally the first (of two) children
const WindowImpl* pWindowImpl = ImplGetWindowImpl();
if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //no label exists
return nullptr;
return pWindowImpl->mpFirstChild;
}
vcl::Window *VclFrame::get_label_widget()
{
return const_cast<vcl::Window*>(const_cast<const VclFrame*>(this)->get_label_widget());
}
const vcl::Window *VclFrame::get_child() const
{
//The child widget is the normally the last (of two) children
const WindowImpl* pWindowImpl = ImplGetWindowImpl();
assert(GetChildCount() == 2 || pWindowImpl->mbInDispose);
if (!m_pLabel)
return pWindowImpl->mpLastChild;
if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //only label exists
return nullptr;
return pWindowImpl->mpLastChild;
}
vcl::Window *VclFrame::get_child()
{
return const_cast<vcl::Window*>(const_cast<const VclFrame*>(this)->get_child());
}
void VclFrame::set_label(const OUString &rLabel)
{
vcl::Window *pLabel = get_label_widget();
assert(pLabel);
pLabel->SetText(rLabel);
}
OUString VclFrame::get_label() const
{
const vcl::Window *pLabel = get_label_widget();
assert(pLabel);
return pLabel->GetText();
}
OUString VclFrame::getDefaultAccessibleName() const
{
const vcl::Window *pLabel = get_label_widget();
if (pLabel)
return pLabel->GetAccessibleName();
return VclBin::getDefaultAccessibleName();
}
boost::property_tree::ptree VclFrame::DumpAsPropertyTree()
{
boost::property_tree::ptree aTree(VclBin::DumpAsPropertyTree());
aTree.put("type", "frame");
return aTree;
}
Size VclAlignment::calculateRequisition() const
{
Size aRet(m_nLeftPadding + m_nRightPadding,
m_nTopPadding + m_nBottomPadding);
const vcl::Window *pChild = get_child();
if (pChild && pChild->IsVisible())
{
Size aChildSize = getLayoutRequisition(*pChild);
aRet.AdjustWidth(aChildSize.Width() );
aRet.AdjustHeight(aChildSize.Height() );
}
return aRet;
}
void VclAlignment::setAllocation(const Size &rAllocation)
{
vcl::Window *pChild = get_child();
if (!pChild || !pChild->IsVisible())
return;
Point aChildPos(m_nLeftPadding, m_nTopPadding);
Size aAllocation;
aAllocation.setWidth( rAllocation.Width() - (m_nLeftPadding + m_nRightPadding) );
aAllocation.setHeight( rAllocation.Height() - (m_nTopPadding + m_nBottomPadding) );
setLayoutAllocation(*pChild, aChildPos, aAllocation);
}
bool VclAlignment::set_property(const OString &rKey, const OUString &rValue)
{
if (rKey == "bottom-padding")
m_nBottomPadding = rValue.toInt32();
else if (rKey == "left-padding")
m_nLeftPadding = rValue.toInt32();
else if (rKey == "right-padding")
m_nRightPadding = rValue.toInt32();
else if (rKey == "top-padding")
m_nTopPadding = rValue.toInt32();
else
return VclBin::set_property(rKey, rValue);
return true;
}
void VclExpander::dispose()
{
m_pDisclosureButton.disposeAndClear();
VclBin::dispose();
}
const vcl::Window *VclExpander::get_child() const
{
const WindowImpl* pWindowImpl = ImplGetWindowImpl();
assert(pWindowImpl->mpFirstChild == m_pDisclosureButton);
return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next);
}
vcl::Window *VclExpander::get_child()
{
return const_cast<vcl::Window*>(const_cast<const VclExpander*>(this)->get_child());
}
Size VclExpander::calculateRequisition() const
{
Size aRet(0, 0);
WindowImpl* pWindowImpl = ImplGetWindowImpl();
const vcl::Window *pChild = get_child();
const vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild ? pWindowImpl->mpLastChild.get() : nullptr;
if (pChild && pChild->IsVisible() && m_pDisclosureButton->IsChecked())
aRet = getLayoutRequisition(*pChild);
Size aExpanderSize = getLayoutRequisition(*m_pDisclosureButton);
if (pLabel && pLabel->IsVisible())
{
Size aLabelSize = getLayoutRequisition(*pLabel);
aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) );
aExpanderSize.AdjustWidth(aLabelSize.Width() );
}
aRet.AdjustHeight(aExpanderSize.Height() );
aRet.setWidth( std::max(aExpanderSize.Width(), aRet.Width()) );
return aRet;
}
void VclExpander::setAllocation(const Size &rAllocation)
{
Size aAllocation(rAllocation);
Point aChildPos;
WindowImpl* pWindowImpl = ImplGetWindowImpl();
//The label widget is the last (of two) children
vcl::Window *pChild = get_child();
vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild.get() ? pWindowImpl->mpLastChild.get() : nullptr;
Size aButtonSize = getLayoutRequisition(*m_pDisclosureButton);
Size aLabelSize;
Size aExpanderSize = aButtonSize;
if (pLabel && pLabel->IsVisible())
{
aLabelSize = getLayoutRequisition(*pLabel);
aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) );
aExpanderSize.AdjustWidth(aLabelSize.Width() );
}
aExpanderSize.setHeight( std::min(aExpanderSize.Height(), aAllocation.Height()) );
aExpanderSize.setWidth( std::min(aExpanderSize.Width(), aAllocation.Width()) );
aButtonSize.setHeight( std::min(aButtonSize.Height(), aExpanderSize.Height()) );
aButtonSize.setWidth( std::min(aButtonSize.Width(), aExpanderSize.Width()) );
long nExtraExpanderHeight = aExpanderSize.Height() - aButtonSize.Height();
Point aButtonPos(aChildPos.X(), aChildPos.Y() + nExtraExpanderHeight/2);
setLayoutAllocation(*m_pDisclosureButton, aButtonPos, aButtonSize);
if (pLabel && pLabel->IsVisible())
{
aLabelSize.setHeight( std::min(aLabelSize.Height(), aExpanderSize.Height()) );
aLabelSize.setWidth( std::min(aLabelSize.Width(),
aExpanderSize.Width() - aButtonSize.Width()) );
long nExtraLabelHeight = aExpanderSize.Height() - aLabelSize.Height();
Point aLabelPos(aChildPos.X() + aButtonSize.Width(), aChildPos.Y() + nExtraLabelHeight/2);
setLayoutAllocation(*pLabel, aLabelPos, aLabelSize);
}
aAllocation.AdjustHeight( -(aExpanderSize.Height()) );
aChildPos.AdjustY(aExpanderSize.Height() );
if (pChild && pChild->IsVisible())
{
if (!m_pDisclosureButton->IsChecked())
aAllocation = Size();
setLayoutAllocation(*pChild, aChildPos, aAllocation);
}
}
bool VclExpander::set_property(const OString &rKey, const OUString &rValue)
{
if (rKey == "expanded")
set_expanded(toBool(rValue));
else if (rKey == "resize-toplevel")
m_bResizeTopLevel = toBool(rValue);
else
return VclBin::set_property(rKey, rValue);
return true;
}
void VclExpander::StateChanged(StateChangedType nType)
{
VclBin::StateChanged( nType );
if (nType == StateChangedType::InitShow)
{
vcl::Window *pChild = get_child();
if (pChild)
pChild->Show(m_pDisclosureButton->IsChecked());
}
}
IMPL_LINK( VclExpander, ClickHdl, CheckBox&, rBtn, void )
{
vcl::Window *pChild = get_child();
if (pChild)
{
pChild->Show(rBtn.IsChecked());
queue_resize();
Dialog* pResizeDialog = m_bResizeTopLevel ? GetParentDialog() : nullptr;
if (pResizeDialog)
pResizeDialog->setOptimalLayoutSize();
}
maExpandedHdl.Call(*this);
}
VclScrolledWindow::VclScrolledWindow(vcl::Window *pParent)
: VclBin(pParent, WB_HIDE | WB_CLIPCHILDREN | WB_AUTOHSCROLL | WB_AUTOVSCROLL | WB_TABSTOP)
, m_bUserManagedScrolling(false)
, m_pVScroll(VclPtr<ScrollBar>::Create(this, WB_HIDE | WB_VERT))
, m_pHScroll(VclPtr<ScrollBar>::Create(this, WB_HIDE | WB_HORZ))
, m_aScrollBarBox(VclPtr<ScrollBarBox>::Create(this, WB_HIDE))
{
SetType(WindowType::SCROLLWINDOW);
Link<ScrollBar*,void> aLink( LINK( this, VclScrolledWindow, ScrollBarHdl ) );
m_pVScroll->SetScrollHdl(aLink);
m_pHScroll->SetScrollHdl(aLink);
}
void VclScrolledWindow::dispose()
{
m_pVScroll.disposeAndClear();
m_pHScroll.disposeAndClear();
m_aScrollBarBox.disposeAndClear();
VclBin::dispose();
}
IMPL_LINK_NOARG(VclScrolledWindow, ScrollBarHdl, ScrollBar*, void)
{
vcl::Window *pChild = get_child();
if (!pChild)
return;
assert(dynamic_cast<VclViewport*>(pChild) && "scrolledwindow child should be a Viewport");
pChild = pChild->GetWindow(GetWindowType::FirstChild);
if (!pChild)
return;
Point aWinPos(-m_pHScroll->GetThumbPos(), -m_pVScroll->GetThumbPos());
pChild->SetPosPixel(aWinPos);
}
const vcl::Window *VclScrolledWindow::get_child() const
{
const WindowImpl* pWindowImpl = ImplGetWindowImpl();
assert(GetChildCount() == 4 || pWindowImpl->mbInDispose);
return pWindowImpl->mpLastChild;
}
vcl::Window *VclScrolledWindow::get_child()
{
return const_cast<vcl::Window*>(const_cast<const VclScrolledWindow*>(this)->get_child());
}
Size VclScrolledWindow::calculateRequisition() const
{
Size aRet(0, 0);
const vcl::Window *pChild = get_child();
if (pChild && pChild->IsVisible())
aRet = getLayoutRequisition(*pChild);
if (GetStyle() & WB_VSCROLL)
aRet.AdjustWidth(getLayoutRequisition(*m_pVScroll).Width() );
if (GetStyle() & WB_HSCROLL)
aRet.AdjustHeight(getLayoutRequisition(*m_pHScroll).Height() );
aRet.AdjustHeight(2);
aRet.AdjustWidth(2);
return aRet;
}
void VclScrolledWindow::InitScrollBars(const Size &rRequest)
{
const vcl::Window *pChild = get_child();
if (!pChild || !pChild->IsVisible())
return;
Size aOutSize(getVisibleChildSize());
m_pVScroll->SetRangeMax(rRequest.Height());
m_pVScroll->SetVisibleSize(aOutSize.Height());
m_pVScroll->SetPageSize(16);
m_pHScroll->SetRangeMax(rRequest.Width());
m_pHScroll->SetVisibleSize(aOutSize.Width());
m_pHScroll->SetPageSize(16);
m_pVScroll->Scroll();
m_pHScroll->Scroll();
}
void VclScrolledWindow::doSetAllocation(const Size &rAllocation, bool bRetryOnFailure)
{
Size aChildReq;
vcl::Window *pChild = get_child();
if (pChild && pChild->IsVisible())
aChildReq = getLayoutRequisition(*pChild);
long nAvailHeight = rAllocation.Height() - 2;
long nAvailWidth = rAllocation.Width() - 2;
// vert. ScrollBar
if (GetStyle() & WB_AUTOVSCROLL)
{
m_pVScroll->Show(nAvailHeight < aChildReq.Height());
}
else if (m_pVScroll->IsVisible() != bool(GetStyle() & WB_VSCROLL))
m_pVScroll->Show((GetStyle() & WB_VSCROLL) != 0);
if (m_pVScroll->IsVisible())
nAvailWidth -= getLayoutRequisition(*m_pVScroll).Width();
// horz. ScrollBar
if (GetStyle() & WB_AUTOHSCROLL)
{
bool bShowHScroll = nAvailWidth < aChildReq.Width();
m_pHScroll->Show(bShowHScroll);
if (bShowHScroll)
nAvailHeight -= getLayoutRequisition(*m_pHScroll).Height();
if (GetStyle() & WB_AUTOVSCROLL)
m_pVScroll->Show(nAvailHeight < aChildReq.Height());
}
else if (m_pHScroll->IsVisible() != bool(GetStyle() & WB_HSCROLL))
m_pHScroll->Show((GetStyle() & WB_HSCROLL) != 0);
Size aInnerSize(rAllocation);
aInnerSize.AdjustWidth(-2);
aInnerSize.AdjustHeight(-2);
bool bBothVisible = m_pVScroll->IsVisible() && m_pHScroll->IsVisible();
auto nScrollBarWidth = getLayoutRequisition(*m_pVScroll).Width();
auto nScrollBarHeight = getLayoutRequisition(*m_pHScroll).Height();
if (m_pVScroll->IsVisible())
{
Point aScrollPos(rAllocation.Width() - nScrollBarWidth - 2, 1);
Size aScrollSize(nScrollBarWidth, rAllocation.Height() - 2);
if (bBothVisible)
aScrollSize.AdjustHeight(-nScrollBarHeight);
setLayoutAllocation(*m_pVScroll, aScrollPos, aScrollSize);
aInnerSize.AdjustWidth( -nScrollBarWidth );
}
if (m_pHScroll->IsVisible())
{
Point aScrollPos(1, rAllocation.Height() - nScrollBarHeight);
Size aScrollSize(rAllocation.Width() - 2, nScrollBarHeight);
if (bBothVisible)
aScrollSize.AdjustWidth(-nScrollBarWidth);
setLayoutAllocation(*m_pHScroll, aScrollPos, aScrollSize);
aInnerSize.AdjustHeight( -nScrollBarHeight );
}
if (bBothVisible)
{
Point aBoxPos(aInnerSize.Width() + 1, aInnerSize.Height() + 1);
m_aScrollBarBox->SetPosSizePixel(aBoxPos, Size(nScrollBarWidth, nScrollBarHeight));
m_aScrollBarBox->Show();
}
else
{
m_aScrollBarBox->Hide();
}
if (pChild && pChild->IsVisible())
{
assert(dynamic_cast<VclViewport*>(pChild) && "scrolledwindow child should be a Viewport");
WinBits nOldBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL));
setLayoutAllocation(*pChild, Point(1, 1), aInnerSize);
// tdf#128758 if the layout allocation triggered some callback that
// immediately invalidates the layout by adding scrollbars then
// normally this would simply retrigger layout and another toplevel
// attempt is made later. But the initial layout attempt blocks
// relayouts, so just make another single effort here.
WinBits nNewBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL));
if (nOldBits != nNewBits && bRetryOnFailure)
{
doSetAllocation(rAllocation, false);
return;
}
}
if (!m_bUserManagedScrolling)
InitScrollBars(aChildReq);
}
void VclScrolledWindow::setAllocation(const Size &rAllocation)
{
doSetAllocation(rAllocation, true);
}
Size VclScrolledWindow::getVisibleChildSize() const
{
Size aRet(GetSizePixel());
if (m_pVScroll->IsVisible())
aRet.AdjustWidth( -(m_pVScroll->GetSizePixel().Width()) );
if (m_pHScroll->IsVisible())
aRet.AdjustHeight( -(m_pHScroll->GetSizePixel().Height()) );
aRet.AdjustHeight(-2);
aRet.AdjustWidth(-2);
return aRet;
}
bool VclScrolledWindow::set_property(const OString &rKey, const OUString &rValue)
{
bool bRet = VclBin::set_property(rKey, rValue);
m_pVScroll->Show((GetStyle() & WB_VSCROLL) != 0);
m_pHScroll->Show((GetStyle() & WB_HSCROLL) != 0);
return bRet;
}
bool VclScrolledWindow::EventNotify(NotifyEvent& rNEvt)
{
bool bDone = false;
if ( rNEvt.GetType() == MouseNotifyEvent::COMMAND )
{
const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
if ( rCEvt.GetCommand() == CommandEventId::Wheel )
{
const CommandWheelData* pData = rCEvt.GetWheelData();
if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
{
bDone = HandleScrollCommand(rCEvt, m_pHScroll, m_pVScroll);
}
}
}
return bDone || VclBin::EventNotify( rNEvt );
}
void VclScrolledWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
VclBin::Paint(rRenderContext, rRect);
DecorationView aDecoView(&rRenderContext);
aDecoView.DrawFrame(tools::Rectangle(Point(0,0), GetSizePixel()));
}
void VclViewport::setAllocation(const Size &rAllocation)
{
vcl::Window *pChild = get_child();
if (pChild && pChild->IsVisible())
{
Size aReq(getLayoutRequisition(*pChild));
aReq.setWidth( std::max(aReq.Width(), rAllocation.Width()) );
aReq.setHeight( std::max(aReq.Height(), rAllocation.Height()) );
Point aKeepPos(pChild->GetPosPixel());
if (m_bInitialAllocation)
{
aKeepPos = Point(0, 0);
m_bInitialAllocation = false;
}
setLayoutAllocation(*pChild, aKeepPos, aReq);
}
}
const vcl::Window *VclEventBox::get_child() const
{
const WindowImpl* pWindowImpl = ImplGetWindowImpl();
assert(pWindowImpl->mpFirstChild.get() == m_aEventBoxHelper.get());
return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next);
}
vcl::Window *VclEventBox::get_child()
{
return const_cast<vcl::Window*>(const_cast<const VclEventBox*>(this)->get_child());
}
void VclEventBox::setAllocation(const Size& rAllocation)
{
Point aChildPos(0, 0);
for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
setLayoutAllocation(*pChild, aChildPos, rAllocation);
}
}
Size VclEventBox::calculateRequisition() const
{
Size aRet(0, 0);
for (const vcl::Window* pChild = get_child(); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
Size aChildSize = getLayoutRequisition(*pChild);
aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) );
aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) );
}
return aRet;
}
void VclEventBox::Command(const CommandEvent&)
{
//discard events by default to block them reaching children
}
VclEventBox::~VclEventBox()
{
disposeOnce();
}
void VclEventBox::dispose()
{
m_aEventBoxHelper.disposeAndClear();
VclBin::dispose();
}
void VclSizeGroup::trigger_queue_resize()
{
//sufficient to trigger one widget to trigger all of them
if (!m_aWindows.empty())
{
(*m_aWindows.begin())->queue_resize();
}
}
void VclSizeGroup::set_ignore_hidden(bool bIgnoreHidden)
{
if (bIgnoreHidden != m_bIgnoreHidden)
{
m_bIgnoreHidden = bIgnoreHidden;
trigger_queue_resize();
}
}
void VclSizeGroup::set_mode(VclSizeGroupMode eMode)
{
if (eMode != m_eMode)
{
m_eMode = eMode;
trigger_queue_resize();
}
}
void VclSizeGroup::set_property(const OString &rKey, const OUString &rValue)
{
if (rKey == "ignore-hidden")
set_ignore_hidden(toBool(rValue));
else if (rKey == "mode")
{
VclSizeGroupMode eMode = VclSizeGroupMode::Horizontal;
if (rValue == "none")
eMode = VclSizeGroupMode::NONE;
else if (rValue == "horizontal")
eMode = VclSizeGroupMode::Horizontal;
else if (rValue == "vertical")
eMode = VclSizeGroupMode::Vertical;
else if (rValue == "both")
eMode = VclSizeGroupMode::Both;
else
{
SAL_WARN("vcl.layout", "unknown size group mode" << rValue);
}
set_mode(eMode);
}
else
{
SAL_INFO("vcl.layout", "unhandled property: " << rKey);
}
}
void MessageDialog::create_message_area()
{
setDeferredProperties();
if (!m_pGrid)
{
VclContainer *pContainer = get_content_area();
assert(pContainer);
m_pGrid.set( VclPtr<VclGrid>::Create(pContainer) );
m_pGrid->reorderWithinParent(0);
m_pGrid->set_column_spacing(12);
m_pMessageBox.set(VclPtr<VclVBox>::Create(m_pGrid));
m_pMessageBox->set_grid_left_attach(1);
m_pMessageBox->set_grid_top_attach(0);
m_pMessageBox->set_spacing(GetTextHeight());
m_pImage = VclPtr<FixedImage>::Create(m_pGrid, WB_CENTER | WB_VCENTER | WB_3DLOOK);
switch (m_eMessageType)
{
case VclMessageType::Info:
m_pImage->SetImage(GetStandardInfoBoxImage());
break;
case VclMessageType::Warning:
m_pImage->SetImage(GetStandardWarningBoxImage());
break;
case VclMessageType::Question:
m_pImage->SetImage(GetStandardQueryBoxImage());
break;
case VclMessageType::Error:
m_pImage->SetImage(GetStandardErrorBoxImage());
break;
}
m_pImage->set_grid_left_attach(0);
m_pImage->set_grid_top_attach(0);
m_pImage->set_valign(VclAlign::Start);
m_pImage->Show();
WinBits nWinStyle = WB_CLIPCHILDREN | WB_LEFT | WB_VCENTER | WB_NOLABEL | WB_NOTABSTOP;
bool bHasSecondaryText = !m_sSecondaryString.isEmpty();
m_pPrimaryMessage = VclPtr<VclMultiLineEdit>::Create(m_pMessageBox, nWinStyle);
m_pPrimaryMessage->SetPaintTransparent(true);
m_pPrimaryMessage->EnableCursor(false);
m_pPrimaryMessage->set_hexpand(true);
m_pPrimaryMessage->SetText(m_sPrimaryString);
m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty());
m_pSecondaryMessage = VclPtr<VclMultiLineEdit>::Create(m_pMessageBox, nWinStyle);
m_pSecondaryMessage->SetPaintTransparent(true);
m_pSecondaryMessage->EnableCursor(false);
m_pSecondaryMessage->set_hexpand(true);
m_pSecondaryMessage->SetText(m_sSecondaryString);
m_pSecondaryMessage->Show(bHasSecondaryText);
MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, bHasSecondaryText ? m_pSecondaryMessage.get() : nullptr);
VclButtonBox *pButtonBox = get_action_area();
assert(pButtonBox);
VclPtr<PushButton> pBtn;
short nDefaultResponse = get_default_response();
switch (m_eButtonsType)
{
case VclButtonsType::NONE:
break;
case VclButtonsType::Ok:
pBtn.set( VclPtr<OKButton>::Create(pButtonBox) );
pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
pBtn->Show();
pBtn->set_id("ok");
add_button(pBtn, RET_OK, true);
nDefaultResponse = RET_OK;
break;
case VclButtonsType::Close:
pBtn.set( VclPtr<CloseButton>::Create(pButtonBox) );
pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
pBtn->Show();
pBtn->set_id("close");
add_button(pBtn, RET_CLOSE, true);
nDefaultResponse = RET_CLOSE;
break;
case VclButtonsType::Cancel:
pBtn.set( VclPtr<CancelButton>::Create(pButtonBox) );
pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
pBtn->Show();
pBtn->set_id("cancel");
add_button(pBtn, RET_CANCEL, true);
nDefaultResponse = RET_CANCEL;
break;
case VclButtonsType::YesNo:
pBtn = VclPtr<PushButton>::Create(pButtonBox);
pBtn->SetText(GetStandardText(StandardButtonType::Yes));
pBtn->Show();
pBtn->set_id("yes");
add_button(pBtn, RET_YES, true);
pBtn.set( VclPtr<PushButton>::Create(pButtonBox) );
pBtn->SetText(GetStandardText(StandardButtonType::No));
pBtn->Show();
pBtn->set_id("no");
add_button(pBtn, RET_NO, true);
nDefaultResponse = RET_NO;
break;
case VclButtonsType::OkCancel:
pBtn.set( VclPtr<OKButton>::Create(pButtonBox) );
pBtn->Show();
pBtn->set_id("ok");
add_button(pBtn, RET_OK, true);
pBtn.set( VclPtr<CancelButton>::Create(pButtonBox) );
pBtn->Show();
pBtn->set_id("cancel");
add_button(pBtn, RET_CANCEL, true);
nDefaultResponse = RET_CANCEL;
break;
}
set_default_response(nDefaultResponse);
pButtonBox->sort_native_button_order();
m_pMessageBox->Show();
m_pGrid->Show();
}
}
void MessageDialog::create_owned_areas()
{
#if defined WNT
set_border_width(3);
#else
set_border_width(12);
#endif
m_pOwnedContentArea.set(VclPtr<VclVBox>::Create(this, false, 24));
set_content_area(m_pOwnedContentArea);
m_pOwnedContentArea->Show();
m_pOwnedActionArea.set( VclPtr<VclHButtonBox>::Create(m_pOwnedContentArea) );
set_action_area(m_pOwnedActionArea);
m_pOwnedActionArea->Show();
}
MessageDialog::MessageDialog(vcl::Window* pParent, WinBits nStyle)
: Dialog(pParent, nStyle)
, m_eButtonsType(VclButtonsType::NONE)
, m_eMessageType(VclMessageType::Info)
, m_pOwnedContentArea(nullptr)
, m_pOwnedActionArea(nullptr)
, m_pGrid(nullptr)
, m_pMessageBox(nullptr)
, m_pImage(nullptr)
, m_pPrimaryMessage(nullptr)
, m_pSecondaryMessage(nullptr)
{
SetType(WindowType::MESSBOX);
}
MessageDialog::MessageDialog(vcl::Window* pParent,
const OUString &rMessage,
VclMessageType eMessageType,
VclButtonsType eButtonsType)
: Dialog(pParent, WB_MOVEABLE | WB_3DLOOK | WB_CLOSEABLE)
, m_eButtonsType(eButtonsType)
, m_eMessageType(eMessageType)
, m_pGrid(nullptr)
, m_pMessageBox(nullptr)
, m_pImage(nullptr)
, m_pPrimaryMessage(nullptr)
, m_pSecondaryMessage(nullptr)
, m_sPrimaryString(rMessage)
{
SetType(WindowType::MESSBOX);
create_owned_areas();
create_message_area();
switch (m_eMessageType)
{
case VclMessageType::Info:
SetText(GetStandardInfoBoxText());
break;
case VclMessageType::Warning:
SetText(GetStandardWarningBoxText());
break;
case VclMessageType::Question:
SetText(GetStandardQueryBoxText());
break;
case VclMessageType::Error:
SetText(GetStandardErrorBoxText());
break;
}
}
void MessageDialog::dispose()
{
disposeOwnedButtons();
m_pPrimaryMessage.disposeAndClear();
m_pSecondaryMessage.disposeAndClear();
m_pImage.disposeAndClear();
m_pMessageBox.disposeAndClear();
m_pGrid.disposeAndClear();
m_pOwnedActionArea.disposeAndClear();
m_pOwnedContentArea.disposeAndClear();
Dialog::dispose();
}
MessageDialog::~MessageDialog()
{
disposeOnce();
}
void MessageDialog::SetMessagesWidths(vcl::Window const *pParent,
VclMultiLineEdit *pPrimaryMessage, VclMultiLineEdit *pSecondaryMessage)
{
if (pSecondaryMessage)
{
assert(pPrimaryMessage);
vcl::Font aFont = pParent->GetSettings().GetStyleSettings().GetLabelFont();
aFont.SetFontSize(Size(0, aFont.GetFontSize().Height() * 1.2));
aFont.SetWeight(WEIGHT_BOLD);
pPrimaryMessage->SetControlFont(aFont);
pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 44);
pSecondaryMessage->SetMaxTextWidth(pSecondaryMessage->approximate_char_width() * 60);
}
else
pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 60);
}
OUString const & MessageDialog::get_primary_text() const
{
const_cast<MessageDialog*>(this)->setDeferredProperties();
return m_sPrimaryString;
}
OUString const & MessageDialog::get_secondary_text() const
{
const_cast<MessageDialog*>(this)->setDeferredProperties();
return m_sSecondaryString;
}
bool MessageDialog::set_property(const OString &rKey, const OUString &rValue)
{
if (rKey == "text")
set_primary_text(rValue);
else if (rKey == "secondary-text")
set_secondary_text(rValue);
else if (rKey == "message-type")
{
VclMessageType eMode = VclMessageType::Info;
if (rValue == "info")
eMode = VclMessageType::Info;
else if (rValue == "warning")
eMode = VclMessageType::Warning;
else if (rValue == "question")
eMode = VclMessageType::Question;
else if (rValue == "error")
eMode = VclMessageType::Error;
else
{
SAL_WARN("vcl.layout", "unknown message type mode" << rValue);
}
m_eMessageType = eMode;
}
else if (rKey == "buttons")
{
VclButtonsType eMode = VclButtonsType::NONE;
if (rValue == "none")
eMode = VclButtonsType::NONE;
else if (rValue == "ok")
eMode = VclButtonsType::Ok;
else if (rValue == "cancel")
eMode = VclButtonsType::Cancel;
else if (rValue == "close")
eMode = VclButtonsType::Close;
else if (rValue == "yes-no")
eMode = VclButtonsType::YesNo;
else if (rValue == "ok-cancel")
eMode = VclButtonsType::OkCancel;
else
{
SAL_WARN("vcl.layout", "unknown buttons type mode" << rValue);
}
m_eButtonsType = eMode;
}
else
return Dialog::set_property(rKey, rValue);
return true;
}
void MessageDialog::set_primary_text(const OUString &rPrimaryString)
{
m_sPrimaryString = rPrimaryString;
if (m_pPrimaryMessage)
{
m_pPrimaryMessage->SetText(m_sPrimaryString);
m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty());
MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr);
}
}
void MessageDialog::set_secondary_text(const OUString &rSecondaryString)
{
m_sSecondaryString = rSecondaryString;
if (m_pSecondaryMessage)
{
m_pSecondaryMessage->SetText("\n" + m_sSecondaryString);
m_pSecondaryMessage->Show(!m_sSecondaryString.isEmpty());
MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr);
}
}
void MessageDialog::StateChanged(StateChangedType nType)
{
Dialog::StateChanged(nType);
if (nType == StateChangedType::InitShow)
{
// MessageBox should be at least as wide as to see the title
auto nTitleWidth = CalcTitleWidth();
// Extra-Width for Close button
nTitleWidth += mpWindowImpl->mnTopBorder;
if (get_preferred_size().Width() < nTitleWidth)
{
set_width_request(nTitleWidth);
DoInitialLayout();
}
}
}
VclPaned::VclPaned(vcl::Window *pParent, bool bVertical)
: VclContainer(pParent, WB_HIDE | WB_CLIPCHILDREN)
, m_pSplitter(VclPtr<Splitter>::Create(this, bVertical ? WB_VSCROLL : WB_HSCROLL))
, m_nPosition(-1)
{
m_pSplitter->SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetFaceColor()));
m_pSplitter->Show();
}
void VclPaned::dispose()
{
m_pSplitter.disposeAndClear();
VclContainer::dispose();
}
VclVPaned::VclVPaned(vcl::Window *pParent)
: VclPaned(pParent, true)
{
m_pSplitter->SetSplitHdl(LINK(this, VclVPaned, SplitHdl));
}
IMPL_LINK(VclVPaned, SplitHdl, Splitter*, pSplitter, void)
{
long nSize = pSplitter->GetSplitPosPixel();
Size aSplitterSize(m_pSplitter->GetSizePixel());
Size aAllocation(GetSizePixel());
arrange(aAllocation, nSize, aAllocation.Height() - nSize - aSplitterSize.Height());
}
void VclVPaned::arrange(const Size& rAllocation, long nFirstHeight, long nSecondHeight)
{
Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height());
Size aFirstChildSize(rAllocation.Width(), nFirstHeight);
Size aSecondChildSize(rAllocation.Width(), nSecondHeight);
int nElement = 0;
for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
if (nElement == 0)
{
Point aSplitterPos(0, aFirstChildSize.Height());
setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize);
set_position(aSplitterPos.Y() + aSplitterSize.Height() / 2);
}
else if (nElement == 1)
{
Point aChildPos(0, 0);
setLayoutAllocation(*pChild, aChildPos, aFirstChildSize);
}
else if (nElement == 2)
{
Point aChildPos(0, aFirstChildSize.Height() + aSplitterSize.Height());
setLayoutAllocation(*pChild, aChildPos, aSecondChildSize);
}
++nElement;
}
}
void VclVPaned::setAllocation(const Size& rAllocation)
{
//supporting "shrink" could be done by adjusting the allowed drag rectangle
m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation));
Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height());
const long nHeight = rAllocation.Height() - aSplitterSize.Height();
long nFirstHeight = 0;
long nSecondHeight = 0;
bool bFirstCanResize = true;
bool bSecondCanResize = true;
const bool bInitialAllocation = get_position() < 0;
int nElement = 0;
for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
if (nElement == 1)
{
if (bInitialAllocation)
nFirstHeight = getLayoutRequisition(*pChild).Height();
else
nFirstHeight = pChild->GetSizePixel().Height();
bFirstCanResize = pChild->get_expand();
}
else if (nElement == 2)
{
if (bInitialAllocation)
nSecondHeight = getLayoutRequisition(*pChild).Height();
else
nSecondHeight = pChild->GetSizePixel().Height();
bSecondCanResize = pChild->get_expand();
}
++nElement;
}
long nHeightRequest = nFirstHeight + nSecondHeight;
long nHeightDiff = nHeight - nHeightRequest;
if (bFirstCanResize == bSecondCanResize)
nFirstHeight += nHeightDiff/2;
else if (bFirstCanResize)
nFirstHeight += nHeightDiff;
arrange(rAllocation, nFirstHeight, rAllocation.Height() - nFirstHeight - aSplitterSize.Height());
}
Size VclVPaned::calculateRequisition() const
{
Size aRet(0, 0);
for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
Size aChildSize = getLayoutRequisition(*pChild);
aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) );
aRet.AdjustHeight(aChildSize.Height() );
}
return aRet;
}
VclHPaned::VclHPaned(vcl::Window *pParent)
: VclPaned(pParent, false)
{
m_pSplitter->SetSplitHdl(LINK(this, VclHPaned, SplitHdl));
}
IMPL_LINK(VclHPaned, SplitHdl, Splitter*, pSplitter, void)
{
long nSize = pSplitter->GetSplitPosPixel();
Size aSplitterSize(m_pSplitter->GetSizePixel());
Size aAllocation(GetSizePixel());
arrange(aAllocation, nSize, aAllocation.Width() - nSize - aSplitterSize.Width());
}
void VclHPaned::arrange(const Size& rAllocation, long nFirstWidth, long nSecondWidth)
{
Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height());
Size aFirstChildSize(nFirstWidth, rAllocation.Height());
Size aSecondChildSize(nSecondWidth, rAllocation.Height());
int nElement = 0;
for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
if (nElement == 0)
{
Point aSplitterPos(aFirstChildSize.Width(), 0);
setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize);
set_position(aSplitterPos.X() + aSplitterSize.Width() / 2);
}
else if (nElement == 1)
{
Point aChildPos(0, 0);
setLayoutAllocation(*pChild, aChildPos, aFirstChildSize);
}
else if (nElement == 2)
{
Point aChildPos(aFirstChildSize.Width() + aSplitterSize.Width(), 0);
setLayoutAllocation(*pChild, aChildPos, aSecondChildSize);
}
++nElement;
}
}
void VclHPaned::setAllocation(const Size& rAllocation)
{
//supporting "shrink" could be done by adjusting the allowed drag rectangle
m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation));
Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height());
const long nWidth = rAllocation.Width() - aSplitterSize.Width();
long nFirstWidth = 0;
long nSecondWidth = 0;
bool bFirstCanResize = true;
bool bSecondCanResize = true;
const bool bInitialAllocation = get_position() < 0;
int nElement = 0;
for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
if (nElement == 1)
{
if (bInitialAllocation)
nFirstWidth = getLayoutRequisition(*pChild).Width();
else
nFirstWidth = pChild->GetSizePixel().Width();
bFirstCanResize = pChild->get_expand();
}
else if (nElement == 2)
{
if (bInitialAllocation)
nSecondWidth = getLayoutRequisition(*pChild).Width();
else
nSecondWidth = pChild->GetSizePixel().Width();
bSecondCanResize = pChild->get_expand();
}
++nElement;
}
long nWidthRequest = nFirstWidth + nSecondWidth;
long nWidthDiff = nWidth - nWidthRequest;
if (bFirstCanResize == bSecondCanResize)
nFirstWidth += nWidthDiff/2;
else if (bFirstCanResize)
nFirstWidth += nWidthDiff;
arrange(rAllocation, nFirstWidth, rAllocation.Width() - nFirstWidth - aSplitterSize.Width());
}
Size VclHPaned::calculateRequisition() const
{
Size aRet(0, 0);
for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
Size aChildSize = getLayoutRequisition(*pChild);
aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) );
aRet.AdjustWidth(aChildSize.Width() );
}
return aRet;
}
Size getLegacyBestSizeForChildren(const vcl::Window &rWindow)
{
tools::Rectangle aBounds;
for (const vcl::Window* pChild = rWindow.GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue;
tools::Rectangle aChildBounds(pChild->GetPosPixel(), pChild->GetSizePixel());
aBounds.Union(aChildBounds);
}
if (aBounds.IsEmpty())
return rWindow.GetSizePixel();
Size aRet(aBounds.GetSize());
Point aTopLeft(aBounds.TopLeft());
aRet.AdjustWidth(aTopLeft.X()*2 );
aRet.AdjustHeight(aTopLeft.Y()*2 );
return aRet;
}
vcl::Window* getNonLayoutParent(vcl::Window *pWindow)
{
while (pWindow)
{
pWindow = pWindow->GetParent();
if (!pWindow || !isContainerWindow(*pWindow))
break;
}
return pWindow;
}
bool isVisibleInLayout(const vcl::Window *pWindow)
{
bool bVisible = true;
while (bVisible)
{
bVisible = pWindow->IsVisible();
pWindow = pWindow->GetParent();
if (!pWindow || !isContainerWindow(*pWindow))
break;
}
return bVisible;
}
bool isEnabledInLayout(const vcl::Window *pWindow)
{
bool bEnabled = true;
while (bEnabled)
{
bEnabled = pWindow->IsEnabled();
pWindow = pWindow->GetParent();
if (!pWindow || !isContainerWindow(*pWindow))
break;
}
return bEnabled;
}
bool isLayoutEnabled(const vcl::Window *pWindow)
{
//Child is a container => we're layout enabled
const vcl::Window *pChild = pWindow ? pWindow->GetWindow(GetWindowType::FirstChild) : nullptr;
return pChild && isContainerWindow(*pChild) && !pChild->GetWindow(GetWindowType::Next);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */