/* -*- 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 .
*/
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()
{
ImplDeleteItems();
if (mxAccessible)
mxAccessible->Invalidate();
}
void ValueSet::ImplDeleteItems()
{ const size_t n = mItemList.size();
for ( size_t i = 0; i < n; ++i )
{ if (ValueSetItem* pItem = mItemList[i].get())
{
rtl::Reference<ValueItemAcc> xItemAcc = pItem->GetAccessible(false); if (xItemAcc.is())
{
Any aOldAny;
Any aNewAny;
aOldAny <<= Reference<XAccessible>(xItemAcc);
ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
if (mpNoneItem && maNoneItemRect.Contains(rPos))
{ return VALUESET_ITEM_NONEITEM;
}
if (maItemListRect.Contains(rPos))
{ constint xc = rPos.X() - maItemListRect.Left(); constint yc = rPos.Y() - maItemListRect.Top(); // The point is inside the area of item list, // let's find the containing item. constint col = xc / (mnItemWidth + mnSpacing); constint x = xc % (mnItemWidth + mnSpacing); constint row = yc / (mnItemHeight + mnSpacing); constint 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;
}
}
}
case KEY_LEFT: if (nCurPos != VALUESET_ITEM_NONEITEM)
{ if (nCurPos)
{
nItemPos = nCurPos-1;
} elseif (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;
} elseif (mpNoneItem)
{
nItemPos = VALUESET_ITEM_NONEITEM;
} elseif (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 ) returntrue;
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();
}
}
bool ValueSet::MouseButtonUp( const MouseEvent& rMouseEvent )
{ if (rMouseEvent.IsLeft() && !rMouseEvent.IsMod2())
{ // tdf#165881 MouseUp may be seen without previous MouseDown. Select // what's under the mouse on mouse release when in menu-alike modes. constbool bMenuAlike = GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET; if (bMenuAlike)
{ if (ValueSetItem* pItem = ImplGetItem(ImplGetItem(rMouseEvent.GetPosPixel())))
SelectItem(pItem->mnId);
}
// tdf#142150 MouseUp seen without previous MouseDown if (mnSelItemId)
Select(); returntrue;
}
void ValueSet::RecalcScrollBar()
{ if (!mxScrolledWindow) return; constbool 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;
}
// 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);
// 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 (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;
} elseif ( 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 (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);
}
// nothing is changed in case of too small items if ((mnItemWidth <= 0) ||
(mnItemHeight <= ((nStyle & WB_ITEMBORDER) ? 4 : 2)) ||
!nItemCount)
{
mbHasVisibleItems = false;
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;
if (!((i + 1) % mnCols))
{
x = nStartX;
y += mnItemHeight + mnSpacing;
} else
x += mnItemWidth + mnSpacing;
} else
{ if (pItem->mbVisible && ImplHasAccessibleListeners())
{
Any aOldAny;
Any aNewAny;
if (pSelectedItem)
{ constbool 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;
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);
}
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 constint nAdjust = 2;
if (bDrawSel)
{ const basegfx::B2DPolygon aRectPoly(
basegfx::utils::createPolygonFromRect(
vcl::unotools::b2DRectangleFromRectangle(aRect)));
constint 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);
}
// delete rectangle and show text constbool 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);
// Remember old and new name for accessibility event.
Any aOldName;
Any aNewName;
OUString sString (pItem->maText);
aOldName <<= sString;
sString = rText;
aNewName <<= sString;
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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.