android: Update app-specific/MIME type icons
[LibreOffice.git] / svtools / source / control / valueset.cxx
blob933f8d42ca49353fcaa7f5f4fb1d69fc4e003ad9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <o3tl/safeint.hxx>
23 #include <basegfx/matrix/b2dhommatrix.hxx>
24 #include <basegfx/polygon/b2dpolygontools.hxx>
25 #include <tools/debug.hxx>
26 #include <vcl/canvastools.hxx>
27 #include <vcl/decoview.hxx>
28 #include <vcl/event.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/settings.hxx>
31 #include <vcl/virdev.hxx>
33 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
34 #include <com/sun/star/lang/XComponent.hpp>
35 #include <rtl/ustring.hxx>
36 #include <sal/log.hxx>
37 #include "valueimp.hxx"
39 #include <svtools/valueset.hxx>
41 #include <uiobject.hxx>
42 #include <vcl/uitest/logger.hxx>
43 #include <vcl/uitest/eventdescription.hxx>
45 using namespace css::uno;
46 using namespace css::lang;
47 using namespace css::accessibility;
49 namespace
51 void collectUIInformation( const OUString& aID , const OUString& aParentID , const OUString& aPos )
53 EventDescription aDescription;
54 aDescription.aID = aID ;
55 aDescription.aParameters = {{"POS", aPos }};
56 aDescription.aAction = "SELECT";
57 aDescription.aKeyWord = "ValueSet";
58 aDescription.aParent = aParentID;
59 UITestLogger::getInstance().logEvent(aDescription);
62 enum
64 ITEM_OFFSET = 4,
65 ITEM_OFFSET_DOUBLE = 6,
66 NAME_LINE_OFF_X = 2,
67 NAME_LINE_OFF_Y = 2,
68 NAME_LINE_HEIGHT = 2,
69 NAME_OFFSET = 2,
74 ValueSet::ValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow)
75 : maVirDev( VclPtr<VirtualDevice>::Create())
76 , mxScrolledWindow(std::move(pScrolledWindow))
77 , mnHighItemId(0)
78 , maColor(COL_TRANSPARENT)
79 , mnStyle(0)
80 , mbFormat(true)
81 , mbHighlight(false)
83 mnItemWidth = 0;
84 mnItemHeight = 0;
85 mnTextOffset = 0;
86 mnVisLines = 0;
87 mnLines = 0;
88 mnUserItemWidth = 0;
89 mnUserItemHeight = 0;
90 mnFirstLine = 0;
91 mnSelItemId = 0;
92 mnSavedItemId = -1;
93 mnCols = 0;
94 mnCurCol = 0;
95 mnUserCols = 0;
96 mnUserVisLines = 0;
97 mnSpacing = 0;
98 mnFrameStyle = DrawFrameStyle::NONE;
99 mbNoSelection = true;
100 mbDoubleSel = false;
101 mbScroll = false;
102 mbFullMode = true;
103 mbEdgeBlending = false;
104 mbHasVisibleItems = false;
106 if (mxScrolledWindow)
107 mxScrolledWindow->connect_vadjustment_changed(LINK(this, ValueSet, ImplScrollHdl));
110 void ValueSet::SetDrawingArea(weld::DrawingArea* pDrawingArea)
112 CustomWidgetController::SetDrawingArea(pDrawingArea);
113 // #106446#, #106601# force mirroring of virtual device
114 maVirDev->EnableRTL(pDrawingArea->get_direction());
117 Reference<XAccessible> ValueSet::CreateAccessible()
119 if (!mxAccessible)
120 mxAccessible.set(new ValueSetAcc(this));
121 return mxAccessible;
124 ValueSet::~ValueSet()
126 Reference<XComponent> xComponent(mxAccessible, UNO_QUERY);
127 if (xComponent.is())
128 xComponent->dispose();
130 ImplDeleteItems();
133 void ValueSet::ImplDeleteItems()
135 const size_t n = mItemList.size();
137 for ( size_t i = 0; i < n; ++i )
139 ValueSetItem* pItem = mItemList[i].get();
140 if ( pItem->mbVisible && ImplHasAccessibleListeners() )
142 Any aOldAny;
143 Any aNewAny;
145 aOldAny <<= pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ );
146 ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
149 mItemList[i].reset();
152 mItemList.clear();
155 void ValueSet::Select()
157 collectUIInformation(GetDrawingArea()->get_buildable_name() , GetDrawingArea()->get_help_id() , OUString::number(GetSelectedItemId()));
158 maSelectHdl.Call( this );
161 void ValueSet::UserDraw( const UserDrawEvent& )
165 size_t ValueSet::ImplGetItem( const Point& rPos ) const
167 if (!mbHasVisibleItems)
169 return VALUESET_ITEM_NOTFOUND;
172 if (mpNoneItem && maNoneItemRect.Contains(rPos))
174 return VALUESET_ITEM_NONEITEM;
177 if (maItemListRect.Contains(rPos))
179 const int xc = rPos.X() - maItemListRect.Left();
180 const int yc = rPos.Y() - maItemListRect.Top();
181 // The point is inside the area of item list,
182 // let's find the containing item.
183 const int col = xc / (mnItemWidth + mnSpacing);
184 const int x = xc % (mnItemWidth + mnSpacing);
185 const int row = yc / (mnItemHeight + mnSpacing);
186 const int y = yc % (mnItemHeight + mnSpacing);
188 if (x < mnItemWidth && y < mnItemHeight)
190 // the point is inside item rect and not inside spacing
191 const size_t item = (mnFirstLine + row) * static_cast<size_t>(mnCols) + col;
192 if (item < mItemList.size())
194 return item;
199 return VALUESET_ITEM_NOTFOUND;
202 ValueSetItem* ValueSet::ImplGetItem( size_t nPos )
204 if (nPos == VALUESET_ITEM_NONEITEM)
205 return mpNoneItem.get();
206 else
207 return (nPos < mItemList.size()) ? mItemList[nPos].get() : nullptr;
210 ValueSetItem* ValueSet::ImplGetFirstItem()
212 return !mItemList.empty() ? mItemList[0].get() : nullptr;
215 sal_uInt16 ValueSet::ImplGetVisibleItemCount() const
217 sal_uInt16 nRet = 0;
218 const size_t nItemCount = mItemList.size();
220 for ( size_t n = 0; n < nItemCount; ++n )
222 if ( mItemList[n]->mbVisible )
223 ++nRet;
226 return nRet;
229 void ValueSet::ImplFireAccessibleEvent( short nEventId, const Any& rOldValue, const Any& rNewValue )
231 ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible);
233 if( pAcc )
234 pAcc->FireAccessibleEvent( nEventId, rOldValue, rNewValue );
237 bool ValueSet::ImplHasAccessibleListeners() const
239 ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible);
240 return( pAcc && pAcc->HasAccessibleListeners() );
243 IMPL_LINK(ValueSet, ImplScrollHdl, weld::ScrolledWindow&, rScrollWin, void)
245 auto nNewFirstLine = rScrollWin.vadjustment_get_value();
246 if ( nNewFirstLine != mnFirstLine )
248 mnFirstLine = nNewFirstLine;
249 mbFormat = true;
250 Invalidate();
254 void ValueSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
256 rRenderContext.SetBackground(Application::GetSettings().GetStyleSettings().GetFaceColor());
257 rRenderContext.Erase();
258 ImplDraw(rRenderContext);
261 void ValueSet::GetFocus()
263 SAL_INFO("svtools", "value set getting focus");
264 Invalidate();
265 CustomWidgetController::GetFocus();
267 // Tell the accessible object that we got the focus.
268 ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible);
269 if (pAcc)
270 pAcc->GetFocus();
273 void ValueSet::LoseFocus()
275 SAL_INFO("svtools", "value set losing focus");
276 Invalidate();
277 CustomWidgetController::LoseFocus();
279 // Tell the accessible object that we lost the focus.
280 ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible);
281 if( pAcc )
282 pAcc->LoseFocus();
285 void ValueSet::Resize()
287 mbFormat = true;
288 if ( IsReallyVisible() && IsUpdateMode() )
289 Invalidate();
290 CustomWidgetController::Resize();
293 bool ValueSet::KeyInput( const KeyEvent& rKeyEvent )
295 size_t nLastItem = mItemList.size();
297 if ( !nLastItem || !ImplGetFirstItem() )
298 return CustomWidgetController::KeyInput(rKeyEvent);
300 if (mbFormat)
301 Invalidate();
303 --nLastItem;
305 const size_t nCurPos
306 = mnSelItemId ? GetItemPos(mnSelItemId) : (mpNoneItem ? VALUESET_ITEM_NONEITEM : 0);
307 size_t nItemPos = VALUESET_ITEM_NOTFOUND;
308 size_t nVStep = mnCols;
310 switch (rKeyEvent.GetKeyCode().GetCode())
312 case KEY_HOME:
313 nItemPos = mpNoneItem ? VALUESET_ITEM_NONEITEM : 0;
314 break;
316 case KEY_END:
317 nItemPos = nLastItem;
318 break;
320 case KEY_LEFT:
321 if (nCurPos != VALUESET_ITEM_NONEITEM)
323 if (nCurPos)
325 nItemPos = nCurPos-1;
327 else if (mpNoneItem)
329 nItemPos = VALUESET_ITEM_NONEITEM;
332 break;
334 case KEY_RIGHT:
335 if (nCurPos < nLastItem)
337 if (nCurPos == VALUESET_ITEM_NONEITEM)
339 nItemPos = 0;
341 else
343 nItemPos = nCurPos+1;
346 break;
348 case KEY_PAGEUP:
349 if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2())
351 return CustomWidgetController::KeyInput(rKeyEvent);
353 nVStep *= mnVisLines;
354 [[fallthrough]];
355 case KEY_UP:
356 if (nCurPos != VALUESET_ITEM_NONEITEM)
358 if (nCurPos == nLastItem)
360 const size_t nCol = mnCols ? nLastItem % mnCols : 0;
361 if (nCol < mnCurCol)
363 // Move to previous row/page, keeping the old column
364 nVStep -= mnCurCol - nCol;
367 if (nCurPos >= nVStep)
369 // Go up of a whole page
370 nItemPos = nCurPos-nVStep;
372 else if (mpNoneItem)
374 nItemPos = VALUESET_ITEM_NONEITEM;
376 else if (nCurPos > mnCols)
378 // Go to same column in first row
379 nItemPos = nCurPos % mnCols;
382 break;
384 case KEY_PAGEDOWN:
385 if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2())
387 return CustomWidgetController::KeyInput(rKeyEvent);
389 nVStep *= mnVisLines;
390 [[fallthrough]];
391 case KEY_DOWN:
392 if (nCurPos != nLastItem)
394 if (nCurPos == VALUESET_ITEM_NONEITEM)
396 nItemPos = nVStep-mnCols+mnCurCol;
398 else
400 nItemPos = nCurPos+nVStep;
402 if (nItemPos > nLastItem)
404 nItemPos = nLastItem;
407 break;
409 case KEY_RETURN:
410 if (GetStyle() & WB_NO_DIRECTSELECT)
412 // tdf#142479 on return select the entry the cursor is in
413 // before calling Select
414 if (nCurPos != VALUESET_ITEM_NONEITEM)
416 const sal_uInt16 nItemId = GetItemId(nCurPos);
417 if (nItemId != mnSelItemId)
418 SelectItem(nItemId);
420 Select();
421 break;
423 [[fallthrough]];
424 default:
425 return CustomWidgetController::KeyInput(rKeyEvent);
428 if ( nItemPos == VALUESET_ITEM_NOTFOUND )
429 return true;
431 if ( nItemPos!=VALUESET_ITEM_NONEITEM && nItemPos<nLastItem )
433 // update current column only in case of a new position
434 // which is also not a "specially" handled one.
435 mnCurCol = mnCols ? nItemPos % mnCols : 0;
437 const sal_uInt16 nItemId = (nItemPos != VALUESET_ITEM_NONEITEM) ? GetItemId( nItemPos ) : 0;
438 if ( nItemId != mnSelItemId )
440 SelectItem( nItemId );
441 if (!(GetStyle() & WB_NO_DIRECTSELECT))
443 // select only if WB_NO_DIRECTSELECT is not set
444 Select();
448 return true;
451 void ValueSet::ImplTracking(bool bLeaveWindow, const Point& rPos)
453 ValueSetItem* pItem = bLeaveWindow ? nullptr : ImplGetItem(ImplGetItem(rPos));
454 if ( pItem )
456 if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET )
457 mbHighlight = true;
459 ImplHighlightItem(pItem->mnId);
461 else
463 if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET )
464 mbHighlight = true;
466 ImplHighlightItem(0);
470 bool ValueSet::MouseButtonDown( const MouseEvent& rMouseEvent )
472 if (rMouseEvent.IsLeft() && !rMouseEvent.IsMod2())
474 bool bConsumed = false;
475 ValueSetItem* pItem = ImplGetItem( ImplGetItem( rMouseEvent.GetPosPixel() ) );
476 if (rMouseEvent.GetClicks() == 1)
478 if (pItem)
479 SelectItem(pItem->mnId);
480 GrabFocus();
481 bConsumed = true;
483 else if (pItem && rMouseEvent.GetClicks() == 2)
485 maDoubleClickHdl.Call(this);
486 bConsumed = true;
488 return bConsumed;
491 return CustomWidgetController::MouseButtonDown( rMouseEvent );
494 bool ValueSet::MouseButtonUp( const MouseEvent& rMouseEvent )
496 if (rMouseEvent.IsLeft() && !rMouseEvent.IsMod2())
498 // tdf#142150 MouseUp seen without previous MouseDown
499 if (mnSelItemId)
500 Select();
501 return true;
504 return CustomWidgetController::MouseButtonUp( rMouseEvent );
507 bool ValueSet::MouseMove(const MouseEvent& rMouseEvent)
509 // because of SelectionMode
510 if ((GetStyle() & WB_MENUSTYLEVALUESET) || (GetStyle() & WB_FLATVALUESET))
511 ImplTracking(rMouseEvent.IsLeaveWindow(), rMouseEvent.GetPosPixel());
512 return CustomWidgetController::MouseMove(rMouseEvent);
515 void ValueSet::QueueReformat()
517 queue_resize();
518 RecalcScrollBar();
519 mbFormat = true;
520 if ( IsReallyVisible() && IsUpdateMode() )
521 Invalidate();
524 void ValueSet::RemoveItem( sal_uInt16 nItemId )
526 size_t nPos = GetItemPos( nItemId );
528 if ( nPos == VALUESET_ITEM_NOTFOUND )
529 return;
531 if ( nPos < mItemList.size() ) {
532 mItemList.erase( mItemList.begin() + nPos );
535 // reset variables
536 if (mnHighItemId == nItemId || mnSelItemId == nItemId)
538 mnCurCol = 0;
539 mnHighItemId = 0;
540 mnSelItemId = 0;
541 mbNoSelection = true;
544 QueueReformat();
547 bool ValueSet::TurnOffScrollBar()
549 if (mxScrolledWindow->get_vpolicy() == VclPolicyType::NEVER)
550 return false;
551 mxScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
552 weld::DrawingArea* pDrawingArea = GetDrawingArea();
553 Size aPrefSize(pDrawingArea->get_preferred_size());
554 pDrawingArea->set_size_request(aPrefSize.Width() + GetScrollWidth(), aPrefSize.Height());
555 return true;
558 void ValueSet::TurnOnScrollBar()
560 if (mxScrolledWindow->get_vpolicy() == VclPolicyType::ALWAYS)
561 return;
562 mxScrolledWindow->set_vpolicy(VclPolicyType::ALWAYS);
563 weld::DrawingArea* pDrawingArea = GetDrawingArea();
564 Size aPrefSize(pDrawingArea->get_preferred_size());
565 pDrawingArea->set_size_request(aPrefSize.Width() - GetScrollWidth(), aPrefSize.Height());
568 void ValueSet::RecalcScrollBar()
570 if (!mxScrolledWindow)
571 return;
572 const bool bScrollAllowed = GetStyle() & WB_VSCROLL;
573 if (!bScrollAllowed)
574 return;
575 // reset scrolled window state to initial value so it will get configured
576 // to the right adjustment on the next format which we toggle on to happen
577 // if the scrolledwindow wasn't in its initial state already
578 if (TurnOffScrollBar())
579 mbFormat = true;
582 void ValueSet::Clear()
584 ImplDeleteItems();
586 // reset variables
587 mnFirstLine = 0;
588 mnCurCol = 0;
589 mnHighItemId = 0;
590 mnSelItemId = 0;
591 mbNoSelection = true;
593 RecalcScrollBar();
595 mbFormat = true;
596 if ( IsReallyVisible() && IsUpdateMode() )
597 Invalidate();
600 size_t ValueSet::GetItemCount() const
602 return mItemList.size();
605 size_t ValueSet::GetItemPos( sal_uInt16 nItemId ) const
607 for ( size_t i = 0, n = mItemList.size(); i < n; ++i ) {
608 if ( mItemList[i]->mnId == nItemId ) {
609 return i;
612 return VALUESET_ITEM_NOTFOUND;
615 sal_uInt16 ValueSet::GetItemId( size_t nPos ) const
617 return ( nPos < mItemList.size() ) ? mItemList[nPos]->mnId : 0 ;
620 sal_uInt16 ValueSet::GetItemId( const Point& rPos ) const
622 size_t nItemPos = ImplGetItem( rPos );
623 if ( nItemPos != VALUESET_ITEM_NOTFOUND )
624 return GetItemId( nItemPos );
626 return 0;
629 tools::Rectangle ValueSet::GetItemRect( sal_uInt16 nItemId ) const
631 const size_t nPos = GetItemPos( nItemId );
633 if ( nPos!=VALUESET_ITEM_NOTFOUND && mItemList[nPos]->mbVisible )
634 return ImplGetItemRect( nPos );
636 return tools::Rectangle();
639 tools::Rectangle ValueSet::ImplGetItemRect( size_t nPos ) const
641 const size_t nVisibleBegin = static_cast<size_t>(mnFirstLine)*mnCols;
642 const size_t nVisibleEnd = nVisibleBegin + static_cast<size_t>(mnVisLines)*mnCols;
644 // Check if the item is inside the range of the displayed ones,
645 // taking into account that last row could be incomplete
646 if ( nPos<nVisibleBegin || nPos>=nVisibleEnd || nPos>=mItemList.size() )
647 return tools::Rectangle();
649 nPos -= nVisibleBegin;
651 const size_t row = mnCols ? nPos/mnCols : 0;
652 const size_t col = mnCols ? nPos%mnCols : 0;
653 const tools::Long x = maItemListRect.Left()+col*(mnItemWidth+mnSpacing);
654 const tools::Long y = maItemListRect.Top()+row*(mnItemHeight+mnSpacing);
656 return tools::Rectangle( Point(x, y), Size(mnItemWidth, mnItemHeight) );
659 void ValueSet::ImplHighlightItem(sal_uInt16 nItemId)
661 if ( mnHighItemId == nItemId )
662 return;
664 // remember the old item to delete the previous selection
665 mnHighItemId = nItemId;
667 // remove the old selection and draw the new one
668 Invalidate();
671 void ValueSet::ImplDraw(vcl::RenderContext& rRenderContext)
673 if (mbFormat)
674 Format(rRenderContext);
676 Point aDefPos;
677 Size aSize = maVirDev->GetOutputSizePixel();
679 rRenderContext.DrawOutDev(aDefPos, aSize, aDefPos, aSize, *maVirDev);
681 // draw parting line to the Namefield
682 if (GetStyle() & WB_NAMEFIELD)
684 if (!(GetStyle() & WB_FLATVALUESET))
686 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
687 Size aWinSize(GetOutputSizePixel());
688 Point aPos1(NAME_LINE_OFF_X, mnTextOffset + NAME_LINE_OFF_Y);
689 Point aPos2(aWinSize.Width() - (NAME_LINE_OFF_X * 2), mnTextOffset + NAME_LINE_OFF_Y);
690 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
692 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
693 rRenderContext.DrawLine(aPos1, aPos2);
694 aPos1.AdjustY( 1 );
695 aPos2.AdjustY( 1 );
696 rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
698 else
699 rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor());
700 rRenderContext.DrawLine(aPos1, aPos2);
704 ImplDrawSelect(rRenderContext);
708 * An inelegant method; sets the item width & height such that
709 * all of the included items and their labels fit; if we can
710 * calculate that.
712 void ValueSet::RecalculateItemSizes()
714 Size aLargestItem = GetLargestItemSize();
716 if ( mnUserItemWidth != aLargestItem.Width() ||
717 mnUserItemHeight != aLargestItem.Height() )
719 mnUserItemWidth = aLargestItem.Width();
720 mnUserItemHeight = aLargestItem.Height();
721 QueueReformat();
725 void ValueSet::SetFirstLine(sal_uInt16 nNewFirstLine)
727 if (nNewFirstLine != mnFirstLine)
729 mnFirstLine = nNewFirstLine;
730 if (mxScrolledWindow)
731 mxScrolledWindow->vadjustment_set_value(mnFirstLine);
735 void ValueSet::SelectItem( sal_uInt16 nItemId )
737 size_t nItemPos = 0;
739 if ( nItemId )
741 nItemPos = GetItemPos( nItemId );
742 if ( nItemPos == VALUESET_ITEM_NOTFOUND )
743 return;
746 if ( !((mnSelItemId != nItemId) || mbNoSelection) )
747 return;
749 const sal_uInt16 nOldItem = mnSelItemId;
750 mnSelItemId = nItemId;
751 mbNoSelection = false;
753 bool bNewOut = !mbFormat && IsReallyVisible() && IsUpdateMode();
754 bool bNewLine = false;
756 if (weld::DrawingArea* pNeedsFormatToScroll = !mnCols ? GetDrawingArea() : nullptr)
758 Format(pNeedsFormatToScroll->get_ref_device());
759 // reset scrollbar so it's set to the later calculated mnFirstLine on
760 // the next Format
761 RecalcScrollBar();
764 // if necessary scroll to the visible area
765 if (mbScroll && nItemId && mnCols)
767 sal_uInt16 nNewLine = static_cast<sal_uInt16>(nItemPos / mnCols);
768 if ( nNewLine < mnFirstLine )
770 SetFirstLine(nNewLine);
771 bNewLine = true;
773 else if ( nNewLine > o3tl::make_unsigned(mnFirstLine+mnVisLines-1) )
775 SetFirstLine(static_cast<sal_uInt16>(nNewLine-mnVisLines+1));
776 bNewLine = true;
780 if ( bNewOut )
782 if ( bNewLine )
784 // redraw everything if the visible area has changed
785 mbFormat = true;
787 Invalidate();
790 if( !ImplHasAccessibleListeners() )
791 return;
793 // focus event (deselect)
794 if( nOldItem )
796 const size_t nPos = GetItemPos( nItemId );
798 if( nPos != VALUESET_ITEM_NOTFOUND )
800 ValueItemAcc* pItemAcc = ValueItemAcc::getImplementation(
801 mItemList[nPos]->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) );
803 if( pItemAcc )
805 Any aOldAny;
806 Any aNewAny;
807 aOldAny <<= Reference(getXWeak(pItemAcc));
808 ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny );
813 // focus event (select)
814 const size_t nPos = GetItemPos( mnSelItemId );
816 ValueSetItem* pItem;
817 if( nPos != VALUESET_ITEM_NOTFOUND )
818 pItem = mItemList[nPos].get();
819 else
820 pItem = mpNoneItem.get();
822 ValueItemAcc* pItemAcc = nullptr;
823 if (pItem != nullptr)
824 pItemAcc = ValueItemAcc::getImplementation( pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) );
826 if( pItemAcc )
828 Any aOldAny;
829 Any aNewAny;
830 aNewAny <<= Reference(getXWeak(pItemAcc));
831 ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny);
834 // selection event
835 Any aOldAny;
836 Any aNewAny;
837 ImplFireAccessibleEvent(AccessibleEventId::SELECTION_CHANGED, aOldAny, aNewAny);
840 void ValueSet::SetNoSelection()
842 mbNoSelection = true;
843 mbHighlight = false;
845 if (IsReallyVisible() && IsUpdateMode())
846 Invalidate();
849 void ValueSet::SetStyle(WinBits nStyle)
851 if (nStyle != mnStyle)
853 mnStyle = nStyle;
854 mbFormat = true;
855 Invalidate();
859 void ValueSet::Format(vcl::RenderContext const & rRenderContext)
861 Size aWinSize(GetOutputSizePixel());
862 size_t nItemCount = mItemList.size();
863 WinBits nStyle = GetStyle();
864 tools::Long nTxtHeight = rRenderContext.GetTextHeight();
865 tools::Long nOff;
866 tools::Long nNoneHeight;
867 tools::Long nNoneSpace;
869 if (mxScrolledWindow && !(nStyle & WB_VSCROLL) && mxScrolledWindow->get_vpolicy() != VclPolicyType::NEVER)
870 TurnOffScrollBar();
872 // calculate item offset
873 if (nStyle & WB_ITEMBORDER)
875 if (nStyle & WB_DOUBLEBORDER)
876 nOff = ITEM_OFFSET_DOUBLE;
877 else
878 nOff = ITEM_OFFSET;
880 else
881 nOff = 0;
883 // consider size, if NameField does exist
884 if (nStyle & WB_NAMEFIELD)
886 mnTextOffset = aWinSize.Height() - nTxtHeight - NAME_OFFSET;
887 aWinSize.AdjustHeight( -(nTxtHeight + NAME_OFFSET) );
889 if (!(nStyle & WB_FLATVALUESET))
891 mnTextOffset -= NAME_LINE_HEIGHT + NAME_LINE_OFF_Y;
892 aWinSize.AdjustHeight( -(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y) );
895 else
896 mnTextOffset = 0;
898 // consider offset and size, if NoneField does exist
899 if (nStyle & WB_NONEFIELD)
901 nNoneHeight = nTxtHeight + nOff;
902 nNoneSpace = mnSpacing;
904 else
906 nNoneHeight = 0;
907 nNoneSpace = 0;
908 mpNoneItem.reset();
911 // calculate number of columns
912 if (!mnUserCols)
914 if (mnUserItemWidth)
916 mnCols = static_cast<sal_uInt16>((aWinSize.Width() - mnSpacing) / (mnUserItemWidth + mnSpacing));
917 if (mnCols <= 0)
918 mnCols = 1;
920 else
922 mnCols = 1;
925 else
927 mnCols = mnUserCols;
930 // calculate number of rows
931 mbScroll = false;
933 auto nOldLines = mnLines;
934 // Floor( (M+N-1)/N )==Ceiling( M/N )
935 mnLines = (static_cast<tools::Long>(nItemCount) + mnCols - 1) / mnCols;
936 if (mnLines <= 0)
937 mnLines = 1;
939 bool bAdjustmentOutOfDate = nOldLines != mnLines;
941 auto nOldVisLines = mnVisLines;
943 tools::Long nCalcHeight = aWinSize.Height() - nNoneHeight;
944 if (mnUserVisLines)
946 mnVisLines = mnUserVisLines;
948 else if (mnUserItemHeight)
950 mnVisLines = (nCalcHeight - nNoneSpace + mnSpacing) / (mnUserItemHeight + mnSpacing);
951 if (!mnVisLines)
952 mnVisLines = 1;
954 else
956 mnVisLines = mnLines;
959 bAdjustmentOutOfDate |= nOldVisLines != mnVisLines;
961 if (mnLines > mnVisLines)
962 mbScroll = true;
964 if (mnLines <= mnVisLines)
966 SetFirstLine(0);
968 else
970 if (mnFirstLine > o3tl::make_unsigned(mnLines - mnVisLines))
971 SetFirstLine(static_cast<sal_uInt16>(mnLines - mnVisLines));
974 // calculate item size
975 const tools::Long nColSpace = (mnCols - 1) * static_cast<tools::Long>(mnSpacing);
976 const tools::Long nLineSpace = ((mnVisLines - 1) * mnSpacing) + nNoneSpace;
977 if (mnUserItemWidth && !mnUserCols)
979 mnItemWidth = mnUserItemWidth;
980 if (mnItemWidth > aWinSize.Width() - nColSpace)
981 mnItemWidth = aWinSize.Width() - nColSpace;
983 else
984 mnItemWidth = (aWinSize.Width() - nColSpace) / mnCols;
985 if (mnUserItemHeight && !mnUserVisLines)
987 mnItemHeight = mnUserItemHeight;
988 if (mnItemHeight > nCalcHeight - nNoneSpace)
989 mnItemHeight = nCalcHeight - nNoneSpace;
991 else
993 nCalcHeight -= nLineSpace;
994 mnItemHeight = nCalcHeight / mnVisLines;
997 // Init VirDev
998 maVirDev->SetBackground(Application::GetSettings().GetStyleSettings().GetFaceColor());
999 maVirDev->SetOutputSizePixel(aWinSize);
1000 maVirDev->Erase();
1002 // nothing is changed in case of too small items
1003 if ((mnItemWidth <= 0) ||
1004 (mnItemHeight <= ((nStyle & WB_ITEMBORDER) ? 4 : 2)) ||
1005 !nItemCount)
1007 mbHasVisibleItems = false;
1009 if ((nStyle & WB_NONEFIELD) && mpNoneItem)
1011 mpNoneItem->mbVisible = false;
1012 mpNoneItem->maText = GetText();
1015 for (size_t i = 0; i < nItemCount; i++)
1017 mItemList[i]->mbVisible = false;
1020 if (mxScrolledWindow && mxScrolledWindow->get_vpolicy() != VclPolicyType::NEVER)
1021 TurnOffScrollBar();
1023 else
1025 mbHasVisibleItems = true;
1027 // determine Frame-Style
1028 if (nStyle & WB_DOUBLEBORDER)
1029 mnFrameStyle = DrawFrameStyle::DoubleIn;
1030 else
1031 mnFrameStyle = DrawFrameStyle::In;
1033 // draw the selection with double width if the items are bigger
1034 if ((nStyle & WB_DOUBLEBORDER) &&
1035 ((mnItemWidth >= 25) && (mnItemHeight >= 20)))
1037 mbDoubleSel = true;
1039 else
1041 mbDoubleSel = false;
1044 // calculate offsets
1045 tools::Long nStartX;
1046 tools::Long nStartY;
1047 if (mbFullMode)
1049 tools::Long nAllItemWidth = (mnItemWidth * mnCols) + nColSpace;
1050 tools::Long nAllItemHeight = (mnItemHeight * mnVisLines) + nNoneHeight + nLineSpace;
1051 nStartX = (aWinSize.Width() - nAllItemWidth) / 2;
1052 nStartY = (aWinSize.Height() - nAllItemHeight) / 2;
1054 else
1056 nStartX = 0;
1057 nStartY = 0;
1060 // calculate and draw items
1061 maVirDev->SetLineColor();
1062 tools::Long x = nStartX;
1063 tools::Long y = nStartY;
1065 // create NoSelection field and show it
1066 if (nStyle & WB_NONEFIELD)
1068 if (!mpNoneItem)
1069 mpNoneItem.reset(new ValueSetItem(*this));
1071 mpNoneItem->mnId = 0;
1072 mpNoneItem->meType = VALUESETITEM_NONE;
1073 mpNoneItem->mbVisible = true;
1074 maNoneItemRect.SetLeft( x );
1075 maNoneItemRect.SetTop( y );
1076 maNoneItemRect.SetRight( maNoneItemRect.Left() + aWinSize.Width() - x - 1 );
1077 maNoneItemRect.SetBottom( y + nNoneHeight - 1 );
1079 ImplFormatItem(rRenderContext, mpNoneItem.get(), maNoneItemRect);
1081 y += nNoneHeight + nNoneSpace;
1084 // draw items
1085 sal_uLong nFirstItem = static_cast<sal_uLong>(mnFirstLine) * mnCols;
1086 sal_uLong nLastItem = nFirstItem + (mnVisLines * mnCols);
1088 maItemListRect.SetLeft( x );
1089 maItemListRect.SetTop( y );
1090 maItemListRect.SetRight( x + mnCols * (mnItemWidth + mnSpacing) - mnSpacing - 1 );
1091 maItemListRect.SetBottom( y + mnVisLines * (mnItemHeight + mnSpacing) - mnSpacing - 1 );
1093 if (!mbFullMode)
1095 // If want also draw parts of items in the last line,
1096 // then we add one more line if parts of these line are
1097 // visible
1098 if (y + (mnVisLines * (mnItemHeight + mnSpacing)) < aWinSize.Height())
1099 nLastItem += mnCols;
1100 maItemListRect.SetBottom( aWinSize.Height() - y );
1102 for (size_t i = 0; i < nItemCount; i++)
1104 ValueSetItem* pItem = mItemList[i].get();
1106 if (i >= nFirstItem && i < nLastItem)
1108 if (!pItem->mbVisible && ImplHasAccessibleListeners())
1110 Any aOldAny;
1111 Any aNewAny;
1113 aNewAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/);
1114 ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
1117 pItem->mbVisible = true;
1118 ImplFormatItem(rRenderContext, pItem, tools::Rectangle(Point(x, y), Size(mnItemWidth, mnItemHeight)));
1120 if (!((i + 1) % mnCols))
1122 x = nStartX;
1123 y += mnItemHeight + mnSpacing;
1125 else
1126 x += mnItemWidth + mnSpacing;
1128 else
1130 if (pItem->mbVisible && ImplHasAccessibleListeners())
1132 Any aOldAny;
1133 Any aNewAny;
1135 aOldAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/);
1136 ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
1139 pItem->mbVisible = false;
1143 // arrange ScrollBar, set values and show it
1144 if (mxScrolledWindow && (nStyle & WB_VSCROLL))
1146 bool bTurnScrollbarOn = mxScrolledWindow->get_vpolicy() != VclPolicyType::ALWAYS;
1147 if (bAdjustmentOutOfDate || bTurnScrollbarOn)
1149 tools::Long nPageSize = mnVisLines;
1150 if (nPageSize < 1)
1151 nPageSize = 1;
1152 mxScrolledWindow->vadjustment_configure(mnFirstLine, 0, mnLines, 1,
1153 mnVisLines, nPageSize);
1156 if (bTurnScrollbarOn)
1157 TurnOnScrollBar();
1161 // waiting for the next since the formatting is finished
1162 mbFormat = false;
1165 void ValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext)
1167 if (!IsReallyVisible())
1168 return;
1170 const bool bFocus = HasFocus();
1172 if (!bFocus && mbNoSelection && !mbHighlight)
1173 return;
1175 tools::Rectangle aSelectedRect, aHoverRect;
1176 ValueSetItem* pSelectedItem = ImplGetDrawSelectItem(mnSelItemId, bFocus, aSelectedRect);
1177 ValueSetItem* pHighlightItem = mnHighItemId ? ImplGetDrawSelectItem(mnHighItemId, false, aHoverRect) : nullptr;
1179 if (pSelectedItem)
1181 const bool bHover = pSelectedItem == pHighlightItem;
1182 ImplDrawSelect(rRenderContext, aSelectedRect, pSelectedItem, bFocus, !mbNoSelection, true, bHover);
1184 if (pHighlightItem && (pSelectedItem != pHighlightItem || mbNoSelection))
1186 // For the case that there isn't a selected item, but due to wanting to
1187 // show focus is in the valueset, the above block will have drawn the
1188 // first item with a focus rect. For that situation; if the valueset is
1189 // the thin WB_MENUSTYLEVALUESET case then blend this highlight border
1190 // on top of that focus rect and it will appear with a highlighted
1191 // focus rect. If it's the other case of a thicker border then redraw
1192 // the focus rect highlighted with the hover color.
1193 bool bDrawFocus;
1194 WinBits nStyle = GetStyle();
1195 if (nStyle & WB_MENUSTYLEVALUESET)
1196 bDrawFocus = false;
1197 else
1198 bDrawFocus = pSelectedItem == pHighlightItem && mbNoSelection;
1200 ImplDrawSelect(rRenderContext, aHoverRect, pHighlightItem, bDrawFocus, mbHighlight, false, true);
1204 ValueSetItem* ValueSet::ImplGetDrawSelectItem(sal_uInt16 nItemId, const bool bFocus, tools::Rectangle& rRect)
1206 ValueSetItem* pItem = nullptr;
1207 if (nItemId)
1209 const size_t nPos = GetItemPos( nItemId );
1210 pItem = mItemList[ nPos ].get();
1211 rRect = ImplGetItemRect( nPos );
1213 else if (mpNoneItem)
1215 pItem = mpNoneItem.get();
1216 rRect = maNoneItemRect;
1218 else if (bFocus && (pItem = ImplGetFirstItem()))
1220 rRect = ImplGetItemRect(0);
1222 return pItem;
1225 void ValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext,
1226 const tools::Rectangle& rRect, const ValueSetItem* pItem,
1227 const bool bFocus, const bool bDrawSel,
1228 const bool bSelected, const bool bHover)
1230 tools::Rectangle aRect(rRect);
1232 // draw selection
1233 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1234 rRenderContext.SetFillColor();
1236 Color aDoubleColor;
1237 Color aSingleColor;
1239 sal_uInt16 nTransparencePercent = 0;
1241 if (bSelected && bHover)
1243 aDoubleColor = rStyleSettings.GetActiveColor();
1244 aSingleColor = rStyleSettings.GetActiveTextColor();
1246 else if (bSelected || bHover)
1248 aDoubleColor = rStyleSettings.GetHighlightColor();
1249 aSingleColor = rStyleSettings.GetHighlightTextColor();
1250 if (bHover)
1252 nTransparencePercent = 55;
1256 // specify selection output
1257 WinBits nStyle = GetStyle();
1258 if (nStyle & WB_MENUSTYLEVALUESET)
1260 if (bFocus)
1261 InvertFocusRect(rRenderContext, aRect);
1262 if (bDrawSel)
1264 rRenderContext.SetLineColor(aDoubleColor);
1265 tools::PolyPolygon aPolyPoly(1);
1266 aPolyPoly.Insert(aRect);
1267 rRenderContext.DrawTransparent(aPolyPoly, nTransparencePercent);
1270 else
1272 rRenderContext.SetLineColor(aDoubleColor);
1273 tools::Rectangle aFocusRect;
1275 if (!mbDoubleSel)
1277 // an outer rectangle surrounding a "focus" rectangle, surrounding
1278 // an inner rectangle. Focus rectangle is always drawn, but rendered
1279 // empty when there is no focus. e.g. as seen in color valuesets
1280 if (bDrawSel)
1282 tools::PolyPolygon aPolyPoly(1);
1283 aPolyPoly.Insert(aRect);
1284 rRenderContext.DrawTransparent(aPolyPoly, nTransparencePercent);
1287 aRect.AdjustLeft( 1 );
1288 aRect.AdjustTop( 1 );
1289 aRect.AdjustRight( -1 );
1290 aRect.AdjustBottom( -1 );
1292 aFocusRect = aRect;
1294 aRect.AdjustLeft( 1 );
1295 aRect.AdjustTop( 1 );
1296 aRect.AdjustRight( -1 );
1297 aRect.AdjustBottom( -1 );
1299 if (bDrawSel)
1301 tools::PolyPolygon aPolyPoly(1);
1302 aPolyPoly.Insert(aRect);
1303 rRenderContext.DrawTransparent(aPolyPoly, nTransparencePercent);
1306 if (bDrawSel)
1307 rRenderContext.SetLineColor(aSingleColor);
1308 else
1309 rRenderContext.SetLineColor(COL_LIGHTGRAY);
1311 rRenderContext.DrawRect(aFocusRect);
1313 else
1315 // a thick bordered rectangle surrounding an optional "focus"
1316 // rectangle which is only drawn when focused, as seen in format,
1317 // bullets and numbering in writer
1318 const int nAdjust = 2;
1320 aRect.AdjustLeft(nAdjust);
1321 aRect.AdjustTop(nAdjust);
1322 aRect.AdjustRight(-nAdjust);
1323 aRect.AdjustBottom(-nAdjust);
1325 aFocusRect = aRect;
1327 if (bDrawSel)
1329 const basegfx::B2DPolygon aRectPoly(
1330 basegfx::utils::createPolygonFromRect(
1331 vcl::unotools::b2DRectangleFromRectangle(aRect)));
1333 const int nThickness = nAdjust * 2;
1335 if (!rRenderContext.DrawPolyLineDirect(basegfx::B2DHomMatrix(),
1336 aRectPoly,
1337 nThickness,
1338 nTransparencePercent / 100.0,
1339 nullptr,
1340 basegfx::B2DLineJoin::Miter))
1342 SAL_WARN("svtools", "presumably impossible in practice, but fallback to see something");
1343 rRenderContext.DrawPolyLine(aRectPoly, nThickness, basegfx::B2DLineJoin::Miter);
1347 if (bFocus)
1349 if (bDrawSel)
1350 rRenderContext.SetLineColor(aSingleColor);
1351 else
1352 rRenderContext.SetLineColor(COL_LIGHTGRAY);
1353 rRenderContext.DrawRect(aFocusRect);
1357 if (bFocus)
1358 InvertFocusRect(rRenderContext, aFocusRect);
1361 ImplDrawItemText(rRenderContext, pItem->maText);
1364 void ValueSet::ImplFormatItem(vcl::RenderContext const & rRenderContext, ValueSetItem* pItem, tools::Rectangle aRect)
1366 WinBits nStyle = GetStyle();
1367 if (nStyle & WB_ITEMBORDER)
1369 aRect.AdjustLeft(1 );
1370 aRect.AdjustTop(1 );
1371 aRect.AdjustRight( -1 );
1372 aRect.AdjustBottom( -1 );
1374 if (nStyle & WB_FLATVALUESET)
1376 sal_Int32 nBorder = (nStyle & WB_DOUBLEBORDER) ? 2 : 1;
1378 aRect.AdjustLeft(nBorder );
1379 aRect.AdjustTop(nBorder );
1380 aRect.AdjustRight( -nBorder );
1381 aRect.AdjustBottom( -nBorder );
1383 else
1385 DecorationView aView(maVirDev.get());
1386 aRect = aView.DrawFrame(aRect, mnFrameStyle);
1390 if (pItem == mpNoneItem.get())
1391 pItem->maText = GetText();
1393 if ((aRect.GetHeight() <= 0) || (aRect.GetWidth() <= 0))
1394 return;
1396 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1398 if (pItem == mpNoneItem.get())
1400 maVirDev->SetFont(rRenderContext.GetFont());
1401 maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor());
1402 maVirDev->SetTextFillColor();
1403 maVirDev->SetFillColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuColor() : rStyleSettings.GetWindowColor());
1404 maVirDev->DrawRect(aRect);
1405 Point aTxtPos(aRect.Left() + 2, aRect.Top());
1406 tools::Long nTxtWidth = rRenderContext.GetTextWidth(pItem->maText);
1407 if ((aTxtPos.X() + nTxtWidth) > aRect.Right())
1409 maVirDev->SetClipRegion(vcl::Region(aRect));
1410 maVirDev->DrawText(aTxtPos, pItem->maText);
1411 maVirDev->SetClipRegion();
1413 else
1414 maVirDev->DrawText(aTxtPos, pItem->maText);
1416 else if (pItem->meType == VALUESETITEM_COLOR)
1418 maVirDev->SetFillColor(pItem->maColor);
1419 maVirDev->DrawRect(aRect);
1421 else
1423 if (IsColor())
1424 maVirDev->SetFillColor(maColor);
1425 else if (nStyle & WB_MENUSTYLEVALUESET)
1426 maVirDev->SetFillColor(rStyleSettings.GetMenuColor());
1427 else if (IsEnabled())
1428 maVirDev->SetFillColor(rStyleSettings.GetWindowColor());
1429 else
1430 maVirDev->SetFillColor(rStyleSettings.GetFaceColor());
1431 maVirDev->DrawRect(aRect);
1433 if (pItem->meType == VALUESETITEM_USERDRAW)
1435 UserDrawEvent aUDEvt(maVirDev.get(), aRect, pItem->mnId);
1436 UserDraw(aUDEvt);
1438 else
1440 Size aImageSize = pItem->maImage.GetSizePixel();
1441 Size aRectSize = aRect.GetSize();
1442 Point aPos(aRect.Left(), aRect.Top());
1443 aPos.AdjustX((aRectSize.Width() - aImageSize.Width()) / 2 );
1445 if (pItem->meType != VALUESETITEM_IMAGE_AND_TEXT)
1446 aPos.AdjustY((aRectSize.Height() - aImageSize.Height()) / 2 );
1448 DrawImageFlags nImageStyle = DrawImageFlags::NONE;
1449 if (!IsEnabled())
1450 nImageStyle |= DrawImageFlags::Disable;
1452 if (aImageSize.Width() > aRectSize.Width() ||
1453 aImageSize.Height() > aRectSize.Height())
1455 maVirDev->SetClipRegion(vcl::Region(aRect));
1456 maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle);
1457 maVirDev->SetClipRegion();
1459 else
1460 maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle);
1462 if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT)
1464 maVirDev->SetFont(rRenderContext.GetFont());
1465 maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor());
1466 maVirDev->SetTextFillColor();
1468 tools::Long nTxtWidth = maVirDev->GetTextWidth(pItem->maText);
1470 if (nTxtWidth > aRect.GetWidth())
1471 maVirDev->SetClipRegion(vcl::Region(aRect));
1473 maVirDev->DrawText(Point(aRect.Left() +
1474 (aRect.GetWidth() - nTxtWidth) / 2,
1475 aRect.Bottom() - maVirDev->GetTextHeight()),
1476 pItem->maText);
1478 if (nTxtWidth > aRect.GetWidth())
1479 maVirDev->SetClipRegion();
1484 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
1486 if (nEdgeBlendingPercent)
1488 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
1489 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
1490 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
1491 const BitmapEx aBlendFrame(createBlendFrame(aRect.GetSize(), nAlpha, rTopLeft, rBottomRight));
1493 if (!aBlendFrame.IsEmpty())
1495 maVirDev->DrawBitmapEx(aRect.TopLeft(), aBlendFrame);
1500 void ValueSet::ImplDrawItemText(vcl::RenderContext& rRenderContext, const OUString& rText)
1502 if (!(GetStyle() & WB_NAMEFIELD))
1503 return;
1505 Size aWinSize(GetOutputSizePixel());
1506 tools::Long nTxtWidth = rRenderContext.GetTextWidth(rText);
1507 tools::Long nTxtOffset = mnTextOffset;
1509 rRenderContext.Push(vcl::PushFlags::TEXTCOLOR);
1511 // delete rectangle and show text
1512 const bool bFlat(GetStyle() & WB_FLATVALUESET);
1513 if (!bFlat)
1514 nTxtOffset += NAME_LINE_HEIGHT+NAME_LINE_OFF_Y;
1516 rRenderContext.SetTextColor(Application::GetSettings().GetStyleSettings().GetButtonTextColor());
1517 // tdf#153787 highlighted entry text is drawn in the same Paint as the selected text, so can
1518 // overwrite already rendered text
1519 rRenderContext.Erase(tools::Rectangle(Point(0, nTxtOffset), Point(aWinSize.Width(), aWinSize.Height())));
1520 rRenderContext.DrawText(Point((aWinSize.Width() - nTxtWidth) / 2, nTxtOffset + (NAME_OFFSET / 2)), rText);
1522 rRenderContext.Pop();
1525 void ValueSet::StyleUpdated()
1527 mbFormat = true;
1528 CustomWidgetController::StyleUpdated();
1531 void ValueSet::EnableFullItemMode( bool bFullMode )
1533 mbFullMode = bFullMode;
1536 void ValueSet::SetColCount( sal_uInt16 nNewCols )
1538 if ( mnUserCols != nNewCols )
1540 mnUserCols = nNewCols;
1541 QueueReformat();
1545 void ValueSet::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
1547 size_t nPos = GetItemPos( nItemId );
1549 if ( nPos == VALUESET_ITEM_NOTFOUND )
1550 return;
1552 ValueSetItem* pItem = mItemList[nPos].get();
1553 pItem->meType = VALUESETITEM_IMAGE;
1554 pItem->maImage = rImage;
1556 if ( !mbFormat && IsReallyVisible() && IsUpdateMode() )
1558 const tools::Rectangle aRect = ImplGetItemRect(nPos);
1559 Invalidate(aRect);
1561 else
1562 mbFormat = true;
1565 void ValueSet::SetItemColor( sal_uInt16 nItemId, const Color& rColor )
1567 size_t nPos = GetItemPos( nItemId );
1569 if ( nPos == VALUESET_ITEM_NOTFOUND )
1570 return;
1572 ValueSetItem* pItem = mItemList[nPos].get();
1573 pItem->meType = VALUESETITEM_COLOR;
1574 pItem->maColor = rColor;
1576 if ( !mbFormat && IsReallyVisible() && IsUpdateMode() )
1578 const tools::Rectangle aRect = ImplGetItemRect(nPos);
1579 Invalidate( aRect );
1581 else
1582 mbFormat = true;
1585 Color ValueSet::GetItemColor( sal_uInt16 nItemId ) const
1587 size_t nPos = GetItemPos( nItemId );
1589 if ( nPos != VALUESET_ITEM_NOTFOUND )
1590 return mItemList[nPos]->maColor;
1591 else
1592 return Color();
1595 Size ValueSet::CalcWindowSizePixel( const Size& rItemSize, sal_uInt16 nDesireCols,
1596 sal_uInt16 nDesireLines ) const
1598 size_t nCalcCols = nDesireCols;
1599 size_t nCalcLines = nDesireLines;
1601 if ( !nCalcCols )
1603 if ( mnUserCols )
1604 nCalcCols = mnUserCols;
1605 else
1606 nCalcCols = 1;
1609 if ( !nCalcLines )
1611 nCalcLines = mnVisLines;
1613 if ( mbFormat )
1615 if ( mnUserVisLines )
1616 nCalcLines = mnUserVisLines;
1617 else
1619 // Floor( (M+N-1)/N )==Ceiling( M/N )
1620 nCalcLines = (mItemList.size()+nCalcCols-1) / nCalcCols;
1621 if ( !nCalcLines )
1622 nCalcLines = 1;
1627 Size aSize( rItemSize.Width() * nCalcCols, rItemSize.Height() * nCalcLines );
1628 WinBits nStyle = GetStyle();
1629 tools::Long nTxtHeight = GetTextHeight();
1630 tools::Long n;
1632 if ( nStyle & WB_ITEMBORDER )
1634 if ( nStyle & WB_DOUBLEBORDER )
1635 n = ITEM_OFFSET_DOUBLE;
1636 else
1637 n = ITEM_OFFSET;
1639 aSize.AdjustWidth(n * nCalcCols );
1640 aSize.AdjustHeight(n * nCalcLines );
1642 else
1643 n = 0;
1645 if ( mnSpacing )
1647 aSize.AdjustWidth(mnSpacing * (nCalcCols - 1) );
1648 aSize.AdjustHeight(mnSpacing * (nCalcLines - 1) );
1651 if ( nStyle & WB_NAMEFIELD )
1653 aSize.AdjustHeight(nTxtHeight + NAME_OFFSET );
1654 if ( !(nStyle & WB_FLATVALUESET) )
1655 aSize.AdjustHeight(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y );
1658 if ( nStyle & WB_NONEFIELD )
1660 aSize.AdjustHeight(nTxtHeight + n + mnSpacing );
1663 return aSize;
1666 void ValueSet::InsertItem( sal_uInt16 nItemId, const Image& rImage )
1668 std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this ));
1669 pItem->mnId = nItemId;
1670 pItem->meType = VALUESETITEM_IMAGE;
1671 pItem->maImage = rImage;
1672 ImplInsertItem( std::move(pItem), VALUESET_APPEND );
1675 void ValueSet::InsertItem( sal_uInt16 nItemId, const Image& rImage,
1676 const OUString& rText, size_t nPos,
1677 bool bShowLegend )
1679 std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this ));
1680 pItem->mnId = nItemId;
1681 pItem->meType = bShowLegend ? VALUESETITEM_IMAGE_AND_TEXT : VALUESETITEM_IMAGE;
1682 pItem->maImage = rImage;
1683 pItem->maText = rText;
1684 ImplInsertItem( std::move(pItem), nPos );
1687 void ValueSet::InsertItem( sal_uInt16 nItemId, size_t nPos )
1689 std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this ));
1690 pItem->mnId = nItemId;
1691 pItem->meType = VALUESETITEM_USERDRAW;
1692 ImplInsertItem( std::move(pItem), nPos );
1695 void ValueSet::InsertItem( sal_uInt16 nItemId, const Color& rColor,
1696 const OUString& rText )
1698 std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this ));
1699 pItem->mnId = nItemId;
1700 pItem->meType = VALUESETITEM_COLOR;
1701 pItem->maColor = rColor;
1702 pItem->maText = rText;
1703 ImplInsertItem( std::move(pItem), VALUESET_APPEND );
1706 void ValueSet::ImplInsertItem( std::unique_ptr<ValueSetItem> pItem, const size_t nPos )
1708 DBG_ASSERT( pItem->mnId, "ValueSet::InsertItem(): ItemId == 0" );
1709 DBG_ASSERT( GetItemPos( pItem->mnId ) == VALUESET_ITEM_NOTFOUND,
1710 "ValueSet::InsertItem(): ItemId already exists" );
1712 if ( nPos < mItemList.size() ) {
1713 mItemList.insert( mItemList.begin() + nPos, std::move(pItem) );
1714 } else {
1715 mItemList.push_back( std::move(pItem) );
1718 QueueReformat();
1721 int ValueSet::GetScrollWidth() const
1723 if (mxScrolledWindow)
1724 return mxScrolledWindow->get_scroll_thickness();
1725 return 0;
1728 void ValueSet::SetEdgeBlending(bool bNew)
1730 if(mbEdgeBlending != bNew)
1732 mbEdgeBlending = bNew;
1733 mbFormat = true;
1735 if (GetDrawingArea() && IsReallyVisible() && IsUpdateMode())
1737 Invalidate();
1742 Size ValueSet::CalcItemSizePixel( const Size& rItemSize) const
1744 Size aSize = rItemSize;
1746 WinBits nStyle = GetStyle();
1747 if ( nStyle & WB_ITEMBORDER )
1749 tools::Long n;
1751 if ( nStyle & WB_DOUBLEBORDER )
1752 n = ITEM_OFFSET_DOUBLE;
1753 else
1754 n = ITEM_OFFSET;
1756 aSize.AdjustWidth(n );
1757 aSize.AdjustHeight(n );
1760 return aSize;
1763 void ValueSet::SetLineCount( sal_uInt16 nNewLines )
1765 if ( mnUserVisLines != nNewLines )
1767 mnUserVisLines = nNewLines;
1768 QueueReformat();
1772 void ValueSet::SetItemWidth( tools::Long nNewItemWidth )
1774 if ( mnUserItemWidth != nNewItemWidth )
1776 mnUserItemWidth = nNewItemWidth;
1777 QueueReformat();
1781 //method to set accessible when the style is user draw.
1782 void ValueSet::InsertItem( sal_uInt16 nItemId, const OUString& rText, size_t nPos )
1784 DBG_ASSERT( nItemId, "ValueSet::InsertItem(): ItemId == 0" );
1785 DBG_ASSERT( GetItemPos( nItemId ) == VALUESET_ITEM_NOTFOUND,
1786 "ValueSet::InsertItem(): ItemId already exists" );
1787 std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this ));
1788 pItem->mnId = nItemId;
1789 pItem->meType = VALUESETITEM_USERDRAW;
1790 pItem->maText = rText;
1791 ImplInsertItem( std::move(pItem), nPos );
1794 void ValueSet::SetItemHeight( tools::Long nNewItemHeight )
1796 if ( mnUserItemHeight != nNewItemHeight )
1798 mnUserItemHeight = nNewItemHeight;
1799 QueueReformat();
1803 OUString ValueSet::RequestHelp(tools::Rectangle& rHelpRect)
1805 Point aPos = rHelpRect.TopLeft();
1806 const size_t nItemPos = ImplGetItem( aPos );
1807 OUString sRet;
1808 if (nItemPos != VALUESET_ITEM_NOTFOUND)
1810 rHelpRect = ImplGetItemRect(nItemPos);
1811 sRet = GetItemText(ImplGetItem(nItemPos)->mnId);
1813 return sRet;
1816 OUString ValueSet::GetItemText(sal_uInt16 nItemId) const
1818 const size_t nPos = GetItemPos(nItemId);
1820 if ( nPos != VALUESET_ITEM_NOTFOUND )
1821 return mItemList[nPos]->maText;
1823 return OUString();
1826 void ValueSet::SetExtraSpacing( sal_uInt16 nNewSpacing )
1828 if ( GetStyle() & WB_ITEMBORDER )
1830 mnSpacing = nNewSpacing;
1831 QueueReformat();
1835 void ValueSet::SetFormat()
1837 mbFormat = true;
1840 void ValueSet::SetItemData( sal_uInt16 nItemId, void* pData )
1842 size_t nPos = GetItemPos( nItemId );
1844 if ( nPos == VALUESET_ITEM_NOTFOUND )
1845 return;
1847 ValueSetItem* pItem = mItemList[nPos].get();
1848 pItem->mpData = pData;
1850 if ( pItem->meType == VALUESETITEM_USERDRAW )
1852 if ( !mbFormat && IsReallyVisible() && IsUpdateMode() )
1854 const tools::Rectangle aRect = ImplGetItemRect(nPos);
1855 Invalidate(aRect);
1857 else
1858 mbFormat = true;
1862 void* ValueSet::GetItemData( sal_uInt16 nItemId ) const
1864 size_t nPos = GetItemPos( nItemId );
1866 if ( nPos != VALUESET_ITEM_NOTFOUND )
1867 return mItemList[nPos]->mpData;
1868 else
1869 return nullptr;
1872 void ValueSet::SetItemText(sal_uInt16 nItemId, const OUString& rText)
1874 size_t nPos = GetItemPos( nItemId );
1876 if ( nPos == VALUESET_ITEM_NOTFOUND )
1877 return;
1879 ValueSetItem* pItem = mItemList[nPos].get();
1881 // Remember old and new name for accessibility event.
1882 Any aOldName;
1883 Any aNewName;
1884 OUString sString (pItem->maText);
1885 aOldName <<= sString;
1886 sString = rText;
1887 aNewName <<= sString;
1889 pItem->maText = rText;
1891 if (!mbFormat && IsReallyVisible() && IsUpdateMode())
1893 sal_uInt16 nTempId = mnSelItemId;
1895 if (mbHighlight)
1896 nTempId = mnHighItemId;
1898 if (nTempId == nItemId)
1899 Invalidate();
1902 if (ImplHasAccessibleListeners())
1904 Reference<XAccessible> xAccessible(pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/));
1905 ValueItemAcc* pValueItemAcc = static_cast<ValueItemAcc*>(xAccessible.get());
1906 pValueItemAcc->FireAccessibleEvent(AccessibleEventId::NAME_CHANGED, aOldName, aNewName);
1910 Size ValueSet::GetLargestItemSize()
1912 Size aLargestItem;
1914 for (const std::unique_ptr<ValueSetItem>& pItem : mItemList)
1916 if (!pItem->mbVisible)
1917 continue;
1919 if (pItem->meType != VALUESETITEM_IMAGE &&
1920 pItem->meType != VALUESETITEM_IMAGE_AND_TEXT)
1922 // handle determining an optimal size for this case
1923 continue;
1926 Size aSize = pItem->maImage.GetSizePixel();
1927 if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT)
1929 aSize.AdjustHeight(3 * NAME_LINE_HEIGHT +
1930 maVirDev->GetTextHeight() );
1931 aSize.setWidth( std::max(aSize.Width(),
1932 maVirDev->GetTextWidth(pItem->maText) + NAME_OFFSET) );
1935 aLargestItem.setWidth( std::max(aLargestItem.Width(), aSize.Width()) );
1936 aLargestItem.setHeight( std::max(aLargestItem.Height(), aSize.Height()) );
1939 return aLargestItem;
1942 void ValueSet::SetOptimalSize()
1944 Size aLargestSize(GetLargestItemSize());
1945 aLargestSize.setWidth(std::max(aLargestSize.Width(), mnUserItemWidth));
1946 aLargestSize.setHeight(std::max(aLargestSize.Height(), mnUserItemHeight));
1947 Size aPrefSize(CalcWindowSizePixel(aLargestSize));
1948 GetDrawingArea()->set_size_request(aPrefSize.Width(), aPrefSize.Height());
1951 Image ValueSet::GetItemImage(sal_uInt16 nItemId) const
1953 size_t nPos = GetItemPos( nItemId );
1955 if ( nPos != VALUESET_ITEM_NOTFOUND )
1956 return mItemList[nPos]->maImage;
1957 else
1958 return Image();
1961 void ValueSet::SetColor(const Color& rColor)
1963 maColor = rColor;
1964 mbFormat = true;
1965 if (IsReallyVisible() && IsUpdateMode())
1966 Invalidate();
1969 void ValueSet::Show()
1971 if (mxScrolledWindow)
1972 mxScrolledWindow->show();
1973 CustomWidgetController::Show();
1976 void ValueSet::Hide()
1978 CustomWidgetController::Hide();
1979 if (mxScrolledWindow)
1980 mxScrolledWindow->hide();
1983 FactoryFunction ValueSet::GetUITestFactory() const
1985 return ValueSetUIObject::create;
1988 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */