diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /svtools/source/control/valueset.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'svtools/source/control/valueset.cxx')
-rw-r--r-- | svtools/source/control/valueset.cxx | 1982 |
1 files changed, 1982 insertions, 0 deletions
diff --git a/svtools/source/control/valueset.cxx b/svtools/source/control/valueset.cxx new file mode 100644 index 0000000000..87696d1a78 --- /dev/null +++ b/svtools/source/control/valueset.cxx @@ -0,0 +1,1982 @@ +/* -*- 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 <o3tl/safeint.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <tools/debug.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/decoview.hxx> +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/virdev.hxx> + +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include "valueimp.hxx" + +#include <svtools/valueset.hxx> + +#include <uiobject.hxx> +#include <vcl/uitest/logger.hxx> +#include <vcl/uitest/eventdescription.hxx> + +using namespace css::uno; +using namespace css::lang; +using namespace css::accessibility; + +namespace +{ +void collectUIInformation( const OUString& aID , const OUString& aParentID , const OUString& aPos ) +{ + EventDescription aDescription; + aDescription.aID = aID ; + aDescription.aParameters = {{"POS", aPos }}; + aDescription.aAction = "SELECT"; + aDescription.aKeyWord = "ValueSet"; + aDescription.aParent = aParentID; + UITestLogger::getInstance().logEvent(aDescription); +} + +enum +{ + ITEM_OFFSET = 4, + ITEM_OFFSET_DOUBLE = 6, + NAME_LINE_OFF_X = 2, + NAME_LINE_OFF_Y = 2, + NAME_LINE_HEIGHT = 2, + NAME_OFFSET = 2, +}; + +} + +ValueSet::ValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow) + : maVirDev( VclPtr<VirtualDevice>::Create()) + , mxScrolledWindow(std::move(pScrolledWindow)) + , mnHighItemId(0) + , maColor(COL_TRANSPARENT) + , mnStyle(0) + , mbFormat(true) + , mbHighlight(false) +{ + mnItemWidth = 0; + mnItemHeight = 0; + mnTextOffset = 0; + mnVisLines = 0; + mnLines = 0; + mnUserItemWidth = 0; + mnUserItemHeight = 0; + mnFirstLine = 0; + mnSelItemId = 0; + mnSavedItemId = -1; + mnCols = 0; + mnCurCol = 0; + mnUserCols = 0; + mnUserVisLines = 0; + mnSpacing = 0; + mnFrameStyle = DrawFrameStyle::NONE; + mbNoSelection = true; + mbDoubleSel = false; + mbScroll = false; + mbFullMode = true; + mbEdgeBlending = false; + mbHasVisibleItems = false; + + if (mxScrolledWindow) + mxScrolledWindow->connect_vadjustment_changed(LINK(this, ValueSet, ImplScrollHdl)); +} + +void ValueSet::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + // #106446#, #106601# force mirroring of virtual device + maVirDev->EnableRTL(pDrawingArea->get_direction()); +} + +Reference<XAccessible> ValueSet::CreateAccessible() +{ + if (!mxAccessible) + mxAccessible.set(new ValueSetAcc(this)); + return mxAccessible; +} + +ValueSet::~ValueSet() +{ + if (mxAccessible) + mxAccessible->Invalidate(); + + ImplDeleteItems(); +} + +void ValueSet::ImplDeleteItems() +{ + const size_t n = mItemList.size(); + + for ( size_t i = 0; i < n; ++i ) + { + ValueSetItem* pItem = mItemList[i].get(); + if ( pItem->mbVisible && ImplHasAccessibleListeners() ) + { + Any aOldAny; + Any aNewAny; + + aOldAny <<= pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ ); + ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny); + } + + mItemList[i].reset(); + } + + mItemList.clear(); +} + +void ValueSet::Select() +{ + collectUIInformation(GetDrawingArea()->get_buildable_name() , GetDrawingArea()->get_help_id() , OUString::number(GetSelectedItemId())); + maSelectHdl.Call( this ); +} + +void ValueSet::UserDraw( const UserDrawEvent& ) +{ +} + +size_t ValueSet::ImplGetItem( const Point& rPos ) const +{ + if (!mbHasVisibleItems) + { + return VALUESET_ITEM_NOTFOUND; + } + + if (mpNoneItem && maNoneItemRect.Contains(rPos)) + { + return VALUESET_ITEM_NONEITEM; + } + + if (maItemListRect.Contains(rPos)) + { + const int xc = rPos.X() - maItemListRect.Left(); + const int yc = rPos.Y() - maItemListRect.Top(); + // The point is inside the area of item list, + // let's find the containing item. + const int col = xc / (mnItemWidth + mnSpacing); + const int x = xc % (mnItemWidth + mnSpacing); + const int row = yc / (mnItemHeight + mnSpacing); + const int y = yc % (mnItemHeight + mnSpacing); + + if (x < mnItemWidth && y < mnItemHeight) + { + // the point is inside item rect and not inside spacing + const size_t item = (mnFirstLine + row) * static_cast<size_t>(mnCols) + col; + if (item < mItemList.size()) + { + return item; + } + } + } + + return VALUESET_ITEM_NOTFOUND; +} + +ValueSetItem* ValueSet::ImplGetItem( size_t nPos ) +{ + if (nPos == VALUESET_ITEM_NONEITEM) + return mpNoneItem.get(); + else + return (nPos < mItemList.size()) ? mItemList[nPos].get() : nullptr; +} + +ValueSetItem* ValueSet::ImplGetFirstItem() +{ + return !mItemList.empty() ? mItemList[0].get() : nullptr; +} + +sal_uInt16 ValueSet::ImplGetVisibleItemCount() const +{ + sal_uInt16 nRet = 0; + const size_t nItemCount = mItemList.size(); + + for ( size_t n = 0; n < nItemCount; ++n ) + { + if ( mItemList[n]->mbVisible ) + ++nRet; + } + + return nRet; +} + +void ValueSet::ImplFireAccessibleEvent( short nEventId, const Any& rOldValue, const Any& rNewValue ) +{ + if( mxAccessible ) + mxAccessible->FireAccessibleEvent( nEventId, rOldValue, rNewValue ); +} + +bool ValueSet::ImplHasAccessibleListeners() const +{ + return mxAccessible && mxAccessible->HasAccessibleListeners(); +} + +IMPL_LINK(ValueSet, ImplScrollHdl, weld::ScrolledWindow&, rScrollWin, void) +{ + auto nNewFirstLine = rScrollWin.vadjustment_get_value(); + if ( nNewFirstLine != mnFirstLine ) + { + mnFirstLine = nNewFirstLine; + mbFormat = true; + Invalidate(); + } +} + +void ValueSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + rRenderContext.SetBackground(Application::GetSettings().GetStyleSettings().GetFaceColor()); + rRenderContext.Erase(); + ImplDraw(rRenderContext); +} + +void ValueSet::GetFocus() +{ + SAL_INFO("svtools", "value set getting focus"); + Invalidate(); + CustomWidgetController::GetFocus(); + + // Tell the accessible object that we got the focus. + if (mxAccessible) + mxAccessible->GetFocus(); +} + +void ValueSet::LoseFocus() +{ + SAL_INFO("svtools", "value set losing focus"); + Invalidate(); + CustomWidgetController::LoseFocus(); + + // Tell the accessible object that we lost the focus. + if( mxAccessible ) + mxAccessible->LoseFocus(); +} + +void ValueSet::Resize() +{ + mbFormat = true; + if ( IsReallyVisible() && IsUpdateMode() ) + Invalidate(); + CustomWidgetController::Resize(); +} + +bool ValueSet::KeyInput( const KeyEvent& rKeyEvent ) +{ + size_t nLastItem = mItemList.size(); + + if ( !nLastItem || !ImplGetFirstItem() ) + return CustomWidgetController::KeyInput(rKeyEvent); + + if (mbFormat) + Invalidate(); + + --nLastItem; + + const size_t nCurPos + = mnSelItemId ? GetItemPos(mnSelItemId) : (mpNoneItem ? VALUESET_ITEM_NONEITEM : 0); + size_t nItemPos = VALUESET_ITEM_NOTFOUND; + size_t nVStep = mnCols; + + switch (rKeyEvent.GetKeyCode().GetCode()) + { + case KEY_HOME: + nItemPos = mpNoneItem ? VALUESET_ITEM_NONEITEM : 0; + break; + + case KEY_END: + nItemPos = nLastItem; + break; + + case KEY_LEFT: + if (nCurPos != VALUESET_ITEM_NONEITEM) + { + if (nCurPos) + { + nItemPos = nCurPos-1; + } + else if (mpNoneItem) + { + nItemPos = VALUESET_ITEM_NONEITEM; + } + } + break; + + case KEY_RIGHT: + if (nCurPos < nLastItem) + { + if (nCurPos == VALUESET_ITEM_NONEITEM) + { + nItemPos = 0; + } + else + { + nItemPos = nCurPos+1; + } + } + break; + + case KEY_PAGEUP: + if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2()) + { + return CustomWidgetController::KeyInput(rKeyEvent); + } + nVStep *= mnVisLines; + [[fallthrough]]; + case KEY_UP: + if (nCurPos != VALUESET_ITEM_NONEITEM) + { + if (nCurPos == nLastItem) + { + const size_t nCol = mnCols ? nLastItem % mnCols : 0; + if (nCol < mnCurCol) + { + // Move to previous row/page, keeping the old column + nVStep -= mnCurCol - nCol; + } + } + if (nCurPos >= nVStep) + { + // Go up of a whole page + nItemPos = nCurPos-nVStep; + } + else if (mpNoneItem) + { + nItemPos = VALUESET_ITEM_NONEITEM; + } + else if (nCurPos > mnCols) + { + // Go to same column in first row + nItemPos = nCurPos % mnCols; + } + } + break; + + case KEY_PAGEDOWN: + if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2()) + { + return CustomWidgetController::KeyInput(rKeyEvent); + } + nVStep *= mnVisLines; + [[fallthrough]]; + case KEY_DOWN: + if (nCurPos != nLastItem) + { + if (nCurPos == VALUESET_ITEM_NONEITEM) + { + nItemPos = nVStep-mnCols+mnCurCol; + } + else + { + nItemPos = nCurPos+nVStep; + } + if (nItemPos > nLastItem) + { + nItemPos = nLastItem; + } + } + break; + + case KEY_RETURN: + if (GetStyle() & WB_NO_DIRECTSELECT) + { + // tdf#142479 on return select the entry the cursor is in + // before calling Select + if (nCurPos != VALUESET_ITEM_NONEITEM) + { + const sal_uInt16 nItemId = GetItemId(nCurPos); + if (nItemId != mnSelItemId) + SelectItem(nItemId); + } + Select(); + break; + } + [[fallthrough]]; + default: + return CustomWidgetController::KeyInput(rKeyEvent); + } + + if ( nItemPos == VALUESET_ITEM_NOTFOUND ) + return true; + + if ( nItemPos!=VALUESET_ITEM_NONEITEM && nItemPos<nLastItem ) + { + // update current column only in case of a new position + // which is also not a "specially" handled one. + mnCurCol = mnCols ? nItemPos % mnCols : 0; + } + const sal_uInt16 nItemId = (nItemPos != VALUESET_ITEM_NONEITEM) ? GetItemId( nItemPos ) : 0; + if ( nItemId != mnSelItemId ) + { + SelectItem( nItemId ); + if (!(GetStyle() & WB_NO_DIRECTSELECT)) + { + // select only if WB_NO_DIRECTSELECT is not set + Select(); + } + } + + return true; +} + +void ValueSet::ImplTracking(bool bLeaveWindow, const Point& rPos) +{ + ValueSetItem* pItem = bLeaveWindow ? nullptr : ImplGetItem(ImplGetItem(rPos)); + if ( pItem ) + { + if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET ) + mbHighlight = true; + + ImplHighlightItem(pItem->mnId); + } + else + { + if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET ) + mbHighlight = true; + + ImplHighlightItem(0); + } +} + +bool ValueSet::MouseButtonDown( const MouseEvent& rMouseEvent ) +{ + if (rMouseEvent.IsLeft() && !rMouseEvent.IsMod2()) + { + bool bConsumed = false; + ValueSetItem* pItem = ImplGetItem( ImplGetItem( rMouseEvent.GetPosPixel() ) ); + if (rMouseEvent.GetClicks() == 1) + { + if (pItem) + SelectItem(pItem->mnId); + GrabFocus(); + bConsumed = true; + } + else if (pItem && rMouseEvent.GetClicks() == 2) + { + maDoubleClickHdl.Call(this); + bConsumed = true; + } + return bConsumed; + } + + return CustomWidgetController::MouseButtonDown( rMouseEvent ); +} + +bool ValueSet::MouseButtonUp( const MouseEvent& rMouseEvent ) +{ + if (rMouseEvent.IsLeft() && !rMouseEvent.IsMod2()) + { + // tdf#142150 MouseUp seen without previous MouseDown + if (mnSelItemId) + Select(); + return true; + } + + return CustomWidgetController::MouseButtonUp( rMouseEvent ); +} + +bool ValueSet::MouseMove(const MouseEvent& rMouseEvent) +{ + // because of SelectionMode + if ((GetStyle() & WB_MENUSTYLEVALUESET) || (GetStyle() & WB_FLATVALUESET)) + ImplTracking(rMouseEvent.IsLeaveWindow(), rMouseEvent.GetPosPixel()); + return CustomWidgetController::MouseMove(rMouseEvent); +} + +void ValueSet::QueueReformat() +{ + queue_resize(); + RecalcScrollBar(); + mbFormat = true; + if ( IsReallyVisible() && IsUpdateMode() ) + Invalidate(); +} + +void ValueSet::RemoveItem( sal_uInt16 nItemId ) +{ + size_t nPos = GetItemPos( nItemId ); + + if ( nPos == VALUESET_ITEM_NOTFOUND ) + return; + + if ( nPos < mItemList.size() ) { + mItemList.erase( mItemList.begin() + nPos ); + } + + // reset variables + if (mnHighItemId == nItemId || mnSelItemId == nItemId) + { + mnCurCol = 0; + mnHighItemId = 0; + mnSelItemId = 0; + mbNoSelection = true; + } + + QueueReformat(); +} + +bool ValueSet::TurnOffScrollBar() +{ + if (mxScrolledWindow->get_vpolicy() == VclPolicyType::NEVER) + return false; + mxScrolledWindow->set_vpolicy(VclPolicyType::NEVER); + weld::DrawingArea* pDrawingArea = GetDrawingArea(); + Size aPrefSize(pDrawingArea->get_preferred_size()); + pDrawingArea->set_size_request(aPrefSize.Width() + GetScrollWidth(), aPrefSize.Height()); + return true; +} + +void ValueSet::TurnOnScrollBar() +{ + if (mxScrolledWindow->get_vpolicy() == VclPolicyType::ALWAYS) + return; + mxScrolledWindow->set_vpolicy(VclPolicyType::ALWAYS); + weld::DrawingArea* pDrawingArea = GetDrawingArea(); + Size aPrefSize(pDrawingArea->get_preferred_size()); + pDrawingArea->set_size_request(aPrefSize.Width() - GetScrollWidth(), aPrefSize.Height()); +} + +void ValueSet::RecalcScrollBar() +{ + if (!mxScrolledWindow) + return; + const bool bScrollAllowed = GetStyle() & WB_VSCROLL; + if (!bScrollAllowed) + return; + // reset scrolled window state to initial value so it will get configured + // to the right adjustment on the next format which we toggle on to happen + // if the scrolledwindow wasn't in its initial state already + if (TurnOffScrollBar()) + mbFormat = true; +} + +void ValueSet::Clear() +{ + ImplDeleteItems(); + + // reset variables + mnFirstLine = 0; + mnCurCol = 0; + mnHighItemId = 0; + mnSelItemId = 0; + mbNoSelection = true; + + RecalcScrollBar(); + + mbFormat = true; + if ( IsReallyVisible() && IsUpdateMode() ) + Invalidate(); +} + +size_t ValueSet::GetItemCount() const +{ + return mItemList.size(); +} + +size_t ValueSet::GetItemPos( sal_uInt16 nItemId ) const +{ + for ( size_t i = 0, n = mItemList.size(); i < n; ++i ) { + if ( mItemList[i]->mnId == nItemId ) { + return i; + } + } + return VALUESET_ITEM_NOTFOUND; +} + +sal_uInt16 ValueSet::GetItemId( size_t nPos ) const +{ + return ( nPos < mItemList.size() ) ? mItemList[nPos]->mnId : 0 ; +} + +sal_uInt16 ValueSet::GetItemId( const Point& rPos ) const +{ + size_t nItemPos = ImplGetItem( rPos ); + if ( nItemPos != VALUESET_ITEM_NOTFOUND ) + return GetItemId( nItemPos ); + + return 0; +} + +tools::Rectangle ValueSet::GetItemRect( sal_uInt16 nItemId ) const +{ + const size_t nPos = GetItemPos( nItemId ); + + if ( nPos!=VALUESET_ITEM_NOTFOUND && mItemList[nPos]->mbVisible ) + return ImplGetItemRect( nPos ); + + return tools::Rectangle(); +} + +tools::Rectangle ValueSet::ImplGetItemRect( size_t nPos ) const +{ + const size_t nVisibleBegin = static_cast<size_t>(mnFirstLine)*mnCols; + const size_t nVisibleEnd = nVisibleBegin + static_cast<size_t>(mnVisLines)*mnCols; + + // Check if the item is inside the range of the displayed ones, + // taking into account that last row could be incomplete + if ( nPos<nVisibleBegin || nPos>=nVisibleEnd || nPos>=mItemList.size() ) + return tools::Rectangle(); + + nPos -= nVisibleBegin; + + const size_t row = mnCols ? nPos/mnCols : 0; + const size_t col = mnCols ? nPos%mnCols : 0; + const tools::Long x = maItemListRect.Left()+col*(mnItemWidth+mnSpacing); + const tools::Long y = maItemListRect.Top()+row*(mnItemHeight+mnSpacing); + + return tools::Rectangle( Point(x, y), Size(mnItemWidth, mnItemHeight) ); +} + +void ValueSet::ImplHighlightItem(sal_uInt16 nItemId) +{ + if ( mnHighItemId == nItemId ) + return; + + // remember the old item to delete the previous selection + mnHighItemId = nItemId; + + // remove the old selection and draw the new one + Invalidate(); +} + +void ValueSet::ImplDraw(vcl::RenderContext& rRenderContext) +{ + if (mbFormat) + Format(rRenderContext); + + Point aDefPos; + Size aSize = maVirDev->GetOutputSizePixel(); + + rRenderContext.DrawOutDev(aDefPos, aSize, aDefPos, aSize, *maVirDev); + + // draw parting line to the Namefield + if (GetStyle() & WB_NAMEFIELD) + { + if (!(GetStyle() & WB_FLATVALUESET)) + { + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + Size aWinSize(GetOutputSizePixel()); + Point aPos1(NAME_LINE_OFF_X, mnTextOffset + NAME_LINE_OFF_Y); + Point aPos2(aWinSize.Width() - (NAME_LINE_OFF_X * 2), mnTextOffset + NAME_LINE_OFF_Y); + if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono)) + { + rRenderContext.SetLineColor(rStyleSettings.GetShadowColor()); + rRenderContext.DrawLine(aPos1, aPos2); + aPos1.AdjustY( 1 ); + aPos2.AdjustY( 1 ); + rRenderContext.SetLineColor(rStyleSettings.GetLightColor()); + } + else + rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor()); + rRenderContext.DrawLine(aPos1, aPos2); + } + } + + ImplDrawSelect(rRenderContext); +} + +/** + * An inelegant method; sets the item width & height such that + * all of the included items and their labels fit; if we can + * calculate that. + */ +void ValueSet::RecalculateItemSizes() +{ + Size aLargestItem = GetLargestItemSize(); + + if ( mnUserItemWidth != aLargestItem.Width() || + mnUserItemHeight != aLargestItem.Height() ) + { + mnUserItemWidth = aLargestItem.Width(); + mnUserItemHeight = aLargestItem.Height(); + QueueReformat(); + } +} + +void ValueSet::SetFirstLine(sal_uInt16 nNewFirstLine) +{ + if (nNewFirstLine != mnFirstLine) + { + mnFirstLine = nNewFirstLine; + if (mxScrolledWindow) + mxScrolledWindow->vadjustment_set_value(mnFirstLine); + } +} + +void ValueSet::SelectItem( sal_uInt16 nItemId ) +{ + size_t nItemPos = 0; + + if ( nItemId ) + { + nItemPos = GetItemPos( nItemId ); + if ( nItemPos == VALUESET_ITEM_NOTFOUND ) + return; + } + + if ( !((mnSelItemId != nItemId) || mbNoSelection) ) + return; + + const sal_uInt16 nOldItem = mnSelItemId; + mnSelItemId = nItemId; + mbNoSelection = false; + + bool bNewOut = !mbFormat && IsReallyVisible() && IsUpdateMode(); + bool bNewLine = false; + + if (weld::DrawingArea* pNeedsFormatToScroll = !mnCols ? GetDrawingArea() : nullptr) + { + Format(pNeedsFormatToScroll->get_ref_device()); + // reset scrollbar so it's set to the later calculated mnFirstLine on + // the next Format + RecalcScrollBar(); + } + + // if necessary scroll to the visible area + if (mbScroll && nItemId && mnCols) + { + sal_uInt16 nNewLine = static_cast<sal_uInt16>(nItemPos / mnCols); + if ( nNewLine < mnFirstLine ) + { + SetFirstLine(nNewLine); + bNewLine = true; + } + else if ( nNewLine > o3tl::make_unsigned(mnFirstLine+mnVisLines-1) ) + { + SetFirstLine(static_cast<sal_uInt16>(nNewLine-mnVisLines+1)); + bNewLine = true; + } + } + + if ( bNewOut ) + { + if ( bNewLine ) + { + // redraw everything if the visible area has changed + mbFormat = true; + } + Invalidate(); + } + + if( !ImplHasAccessibleListeners() ) + return; + + // focus event (deselect) + if( nOldItem ) + { + const size_t nPos = GetItemPos( nItemId ); + + if( nPos != VALUESET_ITEM_NOTFOUND ) + { + ValueItemAcc* pItemAcc = ValueItemAcc::getImplementation( + mItemList[nPos]->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) ); + + if( pItemAcc ) + { + Any aOldAny; + Any aNewAny; + aOldAny <<= Reference(getXWeak(pItemAcc)); + ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny ); + } + } + } + + // focus event (select) + const size_t nPos = GetItemPos( mnSelItemId ); + + ValueSetItem* pItem; + if( nPos != VALUESET_ITEM_NOTFOUND ) + pItem = mItemList[nPos].get(); + else + pItem = mpNoneItem.get(); + + ValueItemAcc* pItemAcc = nullptr; + if (pItem != nullptr) + pItemAcc = ValueItemAcc::getImplementation( pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) ); + + if( pItemAcc ) + { + Any aOldAny; + Any aNewAny; + aNewAny <<= Reference(getXWeak(pItemAcc)); + ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny); + } + + // selection event + Any aOldAny; + Any aNewAny; + ImplFireAccessibleEvent(AccessibleEventId::SELECTION_CHANGED, aOldAny, aNewAny); +} + +void ValueSet::SetNoSelection() +{ + mbNoSelection = true; + mbHighlight = false; + + if (IsReallyVisible() && IsUpdateMode()) + Invalidate(); +} + +void ValueSet::SetStyle(WinBits nStyle) +{ + if (nStyle != mnStyle) + { + mnStyle = nStyle; + mbFormat = true; + Invalidate(); + } +} + +void ValueSet::Format(vcl::RenderContext const & rRenderContext) +{ + Size aWinSize(GetOutputSizePixel()); + size_t nItemCount = mItemList.size(); + WinBits nStyle = GetStyle(); + tools::Long nTxtHeight = rRenderContext.GetTextHeight(); + tools::Long nOff; + tools::Long nNoneHeight; + tools::Long nNoneSpace; + + if (mxScrolledWindow && !(nStyle & WB_VSCROLL) && mxScrolledWindow->get_vpolicy() != VclPolicyType::NEVER) + TurnOffScrollBar(); + + // calculate item offset + if (nStyle & WB_ITEMBORDER) + { + if (nStyle & WB_DOUBLEBORDER) + nOff = ITEM_OFFSET_DOUBLE; + else + nOff = ITEM_OFFSET; + } + else + nOff = 0; + + // consider size, if NameField does exist + if (nStyle & WB_NAMEFIELD) + { + mnTextOffset = aWinSize.Height() - nTxtHeight - NAME_OFFSET; + aWinSize.AdjustHeight( -(nTxtHeight + NAME_OFFSET) ); + + if (!(nStyle & WB_FLATVALUESET)) + { + mnTextOffset -= NAME_LINE_HEIGHT + NAME_LINE_OFF_Y; + aWinSize.AdjustHeight( -(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y) ); + } + } + else + mnTextOffset = 0; + + // consider offset and size, if NoneField does exist + if (nStyle & WB_NONEFIELD) + { + nNoneHeight = nTxtHeight + nOff; + nNoneSpace = mnSpacing; + } + else + { + nNoneHeight = 0; + nNoneSpace = 0; + mpNoneItem.reset(); + } + + // calculate number of columns + if (!mnUserCols) + { + if (mnUserItemWidth) + { + mnCols = static_cast<sal_uInt16>((aWinSize.Width() - mnSpacing) / (mnUserItemWidth + mnSpacing)); + if (mnCols <= 0) + mnCols = 1; + } + else + { + mnCols = 1; + } + } + else + { + mnCols = mnUserCols; + } + + // calculate number of rows + mbScroll = false; + + auto nOldLines = mnLines; + // Floor( (M+N-1)/N )==Ceiling( M/N ) + mnLines = (static_cast<tools::Long>(nItemCount) + mnCols - 1) / mnCols; + if (mnLines <= 0) + mnLines = 1; + + bool bAdjustmentOutOfDate = nOldLines != mnLines; + + auto nOldVisLines = mnVisLines; + + tools::Long nCalcHeight = aWinSize.Height() - nNoneHeight; + if (mnUserVisLines) + { + mnVisLines = mnUserVisLines; + } + else if (mnUserItemHeight) + { + mnVisLines = (nCalcHeight - nNoneSpace + mnSpacing) / (mnUserItemHeight + mnSpacing); + if (!mnVisLines) + mnVisLines = 1; + } + else + { + mnVisLines = mnLines; + } + + bAdjustmentOutOfDate |= nOldVisLines != mnVisLines; + + if (mnLines > mnVisLines) + mbScroll = true; + + if (mnLines <= mnVisLines) + { + SetFirstLine(0); + } + else + { + if (mnFirstLine > o3tl::make_unsigned(mnLines - mnVisLines)) + SetFirstLine(static_cast<sal_uInt16>(mnLines - mnVisLines)); + } + + // calculate item size + const tools::Long nColSpace = (mnCols - 1) * static_cast<tools::Long>(mnSpacing); + const tools::Long nLineSpace = ((mnVisLines - 1) * mnSpacing) + nNoneSpace; + if (mnUserItemWidth && !mnUserCols) + { + mnItemWidth = mnUserItemWidth; + if (mnItemWidth > aWinSize.Width() - nColSpace) + mnItemWidth = aWinSize.Width() - nColSpace; + } + else + mnItemWidth = (aWinSize.Width() - nColSpace) / mnCols; + if (mnUserItemHeight && !mnUserVisLines) + { + mnItemHeight = mnUserItemHeight; + if (mnItemHeight > nCalcHeight - nNoneSpace) + mnItemHeight = nCalcHeight - nNoneSpace; + } + else + { + nCalcHeight -= nLineSpace; + mnItemHeight = nCalcHeight / mnVisLines; + } + + // Init VirDev + maVirDev->SetBackground(Application::GetSettings().GetStyleSettings().GetFaceColor()); + maVirDev->SetOutputSizePixel(aWinSize); + maVirDev->Erase(); + + // nothing is changed in case of too small items + if ((mnItemWidth <= 0) || + (mnItemHeight <= ((nStyle & WB_ITEMBORDER) ? 4 : 2)) || + !nItemCount) + { + mbHasVisibleItems = false; + + if ((nStyle & WB_NONEFIELD) && mpNoneItem) + { + mpNoneItem->mbVisible = false; + mpNoneItem->maText = GetText(); + } + + for (size_t i = 0; i < nItemCount; i++) + { + mItemList[i]->mbVisible = false; + } + + if (mxScrolledWindow && mxScrolledWindow->get_vpolicy() != VclPolicyType::NEVER) + TurnOffScrollBar(); + } + else + { + mbHasVisibleItems = true; + + // determine Frame-Style + if (nStyle & WB_DOUBLEBORDER) + mnFrameStyle = DrawFrameStyle::DoubleIn; + else + mnFrameStyle = DrawFrameStyle::In; + + // draw the selection with double width if the items are bigger + if ((nStyle & WB_DOUBLEBORDER) && + ((mnItemWidth >= 25) && (mnItemHeight >= 20))) + { + mbDoubleSel = true; + } + else + { + mbDoubleSel = false; + } + + // calculate offsets + tools::Long nStartX; + tools::Long nStartY; + if (mbFullMode) + { + tools::Long nAllItemWidth = (mnItemWidth * mnCols) + nColSpace; + tools::Long nAllItemHeight = (mnItemHeight * mnVisLines) + nNoneHeight + nLineSpace; + nStartX = (aWinSize.Width() - nAllItemWidth) / 2; + nStartY = (aWinSize.Height() - nAllItemHeight) / 2; + } + else + { + nStartX = 0; + nStartY = 0; + } + + // calculate and draw items + maVirDev->SetLineColor(); + tools::Long x = nStartX; + tools::Long y = nStartY; + + // create NoSelection field and show it + if (nStyle & WB_NONEFIELD) + { + if (!mpNoneItem) + mpNoneItem.reset(new ValueSetItem(*this)); + + mpNoneItem->mnId = 0; + mpNoneItem->meType = VALUESETITEM_NONE; + mpNoneItem->mbVisible = true; + maNoneItemRect.SetLeft( x ); + maNoneItemRect.SetTop( y ); + maNoneItemRect.SetRight( maNoneItemRect.Left() + aWinSize.Width() - x - 1 ); + maNoneItemRect.SetBottom( y + nNoneHeight - 1 ); + + ImplFormatItem(rRenderContext, mpNoneItem.get(), maNoneItemRect); + + y += nNoneHeight + nNoneSpace; + } + + // draw items + size_t nFirstItem = static_cast<size_t>(mnFirstLine) * mnCols; + size_t nLastItem = nFirstItem + (mnVisLines * mnCols); + + maItemListRect.SetLeft( x ); + maItemListRect.SetTop( y ); + maItemListRect.SetRight( x + mnCols * (mnItemWidth + mnSpacing) - mnSpacing - 1 ); + maItemListRect.SetBottom( y + mnVisLines * (mnItemHeight + mnSpacing) - mnSpacing - 1 ); + + if (!mbFullMode) + { + // If want also draw parts of items in the last line, + // then we add one more line if parts of these line are + // visible + if (y + (mnVisLines * (mnItemHeight + mnSpacing)) < aWinSize.Height()) + nLastItem += mnCols; + maItemListRect.SetBottom( aWinSize.Height() - y ); + } + for (size_t i = 0; i < nItemCount; i++) + { + ValueSetItem* pItem = mItemList[i].get(); + + if (i >= nFirstItem && i < nLastItem) + { + if (!pItem->mbVisible && ImplHasAccessibleListeners()) + { + Any aOldAny; + Any aNewAny; + + aNewAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/); + ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny); + } + + pItem->mbVisible = true; + ImplFormatItem(rRenderContext, pItem, tools::Rectangle(Point(x, y), Size(mnItemWidth, mnItemHeight))); + + if (!((i + 1) % mnCols)) + { + x = nStartX; + y += mnItemHeight + mnSpacing; + } + else + x += mnItemWidth + mnSpacing; + } + else + { + if (pItem->mbVisible && ImplHasAccessibleListeners()) + { + Any aOldAny; + Any aNewAny; + + aOldAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/); + ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny); + } + + pItem->mbVisible = false; + } + } + + // arrange ScrollBar, set values and show it + if (mxScrolledWindow && (nStyle & WB_VSCROLL)) + { + bool bTurnScrollbarOn = mxScrolledWindow->get_vpolicy() != VclPolicyType::ALWAYS; + if (bAdjustmentOutOfDate || bTurnScrollbarOn) + { + tools::Long nPageSize = mnVisLines; + if (nPageSize < 1) + nPageSize = 1; + mxScrolledWindow->vadjustment_configure(mnFirstLine, 0, mnLines, 1, + mnVisLines, nPageSize); + } + + if (bTurnScrollbarOn) + TurnOnScrollBar(); + } + } + + // waiting for the next since the formatting is finished + mbFormat = false; +} + +void ValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext) +{ + if (!IsReallyVisible()) + return; + + const bool bFocus = HasFocus(); + + if (!bFocus && mbNoSelection && !mbHighlight) + return; + + tools::Rectangle aSelectedRect, aHoverRect; + ValueSetItem* pSelectedItem = ImplGetDrawSelectItem(mnSelItemId, bFocus, aSelectedRect); + ValueSetItem* pHighlightItem = mnHighItemId ? ImplGetDrawSelectItem(mnHighItemId, false, aHoverRect) : nullptr; + + if (pSelectedItem) + { + const bool bHover = pSelectedItem == pHighlightItem; + ImplDrawSelect(rRenderContext, aSelectedRect, pSelectedItem, bFocus, !mbNoSelection, true, bHover); + } + if (pHighlightItem && (pSelectedItem != pHighlightItem || mbNoSelection)) + { + // For the case that there isn't a selected item, but due to wanting to + // show focus is in the valueset, the above block will have drawn the + // first item with a focus rect. For that situation; if the valueset is + // the thin WB_MENUSTYLEVALUESET case then blend this highlight border + // on top of that focus rect and it will appear with a highlighted + // focus rect. If it's the other case of a thicker border then redraw + // the focus rect highlighted with the hover color. + bool bDrawFocus; + WinBits nStyle = GetStyle(); + if (nStyle & WB_MENUSTYLEVALUESET) + bDrawFocus = false; + else + bDrawFocus = pSelectedItem == pHighlightItem && mbNoSelection; + + ImplDrawSelect(rRenderContext, aHoverRect, pHighlightItem, bDrawFocus, mbHighlight, false, true); + } +} + +ValueSetItem* ValueSet::ImplGetDrawSelectItem(sal_uInt16 nItemId, const bool bFocus, tools::Rectangle& rRect) +{ + ValueSetItem* pItem = nullptr; + if (nItemId) + { + const size_t nPos = GetItemPos( nItemId ); + pItem = mItemList[ nPos ].get(); + rRect = ImplGetItemRect( nPos ); + } + else if (mpNoneItem) + { + pItem = mpNoneItem.get(); + rRect = maNoneItemRect; + } + else if (bFocus && (pItem = ImplGetFirstItem())) + { + rRect = ImplGetItemRect(0); + } + return pItem; +} + +void ValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext, + const tools::Rectangle& rRect, const ValueSetItem* pItem, + const bool bFocus, const bool bDrawSel, + const bool bSelected, const bool bHover) +{ + tools::Rectangle aRect(rRect); + + // draw selection + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + rRenderContext.SetFillColor(); + + Color aDoubleColor; + Color aSingleColor; + + sal_uInt16 nTransparencePercent = 0; + + if (bSelected && bHover) + { + aDoubleColor = rStyleSettings.GetActiveColor(); + aSingleColor = rStyleSettings.GetActiveTextColor(); + } + else if (bSelected || bHover) + { + aDoubleColor = rStyleSettings.GetHighlightColor(); + aSingleColor = rStyleSettings.GetHighlightTextColor(); + if (bHover) + { + nTransparencePercent = 55; + } + } + + // specify selection output + WinBits nStyle = GetStyle(); + if (nStyle & WB_MENUSTYLEVALUESET) + { + if (bFocus) + InvertFocusRect(rRenderContext, aRect); + if (bDrawSel) + { + rRenderContext.SetLineColor(aDoubleColor); + tools::PolyPolygon aPolyPoly(1); + aPolyPoly.Insert(tools::Polygon(aRect)); + rRenderContext.DrawTransparent(aPolyPoly, nTransparencePercent); + } + } + else + { + rRenderContext.SetLineColor(aDoubleColor); + tools::Rectangle aFocusRect; + + if (!mbDoubleSel) + { + // an outer rectangle surrounding a "focus" rectangle, surrounding + // an inner rectangle. Focus rectangle is always drawn, but rendered + // empty when there is no focus. e.g. as seen in color valuesets + if (bDrawSel) + { + tools::PolyPolygon aPolyPoly(1); + aPolyPoly.Insert(tools::Polygon(aRect)); + rRenderContext.DrawTransparent(aPolyPoly, nTransparencePercent); + } + + aRect.AdjustLeft( 1 ); + aRect.AdjustTop( 1 ); + aRect.AdjustRight( -1 ); + aRect.AdjustBottom( -1 ); + + aFocusRect = aRect; + + aRect.AdjustLeft( 1 ); + aRect.AdjustTop( 1 ); + aRect.AdjustRight( -1 ); + aRect.AdjustBottom( -1 ); + + if (bDrawSel) + { + tools::PolyPolygon aPolyPoly(1); + aPolyPoly.Insert(tools::Polygon(aRect)); + rRenderContext.DrawTransparent(aPolyPoly, nTransparencePercent); + } + + if (bDrawSel) + rRenderContext.SetLineColor(aSingleColor); + else + rRenderContext.SetLineColor(COL_LIGHTGRAY); + + rRenderContext.DrawRect(aFocusRect); + } + else + { + // a thick bordered rectangle surrounding an optional "focus" + // rectangle which is only drawn when focused, as seen in format, + // bullets and numbering in writer + const int nAdjust = 2; + + aRect.AdjustLeft(nAdjust); + aRect.AdjustTop(nAdjust); + aRect.AdjustRight(-nAdjust); + aRect.AdjustBottom(-nAdjust); + + aFocusRect = aRect; + + if (bDrawSel) + { + const basegfx::B2DPolygon aRectPoly( + basegfx::utils::createPolygonFromRect( + vcl::unotools::b2DRectangleFromRectangle(aRect))); + + const int nThickness = nAdjust * 2; + + if (!rRenderContext.DrawPolyLineDirect(basegfx::B2DHomMatrix(), + aRectPoly, + nThickness, + nTransparencePercent / 100.0, + nullptr, + basegfx::B2DLineJoin::Miter)) + { + SAL_WARN("svtools", "presumably impossible in practice, but fallback to see something"); + rRenderContext.DrawPolyLine(aRectPoly, nThickness, basegfx::B2DLineJoin::Miter); + } + } + + if (bFocus) + { + if (bDrawSel) + rRenderContext.SetLineColor(aSingleColor); + else + rRenderContext.SetLineColor(COL_LIGHTGRAY); + rRenderContext.DrawRect(aFocusRect); + } + } + + if (bFocus) + InvertFocusRect(rRenderContext, aFocusRect); + } + + ImplDrawItemText(rRenderContext, pItem->maText); +} + +void ValueSet::ImplFormatItem(vcl::RenderContext const & rRenderContext, ValueSetItem* pItem, tools::Rectangle aRect) +{ + WinBits nStyle = GetStyle(); + if (nStyle & WB_ITEMBORDER) + { + aRect.AdjustLeft(1 ); + aRect.AdjustTop(1 ); + aRect.AdjustRight( -1 ); + aRect.AdjustBottom( -1 ); + + if (nStyle & WB_FLATVALUESET) + { + sal_Int32 nBorder = (nStyle & WB_DOUBLEBORDER) ? 2 : 1; + + aRect.AdjustLeft(nBorder ); + aRect.AdjustTop(nBorder ); + aRect.AdjustRight( -nBorder ); + aRect.AdjustBottom( -nBorder ); + } + else + { + DecorationView aView(maVirDev.get()); + aRect = aView.DrawFrame(aRect, mnFrameStyle); + } + } + + if (pItem == mpNoneItem.get()) + pItem->maText = GetText(); + + if ((aRect.GetHeight() <= 0) || (aRect.GetWidth() <= 0)) + return; + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + if (pItem == mpNoneItem.get()) + { + maVirDev->SetFont(rRenderContext.GetFont()); + maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor()); + maVirDev->SetTextFillColor(); + maVirDev->SetFillColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuColor() : rStyleSettings.GetWindowColor()); + maVirDev->DrawRect(aRect); + Point aTxtPos(aRect.Left() + 2, aRect.Top()); + tools::Long nTxtWidth = rRenderContext.GetTextWidth(pItem->maText); + if ((aTxtPos.X() + nTxtWidth) > aRect.Right()) + { + maVirDev->SetClipRegion(vcl::Region(aRect)); + maVirDev->DrawText(aTxtPos, pItem->maText); + maVirDev->SetClipRegion(); + } + else + maVirDev->DrawText(aTxtPos, pItem->maText); + } + else if (pItem->meType == VALUESETITEM_COLOR) + { + maVirDev->SetFillColor(pItem->maColor); + maVirDev->DrawRect(aRect); + } + else + { + if (IsColor()) + maVirDev->SetFillColor(maColor); + else if (nStyle & WB_MENUSTYLEVALUESET) + maVirDev->SetFillColor(rStyleSettings.GetMenuColor()); + else if (IsEnabled()) + maVirDev->SetFillColor(rStyleSettings.GetWindowColor()); + else + maVirDev->SetFillColor(rStyleSettings.GetFaceColor()); + maVirDev->DrawRect(aRect); + + if (pItem->meType == VALUESETITEM_USERDRAW) + { + UserDrawEvent aUDEvt(maVirDev.get(), aRect, pItem->mnId); + UserDraw(aUDEvt); + } + else + { + Size aImageSize = pItem->maImage.GetSizePixel(); + Size aRectSize = aRect.GetSize(); + Point aPos(aRect.Left(), aRect.Top()); + aPos.AdjustX((aRectSize.Width() - aImageSize.Width()) / 2 ); + + if (pItem->meType != VALUESETITEM_IMAGE_AND_TEXT) + aPos.AdjustY((aRectSize.Height() - aImageSize.Height()) / 2 ); + + DrawImageFlags nImageStyle = DrawImageFlags::NONE; + if (!IsEnabled()) + nImageStyle |= DrawImageFlags::Disable; + + if (aImageSize.Width() > aRectSize.Width() || + aImageSize.Height() > aRectSize.Height()) + { + maVirDev->SetClipRegion(vcl::Region(aRect)); + maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle); + maVirDev->SetClipRegion(); + } + else + maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle); + + if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT) + { + maVirDev->SetFont(rRenderContext.GetFont()); + maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor()); + maVirDev->SetTextFillColor(); + + tools::Long nTxtWidth = maVirDev->GetTextWidth(pItem->maText); + + if (nTxtWidth > aRect.GetWidth()) + maVirDev->SetClipRegion(vcl::Region(aRect)); + + maVirDev->DrawText(Point(aRect.Left() + + (aRect.GetWidth() - nTxtWidth) / 2, + aRect.Bottom() - maVirDev->GetTextHeight()), + pItem->maText); + + if (nTxtWidth > aRect.GetWidth()) + maVirDev->SetClipRegion(); + } + } + } + + const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0); + + if (nEdgeBlendingPercent) + { + const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor()); + const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor()); + const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100); + const BitmapEx aBlendFrame(createBlendFrame(aRect.GetSize(), nAlpha, rTopLeft, rBottomRight)); + + if (!aBlendFrame.IsEmpty()) + { + maVirDev->DrawBitmapEx(aRect.TopLeft(), aBlendFrame); + } + } +} + +void ValueSet::ImplDrawItemText(vcl::RenderContext& rRenderContext, const OUString& rText) +{ + if (!(GetStyle() & WB_NAMEFIELD)) + return; + + Size aWinSize(GetOutputSizePixel()); + tools::Long nTxtWidth = rRenderContext.GetTextWidth(rText); + tools::Long nTxtOffset = mnTextOffset; + + rRenderContext.Push(vcl::PushFlags::TEXTCOLOR); + + // delete rectangle and show text + const bool bFlat(GetStyle() & WB_FLATVALUESET); + if (!bFlat) + nTxtOffset += NAME_LINE_HEIGHT+NAME_LINE_OFF_Y; + + rRenderContext.SetTextColor(Application::GetSettings().GetStyleSettings().GetButtonTextColor()); + // tdf#153787 highlighted entry text is drawn in the same Paint as the selected text, so can + // overwrite already rendered text + rRenderContext.Erase(tools::Rectangle(Point(0, nTxtOffset), Point(aWinSize.Width(), aWinSize.Height()))); + rRenderContext.DrawText(Point((aWinSize.Width() - nTxtWidth) / 2, nTxtOffset + (NAME_OFFSET / 2)), rText); + + rRenderContext.Pop(); +} + +void ValueSet::StyleUpdated() +{ + mbFormat = true; + CustomWidgetController::StyleUpdated(); +} + +void ValueSet::EnableFullItemMode( bool bFullMode ) +{ + mbFullMode = bFullMode; +} + +void ValueSet::SetColCount( sal_uInt16 nNewCols ) +{ + if ( mnUserCols != nNewCols ) + { + mnUserCols = nNewCols; + QueueReformat(); + } +} + +void ValueSet::SetItemImage( sal_uInt16 nItemId, const Image& rImage ) +{ + size_t nPos = GetItemPos( nItemId ); + + if ( nPos == VALUESET_ITEM_NOTFOUND ) + return; + + ValueSetItem* pItem = mItemList[nPos].get(); + pItem->meType = VALUESETITEM_IMAGE; + pItem->maImage = rImage; + + if ( !mbFormat && IsReallyVisible() && IsUpdateMode() ) + { + const tools::Rectangle aRect = ImplGetItemRect(nPos); + Invalidate(aRect); + } + else + mbFormat = true; +} + +void ValueSet::SetItemColor( sal_uInt16 nItemId, const Color& rColor ) +{ + size_t nPos = GetItemPos( nItemId ); + + if ( nPos == VALUESET_ITEM_NOTFOUND ) + return; + + ValueSetItem* pItem = mItemList[nPos].get(); + pItem->meType = VALUESETITEM_COLOR; + pItem->maColor = rColor; + + if ( !mbFormat && IsReallyVisible() && IsUpdateMode() ) + { + const tools::Rectangle aRect = ImplGetItemRect(nPos); + Invalidate( aRect ); + } + else + mbFormat = true; +} + +Color ValueSet::GetItemColor( sal_uInt16 nItemId ) const +{ + size_t nPos = GetItemPos( nItemId ); + + if ( nPos != VALUESET_ITEM_NOTFOUND ) + return mItemList[nPos]->maColor; + else + return Color(); +} + +Size ValueSet::CalcWindowSizePixel( const Size& rItemSize, sal_uInt16 nDesireCols, + sal_uInt16 nDesireLines ) const +{ + size_t nCalcCols = nDesireCols; + size_t nCalcLines = nDesireLines; + + if ( !nCalcCols ) + { + if ( mnUserCols ) + nCalcCols = mnUserCols; + else + nCalcCols = 1; + } + + if ( !nCalcLines ) + { + nCalcLines = mnVisLines; + + if ( mbFormat ) + { + if ( mnUserVisLines ) + nCalcLines = mnUserVisLines; + else + { + // Floor( (M+N-1)/N )==Ceiling( M/N ) + nCalcLines = (mItemList.size()+nCalcCols-1) / nCalcCols; + if ( !nCalcLines ) + nCalcLines = 1; + } + } + } + + Size aSize( rItemSize.Width() * nCalcCols, rItemSize.Height() * nCalcLines ); + WinBits nStyle = GetStyle(); + tools::Long nTxtHeight = GetTextHeight(); + tools::Long n; + + if ( nStyle & WB_ITEMBORDER ) + { + if ( nStyle & WB_DOUBLEBORDER ) + n = ITEM_OFFSET_DOUBLE; + else + n = ITEM_OFFSET; + + aSize.AdjustWidth(n * nCalcCols ); + aSize.AdjustHeight(n * nCalcLines ); + } + else + n = 0; + + if ( mnSpacing ) + { + aSize.AdjustWidth(mnSpacing * (nCalcCols - 1) ); + aSize.AdjustHeight(mnSpacing * (nCalcLines - 1) ); + } + + if ( nStyle & WB_NAMEFIELD ) + { + aSize.AdjustHeight(nTxtHeight + NAME_OFFSET ); + if ( !(nStyle & WB_FLATVALUESET) ) + aSize.AdjustHeight(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y ); + } + + if ( nStyle & WB_NONEFIELD ) + { + aSize.AdjustHeight(nTxtHeight + n + mnSpacing ); + } + + return aSize; +} + +void ValueSet::InsertItem( sal_uInt16 nItemId, const Image& rImage ) +{ + std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this )); + pItem->mnId = nItemId; + pItem->meType = VALUESETITEM_IMAGE; + pItem->maImage = rImage; + ImplInsertItem( std::move(pItem), VALUESET_APPEND ); +} + +void ValueSet::InsertItem( sal_uInt16 nItemId, const Image& rImage, + const OUString& rText, size_t nPos, + bool bShowLegend ) +{ + std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this )); + pItem->mnId = nItemId; + pItem->meType = bShowLegend ? VALUESETITEM_IMAGE_AND_TEXT : VALUESETITEM_IMAGE; + pItem->maImage = rImage; + pItem->maText = rText; + ImplInsertItem( std::move(pItem), nPos ); +} + +void ValueSet::InsertItem( sal_uInt16 nItemId, size_t nPos ) +{ + std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this )); + pItem->mnId = nItemId; + pItem->meType = VALUESETITEM_USERDRAW; + ImplInsertItem( std::move(pItem), nPos ); +} + +void ValueSet::InsertItem( sal_uInt16 nItemId, const Color& rColor, + const OUString& rText ) +{ + std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this )); + pItem->mnId = nItemId; + pItem->meType = VALUESETITEM_COLOR; + pItem->maColor = rColor; + pItem->maText = rText; + ImplInsertItem( std::move(pItem), VALUESET_APPEND ); +} + +void ValueSet::ImplInsertItem( std::unique_ptr<ValueSetItem> pItem, const size_t nPos ) +{ + DBG_ASSERT( pItem->mnId, "ValueSet::InsertItem(): ItemId == 0" ); + DBG_ASSERT( GetItemPos( pItem->mnId ) == VALUESET_ITEM_NOTFOUND, + "ValueSet::InsertItem(): ItemId already exists" ); + + if ( nPos < mItemList.size() ) { + mItemList.insert( mItemList.begin() + nPos, std::move(pItem) ); + } else { + mItemList.push_back( std::move(pItem) ); + } + + QueueReformat(); +} + +int ValueSet::GetScrollWidth() const +{ + if (mxScrolledWindow) + return mxScrolledWindow->get_scroll_thickness(); + return 0; +} + +void ValueSet::SetEdgeBlending(bool bNew) +{ + if(mbEdgeBlending != bNew) + { + mbEdgeBlending = bNew; + mbFormat = true; + + if (GetDrawingArea() && IsReallyVisible() && IsUpdateMode()) + { + Invalidate(); + } + } +} + +Size ValueSet::CalcItemSizePixel( const Size& rItemSize) const +{ + Size aSize = rItemSize; + + WinBits nStyle = GetStyle(); + if ( nStyle & WB_ITEMBORDER ) + { + tools::Long n; + + if ( nStyle & WB_DOUBLEBORDER ) + n = ITEM_OFFSET_DOUBLE; + else + n = ITEM_OFFSET; + + aSize.AdjustWidth(n ); + aSize.AdjustHeight(n ); + } + + return aSize; +} + +void ValueSet::SetLineCount( sal_uInt16 nNewLines ) +{ + if ( mnUserVisLines != nNewLines ) + { + mnUserVisLines = nNewLines; + QueueReformat(); + } +} + +void ValueSet::SetItemWidth( tools::Long nNewItemWidth ) +{ + if ( mnUserItemWidth != nNewItemWidth ) + { + mnUserItemWidth = nNewItemWidth; + QueueReformat(); + } +} + +//method to set accessible when the style is user draw. +void ValueSet::InsertItem( sal_uInt16 nItemId, const OUString& rText, size_t nPos ) +{ + DBG_ASSERT( nItemId, "ValueSet::InsertItem(): ItemId == 0" ); + DBG_ASSERT( GetItemPos( nItemId ) == VALUESET_ITEM_NOTFOUND, + "ValueSet::InsertItem(): ItemId already exists" ); + std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this )); + pItem->mnId = nItemId; + pItem->meType = VALUESETITEM_USERDRAW; + pItem->maText = rText; + ImplInsertItem( std::move(pItem), nPos ); +} + +void ValueSet::SetItemHeight( tools::Long nNewItemHeight ) +{ + if ( mnUserItemHeight != nNewItemHeight ) + { + mnUserItemHeight = nNewItemHeight; + QueueReformat(); + } +} + +OUString ValueSet::RequestHelp(tools::Rectangle& rHelpRect) +{ + Point aPos = rHelpRect.TopLeft(); + const size_t nItemPos = ImplGetItem( aPos ); + OUString sRet; + if (nItemPos != VALUESET_ITEM_NOTFOUND) + { + rHelpRect = ImplGetItemRect(nItemPos); + sRet = GetItemText(ImplGetItem(nItemPos)->mnId); + } + return sRet; +} + +OUString ValueSet::GetItemText(sal_uInt16 nItemId) const +{ + const size_t nPos = GetItemPos(nItemId); + + if ( nPos != VALUESET_ITEM_NOTFOUND ) + return mItemList[nPos]->maText; + + return OUString(); +} + +void ValueSet::SetExtraSpacing( sal_uInt16 nNewSpacing ) +{ + if ( GetStyle() & WB_ITEMBORDER ) + { + mnSpacing = nNewSpacing; + QueueReformat(); + } +} + +void ValueSet::SetFormat() +{ + mbFormat = true; +} + +void ValueSet::SetItemData( sal_uInt16 nItemId, void* pData ) +{ + size_t nPos = GetItemPos( nItemId ); + + if ( nPos == VALUESET_ITEM_NOTFOUND ) + return; + + ValueSetItem* pItem = mItemList[nPos].get(); + pItem->mpData = pData; + + if ( pItem->meType == VALUESETITEM_USERDRAW ) + { + if ( !mbFormat && IsReallyVisible() && IsUpdateMode() ) + { + const tools::Rectangle aRect = ImplGetItemRect(nPos); + Invalidate(aRect); + } + else + mbFormat = true; + } +} + +void* ValueSet::GetItemData( sal_uInt16 nItemId ) const +{ + size_t nPos = GetItemPos( nItemId ); + + if ( nPos != VALUESET_ITEM_NOTFOUND ) + return mItemList[nPos]->mpData; + else + return nullptr; +} + +void ValueSet::SetItemText(sal_uInt16 nItemId, const OUString& rText) +{ + size_t nPos = GetItemPos( nItemId ); + + if ( nPos == VALUESET_ITEM_NOTFOUND ) + return; + + ValueSetItem* pItem = mItemList[nPos].get(); + + // Remember old and new name for accessibility event. + Any aOldName; + Any aNewName; + OUString sString (pItem->maText); + aOldName <<= sString; + sString = rText; + aNewName <<= sString; + + pItem->maText = rText; + + if (!mbFormat && IsReallyVisible() && IsUpdateMode()) + { + sal_uInt16 nTempId = mnSelItemId; + + if (mbHighlight) + nTempId = mnHighItemId; + + if (nTempId == nItemId) + Invalidate(); + } + + if (ImplHasAccessibleListeners()) + { + Reference<XAccessible> xAccessible(pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/)); + ValueItemAcc* pValueItemAcc = static_cast<ValueItemAcc*>(xAccessible.get()); + pValueItemAcc->FireAccessibleEvent(AccessibleEventId::NAME_CHANGED, aOldName, aNewName); + } +} + +Size ValueSet::GetLargestItemSize() +{ + Size aLargestItem; + + for (const std::unique_ptr<ValueSetItem>& pItem : mItemList) + { + if (!pItem->mbVisible) + continue; + + if (pItem->meType != VALUESETITEM_IMAGE && + pItem->meType != VALUESETITEM_IMAGE_AND_TEXT) + { + // handle determining an optimal size for this case + continue; + } + + Size aSize = pItem->maImage.GetSizePixel(); + if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT) + { + aSize.AdjustHeight(3 * NAME_LINE_HEIGHT + + maVirDev->GetTextHeight() ); + aSize.setWidth( std::max(aSize.Width(), + maVirDev->GetTextWidth(pItem->maText) + NAME_OFFSET) ); + } + + aLargestItem.setWidth( std::max(aLargestItem.Width(), aSize.Width()) ); + aLargestItem.setHeight( std::max(aLargestItem.Height(), aSize.Height()) ); + } + + return aLargestItem; +} + +void ValueSet::SetOptimalSize() +{ + Size aLargestSize(GetLargestItemSize()); + aLargestSize.setWidth(std::max(aLargestSize.Width(), mnUserItemWidth)); + aLargestSize.setHeight(std::max(aLargestSize.Height(), mnUserItemHeight)); + Size aPrefSize(CalcWindowSizePixel(aLargestSize)); + GetDrawingArea()->set_size_request(aPrefSize.Width(), aPrefSize.Height()); +} + +Image ValueSet::GetItemImage(sal_uInt16 nItemId) const +{ + size_t nPos = GetItemPos( nItemId ); + + if ( nPos != VALUESET_ITEM_NOTFOUND ) + return mItemList[nPos]->maImage; + else + return Image(); +} + +void ValueSet::SetColor(const Color& rColor) +{ + maColor = rColor; + mbFormat = true; + if (IsReallyVisible() && IsUpdateMode()) + Invalidate(); +} + +void ValueSet::Show() +{ + if (mxScrolledWindow) + mxScrolledWindow->show(); + CustomWidgetController::Show(); +} + +void ValueSet::Hide() +{ + CustomWidgetController::Hide(); + if (mxScrolledWindow) + mxScrolledWindow->hide(); +} + +FactoryFunction ValueSet::GetUITestFactory() const +{ + return ValueSetUIObject::create; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |