xref: /core/svtools/source/control/valueset.cxx (revision d6c32cff)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <o3tl/safeint.hxx>
23 #include <tools/debug.hxx>
24 #include <tools/stream.hxx>
25 #include <comphelper/base64.hxx>
26 #include <vcl/decoview.hxx>
27 #include <vcl/event.hxx>
28 #include <vcl/graph.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/scrbar.hxx>
31 #include <vcl/cvtgrf.hxx>
32 #include <vcl/help.hxx>
33 #include <vcl/settings.hxx>
34 #include <vcl/commandevent.hxx>
35 #include <vcl/virdev.hxx>
36 
37 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
38 #include <com/sun/star/lang/XComponent.hpp>
39 #include <rtl/ustring.hxx>
40 #include <sal/log.hxx>
41 #include "valueimp.hxx"
42 
43 #include <svtools/valueset.hxx>
44 #include <boost/property_tree/ptree.hpp>
45 
46 using namespace css::uno;
47 using namespace css::lang;
48 using namespace css::accessibility;
49 
50 namespace
51 {
52 
53 enum
54 {
55     ITEM_OFFSET = 4,
56     ITEM_OFFSET_DOUBLE = 6,
57     NAME_LINE_OFF_X = 2,
58     NAME_LINE_OFF_Y = 2,
59     NAME_LINE_HEIGHT = 2,
60     NAME_OFFSET = 2,
61     SCRBAR_OFFSET = 1
62 };
63 
64 }
65 
66 ValueSet::ValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow)
67     : maVirDev( VclPtr<VirtualDevice>::Create())
68     , mxScrolledWindow(std::move(pScrolledWindow))
69     , mnHighItemId(0)
70     , maColor(COL_TRANSPARENT)
71     , mnStyle(0)
72     , mbFormat(true)
73     , mbHighlight(false)
74 {
75     maVirDev->SetBackground(Application::GetSettings().GetStyleSettings().GetFaceColor());
76 
77     mnItemWidth         = 0;
78     mnItemHeight        = 0;
79     mnTextOffset        = 0;
80     mnVisLines          = 0;
81     mnLines             = 0;
82     mnUserItemWidth     = 0;
83     mnUserItemHeight    = 0;
84     mnFirstLine         = 0;
85     mnSelItemId         = 0;
86     mnSavedItemId       = -1;
87     mnCols              = 0;
88     mnCurCol            = 0;
89     mnUserCols          = 0;
90     mnUserVisLines      = 0;
91     mnSpacing           = 0;
92     mnFrameStyle        = DrawFrameStyle::NONE;
93     mbNoSelection       = true;
94     mbDrawSelection     = true;
95     mbBlackSel          = false;
96     mbDoubleSel         = false;
97     mbScroll            = false;
98     mbFullMode          = true;
99     mbEdgeBlending      = false;
100     mbHasVisibleItems   = false;
101 
102     if (mxScrolledWindow)
103     {
104         mxScrolledWindow->set_user_managed_scrolling();
105         mxScrolledWindow->connect_vadjustment_changed(LINK(this, ValueSet, ImplScrollHdl));
106     }
107 }
108 
109 void ValueSet::SetDrawingArea(weld::DrawingArea* pDrawingArea)
110 {
111     CustomWidgetController::SetDrawingArea(pDrawingArea);
112     // #106446#, #106601# force mirroring of virtual device
113     maVirDev->EnableRTL(pDrawingArea->get_direction());
114 }
115 
116 Reference<XAccessible> ValueSet::CreateAccessible()
117 {
118     if (!mxAccessible)
119         mxAccessible.set(new ValueSetAcc(this));
120     return mxAccessible;
121 }
122 
123 ValueSet::~ValueSet()
124 {
125     Reference<XComponent> xComponent(mxAccessible, UNO_QUERY);
126     if (xComponent.is())
127         xComponent->dispose();
128 
129     ImplDeleteItems();
130 }
131 
132 void ValueSet::ImplDeleteItems()
133 {
134     const size_t n = mItemList.size();
135 
136     for ( size_t i = 0; i < n; ++i )
137     {
138         ValueSetItem* pItem = mItemList[i].get();
139         if ( pItem->mbVisible && ImplHasAccessibleListeners() )
140         {
141             Any aOldAny;
142             Any aNewAny;
143 
144             aOldAny <<= pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ );
145             ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
146         }
147 
148         mItemList[i].reset();
149     }
150 
151     mItemList.clear();
152 }
153 
154 void ValueSet::Select()
155 {
156     maSelectHdl.Call( this );
157 }
158 
159 void ValueSet::UserDraw( const UserDrawEvent& )
160 {
161 }
162 
163 size_t ValueSet::ImplGetItem( const Point& rPos ) const
164 {
165     if (!mbHasVisibleItems)
166     {
167         return VALUESET_ITEM_NOTFOUND;
168     }
169 
170     if (mpNoneItem && maNoneItemRect.IsInside(rPos))
171     {
172         return VALUESET_ITEM_NONEITEM;
173     }
174 
175     if (maItemListRect.IsInside(rPos))
176     {
177         const int xc = rPos.X() - maItemListRect.Left();
178         const int yc = rPos.Y() - maItemListRect.Top();
179         // The point is inside the area of item list,
180         // let's find the containing item.
181         const int col = xc / (mnItemWidth + mnSpacing);
182         const int x = xc % (mnItemWidth + mnSpacing);
183         const int row = yc / (mnItemHeight + mnSpacing);
184         const int y = yc % (mnItemHeight + mnSpacing);
185 
186         if (x < mnItemWidth && y < mnItemHeight)
187         {
188             // the point is inside item rect and not inside spacing
189             const size_t item = (mnFirstLine + row) * static_cast<size_t>(mnCols) + col;
190             if (item < mItemList.size())
191             {
192                 return item;
193             }
194         }
195     }
196 
197     return VALUESET_ITEM_NOTFOUND;
198 }
199 
200 ValueSetItem* ValueSet::ImplGetItem( size_t nPos )
201 {
202     if (nPos == VALUESET_ITEM_NONEITEM)
203         return mpNoneItem.get();
204     else
205         return (nPos < mItemList.size()) ? mItemList[nPos].get() : nullptr;
206 }
207 
208 ValueSetItem* ValueSet::ImplGetFirstItem()
209 {
210     return !mItemList.empty() ? mItemList[0].get() : nullptr;
211 }
212 
213 sal_uInt16 ValueSet::ImplGetVisibleItemCount() const
214 {
215     sal_uInt16 nRet = 0;
216     const size_t nItemCount = mItemList.size();
217 
218     for ( size_t n = 0; n < nItemCount; ++n )
219     {
220         if ( mItemList[n]->mbVisible )
221             ++nRet;
222     }
223 
224     return nRet;
225 }
226 
227 void ValueSet::ImplFireAccessibleEvent( short nEventId, const Any& rOldValue, const Any& rNewValue )
228 {
229     ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible);
230 
231     if( pAcc )
232         pAcc->FireAccessibleEvent( nEventId, rOldValue, rNewValue );
233 }
234 
235 bool ValueSet::ImplHasAccessibleListeners()
236 {
237     ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible);
238     return( pAcc && pAcc->HasAccessibleListeners() );
239 }
240 
241 IMPL_LINK(ValueSet, ImplScrollHdl, weld::ScrolledWindow&, rScrollWin, void)
242 {
243     auto nNewFirstLine = rScrollWin.vadjustment_get_value();
244     if ( nNewFirstLine != mnFirstLine )
245     {
246         mnFirstLine = nNewFirstLine;
247         mbFormat = true;
248         Invalidate();
249     }
250 }
251 
252 void ValueSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
253 {
254     if (GetStyle() & WB_FLATVALUESET)
255     {
256         const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
257         rRenderContext.SetLineColor();
258         rRenderContext.SetFillColor(rStyleSettings.GetFaceColor());
259         long nOffY = maVirDev->GetOutputSizePixel().Height();
260         Size aWinSize(GetOutputSizePixel());
261         rRenderContext.DrawRect(tools::Rectangle(Point(0, nOffY ), Point( aWinSize.Width(), aWinSize.Height())));
262     }
263 
264     ImplDraw(rRenderContext);
265 }
266 
267 void ValueSet::GetFocus()
268 {
269     SAL_INFO("svtools", "value set getting focus");
270     Invalidate();
271     CustomWidgetController::GetFocus();
272 
273     // Tell the accessible object that we got the focus.
274     ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible);
275     if (pAcc)
276         pAcc->GetFocus();
277 }
278 
279 void ValueSet::LoseFocus()
280 {
281     SAL_INFO("svtools", "value set losing focus");
282     Invalidate();
283     CustomWidgetController::LoseFocus();
284 
285     // Tell the accessible object that we lost the focus.
286     ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible);
287     if( pAcc )
288         pAcc->LoseFocus();
289 }
290 
291 void ValueSet::Resize()
292 {
293     mbFormat = true;
294     if ( IsReallyVisible() && IsUpdateMode() )
295         Invalidate();
296     CustomWidgetController::Resize();
297 }
298 
299 bool ValueSet::KeyInput( const KeyEvent& rKeyEvent )
300 {
301     size_t nLastItem = mItemList.size();
302 
303     if ( !nLastItem || !ImplGetFirstItem() )
304         return CustomWidgetController::KeyInput(rKeyEvent);
305 
306     if (mbFormat)
307         Invalidate();
308 
309     --nLastItem;
310 
311     const size_t nCurPos
312         = mnSelItemId ? GetItemPos(mnSelItemId) : (mpNoneItem ? VALUESET_ITEM_NONEITEM : 0);
313     size_t nItemPos = VALUESET_ITEM_NOTFOUND;
314     size_t nVStep = mnCols;
315 
316     switch (rKeyEvent.GetKeyCode().GetCode())
317     {
318         case KEY_HOME:
319             nItemPos = mpNoneItem ? VALUESET_ITEM_NONEITEM : 0;
320             break;
321 
322         case KEY_END:
323             nItemPos = nLastItem;
324             break;
325 
326         case KEY_LEFT:
327             if (nCurPos != VALUESET_ITEM_NONEITEM)
328             {
329                 if (nCurPos)
330                 {
331                     nItemPos = nCurPos-1;
332                 }
333                 else if (mpNoneItem)
334                 {
335                     nItemPos = VALUESET_ITEM_NONEITEM;
336                 }
337             }
338             break;
339 
340         case KEY_RIGHT:
341             if (nCurPos < nLastItem)
342             {
343                 if (nCurPos == VALUESET_ITEM_NONEITEM)
344                 {
345                     nItemPos = 0;
346                 }
347                 else
348                 {
349                     nItemPos = nCurPos+1;
350                 }
351             }
352             break;
353 
354         case KEY_PAGEUP:
355             if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2())
356             {
357                 return CustomWidgetController::KeyInput(rKeyEvent);
358             }
359             nVStep *= mnVisLines;
360             [[fallthrough]];
361         case KEY_UP:
362             if (nCurPos != VALUESET_ITEM_NONEITEM)
363             {
364                 if (nCurPos == nLastItem)
365                 {
366                     const size_t nCol = mnCols ? nLastItem % mnCols : 0;
367                     if (nCol < mnCurCol)
368                     {
369                         // Move to previous row/page, keeping the old column
370                         nVStep -= mnCurCol - nCol;
371                     }
372                 }
373                 if (nCurPos >= nVStep)
374                 {
375                     // Go up of a whole page
376                     nItemPos = nCurPos-nVStep;
377                 }
378                 else if (mpNoneItem)
379                 {
380                     nItemPos = VALUESET_ITEM_NONEITEM;
381                 }
382                 else if (nCurPos > mnCols)
383                 {
384                     // Go to same column in first row
385                     nItemPos = nCurPos % mnCols;
386                 }
387             }
388             break;
389 
390         case KEY_PAGEDOWN:
391             if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2())
392             {
393                 return CustomWidgetController::KeyInput(rKeyEvent);
394             }
395             nVStep *= mnVisLines;
396             [[fallthrough]];
397         case KEY_DOWN:
398             if (nCurPos != nLastItem)
399             {
400                 if (nCurPos == VALUESET_ITEM_NONEITEM)
401                 {
402                     nItemPos = nVStep-mnCols+mnCurCol;
403                 }
404                 else
405                 {
406                     nItemPos = nCurPos+nVStep;
407                 }
408                 if (nItemPos > nLastItem)
409                 {
410                     nItemPos = nLastItem;
411                 }
412             }
413             break;
414 
415         case KEY_RETURN:
416             if (GetStyle() & WB_NO_DIRECTSELECT)
417             {
418                 Select();
419                 break;
420             }
421             [[fallthrough]];
422         default:
423             return CustomWidgetController::KeyInput(rKeyEvent);
424     }
425 
426     if ( nItemPos == VALUESET_ITEM_NOTFOUND )
427         return true;
428 
429     if ( nItemPos!=VALUESET_ITEM_NONEITEM && nItemPos<nLastItem )
430     {
431         // update current column only in case of a new position
432         // which is also not a "specially" handled one.
433         mnCurCol = mnCols ? nItemPos % mnCols : 0;
434     }
435     const sal_uInt16 nItemId = (nItemPos != VALUESET_ITEM_NONEITEM) ? GetItemId( nItemPos ) : 0;
436     if ( nItemId != mnSelItemId )
437     {
438         SelectItem( nItemId );
439         if (!(GetStyle() & WB_NO_DIRECTSELECT))
440         {
441             // select only if WB_NO_DIRECTSELECT is not set
442             Select();
443         }
444     }
445 
446     return true;
447 }
448 
449 void ValueSet::ImplTracking(const Point& rPos)
450 {
451     ValueSetItem* pItem = ImplGetItem( ImplGetItem( rPos ) );
452     if ( pItem )
453     {
454         if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET )
455             mbHighlight = true;
456 
457         ImplHighlightItem( pItem->mnId );
458     }
459     else
460     {
461         if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET )
462             mbHighlight = true;
463 
464         ImplHighlightItem( mnSelItemId, false );
465     }
466 }
467 
468 bool ValueSet::MouseButtonDown( const MouseEvent& rMouseEvent )
469 {
470     if ( rMouseEvent.IsLeft() )
471     {
472         ValueSetItem* pItem = ImplGetItem( ImplGetItem( rMouseEvent.GetPosPixel() ) );
473         if (pItem && !rMouseEvent.IsMod2())
474         {
475             if (rMouseEvent.GetClicks() == 1)
476             {
477                 SelectItem( pItem->mnId );
478                 if (!(GetStyle() & WB_NOPOINTERFOCUS))
479                     GrabFocus();
480             }
481             else if ( rMouseEvent.GetClicks() == 2 )
482                 maDoubleClickHdl.Call( this );
483 
484             return true;
485         }
486     }
487 
488     return CustomWidgetController::MouseButtonDown( rMouseEvent );
489 }
490 
491 bool ValueSet::MouseButtonUp( const MouseEvent& rMouseEvent )
492 {
493     if (rMouseEvent.IsLeft() && !rMouseEvent.IsMod2())
494     {
495         Select();
496         return true;
497     }
498 
499     return CustomWidgetController::MouseButtonUp( rMouseEvent );
500 }
501 
502 bool ValueSet::MouseMove(const MouseEvent& rMouseEvent)
503 {
504     // because of SelectionMode
505     if ((GetStyle() & WB_MENUSTYLEVALUESET) || (GetStyle() & WB_FLATVALUESET))
506         ImplTracking(rMouseEvent.GetPosPixel());
507     return CustomWidgetController::MouseMove(rMouseEvent);
508 }
509 
510 void ValueSet::RemoveItem( sal_uInt16 nItemId )
511 {
512     size_t nPos = GetItemPos( nItemId );
513 
514     if ( nPos == VALUESET_ITEM_NOTFOUND )
515         return;
516 
517     if ( nPos < mItemList.size() ) {
518         mItemList.erase( mItemList.begin() + nPos );
519     }
520 
521     // reset variables
522     if (mnHighItemId == nItemId || mnSelItemId == nItemId)
523     {
524         mnCurCol        = 0;
525         mnHighItemId    = 0;
526         mnSelItemId     = 0;
527         mbNoSelection   = true;
528     }
529 
530     queue_resize();
531 
532     mbFormat = true;
533     if ( IsReallyVisible() && IsUpdateMode() )
534         Invalidate();
535 }
536 
537 void ValueSet::RecalcScrollBar()
538 {
539     // reset scrolled window state to initial value
540     // so it will get configured to the right adjustment
541     WinBits nStyle = GetStyle();
542     if (mxScrolledWindow && (nStyle & WB_VSCROLL))
543         mxScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
544 }
545 
546 void ValueSet::Clear()
547 {
548     ImplDeleteItems();
549 
550     // reset variables
551     mnFirstLine     = 0;
552     mnCurCol        = 0;
553     mnHighItemId    = 0;
554     mnSelItemId     = 0;
555     mbNoSelection   = true;
556 
557     RecalcScrollBar();
558 
559     mbFormat = true;
560     if ( IsReallyVisible() && IsUpdateMode() )
561         Invalidate();
562 }
563 
564 size_t ValueSet::GetItemCount() const
565 {
566     return mItemList.size();
567 }
568 
569 size_t ValueSet::GetItemPos( sal_uInt16 nItemId ) const
570 {
571     for ( size_t i = 0, n = mItemList.size(); i < n; ++i ) {
572         if ( mItemList[i]->mnId == nItemId ) {
573             return i;
574         }
575     }
576     return VALUESET_ITEM_NOTFOUND;
577 }
578 
579 sal_uInt16 ValueSet::GetItemId( size_t nPos ) const
580 {
581     return ( nPos < mItemList.size() ) ? mItemList[nPos]->mnId : 0 ;
582 }
583 
584 sal_uInt16 ValueSet::GetItemId( const Point& rPos ) const
585 {
586     size_t nItemPos = ImplGetItem( rPos );
587     if ( nItemPos != VALUESET_ITEM_NOTFOUND )
588         return GetItemId( nItemPos );
589 
590     return 0;
591 }
592 
593 tools::Rectangle ValueSet::GetItemRect( sal_uInt16 nItemId ) const
594 {
595     const size_t nPos = GetItemPos( nItemId );
596 
597     if ( nPos!=VALUESET_ITEM_NOTFOUND && mItemList[nPos]->mbVisible )
598         return ImplGetItemRect( nPos );
599 
600     return tools::Rectangle();
601 }
602 
603 tools::Rectangle ValueSet::ImplGetItemRect( size_t nPos ) const
604 {
605     const size_t nVisibleBegin = static_cast<size_t>(mnFirstLine)*mnCols;
606     const size_t nVisibleEnd = nVisibleBegin + static_cast<size_t>(mnVisLines)*mnCols;
607 
608     // Check if the item is inside the range of the displayed ones,
609     // taking into account that last row could be incomplete
610     if ( nPos<nVisibleBegin || nPos>=nVisibleEnd || nPos>=mItemList.size() )
611         return tools::Rectangle();
612 
613     nPos -= nVisibleBegin;
614 
615     const size_t row = mnCols ? nPos/mnCols : 0;
616     const size_t col = mnCols ? nPos%mnCols : 0;
617     const long x = maItemListRect.Left()+col*(mnItemWidth+mnSpacing);
618     const long y = maItemListRect.Top()+row*(mnItemHeight+mnSpacing);
619 
620     return tools::Rectangle( Point(x, y), Size(mnItemWidth, mnItemHeight) );
621 }
622 
623 void ValueSet::ImplHighlightItem( sal_uInt16 nItemId, bool bIsSelection )
624 {
625     if ( mnHighItemId == nItemId )
626         return;
627 
628     // remember the old item to delete the previous selection
629     mnHighItemId = nItemId;
630 
631     // don't draw the selection if nothing is selected
632     if ( !bIsSelection && mbNoSelection )
633         mbDrawSelection = false;
634 
635     // remove the old selection and draw the new one
636     Invalidate();
637     mbDrawSelection = true;
638 }
639 
640 void ValueSet::ImplDraw(vcl::RenderContext& rRenderContext)
641 {
642     if (mbFormat)
643         Format(rRenderContext);
644 
645     Point aDefPos;
646     Size aSize = maVirDev->GetOutputSizePixel();
647 
648     rRenderContext.DrawOutDev(aDefPos, aSize, aDefPos, aSize, *maVirDev);
649 
650     // draw parting line to the Namefield
651     if (GetStyle() & WB_NAMEFIELD)
652     {
653         if (!(GetStyle() & WB_FLATVALUESET))
654         {
655             const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
656             Size aWinSize(GetOutputSizePixel());
657             Point aPos1(NAME_LINE_OFF_X, mnTextOffset + NAME_LINE_OFF_Y);
658             Point aPos2(aWinSize.Width() - (NAME_LINE_OFF_X * 2), mnTextOffset + NAME_LINE_OFF_Y);
659             if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
660             {
661                 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
662                 rRenderContext.DrawLine(aPos1, aPos2);
663                 aPos1.AdjustY( 1 );
664                 aPos2.AdjustY( 1 );
665                 rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
666             }
667             else
668                 rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor());
669             rRenderContext.DrawLine(aPos1, aPos2);
670         }
671     }
672 
673     ImplDrawSelect(rRenderContext);
674 }
675 
676 /**
677  * An inelegant method; sets the item width & height such that
678  * all of the included items and their labels fit; if we can
679  * calculate that.
680  */
681 void ValueSet::RecalculateItemSizes()
682 {
683     Size aLargestItem = GetLargestItemSize();
684 
685     if ( mnUserItemWidth != aLargestItem.Width() ||
686          mnUserItemHeight != aLargestItem.Height() )
687     {
688         mnUserItemWidth = aLargestItem.Width();
689         mnUserItemHeight = aLargestItem.Height();
690         mbFormat = true;
691         queue_resize();
692         if ( IsReallyVisible() && IsUpdateMode() )
693             Invalidate();
694     }
695 }
696 
697 void ValueSet::SelectItem( sal_uInt16 nItemId )
698 {
699     size_t nItemPos = 0;
700 
701     if ( nItemId )
702     {
703         nItemPos = GetItemPos( nItemId );
704         if ( nItemPos == VALUESET_ITEM_NOTFOUND )
705             return;
706     }
707 
708     if ( !((mnSelItemId != nItemId) || mbNoSelection) )
709         return;
710 
711     const sal_uInt16 nOldItem = mnSelItemId;
712     mnSelItemId = nItemId;
713     mbNoSelection = false;
714 
715     bool bNewOut = !mbFormat && IsReallyVisible() && IsUpdateMode();
716     bool bNewLine = false;
717 
718     if (weld::DrawingArea* pNeedsFormatToScroll = !mnCols ? GetDrawingArea() : nullptr)
719     {
720         Format(pNeedsFormatToScroll->get_ref_device());
721         // reset scrollbar so its set to the later calculated mnFirstLine on
722         // the next Format
723         RecalcScrollBar(); // reset scrollbar so its set to the later calculated
724     }
725 
726     // if necessary scroll to the visible area
727     if (mbScroll && nItemId && mnCols)
728     {
729         sal_uInt16 nNewLine = static_cast<sal_uInt16>(nItemPos / mnCols);
730         if ( nNewLine < mnFirstLine )
731         {
732             mnFirstLine = nNewLine;
733             bNewLine = true;
734         }
735         else if ( nNewLine > o3tl::make_unsigned(mnFirstLine+mnVisLines-1) )
736         {
737             mnFirstLine = static_cast<sal_uInt16>(nNewLine-mnVisLines+1);
738             bNewLine = true;
739         }
740     }
741 
742     if ( bNewOut )
743     {
744         if ( bNewLine )
745         {
746             // redraw everything if the visible area has changed
747             mbFormat = true;
748         }
749         Invalidate();
750     }
751 
752     if( ImplHasAccessibleListeners() )
753     {
754         // focus event (deselect)
755         if( nOldItem )
756         {
757             const size_t nPos = GetItemPos( nItemId );
758 
759             if( nPos != VALUESET_ITEM_NOTFOUND )
760             {
761                 ValueItemAcc* pItemAcc = ValueItemAcc::getImplementation(
762                     mItemList[nPos]->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) );
763 
764                 if( pItemAcc )
765                 {
766                     Any aOldAny;
767                     Any aNewAny;
768                     aOldAny <<= Reference<XInterface>(static_cast<cppu::OWeakObject*>(pItemAcc));
769                     ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny );
770                 }
771             }
772         }
773 
774         // focus event (select)
775         const size_t nPos = GetItemPos( mnSelItemId );
776 
777         ValueSetItem* pItem;
778         if( nPos != VALUESET_ITEM_NOTFOUND )
779             pItem = mItemList[nPos].get();
780         else
781             pItem = mpNoneItem.get();
782 
783         ValueItemAcc* pItemAcc = nullptr;
784         if (pItem != nullptr)
785             pItemAcc = ValueItemAcc::getImplementation( pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) );
786 
787         if( pItemAcc )
788         {
789             Any aOldAny;
790             Any aNewAny;
791             aNewAny <<= Reference<XInterface>(static_cast<cppu::OWeakObject*>(pItemAcc));
792             ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny);
793         }
794 
795         // selection event
796         Any aOldAny;
797         Any aNewAny;
798         ImplFireAccessibleEvent(AccessibleEventId::SELECTION_CHANGED, aOldAny, aNewAny);
799     }
800 }
801 
802 void ValueSet::SetNoSelection()
803 {
804     mbNoSelection   = true;
805     mbHighlight     = false;
806 
807     if (IsReallyVisible() && IsUpdateMode())
808         Invalidate();
809 }
810 
811 void ValueSet::SetStyle(WinBits nStyle)
812 {
813     if (nStyle != mnStyle)
814     {
815         mnStyle = nStyle;
816         mbFormat = true;
817         Invalidate();
818     }
819 }
820 
821 void ValueSet::Format(vcl::RenderContext const & rRenderContext)
822 {
823     Size aWinSize(GetOutputSizePixel());
824     size_t nItemCount = mItemList.size();
825     WinBits nStyle = GetStyle();
826     long nTxtHeight = rRenderContext.GetTextHeight();
827     long nOff;
828     long nNoneHeight;
829     long nNoneSpace;
830 
831     if (mxScrolledWindow && !(nStyle & WB_VSCROLL) && mxScrolledWindow->get_vpolicy() != VclPolicyType::NEVER)
832     {
833         mxScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
834         Size aPrefSize(GetDrawingArea()->get_preferred_size());
835         GetDrawingArea()->set_size_request(aPrefSize.Width() + GetScrollWidth(), aPrefSize.Height());
836     }
837 
838     // calculate item offset
839     if (nStyle & WB_ITEMBORDER)
840     {
841         if (nStyle & WB_DOUBLEBORDER)
842             nOff = ITEM_OFFSET_DOUBLE;
843         else
844             nOff = ITEM_OFFSET;
845     }
846     else
847         nOff = 0;
848 
849     // consider size, if NameField does exist
850     if (nStyle & WB_NAMEFIELD)
851     {
852         mnTextOffset = aWinSize.Height() - nTxtHeight - NAME_OFFSET;
853         aWinSize.AdjustHeight( -(nTxtHeight + NAME_OFFSET) );
854 
855         if (!(nStyle & WB_FLATVALUESET))
856         {
857             mnTextOffset -= NAME_LINE_HEIGHT + NAME_LINE_OFF_Y;
858             aWinSize.AdjustHeight( -(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y) );
859         }
860     }
861     else
862         mnTextOffset = 0;
863 
864     // consider offset and size, if NoneField does exist
865     if (nStyle & WB_NONEFIELD)
866     {
867         nNoneHeight = nTxtHeight + nOff;
868         nNoneSpace = mnSpacing;
869     }
870     else
871     {
872         nNoneHeight = 0;
873         nNoneSpace = 0;
874         mpNoneItem.reset();
875     }
876 
877     // calculate number of columns
878     if (!mnUserCols)
879     {
880         if (mnUserItemWidth)
881         {
882             mnCols = static_cast<sal_uInt16>((aWinSize.Width() - mnSpacing) / (mnUserItemWidth + mnSpacing));
883             if (mnCols <= 0)
884                 mnCols = 1;
885         }
886         else
887         {
888             mnCols = 1;
889         }
890     }
891     else
892     {
893         mnCols = mnUserCols;
894     }
895 
896     // calculate number of rows
897     mbScroll = false;
898 
899     // Floor( (M+N-1)/N )==Ceiling( M/N )
900     mnLines = (static_cast<long>(nItemCount) + mnCols - 1) / mnCols;
901     if (mnLines <= 0)
902         mnLines = 1;
903 
904     long nCalcHeight = aWinSize.Height() - nNoneHeight;
905     if (mnUserVisLines)
906     {
907         mnVisLines = mnUserVisLines;
908     }
909     else if (mnUserItemHeight)
910     {
911         mnVisLines = (nCalcHeight - nNoneSpace + mnSpacing) / (mnUserItemHeight + mnSpacing);
912         if (!mnVisLines)
913             mnVisLines = 1;
914     }
915     else
916     {
917         mnVisLines = mnLines;
918     }
919 
920     if (mnLines > mnVisLines)
921         mbScroll = true;
922 
923     if (mnLines <= mnVisLines)
924     {
925         mnFirstLine = 0;
926     }
927     else
928     {
929         if (mnFirstLine > o3tl::make_unsigned(mnLines - mnVisLines))
930             mnFirstLine = static_cast<sal_uInt16>(mnLines - mnVisLines);
931     }
932 
933     // calculate item size
934     const long nColSpace  = (mnCols - 1) * static_cast<long>(mnSpacing);
935     const long nLineSpace = ((mnVisLines - 1) * mnSpacing) + nNoneSpace;
936     if (mnUserItemWidth && !mnUserCols)
937     {
938         mnItemWidth = mnUserItemWidth;
939         if (mnItemWidth > aWinSize.Width() - nColSpace)
940             mnItemWidth = aWinSize.Width() - nColSpace;
941     }
942     else
943         mnItemWidth = (aWinSize.Width() - nColSpace) / mnCols;
944     if (mnUserItemHeight && !mnUserVisLines)
945     {
946         mnItemHeight = mnUserItemHeight;
947         if (mnItemHeight > nCalcHeight - nNoneSpace)
948             mnItemHeight = nCalcHeight - nNoneSpace;
949     }
950     else
951     {
952         nCalcHeight -= nLineSpace;
953         mnItemHeight = nCalcHeight / mnVisLines;
954     }
955 
956     // Init VirDev
957     maVirDev->SetSettings(rRenderContext.GetSettings());
958     maVirDev->SetOutputSizePixel(aWinSize);
959 
960     // nothing is changed in case of too small items
961     if ((mnItemWidth <= 0) ||
962         (mnItemHeight <= ((nStyle & WB_ITEMBORDER) ? 4 : 2)) ||
963         !nItemCount)
964     {
965         mbHasVisibleItems = false;
966 
967         if ((nStyle & WB_NONEFIELD) && mpNoneItem)
968         {
969             mpNoneItem->mbVisible = false;
970             mpNoneItem->maText = GetText();
971         }
972 
973         for (size_t i = 0; i < nItemCount; i++)
974         {
975             mItemList[i]->mbVisible = false;
976         }
977 
978         if (mxScrolledWindow && mxScrolledWindow->get_vpolicy() != VclPolicyType::NEVER)
979         {
980             mxScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
981             Size aPrefSize(GetDrawingArea()->get_preferred_size());
982             GetDrawingArea()->set_size_request(aPrefSize.Width() + GetScrollWidth(), aPrefSize.Height());
983         }
984     }
985     else
986     {
987         mbHasVisibleItems = true;
988 
989         // determine Frame-Style
990         if (nStyle & WB_DOUBLEBORDER)
991             mnFrameStyle = DrawFrameStyle::DoubleIn;
992         else
993             mnFrameStyle = DrawFrameStyle::In;
994 
995         // determine selected color and width
996         // if necessary change the colors, to make the selection
997         // better detectable
998         const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
999         Color aHighColor(rStyleSettings.GetHighlightColor());
1000         if (((aHighColor.GetRed() > 0x80) || (aHighColor.GetGreen() > 0x80) ||
1001              (aHighColor.GetBlue() > 0x80)) ||
1002             ((aHighColor.GetRed() == 0x80) && (aHighColor.GetGreen() == 0x80) &&
1003              (aHighColor.GetBlue() == 0x80)))
1004         {
1005             mbBlackSel = true;
1006         }
1007         else
1008         {
1009             mbBlackSel = false;
1010         }
1011         // draw the selection with double width if the items are bigger
1012         if ((nStyle & WB_DOUBLEBORDER) &&
1013             ((mnItemWidth >= 25) && (mnItemHeight >= 20)))
1014         {
1015             mbDoubleSel = true;
1016         }
1017         else
1018         {
1019             mbDoubleSel = false;
1020         }
1021 
1022         // calculate offsets
1023         long nStartX;
1024         long nStartY;
1025         if (mbFullMode)
1026         {
1027             long nAllItemWidth = (mnItemWidth * mnCols) + nColSpace;
1028             long nAllItemHeight = (mnItemHeight * mnVisLines) + nNoneHeight + nLineSpace;
1029             nStartX = (aWinSize.Width() - nAllItemWidth) / 2;
1030             nStartY = (aWinSize.Height() - nAllItemHeight) / 2;
1031         }
1032         else
1033         {
1034             nStartX = 0;
1035             nStartY = 0;
1036         }
1037 
1038         // calculate and draw items
1039         maVirDev->SetLineColor();
1040         long x = nStartX;
1041         long y = nStartY;
1042 
1043         // create NoSelection field and show it
1044         if (nStyle & WB_NONEFIELD)
1045         {
1046             if (!mpNoneItem)
1047                 mpNoneItem.reset(new ValueSetItem(*this));
1048 
1049             mpNoneItem->mnId = 0;
1050             mpNoneItem->meType = VALUESETITEM_NONE;
1051             mpNoneItem->mbVisible = true;
1052             maNoneItemRect.SetLeft( x );
1053             maNoneItemRect.SetTop( y );
1054             maNoneItemRect.SetRight( maNoneItemRect.Left() + aWinSize.Width() - x - 1 );
1055             maNoneItemRect.SetBottom( y + nNoneHeight - 1 );
1056 
1057             ImplFormatItem(rRenderContext, mpNoneItem.get(), maNoneItemRect);
1058 
1059             y += nNoneHeight + nNoneSpace;
1060         }
1061 
1062         // draw items
1063         sal_uLong nFirstItem = static_cast<sal_uLong>(mnFirstLine) * mnCols;
1064         sal_uLong nLastItem = nFirstItem + (mnVisLines * mnCols);
1065 
1066         maItemListRect.SetLeft( x );
1067         maItemListRect.SetTop( y );
1068         maItemListRect.SetRight( x + mnCols * (mnItemWidth + mnSpacing) - mnSpacing - 1 );
1069         maItemListRect.SetBottom( y + mnVisLines * (mnItemHeight + mnSpacing) - mnSpacing - 1 );
1070 
1071         if (!mbFullMode)
1072         {
1073             // If want also draw parts of items in the last line,
1074             // then we add one more line if parts of these line are
1075             // visible
1076             if (y + (mnVisLines * (mnItemHeight + mnSpacing)) < aWinSize.Height())
1077                 nLastItem += mnCols;
1078             maItemListRect.SetBottom( aWinSize.Height() - y );
1079         }
1080         for (size_t i = 0; i < nItemCount; i++)
1081         {
1082             ValueSetItem* pItem = mItemList[i].get();
1083 
1084             if (i >= nFirstItem && i < nLastItem)
1085             {
1086                 if (!pItem->mbVisible && ImplHasAccessibleListeners())
1087                 {
1088                     Any aOldAny;
1089                     Any aNewAny;
1090 
1091                     aNewAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/);
1092                     ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
1093                 }
1094 
1095                 pItem->mbVisible = true;
1096                 ImplFormatItem(rRenderContext, pItem, tools::Rectangle(Point(x, y), Size(mnItemWidth, mnItemHeight)));
1097 
1098                 if (!((i + 1) % mnCols))
1099                 {
1100                     x = nStartX;
1101                     y += mnItemHeight + mnSpacing;
1102                 }
1103                 else
1104                     x += mnItemWidth + mnSpacing;
1105             }
1106             else
1107             {
1108                 if (pItem->mbVisible && ImplHasAccessibleListeners())
1109                 {
1110                     Any aOldAny;
1111                     Any aNewAny;
1112 
1113                     aOldAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/);
1114                     ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny);
1115                 }
1116 
1117                 pItem->mbVisible = false;
1118             }
1119         }
1120 
1121         // arrange ScrollBar, set values and show it
1122         if (mxScrolledWindow && (nStyle & WB_VSCROLL) && mxScrolledWindow->get_vpolicy() != VclPolicyType::ALWAYS)
1123         {
1124             long nPageSize = mnVisLines;
1125             if (nPageSize < 1)
1126                 nPageSize = 1;
1127             mxScrolledWindow->vadjustment_configure(mnFirstLine, 0, mnLines, 1,
1128                                                     mnVisLines, nPageSize);
1129             mxScrolledWindow->set_vpolicy(VclPolicyType::ALWAYS);
1130             Size aPrefSize(GetDrawingArea()->get_preferred_size());
1131             GetDrawingArea()->set_size_request(aPrefSize.Width() - GetScrollWidth(), aPrefSize.Height());
1132         }
1133     }
1134 
1135     // waiting for the next since the formatting is finished
1136     mbFormat = false;
1137 }
1138 
1139 void ValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext)
1140 {
1141     if (!IsReallyVisible())
1142         return;
1143 
1144     const bool bFocus = HasFocus();
1145     const bool bDrawSel = !((mbNoSelection && !mbHighlight) || (!mbDrawSelection && mbHighlight));
1146 
1147     if (!bFocus && !bDrawSel)
1148     {
1149         ImplDrawItemText(rRenderContext, OUString());
1150         return;
1151     }
1152 
1153     ImplDrawSelect(rRenderContext, mnSelItemId, bFocus, bDrawSel);
1154     if (mbHighlight)
1155     {
1156         ImplDrawSelect(rRenderContext, mnHighItemId, bFocus, bDrawSel);
1157     }
1158 }
1159 
1160 void ValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext, sal_uInt16 nItemId, const bool bFocus, const bool bDrawSel )
1161 {
1162     ValueSetItem* pItem;
1163     tools::Rectangle aRect;
1164     if (nItemId)
1165     {
1166         const size_t nPos = GetItemPos( nItemId );
1167         pItem = mItemList[ nPos ].get();
1168         aRect = ImplGetItemRect( nPos );
1169     }
1170     else if (mpNoneItem)
1171     {
1172         pItem = mpNoneItem.get();
1173         aRect = maNoneItemRect;
1174     }
1175     else if (bFocus && (pItem = ImplGetFirstItem()))
1176     {
1177         aRect = ImplGetItemRect(0);
1178     }
1179     else
1180     {
1181         return;
1182     }
1183 
1184     if (!pItem->mbVisible)
1185         return;
1186 
1187     // draw selection
1188     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1189     rRenderContext.SetFillColor();
1190 
1191     Color aDoubleColor(rStyleSettings.GetHighlightColor());
1192     Color aSingleColor(rStyleSettings.GetHighlightTextColor());
1193     if (!mbDoubleSel)
1194     {
1195         /*
1196         *  #99777# contrast enhancement for thin mode
1197         */
1198         const Wallpaper& rWall = maVirDev->GetBackground();
1199         if (!rWall.IsBitmap() && ! rWall.IsGradient())
1200         {
1201             const Color& rBack = rWall.GetColor();
1202             if (rBack.IsDark() && ! aDoubleColor.IsBright())
1203             {
1204                 aDoubleColor = COL_WHITE;
1205                 aSingleColor = COL_BLACK;
1206             }
1207             else if (rBack.IsBright() && !aDoubleColor.IsDark())
1208             {
1209                 aDoubleColor = COL_BLACK;
1210                 aSingleColor = COL_WHITE;
1211             }
1212         }
1213     }
1214 
1215     // specify selection output
1216     WinBits nStyle = GetStyle();
1217     if (nStyle & WB_MENUSTYLEVALUESET)
1218     {
1219         if (bFocus)
1220             DrawFocusRect(rRenderContext, aRect);
1221         if (bDrawSel)
1222         {
1223             rRenderContext.SetLineColor(mbBlackSel ? COL_BLACK : aDoubleColor);
1224             rRenderContext.DrawRect(aRect);
1225         }
1226     }
1227     else
1228     {
1229         if (bDrawSel)
1230         {
1231             rRenderContext.SetLineColor(mbBlackSel ? COL_BLACK : aDoubleColor);
1232             rRenderContext.DrawRect(aRect);
1233         }
1234         if (mbDoubleSel)
1235         {
1236             aRect.AdjustLeft( 1 );
1237             aRect.AdjustTop( 1 );
1238             aRect.AdjustRight( -1 );
1239             aRect.AdjustBottom( -1 );
1240             if (bDrawSel)
1241                 rRenderContext.DrawRect(aRect);
1242         }
1243         aRect.AdjustLeft( 1 );
1244         aRect.AdjustTop( 1 );
1245         aRect.AdjustRight( -1 );
1246         aRect.AdjustBottom( -1 );
1247         tools::Rectangle aRect2 = aRect;
1248         aRect.AdjustLeft( 1 );
1249         aRect.AdjustTop( 1 );
1250         aRect.AdjustRight( -1 );
1251         aRect.AdjustBottom( -1 );
1252         if (bDrawSel)
1253             rRenderContext.DrawRect(aRect);
1254         if (mbDoubleSel)
1255         {
1256             aRect.AdjustLeft( 1 );
1257             aRect.AdjustTop( 1 );
1258             aRect.AdjustRight( -1 );
1259             aRect.AdjustBottom( -1 );
1260             if (bDrawSel)
1261                 rRenderContext.DrawRect(aRect);
1262         }
1263 
1264         if (bDrawSel)
1265         {
1266             rRenderContext.SetLineColor(mbBlackSel ? COL_WHITE : aSingleColor);
1267         }
1268         else
1269         {
1270             rRenderContext.SetLineColor(COL_LIGHTGRAY);
1271         }
1272         rRenderContext.DrawRect(aRect2);
1273         if (bFocus)
1274             DrawFocusRect(rRenderContext, aRect2);
1275     }
1276 
1277     ImplDrawItemText(rRenderContext, pItem->maText);
1278 }
1279 
1280 void ValueSet::ImplFormatItem(vcl::RenderContext const & rRenderContext, ValueSetItem* pItem, tools::Rectangle aRect)
1281 {
1282     WinBits nStyle = GetStyle();
1283     if (nStyle & WB_ITEMBORDER)
1284     {
1285         aRect.AdjustLeft(1 );
1286         aRect.AdjustTop(1 );
1287         aRect.AdjustRight( -1 );
1288         aRect.AdjustBottom( -1 );
1289 
1290         if (nStyle & WB_FLATVALUESET)
1291         {
1292             sal_Int32 nBorder = (nStyle & WB_DOUBLEBORDER) ? 2 : 1;
1293 
1294             aRect.AdjustLeft(nBorder );
1295             aRect.AdjustTop(nBorder );
1296             aRect.AdjustRight( -nBorder );
1297             aRect.AdjustBottom( -nBorder );
1298         }
1299         else
1300         {
1301             DecorationView aView(maVirDev.get());
1302             aRect = aView.DrawFrame(aRect, mnFrameStyle);
1303         }
1304     }
1305 
1306     if (pItem == mpNoneItem.get())
1307         pItem->maText = GetText();
1308 
1309     if ((aRect.GetHeight() <= 0) || (aRect.GetWidth() <= 0))
1310         return;
1311 
1312     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1313 
1314     if (pItem == mpNoneItem.get())
1315     {
1316         maVirDev->SetFont(rRenderContext.GetFont());
1317         maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor());
1318         maVirDev->SetTextFillColor();
1319         maVirDev->SetFillColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuColor() : rStyleSettings.GetWindowColor());
1320         maVirDev->DrawRect(aRect);
1321         Point aTxtPos(aRect.Left() + 2, aRect.Top());
1322         long nTxtWidth = rRenderContext.GetTextWidth(pItem->maText);
1323         if ((aTxtPos.X() + nTxtWidth) > aRect.Right())
1324         {
1325             maVirDev->SetClipRegion(vcl::Region(aRect));
1326             maVirDev->DrawText(aTxtPos, pItem->maText);
1327             maVirDev->SetClipRegion();
1328         }
1329         else
1330             maVirDev->DrawText(aTxtPos, pItem->maText);
1331     }
1332     else if (pItem->meType == VALUESETITEM_COLOR)
1333     {
1334         maVirDev->SetFillColor(pItem->maColor);
1335         maVirDev->DrawRect(aRect);
1336     }
1337     else
1338     {
1339         if (IsColor())
1340             maVirDev->SetFillColor(maColor);
1341         else if (nStyle & WB_MENUSTYLEVALUESET)
1342             maVirDev->SetFillColor(rStyleSettings.GetMenuColor());
1343         else if (IsEnabled())
1344             maVirDev->SetFillColor(rStyleSettings.GetWindowColor());
1345         else
1346             maVirDev->SetFillColor(rStyleSettings.GetFaceColor());
1347         maVirDev->DrawRect(aRect);
1348 
1349         if (pItem->meType == VALUESETITEM_USERDRAW)
1350         {
1351             UserDrawEvent aUDEvt(nullptr, maVirDev.get(), aRect, pItem->mnId);
1352             UserDraw(aUDEvt);
1353         }
1354         else
1355         {
1356             Size aImageSize = pItem->maImage.GetSizePixel();
1357             Size  aRectSize = aRect.GetSize();
1358             Point aPos(aRect.Left(), aRect.Top());
1359             aPos.AdjustX((aRectSize.Width() - aImageSize.Width()) / 2 );
1360 
1361             if (pItem->meType != VALUESETITEM_IMAGE_AND_TEXT)
1362                 aPos.AdjustY((aRectSize.Height() - aImageSize.Height()) / 2 );
1363 
1364             DrawImageFlags  nImageStyle  = DrawImageFlags::NONE;
1365             if (!IsEnabled())
1366                 nImageStyle  |= DrawImageFlags::Disable;
1367 
1368             if (aImageSize.Width()  > aRectSize.Width() ||
1369                 aImageSize.Height() > aRectSize.Height())
1370             {
1371                 maVirDev->SetClipRegion(vcl::Region(aRect));
1372                 maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle);
1373                 maVirDev->SetClipRegion();
1374             }
1375             else
1376                 maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle);
1377 
1378             if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT)
1379             {
1380                 maVirDev->SetFont(rRenderContext.GetFont());
1381                 maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor());
1382                 maVirDev->SetTextFillColor();
1383 
1384                 long nTxtWidth = maVirDev->GetTextWidth(pItem->maText);
1385 
1386                 if (nTxtWidth > aRect.GetWidth())
1387                     maVirDev->SetClipRegion(vcl::Region(aRect));
1388 
1389                 maVirDev->DrawText(Point(aRect.Left() +
1390                                          (aRect.GetWidth() - nTxtWidth) / 2,
1391                                          aRect.Bottom() - maVirDev->GetTextHeight()),
1392                                    pItem->maText);
1393 
1394                 if (nTxtWidth > aRect.GetWidth())
1395                     maVirDev->SetClipRegion();
1396             }
1397         }
1398     }
1399 
1400     const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
1401 
1402     if (nEdgeBlendingPercent)
1403     {
1404         const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
1405         const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
1406         const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
1407         const BitmapEx aBlendFrame(createBlendFrame(aRect.GetSize(), nAlpha, rTopLeft, rBottomRight));
1408 
1409         if (!aBlendFrame.IsEmpty())
1410         {
1411             maVirDev->DrawBitmapEx(aRect.TopLeft(), aBlendFrame);
1412         }
1413     }
1414 }
1415 
1416 void ValueSet::ImplDrawItemText(vcl::RenderContext& rRenderContext, const OUString& rText)
1417 {
1418     if (!(GetStyle() & WB_NAMEFIELD))
1419         return;
1420 
1421     Size aWinSize(GetOutputSizePixel());
1422     long nTxtWidth = rRenderContext.GetTextWidth(rText);
1423     long nTxtOffset = mnTextOffset;
1424 
1425     // delete rectangle and show text
1426     if (GetStyle() & WB_FLATVALUESET)
1427     {
1428         const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1429         rRenderContext.SetLineColor();
1430         rRenderContext.SetFillColor(rStyleSettings.GetFaceColor());
1431         rRenderContext.DrawRect(tools::Rectangle(Point(0, nTxtOffset), Point(aWinSize.Width(), aWinSize.Height())));
1432         rRenderContext.SetTextColor(rStyleSettings.GetButtonTextColor());
1433     }
1434     else
1435     {
1436         nTxtOffset += NAME_LINE_HEIGHT+NAME_LINE_OFF_Y;
1437         rRenderContext.SetBackground(Application::GetSettings().GetStyleSettings().GetFaceColor());
1438         rRenderContext.Erase(tools::Rectangle(Point(0, nTxtOffset), Point(aWinSize.Width(), aWinSize.Height())));
1439     }
1440     rRenderContext.DrawText(Point((aWinSize.Width() - nTxtWidth) / 2, nTxtOffset + (NAME_OFFSET / 2)), rText);
1441 }
1442 
1443 void ValueSet::StyleUpdated()
1444 {
1445     mbFormat = true;
1446     CustomWidgetController::StyleUpdated();
1447 }
1448 
1449 void ValueSet::EnableFullItemMode( bool bFullMode )
1450 {
1451     mbFullMode = bFullMode;
1452 }
1453 
1454 void ValueSet::SetColCount( sal_uInt16 nNewCols )
1455 {
1456     if ( mnUserCols != nNewCols )
1457     {
1458         mnUserCols = nNewCols;
1459         mbFormat = true;
1460         queue_resize();
1461         if (IsReallyVisible() && IsUpdateMode())
1462             Invalidate();
1463     }
1464 }
1465 
1466 void ValueSet::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
1467 {
1468     size_t nPos = GetItemPos( nItemId );
1469 
1470     if ( nPos == VALUESET_ITEM_NOTFOUND )
1471         return;
1472 
1473     ValueSetItem* pItem = mItemList[nPos].get();
1474     pItem->meType  = VALUESETITEM_IMAGE;
1475     pItem->maImage = rImage;
1476 
1477     if ( !mbFormat && IsReallyVisible() && IsUpdateMode() )
1478     {
1479         const tools::Rectangle aRect = ImplGetItemRect(nPos);
1480         Invalidate(aRect);
1481     }
1482     else
1483         mbFormat = true;
1484 }
1485 
1486 void ValueSet::SetItemColor( sal_uInt16 nItemId, const Color& rColor )
1487 {
1488     size_t nPos = GetItemPos( nItemId );
1489 
1490     if ( nPos == VALUESET_ITEM_NOTFOUND )
1491         return;
1492 
1493     ValueSetItem* pItem = mItemList[nPos].get();
1494     pItem->meType  = VALUESETITEM_COLOR;
1495     pItem->maColor = rColor;
1496 
1497     if ( !mbFormat && IsReallyVisible() && IsUpdateMode() )
1498     {
1499         const tools::Rectangle aRect = ImplGetItemRect(nPos);
1500         Invalidate( aRect );
1501     }
1502     else
1503         mbFormat = true;
1504 }
1505 
1506 Color ValueSet::GetItemColor( sal_uInt16 nItemId ) const
1507 {
1508     size_t nPos = GetItemPos( nItemId );
1509 
1510     if ( nPos != VALUESET_ITEM_NOTFOUND )
1511         return mItemList[nPos]->maColor;
1512     else
1513         return Color();
1514 }
1515 
1516 Size ValueSet::CalcWindowSizePixel( const Size& rItemSize, sal_uInt16 nDesireCols,
1517                                     sal_uInt16 nDesireLines ) const
1518 {
1519     size_t nCalcCols = nDesireCols;
1520     size_t nCalcLines = nDesireLines;
1521 
1522     if ( !nCalcCols )
1523     {
1524         if ( mnUserCols )
1525             nCalcCols = mnUserCols;
1526         else
1527             nCalcCols = 1;
1528     }
1529 
1530     if ( !nCalcLines )
1531     {
1532         nCalcLines = mnVisLines;
1533 
1534         if ( mbFormat )
1535         {
1536             if ( mnUserVisLines )
1537                 nCalcLines = mnUserVisLines;
1538             else
1539             {
1540                 // Floor( (M+N-1)/N )==Ceiling( M/N )
1541                 nCalcLines = (mItemList.size()+nCalcCols-1) / nCalcCols;
1542                 if ( !nCalcLines )
1543                     nCalcLines = 1;
1544             }
1545         }
1546     }
1547 
1548     Size        aSize( rItemSize.Width() * nCalcCols, rItemSize.Height() * nCalcLines );
1549     WinBits     nStyle = GetStyle();
1550     long        nTxtHeight = GetTextHeight();
1551     long        n;
1552 
1553     if ( nStyle & WB_ITEMBORDER )
1554     {
1555         if ( nStyle & WB_DOUBLEBORDER )
1556             n = ITEM_OFFSET_DOUBLE;
1557         else
1558             n = ITEM_OFFSET;
1559 
1560         aSize.AdjustWidth(n * nCalcCols );
1561         aSize.AdjustHeight(n * nCalcLines );
1562     }
1563     else
1564         n = 0;
1565 
1566     if ( mnSpacing )
1567     {
1568         aSize.AdjustWidth(mnSpacing * (nCalcCols - 1) );
1569         aSize.AdjustHeight(mnSpacing * (nCalcLines - 1) );
1570     }
1571 
1572     if ( nStyle & WB_NAMEFIELD )
1573     {
1574         aSize.AdjustHeight(nTxtHeight + NAME_OFFSET );
1575         if ( !(nStyle & WB_FLATVALUESET) )
1576             aSize.AdjustHeight(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y );
1577     }
1578 
1579     if ( nStyle & WB_NONEFIELD )
1580     {
1581         aSize.AdjustHeight(nTxtHeight + n + mnSpacing );
1582     }
1583 
1584     return aSize;
1585 }
1586 
1587 void ValueSet::InsertItem( sal_uInt16 nItemId, const Image& rImage )
1588 {
1589     std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this ));
1590     pItem->mnId     = nItemId;
1591     pItem->meType   = VALUESETITEM_IMAGE;
1592     pItem->maImage  = rImage;
1593     ImplInsertItem( std::move(pItem), VALUESET_APPEND );
1594 }
1595 
1596 void ValueSet::InsertItem( sal_uInt16 nItemId, const Image& rImage,
1597                            const OUString& rText, size_t nPos,
1598                            bool bShowLegend )
1599 {
1600     std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this ));
1601     pItem->mnId     = nItemId;
1602     pItem->meType   = bShowLegend ? VALUESETITEM_IMAGE_AND_TEXT : VALUESETITEM_IMAGE;
1603     pItem->maImage  = rImage;
1604     pItem->maText   = rText;
1605     ImplInsertItem( std::move(pItem), nPos );
1606 }
1607 
1608 void ValueSet::InsertItem( sal_uInt16 nItemId, size_t nPos )
1609 {
1610     std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this ));
1611     pItem->mnId     = nItemId;
1612     pItem->meType   = VALUESETITEM_USERDRAW;
1613     ImplInsertItem( std::move(pItem), nPos );
1614 }
1615 
1616 void ValueSet::InsertItem( sal_uInt16 nItemId, const Color& rColor,
1617                            const OUString& rText )
1618 {
1619     std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this ));
1620     pItem->mnId     = nItemId;
1621     pItem->meType   = VALUESETITEM_COLOR;
1622     pItem->maColor  = rColor;
1623     pItem->maText   = rText;
1624     ImplInsertItem( std::move(pItem), VALUESET_APPEND );
1625 }
1626 
1627 void ValueSet::ImplInsertItem( std::unique_ptr<ValueSetItem> pItem, const size_t nPos )
1628 {
1629     DBG_ASSERT( pItem->mnId, "ValueSet::InsertItem(): ItemId == 0" );
1630     DBG_ASSERT( GetItemPos( pItem->mnId ) == VALUESET_ITEM_NOTFOUND,
1631                 "ValueSet::InsertItem(): ItemId already exists" );
1632 
1633     if ( nPos < mItemList.size() ) {
1634         mItemList.insert( mItemList.begin() + nPos, std::move(pItem) );
1635     } else {
1636         mItemList.push_back( std::move(pItem) );
1637     }
1638 
1639     queue_resize();
1640 
1641     mbFormat = true;
1642     if ( IsReallyVisible() && IsUpdateMode() )
1643         Invalidate();
1644 }
1645 
1646 int ValueSet::GetScrollWidth() const
1647 {
1648     if (mxScrolledWindow)
1649         return mxScrolledWindow->get_vscroll_width();
1650     return 0;
1651 }
1652 
1653 void ValueSet::SetEdgeBlending(bool bNew)
1654 {
1655     if(mbEdgeBlending != bNew)
1656     {
1657         mbEdgeBlending = bNew;
1658         mbFormat = true;
1659 
1660         if (GetDrawingArea() && IsReallyVisible() && IsUpdateMode())
1661         {
1662             Invalidate();
1663         }
1664     }
1665 }
1666 
1667 Size ValueSet::CalcItemSizePixel( const Size& rItemSize) const
1668 {
1669     Size aSize = rItemSize;
1670 
1671     WinBits nStyle = GetStyle();
1672     if ( nStyle & WB_ITEMBORDER )
1673     {
1674         long n;
1675 
1676         if ( nStyle & WB_DOUBLEBORDER )
1677             n = ITEM_OFFSET_DOUBLE;
1678         else
1679             n = ITEM_OFFSET;
1680 
1681         aSize.AdjustWidth(n );
1682         aSize.AdjustHeight(n );
1683     }
1684 
1685     return aSize;
1686 }
1687 
1688 void ValueSet::SetLineCount( sal_uInt16 nNewLines )
1689 {
1690     if ( mnUserVisLines != nNewLines )
1691     {
1692         mnUserVisLines = nNewLines;
1693         mbFormat = true;
1694         queue_resize();
1695         if ( IsReallyVisible() && IsUpdateMode() )
1696             Invalidate();
1697     }
1698 }
1699 
1700 void ValueSet::SetItemWidth( long nNewItemWidth )
1701 {
1702     if ( mnUserItemWidth != nNewItemWidth )
1703     {
1704         mnUserItemWidth = nNewItemWidth;
1705         mbFormat = true;
1706         queue_resize();
1707         if ( IsReallyVisible() && IsUpdateMode() )
1708             Invalidate();
1709     }
1710 }
1711 
1712 //method to set accessible when the style is user draw.
1713 void ValueSet::InsertItem( sal_uInt16 nItemId, const OUString& rText, size_t nPos  )
1714 {
1715     DBG_ASSERT( nItemId, "ValueSet::InsertItem(): ItemId == 0" );
1716     DBG_ASSERT( GetItemPos( nItemId ) == VALUESET_ITEM_NOTFOUND,
1717                 "ValueSet::InsertItem(): ItemId already exists" );
1718     std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this ));
1719     pItem->mnId     = nItemId;
1720     pItem->meType   = VALUESETITEM_USERDRAW;
1721     pItem->maText   = rText;
1722     ImplInsertItem( std::move(pItem), nPos );
1723 }
1724 
1725 void ValueSet::SetItemHeight( long nNewItemHeight )
1726 {
1727     if ( mnUserItemHeight != nNewItemHeight )
1728     {
1729         mnUserItemHeight = nNewItemHeight;
1730         mbFormat = true;
1731         queue_resize();
1732         if ( IsReallyVisible() && IsUpdateMode() )
1733             Invalidate();
1734     }
1735 }
1736 
1737 OUString ValueSet::RequestHelp(tools::Rectangle& rHelpRect)
1738 {
1739     Point aPos = rHelpRect.TopLeft();
1740     const size_t nItemPos = ImplGetItem( aPos );
1741     OUString sRet;
1742     if (nItemPos != VALUESET_ITEM_NOTFOUND)
1743     {
1744         rHelpRect = ImplGetItemRect(nItemPos);
1745         sRet = GetItemText(ImplGetItem(nItemPos)->mnId);
1746     }
1747     return sRet;
1748 }
1749 
1750 OUString ValueSet::GetItemText(sal_uInt16 nItemId) const
1751 {
1752     const size_t nPos = GetItemPos(nItemId);
1753 
1754     if ( nPos != VALUESET_ITEM_NOTFOUND )
1755         return mItemList[nPos]->maText;
1756 
1757     return OUString();
1758 }
1759 
1760 void ValueSet::SetExtraSpacing( sal_uInt16 nNewSpacing )
1761 {
1762     if ( GetStyle() & WB_ITEMBORDER )
1763     {
1764         mnSpacing = nNewSpacing;
1765 
1766         mbFormat = true;
1767         queue_resize();
1768         if ( IsReallyVisible() && IsUpdateMode() )
1769             Invalidate();
1770     }
1771 }
1772 
1773 void ValueSet::SetFormat()
1774 {
1775     mbFormat = true;
1776 }
1777 
1778 void ValueSet::SetItemData( sal_uInt16 nItemId, void* pData )
1779 {
1780     size_t nPos = GetItemPos( nItemId );
1781 
1782     if ( nPos == VALUESET_ITEM_NOTFOUND )
1783         return;
1784 
1785     ValueSetItem* pItem = mItemList[nPos].get();
1786     pItem->mpData = pData;
1787 
1788     if ( pItem->meType == VALUESETITEM_USERDRAW )
1789     {
1790         if ( !mbFormat && IsReallyVisible() && IsUpdateMode() )
1791         {
1792             const tools::Rectangle aRect = ImplGetItemRect(nPos);
1793             Invalidate(aRect);
1794         }
1795         else
1796             mbFormat = true;
1797     }
1798 }
1799 
1800 void* ValueSet::GetItemData( sal_uInt16 nItemId ) const
1801 {
1802     size_t nPos = GetItemPos( nItemId );
1803 
1804     if ( nPos != VALUESET_ITEM_NOTFOUND )
1805         return mItemList[nPos]->mpData;
1806     else
1807         return nullptr;
1808 }
1809 
1810 void ValueSet::SetItemText(sal_uInt16 nItemId, const OUString& rText)
1811 {
1812     size_t nPos = GetItemPos( nItemId );
1813 
1814     if ( nPos == VALUESET_ITEM_NOTFOUND )
1815         return;
1816 
1817     ValueSetItem* pItem = mItemList[nPos].get();
1818 
1819     // Remember old and new name for accessibility event.
1820     Any aOldName;
1821     Any aNewName;
1822     OUString sString (pItem->maText);
1823     aOldName <<= sString;
1824     sString = rText;
1825     aNewName <<= sString;
1826 
1827     pItem->maText = rText;
1828 
1829     if (!mbFormat && IsReallyVisible() && IsUpdateMode())
1830     {
1831         sal_uInt16 nTempId = mnSelItemId;
1832 
1833         if (mbHighlight)
1834             nTempId = mnHighItemId;
1835 
1836         if (nTempId == nItemId)
1837             Invalidate();
1838     }
1839 
1840     if (ImplHasAccessibleListeners())
1841     {
1842         Reference<XAccessible> xAccessible(pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/));
1843         ValueItemAcc* pValueItemAcc = static_cast<ValueItemAcc*>(xAccessible.get());
1844         pValueItemAcc->FireAccessibleEvent(AccessibleEventId::NAME_CHANGED, aOldName, aNewName);
1845     }
1846 }
1847 
1848 Size ValueSet::GetLargestItemSize()
1849 {
1850     Size aLargestItem;
1851 
1852     for (const std::unique_ptr<ValueSetItem>& pItem : mItemList)
1853     {
1854         if (!pItem->mbVisible)
1855             continue;
1856 
1857         if (pItem->meType != VALUESETITEM_IMAGE &&
1858             pItem->meType != VALUESETITEM_IMAGE_AND_TEXT)
1859         {
1860             // handle determining an optimal size for this case
1861             continue;
1862         }
1863 
1864         Size aSize = pItem->maImage.GetSizePixel();
1865         if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT)
1866         {
1867             aSize.AdjustHeight(3 * NAME_LINE_HEIGHT +
1868                 maVirDev->GetTextHeight() );
1869             aSize.setWidth( std::max(aSize.Width(),
1870                                      maVirDev->GetTextWidth(pItem->maText) + NAME_OFFSET) );
1871         }
1872 
1873         aLargestItem.setWidth( std::max(aLargestItem.Width(), aSize.Width()) );
1874         aLargestItem.setHeight( std::max(aLargestItem.Height(), aSize.Height()) );
1875     }
1876 
1877     return aLargestItem;
1878 }
1879 
1880 void ValueSet::SetOptimalSize()
1881 {
1882     Size aLargestSize(GetLargestItemSize());
1883     aLargestSize.setWidth(std::max(aLargestSize.Width(), mnUserItemWidth));
1884     aLargestSize.setHeight(std::max(aLargestSize.Height(), mnUserItemHeight));
1885     Size aPrefSize(CalcWindowSizePixel(aLargestSize));
1886     GetDrawingArea()->set_size_request(aPrefSize.Width(), aPrefSize.Height());
1887 }
1888 
1889 Image ValueSet::GetItemImage(sal_uInt16 nItemId) const
1890 {
1891     size_t nPos = GetItemPos( nItemId );
1892 
1893     if ( nPos != VALUESET_ITEM_NOTFOUND )
1894         return mItemList[nPos]->maImage;
1895     else
1896         return Image();
1897 }
1898 
1899 void ValueSet::SetColor(const Color& rColor)
1900 {
1901     maColor  = rColor;
1902     mbFormat = true;
1903     if (IsReallyVisible() && IsUpdateMode())
1904         Invalidate();
1905 }
1906 
1907 void ValueSet::Show()
1908 {
1909     if (mxScrolledWindow)
1910         mxScrolledWindow->show();
1911     CustomWidgetController::Show();
1912 }
1913 
1914 void ValueSet::Hide()
1915 {
1916     CustomWidgetController::Hide();
1917     if (mxScrolledWindow)
1918         mxScrolledWindow->hide();
1919 }
1920 
1921 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1922