xref: /core/sfx2/source/control/thumbnailview.cxx (revision e6211a4b3bd74f2bf392fd5cda7dea51552cd250)
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 
10 #include <config_wasm_strip.h>
11 
12 #include <sfx2/thumbnailview.hxx>
13 #include <sfx2/thumbnailviewitem.hxx>
14 
15 #include <utility>
16 
17 #include "thumbnailviewacc.hxx"
18 #include "thumbnailviewitemacc.hxx"
19 
20 #include <basegfx/color/bcolortools.hxx>
21 #include <comphelper/processfactory.hxx>
22 #include <comphelper/propertyvalue.hxx>
23 #include <drawinglayer/attribute/fontattribute.hxx>
24 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
25 #include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
26 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
27 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
28 #include <drawinglayer/processor2d/processor2dtools.hxx>
29 #include <o3tl/safeint.hxx>
30 #include <rtl/ustring.hxx>
31 #include <sal/log.hxx>
32 #include <svtools/optionsdrawinglayer.hxx>
33 #include <comphelper/diagnose_ex.hxx>
34 #include <unotools/ucbstreamhelper.hxx>
35 #include <vcl/svapp.hxx>
36 #include <vcl/settings.hxx>
37 #include <vcl/event.hxx>
38 #include <vcl/filter/PngImageReader.hxx>
39 #include <vcl/graphicfilter.hxx>
40 #include <vcl/weldutils.hxx>
41 
42 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
43 #include <com/sun/star/embed/ElementModes.hpp>
44 #include <com/sun/star/embed/StorageFactory.hpp>
45 #include <com/sun/star/embed/StorageFormats.hpp>
46 #include <com/sun/star/embed/XHierarchicalStorageAccess.hpp>
47 #include <com/sun/star/embed/XRelationshipAccess.hpp>
48 #include <com/sun/star/embed/XStorage.hpp>
49 #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
50 
51 #include <memory>
52 #if !ENABLE_WASM_STRIP_RECENT
53 #include "recentdocsviewitem.hxx"
54 #endif
55 
56 using namespace basegfx;
57 using namespace drawinglayer::attribute;
58 using namespace drawinglayer::primitive2d;
59 
60 constexpr int gnFineness = 5;
61 
renameItem(ThumbnailViewItem &,const OUString &)62 bool ThumbnailView::renameItem(ThumbnailViewItem&, const OUString&)
63 {
64     // Do nothing by default
65     return false;
66 }
67 
68 static css::uno::Reference<css::embed::XHierarchicalStorageAccess>
getStorageAccess(const OUString & URL,sal_Int32 format)69 getStorageAccess(const OUString& URL, sal_Int32 format)
70 {
71     auto xFactory = css::embed::StorageFactory::create(comphelper::getProcessComponentContext());
72     css::uno::Sequence descriptor{ comphelper::makePropertyValue(u"StorageFormat"_ustr, format) };
73     css::uno::Sequence args{ css::uno::Any(URL), css::uno::Any(css::embed::ElementModes::READ),
74                              css::uno::Any(descriptor) };
75     return xFactory->createInstanceWithArguments(args)
76         .queryThrow<css::embed::XHierarchicalStorageAccess>();
77 }
78 
79 static css::uno::Reference<css::io::XInputStream>
getHierarchicalStream(const css::uno::Reference<css::embed::XHierarchicalStorageAccess> & xStorage,const OUString & name)80 getHierarchicalStream(const css::uno::Reference<css::embed::XHierarchicalStorageAccess>& xStorage,
81                       const OUString& name)
82 {
83     auto xStream
84         = xStorage->openStreamElementByHierarchicalName(name, css::embed::ElementModes::READ);
85     return xStream->getInputStream();
86 }
87 
88 static css::uno::Reference<css::io::XInputStream>
getFirstHierarchicalStream(const OUString & URL,sal_Int32 format,std::initializer_list<OUString> names)89 getFirstHierarchicalStream(const OUString& URL, sal_Int32 format,
90                            std::initializer_list<OUString> names)
91 {
92     auto xStorage(getStorageAccess(URL, format));
93     for (const auto& name : names)
94     {
95         try
96         {
97             return getHierarchicalStream(xStorage, name);
98         }
99         catch (const css::uno::Exception&)
100         {
101             TOOLS_WARN_EXCEPTION("sfx", "caught exception while trying to access " << name << " of "
102                                                                                    << URL);
103         }
104     }
105     return {};
106 }
107 
108 static css::uno::Reference<css::io::XInputStream>
getFirstStreamByRelType(const OUString & URL,std::initializer_list<OUString> types)109 getFirstStreamByRelType(const OUString& URL, std::initializer_list<OUString> types)
110 {
111     auto xStorage(getStorageAccess(URL, css::embed::StorageFormats::OFOPXML));
112     if (auto xRelationshipAccess = xStorage.query<css::embed::XRelationshipAccess>())
113     {
114         for (const auto& type : types)
115         {
116             auto rels = xRelationshipAccess->getRelationshipsByType(type);
117             if (rels.hasElements())
118             {
119                 // ISO/IEC 29500-1:2016(E) 15.2.16 Thumbnail Part: "Packages shall not contain
120                 // more than one thumbnail relationship associated with the package as a whole"
121                 for (const auto& [tag, value] : rels[0])
122                 {
123                     if (tag == "Id")
124                     {
125                         return getHierarchicalStream(xStorage,
126                                                      xRelationshipAccess->getTargetByID(value));
127                     }
128                 }
129             }
130         }
131     }
132     return {};
133 }
134 
readThumbnail(const OUString & msURL)135 Bitmap ThumbnailView::readThumbnail(const OUString &msURL)
136 {
137     using namespace ::com::sun::star;
138     using namespace ::com::sun::star::uno;
139 
140     // Load the thumbnail from a template document.
141     uno::Reference<io::XInputStream> xIStream;
142 
143     try
144     {
145         // An (older) implementation had a bug - The storage
146         // name was "Thumbnail" instead of "Thumbnails".  The
147         // old name is still used as fallback but this code can
148         // be removed soon.
149         xIStream = getFirstHierarchicalStream(
150             msURL, embed::StorageFormats::PACKAGE,
151             { u"Thumbnails/thumbnail.png"_ustr, u"Thumbnail/thumbnail.png"_ustr });
152     }
153     catch (const uno::Exception&)
154     {
155         TOOLS_WARN_EXCEPTION("sfx",
156             "caught exception while trying to access thumbnail of "
157             << msURL);
158     }
159 
160     if (!xIStream.is())
161     {
162         // OOXML?
163         try
164         {
165             // Check both Transitional and Strict relationships
166             xIStream = getFirstStreamByRelType(
167                 msURL,
168                 { u"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"_ustr,
169                   u"http://purl.oclc.org/ooxml/officeDocument/relationships/metadata/thumbnail"_ustr });
170         }
171         catch (const uno::Exception&)
172         {
173             // Not an OOXML; fine
174         }
175     }
176 
177     // Extract the image from the stream.
178     Bitmap aThumbnail;
179     if (auto pStream = utl::UcbStreamHelper::CreateStream(xIStream, /*CloseStream=*/true))
180     {
181         Graphic aGraphic = GraphicFilter::GetGraphicFilter().ImportUnloadedGraphic(*pStream);
182         aThumbnail = aGraphic.GetBitmap();
183     }
184 
185     // Note that the preview is returned without scaling it to the desired
186     // width.  This gives the caller the chance to take advantage of a
187     // possibly larger resolution then was asked for.
188     return aThumbnail;
189 }
190 
ThumbnailView(std::unique_ptr<weld::ScrolledWindow> xWindow,std::unique_ptr<weld::Menu> xMenu)191 ThumbnailView::ThumbnailView(std::unique_ptr<weld::ScrolledWindow> xWindow, std::unique_ptr<weld::Menu> xMenu)
192     : mnThumbnailHeight(0)
193     , mnDisplayHeight(0)
194     , mnVItemSpace(-1)
195     , mbAllowVScrollBar(xWindow->get_vpolicy() != VclPolicyType::NEVER)
196     , mbSelectOnFocus(true)
197     , mpItemAttrs(new ThumbnailItemAttributes)
198     , mxScrolledWindow(std::move(xWindow))
199     , mxContextMenu(std::move(xMenu))
200 {
201     ImplInit();
202     mxScrolledWindow->connect_vadjustment_value_changed(LINK(this, ThumbnailView, ImplScrollHdl));
203 }
204 
~ThumbnailView()205 ThumbnailView::~ThumbnailView()
206 {
207     ImplDeleteItems();
208 
209     if (mxAccessible.is())
210         mxAccessible->dispose();
211 
212     mpItemAttrs.reset();
213 }
214 
MouseMove(const MouseEvent & rMEvt)215 bool ThumbnailView::MouseMove(const MouseEvent& rMEvt)
216 {
217     size_t nItemCount = mFilteredItemList.size();
218     Point aPoint = rMEvt.GetPosPixel();
219 
220     for (size_t i = 0; i < nItemCount; i++)
221     {
222         ThumbnailViewItem *pItem = mFilteredItemList[i];
223         ::tools::Rectangle aToInvalidate(pItem->updateHighlight(pItem->mbVisible && !rMEvt.IsLeaveWindow(), aPoint));
224         if (!aToInvalidate.IsEmpty() && IsReallyVisible() && IsUpdateMode())
225             Invalidate(aToInvalidate);
226     }
227 
228     return true;
229 }
230 
RequestHelp(tools::Rectangle & rHelpRect)231 OUString ThumbnailView::RequestHelp(tools::Rectangle& rHelpRect)
232 {
233     if (!mbShowTooltips)
234         return OUString();
235 
236     Point aPos = rHelpRect.TopLeft();
237     size_t nItemCount = mFilteredItemList.size();
238     for (size_t i = 0; i < nItemCount; i++)
239     {
240         ThumbnailViewItem *pItem = mFilteredItemList[i];
241         if (!pItem->mbVisible)
242             continue;
243         const tools::Rectangle& rDrawArea = pItem->getDrawArea();
244         if (rDrawArea.Contains(aPos))
245         {
246             rHelpRect = rDrawArea;
247             return pItem->getHelpText();
248         }
249     }
250 
251     return OUString();
252 }
253 
AppendItem(std::unique_ptr<ThumbnailViewItem> pItem)254 void ThumbnailView::AppendItem(std::unique_ptr<ThumbnailViewItem> pItem)
255 {
256     if (maFilterFunc(pItem.get()))
257     {
258         // Save current start,end range, iterator might get invalidated
259         size_t nSelStartPos = 0;
260         ThumbnailViewItem *pSelStartItem = nullptr;
261 
262         if (mpStartSelRange != mFilteredItemList.end())
263         {
264             pSelStartItem = *mpStartSelRange;
265             nSelStartPos = mpStartSelRange - mFilteredItemList.begin();
266         }
267 
268         mFilteredItemList.push_back(pItem.get());
269         mpStartSelRange = pSelStartItem != nullptr ? mFilteredItemList.begin() + nSelStartPos : mFilteredItemList.end();
270     }
271 
272     mItemList.push_back(std::move(pItem));
273 }
274 
ImplInit()275 void ThumbnailView::ImplInit()
276 {
277     mnItemWidth = 0;
278     mnItemHeight = 0;
279     mnItemPadding = 0;
280     mnVisLines = 0;
281     mnLines = 0;
282     mnFirstLine = 0;
283     mnCols = 0;
284     mbScroll = false;
285     mbHasVisibleItems = false;
286     mbShowTooltips = false;
287     mbDrawMnemonics = false;
288     mbAllowMultiSelection = true;
289     maFilterFunc = ViewFilterAll();
290 
291     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
292     maFillColor = rSettings.GetFieldColor();
293     maTextColor = rSettings.GetWindowTextColor();
294     maHighlightColor = rSettings.GetHighlightColor();
295     maHighlightTextColor = rSettings.GetHighlightTextColor();
296 
297     mfHighlightTransparence = SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01;
298 
299     mpStartSelRange = mFilteredItemList.end();
300 
301     UpdateColors();
302 
303     mpItemAttrs->nMaxTextLength = 0;
304 }
305 
UpdateColors()306 void ThumbnailView::UpdateColors()
307 {
308     mpItemAttrs->aFillColor = maFillColor.getBColor();
309     mpItemAttrs->aTextColor = maTextColor.getBColor();
310     mpItemAttrs->aHighlightColor = maHighlightColor.getBColor();
311     mpItemAttrs->aHighlightTextColor = maHighlightTextColor.getBColor();
312     mpItemAttrs->fHighlightTransparence = mfHighlightTransparence;
313 }
314 
ImplDeleteItems()315 void ThumbnailView::ImplDeleteItems()
316 {
317     const size_t n = mItemList.size();
318 
319     for ( size_t i = 0; i < n; ++i )
320     {
321         ThumbnailViewItem *const pItem = mItemList[i].get();
322 
323         // deselect all current selected items and fire events
324         if (pItem->isSelected())
325         {
326             pItem->setSelection(false);
327             maItemStateHdl.Call(pItem);
328 
329             // fire accessible event???
330         }
331 
332         rtl::Reference<ThumbnailViewItemAcc> xItemAcc = pItem->GetAccessible(false);
333         if (xItemAcc.is())
334         {
335             css::uno::Any aOldAny, aNewAny;
336             aOldAny <<= css::uno::Reference<css::accessibility::XAccessible>(pItem->GetAccessible());
337             ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
338 
339             xItemAcc->dispose();
340         }
341 
342         mItemList[i].reset();
343     }
344 
345     mItemList.clear();
346     mFilteredItemList.clear();
347 
348     mpStartSelRange = mFilteredItemList.end();
349 }
350 
DrawItem(ThumbnailViewItem const * pItem)351 void ThumbnailView::DrawItem(ThumbnailViewItem const *pItem)
352 {
353     if (pItem->isVisible())
354     {
355         ::tools::Rectangle aRect = pItem->getDrawArea();
356 
357         if (!aRect.IsEmpty())
358             Invalidate(aRect);
359     }
360 }
361 
OnItemDblClicked(ThumbnailViewItem *)362 void ThumbnailView::OnItemDblClicked (ThumbnailViewItem*)
363 {
364 }
365 
CreateAccessible()366 rtl::Reference<comphelper::OAccessible> ThumbnailView::CreateAccessible()
367 {
368     mxAccessible.set(new ThumbnailViewAcc(this));
369     return mxAccessible;
370 }
371 
getAccessible() const372 const rtl::Reference< ThumbnailViewAcc > & ThumbnailView::getAccessible() const
373 {
374     return mxAccessible;
375 }
376 
CalculateItemPositions(bool bScrollBarUsed)377 void ThumbnailView::CalculateItemPositions(bool bScrollBarUsed)
378 {
379     if (!mnItemHeight || !mnItemWidth)
380         return;
381 
382     Size        aWinSize = GetOutputSizePixel();
383     size_t      nItemCount = mFilteredItemList.size();
384 
385     // calculate window scroll ratio
386     float nScrollRatio;
387     if (bScrollBarUsed)
388     {
389         nScrollRatio = static_cast<float>(mxScrolledWindow->vadjustment_get_value()) /
390                        static_cast<float>(mxScrolledWindow->vadjustment_get_upper() -
391                                           mxScrolledWindow->vadjustment_get_page_size());
392     }
393     else
394         nScrollRatio = 0;
395 
396     // calculate ScrollBar width
397     tools::Long nScrBarWidth = mbAllowVScrollBar ? mxScrolledWindow->get_scroll_thickness() : 0;
398 
399     // calculate maximum number of visible columns
400     mnCols = static_cast<sal_uInt16>((aWinSize.Width()-nScrBarWidth) / mnItemWidth);
401 
402     if (!mnCols)
403         mnCols = 1;
404 
405     // calculate maximum number of visible rows
406     mnVisLines = static_cast<sal_uInt16>(aWinSize.Height() / mnItemHeight);
407 
408     // calculate empty space
409     tools::Long nHSpace = aWinSize.Width()-nScrBarWidth - mnCols*mnItemWidth;
410     tools::Long nVSpace = aWinSize.Height() - mnVisLines*mnItemHeight;
411     tools::Long nHItemSpace = nHSpace / (mnCols+1);
412     tools::Long nVItemSpace = mnVItemSpace;
413     if (nVItemSpace == -1) // auto, split up extra space to use as vertical spacing
414         nVItemSpace = nVSpace / (mnVisLines+1);
415 
416     // tdf#162510 - calculate maximum number of rows
417     size_t nItemCountPinned = 0;
418 #if !ENABLE_WASM_STRIP_RECENT
419     bool bPinnedItems = true;
420     for (size_t i = 0; bPinnedItems && i < nItemCount; ++i)
421     {
422         ThumbnailViewItem& rItem = *mFilteredItemList[i];
423         if (auto const pRecentDocsItem = dynamic_cast<RecentDocsViewItem*>(&rItem))
424         {
425             if (pRecentDocsItem->isPinned())
426                 ++nItemCountPinned;
427             else
428                 bPinnedItems = false;
429         }
430     }
431 #endif
432 
433     // calculate maximum number of rows
434     // Floor( (M+N-1)/N )==Ceiling( M/N )
435     mnLines = (static_cast<tools::Long>(nItemCount - nItemCountPinned) + mnCols - 1) / mnCols;
436     // tdf#162510 - add pinned items to number of lines
437     mnLines += (static_cast<tools::Long>(nItemCountPinned) + mnCols - 1) / mnCols;
438 
439     if ( !mnLines )
440         mnLines = 1;
441 
442     if ( mnLines <= mnVisLines )
443         mnFirstLine = 0;
444     else if ( mnFirstLine > o3tl::make_unsigned(mnLines-mnVisLines) )
445         mnFirstLine = static_cast<sal_uInt16>(mnLines-mnVisLines);
446 
447     mbHasVisibleItems = true;
448 
449     tools::Long nFullSteps = (mnLines > mnVisLines) ? mnLines - mnVisLines + 1 : 1;
450 
451     tools::Long nItemHeightOffset = mnItemHeight + nVItemSpace;
452     tools::Long nHiddenLines = static_cast<tools::Long>((nFullSteps - 1) * nScrollRatio);
453 
454     // calculate offsets
455     tools::Long nStartX = nHItemSpace;
456     tools::Long nStartY = nVItemSpace;
457 
458     // calculate and draw items
459     tools::Long x = nStartX;
460     tools::Long y = nStartY - ((nFullSteps - 1) * nScrollRatio - nHiddenLines) * nItemHeightOffset;
461 
462     // draw items
463     // Unless we are scrolling (via scrollbar) we just use the precalculated
464     // mnFirstLine -- our nHiddenLines calculation takes into account only
465     // what the user has done with the scrollbar but not any changes of selection
466     // using the keyboard, meaning we could accidentally hide the selected item
467     // if we believe the scrollbar (fdo#72287).
468     size_t nFirstItem = (bScrollBarUsed ? nHiddenLines : mnFirstLine) * mnCols;
469     size_t nLastItem = nFirstItem + (mnVisLines + 1) * mnCols;
470 
471     // tdf#162510 - helper for in order to handle accessibility events
472     auto handleAccessibleEvent = [&](ThumbnailViewItem& rItem, bool bIsVisible)
473     {
474         if (ImplHasAccessibleListeners())
475         {
476             css::uno::Any aOldAny, aNewAny;
477             if (bIsVisible)
478                 aNewAny <<= css::uno::Reference<css::accessibility::XAccessible>(rItem.GetAccessible());
479             else
480                 aOldAny <<= css::uno::Reference<css::accessibility::XAccessible>(rItem.GetAccessible());
481             ImplFireAccessibleEvent(css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny);
482         }
483     };
484 
485     // tdf#162510 - helper to set visibility and update layout
486     auto updateItemLayout = [&](ThumbnailViewItem& rItem, bool bIsVisible, size_t& nVisibleCount)
487     {
488         if (bIsVisible != rItem.isVisible())
489         {
490             handleAccessibleEvent(rItem, bIsVisible);
491             rItem.show(bIsVisible);
492             maItemStateHdl.Call(&rItem);
493         }
494 
495         if (bIsVisible)
496         {
497             rItem.setDrawArea(::tools::Rectangle(Point(x, y), Size(mnItemWidth, mnItemHeight)));
498             rItem.calculateItemsPosition(mnThumbnailHeight, mnItemPadding,
499                                          mpItemAttrs->nMaxTextLength, mpItemAttrs.get());
500 
501             if ((nVisibleCount + 1) % mnCols)
502                 x += mnItemWidth + nHItemSpace;
503             else
504             {
505                 x = nStartX;
506                 y += mnItemHeight + nVItemSpace;
507             }
508             ++nVisibleCount;
509         }
510     };
511 
512     size_t nCurCountVisible = 0;
513 #if !ENABLE_WASM_STRIP_RECENT
514     // tdf#162510 - process pinned items
515     for (size_t i = 0; i < nItemCountPinned; i++)
516         updateItemLayout(*mFilteredItemList[i], nFirstItem <= i && i < nLastItem, nCurCountVisible);
517 
518     // tdf#162510 - start a new line only if the entire line is not filled with pinned items
519     if (nCurCountVisible && nCurCountVisible % mnCols)
520     {
521         x = nStartX;
522         y += mnItemHeight + nVItemSpace;
523     }
524 
525     // tdf#164102 - adjust first item only if there are any pinned items
526     if (const auto nRemainingPinnedSlots = nItemCountPinned % mnCols)
527     {
528         // tdf#162510 - adjust first item to take into account the new line after pinned items
529         const auto nFirstItemAdjustment = mnCols - nRemainingPinnedSlots;
530         if (nFirstItemAdjustment <= nFirstItem)
531             nFirstItem -= nFirstItemAdjustment;
532     }
533 
534 #endif
535 
536     // If want also draw parts of items in the last line,
537     // then we add one more line if parts of this line are visible
538     nCurCountVisible = 0;
539     for (size_t i = nItemCountPinned; i < nItemCount; i++)
540         updateItemLayout(*mFilteredItemList[i], nFirstItem <= i && i < nLastItem, nCurCountVisible);
541 
542     // check if scroll is needed
543     mbScroll = mnLines > mnVisLines;
544 
545     mxScrolledWindow->vadjustment_set_upper(mnLines * gnFineness);
546     mxScrolledWindow->vadjustment_set_page_size(mnVisLines * gnFineness);
547     if (!bScrollBarUsed)
548         mxScrolledWindow->vadjustment_set_value(static_cast<tools::Long>(mnFirstLine)*gnFineness);
549     tools::Long nPageSize = mnVisLines;
550     if ( nPageSize < 1 )
551         nPageSize = 1;
552     mxScrolledWindow->vadjustment_set_page_increment(nPageSize*gnFineness);
553     if (mbAllowVScrollBar)
554         mxScrolledWindow->set_vpolicy(mbScroll ? VclPolicyType::ALWAYS : VclPolicyType::NEVER);
555 }
556 
ImplGetItem(const Point & rPos) const557 size_t ThumbnailView::ImplGetItem( const Point& rPos ) const
558 {
559     if ( !mbHasVisibleItems )
560     {
561         return THUMBNAILVIEW_ITEM_NOTFOUND;
562     }
563 
564     for (size_t i = 0; i < mFilteredItemList.size(); ++i)
565     {
566         if (mFilteredItemList[i]->isVisible() && mFilteredItemList[i]->getDrawArea().Contains(rPos))
567             return i;
568     }
569 
570     return THUMBNAILVIEW_ITEM_NOTFOUND;
571 }
572 
ImplGetItem(size_t nPos)573 ThumbnailViewItem* ThumbnailView::ImplGetItem( size_t nPos )
574 {
575     return ( nPos < mFilteredItemList.size() ) ? mFilteredItemList[nPos] : nullptr;
576 }
577 
ImplGetVisibleItemCount() const578 sal_uInt16 ThumbnailView::ImplGetVisibleItemCount() const
579 {
580     sal_uInt16 nRet = 0;
581     const size_t nItemCount = mItemList.size();
582 
583     for ( size_t n = 0; n < nItemCount; ++n )
584     {
585         if ( mItemList[n]->isVisible() )
586             ++nRet;
587     }
588 
589     return nRet;
590 }
591 
ImplGetVisibleItem(sal_uInt16 nVisiblePos)592 ThumbnailViewItem* ThumbnailView::ImplGetVisibleItem( sal_uInt16 nVisiblePos )
593 {
594     const size_t nItemCount = mItemList.size();
595 
596     for ( size_t n = 0; n < nItemCount; ++n )
597     {
598         ThumbnailViewItem *const pItem = mItemList[n].get();
599 
600         if ( pItem->isVisible() && !nVisiblePos-- )
601             return pItem;
602     }
603 
604     return nullptr;
605 }
606 
ImplFireAccessibleEvent(short nEventId,const css::uno::Any & rOldValue,const css::uno::Any & rNewValue)607 void ThumbnailView::ImplFireAccessibleEvent( short nEventId, const css::uno::Any& rOldValue, const css::uno::Any& rNewValue )
608 {
609     if( mxAccessible )
610         mxAccessible->FireAccessibleEvent( nEventId, rOldValue, rNewValue );
611 }
612 
ImplHasAccessibleListeners() const613 bool ThumbnailView::ImplHasAccessibleListeners() const
614 {
615     return mxAccessible && mxAccessible->HasAccessibleListeners();
616 }
617 
IMPL_LINK_NOARG(ThumbnailView,ImplScrollHdl,weld::ScrolledWindow &,void)618 IMPL_LINK_NOARG(ThumbnailView, ImplScrollHdl, weld::ScrolledWindow&, void)
619 {
620     CalculateItemPositions(true);
621     if (IsReallyVisible() && IsUpdateMode())
622         Invalidate();
623 }
624 
KeyInput(const KeyEvent & rKEvt)625 bool ThumbnailView::KeyInput( const KeyEvent& rKEvt )
626 {
627     bool bHandled = true;
628 
629     // Get the last selected item in the list
630     size_t nLastPos = 0;
631     bool bFoundLast = false;
632     for ( tools::Long i = mFilteredItemList.size() - 1; !bFoundLast && i >= 0; --i )
633     {
634         ThumbnailViewItem* pItem = mFilteredItemList[i];
635         if ( pItem->isSelected() )
636         {
637             nLastPos = i;
638             bFoundLast = true;
639         }
640     }
641 
642     bool bValidRange = false;
643     bool bHasSelRange = mpStartSelRange != mFilteredItemList.end();
644     size_t nNextPos = nLastPos;
645     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
646     ThumbnailViewItem* pNext = nullptr;
647 
648     if (aKeyCode.IsShift() && bHasSelRange)
649     {
650         //If the last element selected is the start range position
651         //search for the first selected item
652         size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
653 
654         if (nLastPos == nSelPos)
655         {
656             while (nLastPos && mFilteredItemList[nLastPos-1]->isSelected())
657                 --nLastPos;
658         }
659     }
660 
661     switch ( aKeyCode.GetCode() )
662     {
663         case KEY_RIGHT:
664             if (!mFilteredItemList.empty())
665             {
666                 if ( bFoundLast && nLastPos + 1 < mFilteredItemList.size() )
667                 {
668                     bValidRange = true;
669                     nNextPos = nLastPos + 1;
670                 }
671 
672                 pNext = mFilteredItemList[nNextPos];
673             }
674             break;
675         case KEY_LEFT:
676             if (!mFilteredItemList.empty())
677             {
678                 if ( nLastPos > 0 )
679                 {
680                     bValidRange = true;
681                     nNextPos = nLastPos - 1;
682                 }
683 
684                 pNext = mFilteredItemList[nNextPos];
685             }
686             break;
687         case KEY_DOWN:
688             if (!mFilteredItemList.empty())
689             {
690                 if ( bFoundLast )
691                 {
692                     //If we are in the second last row just go the one in
693                     //the row below, if there's not row below just go to the
694                     //last item but for the last row don't do anything.
695                     if ( nLastPos + mnCols < mFilteredItemList.size( ) )
696                     {
697                         bValidRange = true;
698                         nNextPos = nLastPos + mnCols;
699                     }
700                     else
701                     {
702                         int curRow = nLastPos/mnCols;
703 
704                         if (curRow < mnLines-1)
705                             nNextPos = mFilteredItemList.size()-1;
706                     }
707                 }
708 
709                 pNext = mFilteredItemList[nNextPos];
710             }
711             break;
712         case KEY_UP:
713             if (!mFilteredItemList.empty())
714             {
715                 if ( nLastPos >= mnCols )
716                 {
717                     bValidRange = true;
718                     nNextPos = nLastPos - mnCols;
719                 }
720 
721                 pNext = mFilteredItemList[nNextPos];
722             }
723             break;
724         case KEY_RETURN:
725             {
726                 if ( bFoundLast )
727                     OnItemDblClicked( mFilteredItemList[nLastPos] );
728             }
729             [[fallthrough]];
730         default:
731             bHandled = CustomWidgetController::KeyInput(rKEvt);
732     }
733 
734     if ( pNext )
735     {
736         if (aKeyCode.IsShift() && bValidRange && mbAllowMultiSelection)
737         {
738             std::pair<size_t,size_t> aRange;
739             size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
740 
741             if (nLastPos < nSelPos)
742             {
743                 if (nNextPos > nLastPos)
744                 {
745                     if ( nNextPos > nSelPos)
746                         aRange = std::make_pair(nLastPos,nNextPos);
747                     else
748                         aRange = std::make_pair(nLastPos,nNextPos-1);
749                 }
750                 else
751                 {
752                     assert(nLastPos > 0);
753                     aRange = std::make_pair(nNextPos,nLastPos-1);
754                 }
755             }
756             else if (nLastPos == nSelPos)
757             {
758                 if (nNextPos > nLastPos)
759                     aRange = std::make_pair(nLastPos+1,nNextPos);
760                 else
761                 {
762                     assert(nLastPos > 0);
763                     aRange = std::make_pair(nNextPos,nLastPos-1);
764                 }
765             }
766             else
767             {
768                 if (nNextPos > nLastPos)
769                     aRange = std::make_pair(nLastPos+1,nNextPos);
770                 else
771                 {
772                     if ( nNextPos < nSelPos)
773                         aRange = std::make_pair(nNextPos,nLastPos);
774                     else
775                         aRange = std::make_pair(nNextPos+1,nLastPos);
776                 }
777             }
778 
779             for (size_t i = aRange.first; i <= aRange.second; ++i)
780             {
781                 if (i != nSelPos)
782                 {
783                     ThumbnailViewItem *pCurItem = mFilteredItemList[i];
784 
785                     pCurItem->setSelection(!pCurItem->isSelected());
786 
787                     DrawItem(pCurItem);
788 
789                     maItemStateHdl.Call(pCurItem);
790                 }
791             }
792         }
793         else if (!aKeyCode.IsShift())
794         {
795             deselectItems();
796             SelectItem(pNext->mnId);
797 
798             //Mark it as the selection range start position
799             mpStartSelRange = mFilteredItemList.begin() + nNextPos;
800         }
801 
802         MakeItemVisible(pNext->mnId);
803     }
804     return bHandled;
805 }
806 
MakeItemVisible(sal_uInt16 nItemId)807 void ThumbnailView::MakeItemVisible( sal_uInt16 nItemId )
808 {
809     // Get the item row
810     size_t nPos = 0;
811     bool bFound = false;
812     for ( size_t i = 0; !bFound && i < mFilteredItemList.size(); ++i )
813     {
814         ThumbnailViewItem* pItem = mFilteredItemList[i];
815         if ( pItem->mnId == nItemId )
816         {
817             nPos = i;
818             bFound = true;
819         }
820     }
821     sal_uInt16 nRow = mnCols ? nPos / mnCols : 0;
822 
823     // Move the visible rows as little as possible to include that one
824     if ( nRow < mnFirstLine )
825         mnFirstLine = nRow;
826     else if ( nRow > mnFirstLine + mnVisLines )
827         mnFirstLine = nRow - mnVisLines;
828 
829     CalculateItemPositions();
830     Invalidate();
831 }
832 
MouseButtonDown(const MouseEvent & rMEvt)833 bool ThumbnailView::MouseButtonDown( const MouseEvent& rMEvt )
834 {
835     GrabFocus();
836 
837     if (!rMEvt.IsLeft())
838     {
839         return CustomWidgetController::MouseButtonDown( rMEvt );
840     }
841 
842     size_t nPos = ImplGetItem(rMEvt.GetPosPixel());
843     ThumbnailViewItem* pItem = ImplGetItem(nPos);
844 
845     if ( !pItem )
846     {
847         deselectItems();
848         return CustomWidgetController::MouseButtonDown( rMEvt );
849     }
850 
851     if ( rMEvt.GetClicks() == 2 )
852     {
853         OnItemDblClicked(pItem);
854         return true;
855     }
856 
857     if(rMEvt.GetClicks() == 1)
858     {
859         if (rMEvt.IsMod1())
860         {
861             //Keep selected item group state and just invert current desired one state
862             pItem->setSelection(!pItem->isSelected());
863 
864             //This one becomes the selection range start position if it changes its state to selected otherwise resets it
865             mpStartSelRange = pItem->isSelected() ? mFilteredItemList.begin() + nPos : mFilteredItemList.end();
866         }
867         else if (rMEvt.IsShift() && mpStartSelRange != mFilteredItemList.end())
868         {
869             std::pair<size_t,size_t> aNewRange;
870             aNewRange.first = mpStartSelRange - mFilteredItemList.begin();
871             aNewRange.second = nPos;
872 
873             if (aNewRange.first > aNewRange.second)
874                 std::swap(aNewRange.first,aNewRange.second);
875 
876             //Deselect the ones outside of it
877             for (size_t i = 0, n = mFilteredItemList.size(); i < n; ++i)
878             {
879                 ThumbnailViewItem *pCurItem  = mFilteredItemList[i];
880 
881                 if (pCurItem->isSelected() && (i < aNewRange.first || i > aNewRange.second))
882                 {
883                     pCurItem->setSelection(false);
884 
885                     DrawItem(pCurItem);
886 
887                     maItemStateHdl.Call(pCurItem);
888                 }
889             }
890 
891             size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
892 
893             //Select the items between start range and the selected item
894             if (nSelPos != nPos)
895             {
896                 int dir = nSelPos < nPos ? 1 : -1;
897                 size_t nCurPos = nSelPos + dir;
898 
899                 while (nCurPos != nPos)
900                 {
901                     ThumbnailViewItem *pCurItem  = mFilteredItemList[nCurPos];
902 
903                     if (!pCurItem->isSelected())
904                     {
905                         pCurItem->setSelection(true);
906 
907                         DrawItem(pCurItem);
908 
909                         maItemStateHdl.Call(pCurItem);
910                     }
911 
912                     nCurPos += dir;
913                 }
914             }
915 
916             pItem->setSelection(true);
917         }
918         else
919         {
920             //If we got a group of selected items deselect the rest and only keep the desired one
921             //mark items as not selected to not fire unnecessary change state events.
922             pItem->setSelection(false);
923             deselectItems();
924             pItem->setSelection(true);
925 
926             //Mark as initial selection range position and reset end one
927             mpStartSelRange = mFilteredItemList.begin() + nPos;
928         }
929 
930         if (!pItem->isHighlighted())
931             DrawItem(pItem);
932 
933         maItemStateHdl.Call(pItem);
934 
935         //fire accessible event??
936     }
937     return true;
938 }
939 
SetDrawingArea(weld::DrawingArea * pDrawingArea)940 void ThumbnailView::SetDrawingArea(weld::DrawingArea* pDrawingArea)
941 {
942     CustomWidgetController::SetDrawingArea(pDrawingArea);
943 
944     OutputDevice& rDevice = pDrawingArea->get_ref_device();
945     weld::SetPointFont(rDevice, pDrawingArea->get_font());
946     mpItemAttrs->aFontAttr = getFontAttributeFromVclFont(mpItemAttrs->aFontSize, rDevice.GetFont(), false, true);
947 
948     SetOutputSizePixel(pDrawingArea->get_preferred_size());
949 }
950 
Paint(vcl::RenderContext & rRenderContext,const::tools::Rectangle &)951 void ThumbnailView::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& /*rRect*/)
952 {
953     auto popIt = rRenderContext.ScopedPush(vcl::PushFlags::ALL);
954 
955     rRenderContext.SetTextFillColor();
956     rRenderContext.SetBackground(maFillColor);
957 
958     size_t nItemCount = mItemList.size();
959 
960     // Draw background
961     drawinglayer::primitive2d::Primitive2DContainer aSeq(1);
962     aSeq[0] = drawinglayer::primitive2d::Primitive2DReference(
963             new PolyPolygonColorPrimitive2D(
964                     B2DPolyPolygon( ::tools::Polygon(::tools::Rectangle(Point(), GetOutputSizePixel()), 0, 0).getB2DPolygon()),
965                     maFillColor.getBColor()));
966 
967     // Create the processor and process the primitives
968     const drawinglayer::geometry::ViewInformation2D aNewViewInfos;
969 
970     std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(
971         drawinglayer::processor2d::createProcessor2DFromOutputDevice(rRenderContext, aNewViewInfos));
972     pProcessor->process(aSeq);
973 
974     // draw items
975     for (size_t i = 0; i < nItemCount; i++)
976     {
977         ThumbnailViewItem *const pItem = mItemList[i].get();
978         if (!pItem->isVisible())
979             continue;
980         pItem->Paint(pProcessor.get(), mpItemAttrs.get());
981     }
982 }
983 
GetFocus()984 void ThumbnailView::GetFocus()
985 {
986     if (mbSelectOnFocus)
987     {
988         // Select the first item if nothing selected
989         int nSelected = -1;
990         for (size_t i = 0, n = mItemList.size(); i < n && nSelected == -1; ++i)
991         {
992             if (mItemList[i]->isSelected())
993                 nSelected = i;
994         }
995 
996         if (nSelected == -1 && !mItemList.empty())
997         {
998             ThumbnailViewItem* pFirst = nullptr;
999             if (!mFilteredItemList.empty()) {
1000                 pFirst = mFilteredItemList[0];
1001             } else {
1002                 pFirst = mItemList[0].get();
1003             }
1004 
1005             SelectItem(pFirst->mnId);
1006         }
1007     }
1008 
1009     // Tell the accessible object that we got the focus.
1010     if( mxAccessible )
1011         mxAccessible->GetFocus();
1012 
1013     CustomWidgetController::GetFocus();
1014 }
1015 
LoseFocus()1016 void ThumbnailView::LoseFocus()
1017 {
1018     CustomWidgetController::LoseFocus();
1019 
1020     // Tell the accessible object that we lost the focus.
1021     if( mxAccessible )
1022         mxAccessible->LoseFocus();
1023 }
1024 
Resize()1025 void ThumbnailView::Resize()
1026 {
1027     CustomWidgetController::Resize();
1028     CalculateItemPositions();
1029 
1030     if ( IsReallyVisible() && IsUpdateMode() )
1031         Invalidate();
1032 }
1033 
RemoveItem(sal_uInt16 nItemId)1034 void ThumbnailView::RemoveItem( sal_uInt16 nItemId )
1035 {
1036     size_t nPos = GetItemPos( nItemId );
1037 
1038     if ( nPos == THUMBNAILVIEW_ITEM_NOTFOUND )
1039         return;
1040 
1041     if ( nPos < mFilteredItemList.size() ) {
1042 
1043         // keep it alive until after we have deleted it from the filter item list
1044         std::unique_ptr<ThumbnailViewItem> xKeepAliveViewItem;
1045 
1046         // delete item from the thumbnail list
1047         for (auto it = mItemList.begin(); it != mItemList.end(); ++it)
1048         {
1049             if ((*it)->mnId == nItemId)
1050             {
1051                 xKeepAliveViewItem = std::move(*it);
1052                 mItemList.erase(it);
1053                 break;
1054             }
1055         }
1056 
1057         // delete item from the filter item list
1058         ThumbnailValueItemList::iterator it = mFilteredItemList.begin();
1059         ::std::advance( it, nPos );
1060 
1061         if ((*it)->isSelected())
1062         {
1063             (*it)->setSelection(false);
1064             maItemStateHdl.Call(*it);
1065         }
1066 
1067         mFilteredItemList.erase( it );
1068         mpStartSelRange = mFilteredItemList.end();
1069     }
1070 
1071     CalculateItemPositions();
1072 
1073     if ( IsReallyVisible() && IsUpdateMode() )
1074         Invalidate();
1075 }
1076 
Clear()1077 void ThumbnailView::Clear()
1078 {
1079     ImplDeleteItems();
1080 
1081     // reset variables
1082     mnFirstLine     = 0;
1083 
1084     CalculateItemPositions();
1085 
1086     if ( IsReallyVisible() && IsUpdateMode() )
1087         Invalidate();
1088 }
1089 
updateItems(std::vector<std::unique_ptr<ThumbnailViewItem>> items)1090 void ThumbnailView::updateItems (std::vector<std::unique_ptr<ThumbnailViewItem>> items)
1091 {
1092     ImplDeleteItems();
1093 
1094     // reset variables
1095     mnFirstLine     = 0;
1096 
1097     mItemList = std::move(items);
1098 
1099     filterItems(maFilterFunc);
1100 }
1101 
GetItemPos(sal_uInt16 nItemId) const1102 size_t ThumbnailView::GetItemPos( sal_uInt16 nItemId ) const
1103 {
1104     for ( size_t i = 0, n = mFilteredItemList.size(); i < n; ++i ) {
1105         if ( mFilteredItemList[i]->mnId == nItemId ) {
1106             return i;
1107         }
1108     }
1109     return THUMBNAILVIEW_ITEM_NOTFOUND;
1110 }
1111 
GetItemId(size_t nPos) const1112 sal_uInt16 ThumbnailView::GetItemId( size_t nPos ) const
1113 {
1114     return ( nPos < mFilteredItemList.size() ) ? mFilteredItemList[nPos]->mnId : 0 ;
1115 }
1116 
GetItemId(const Point & rPos) const1117 sal_uInt16 ThumbnailView::GetItemId( const Point& rPos ) const
1118 {
1119     size_t nItemPos = ImplGetItem( rPos );
1120     if ( nItemPos != THUMBNAILVIEW_ITEM_NOTFOUND )
1121         return GetItemId( nItemPos );
1122 
1123     return 0;
1124 }
1125 
setItemMaxTextLength(sal_uInt32 nLength)1126 void ThumbnailView::setItemMaxTextLength(sal_uInt32 nLength)
1127 {
1128     mpItemAttrs->nMaxTextLength = nLength;
1129 }
1130 
setItemDimensions(tools::Long itemWidth,tools::Long thumbnailHeight,tools::Long displayHeight,int itemPadding)1131 void ThumbnailView::setItemDimensions(tools::Long itemWidth, tools::Long thumbnailHeight, tools::Long displayHeight, int itemPadding)
1132 {
1133     mnItemWidth = itemWidth + 2*itemPadding;
1134     mnThumbnailHeight = thumbnailHeight;
1135     mnDisplayHeight = displayHeight;
1136     mnItemPadding = itemPadding;
1137     mnItemHeight = mnDisplayHeight + mnThumbnailHeight + 2*itemPadding;
1138 }
1139 
SelectItem(sal_uInt16 nItemId)1140 void ThumbnailView::SelectItem( sal_uInt16 nItemId )
1141 {
1142     size_t nItemPos = GetItemPos( nItemId );
1143     if ( nItemPos == THUMBNAILVIEW_ITEM_NOTFOUND )
1144         return;
1145 
1146     ThumbnailViewItem* pItem = mFilteredItemList[nItemPos];
1147     if (pItem->isSelected())
1148         return;
1149 
1150     pItem->setSelection(true);
1151     maItemStateHdl.Call(pItem);
1152 
1153     if (IsReallyVisible() && IsUpdateMode())
1154         Invalidate();
1155 
1156     bool bNewOut = IsReallyVisible() && IsUpdateMode();
1157 
1158     // if necessary scroll to the visible area
1159     if (mbScroll && nItemId && mnCols)
1160     {
1161         sal_uInt16 nNewLine = static_cast<sal_uInt16>(nItemPos / mnCols);
1162         if ( nNewLine < mnFirstLine )
1163         {
1164             mnFirstLine = nNewLine;
1165         }
1166         else if ( mnVisLines != 0 && nNewLine > o3tl::make_unsigned(mnFirstLine+mnVisLines-1) )
1167         {
1168             mnFirstLine = static_cast<sal_uInt16>(nNewLine-mnVisLines+1);
1169         }
1170     }
1171 
1172     if ( bNewOut )
1173     {
1174         if ( IsReallyVisible() && IsUpdateMode() )
1175             Invalidate();
1176     }
1177 
1178     if( !ImplHasAccessibleListeners() )
1179         return;
1180 
1181     // focus event (select)
1182     const rtl::Reference<ThumbnailViewItemAcc>& pItemAcc = pItem->GetAccessible();
1183 
1184     if( pItemAcc )
1185     {
1186         css::uno::Any aOldAny, aNewAny;
1187         aNewAny <<= css::uno::Reference<css::accessibility::XAccessible>( pItemAcc );
1188         ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny );
1189     }
1190 
1191     // selection event
1192     css::uno::Any aOldAny, aNewAny;
1193     ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::SELECTION_CHANGED, aOldAny, aNewAny );
1194 }
1195 
IsItemSelected(sal_uInt16 nItemId) const1196 bool ThumbnailView::IsItemSelected( sal_uInt16 nItemId ) const
1197 {
1198     size_t nItemPos = GetItemPos( nItemId );
1199     if ( nItemPos == THUMBNAILVIEW_ITEM_NOTFOUND )
1200         return false;
1201 
1202     ThumbnailViewItem* pItem = mFilteredItemList[nItemPos];
1203     return pItem->isSelected();
1204 }
1205 
deselectItems()1206 void ThumbnailView::deselectItems()
1207 {
1208     for (std::unique_ptr<ThumbnailViewItem>& p : mItemList)
1209     {
1210         if (p->isSelected())
1211         {
1212             p->setSelection(false);
1213 
1214             maItemStateHdl.Call(p.get());
1215         }
1216     }
1217 
1218     if (IsReallyVisible() && IsUpdateMode())
1219         Invalidate();
1220 }
1221 
ShowTooltips(bool bShowTooltips)1222 void ThumbnailView::ShowTooltips( bool bShowTooltips )
1223 {
1224     mbShowTooltips = bShowTooltips;
1225 }
1226 
DrawMnemonics(bool bDrawMnemonics)1227 void ThumbnailView::DrawMnemonics( bool bDrawMnemonics )
1228 {
1229     mbDrawMnemonics = bDrawMnemonics;
1230 }
1231 
filterItems(const std::function<bool (const ThumbnailViewItem *)> & func)1232 void ThumbnailView::filterItems(const std::function<bool (const ThumbnailViewItem*)> &func)
1233 {
1234     mnFirstLine = 0;        // start at the top of the list instead of the current position
1235     maFilterFunc = func;
1236 
1237     size_t nSelPos = 0;
1238     bool bHasSelRange = false;
1239     ThumbnailViewItem *curSel = mpStartSelRange != mFilteredItemList.end() ? *mpStartSelRange : nullptr;
1240 
1241     mFilteredItemList.clear();
1242 
1243     for (size_t i = 0, n = mItemList.size(); i < n; ++i)
1244     {
1245         ThumbnailViewItem *const pItem = mItemList[i].get();
1246 
1247         if (maFilterFunc(pItem))
1248         {
1249             if (curSel == pItem)
1250             {
1251                 nSelPos = i;
1252                 bHasSelRange = true;
1253             }
1254 
1255             mFilteredItemList.push_back(pItem);
1256         }
1257         else
1258         {
1259             if( pItem->isVisible())
1260             {
1261                 if ( ImplHasAccessibleListeners() )
1262                 {
1263                     css::uno::Any aOldAny, aNewAny;
1264 
1265                     aOldAny <<= css::uno::Reference<css::accessibility::XAccessible>(pItem->GetAccessible());
1266                     ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
1267                 }
1268 
1269                 pItem->show(false);
1270                 pItem->setSelection(false);
1271 
1272                 maItemStateHdl.Call(pItem);
1273             }
1274         }
1275     }
1276 
1277     mpStartSelRange = bHasSelRange ? mFilteredItemList.begin()  + nSelPos : mFilteredItemList.end();
1278     CalculateItemPositions();
1279 
1280     Invalidate();
1281 }
1282 
1283 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1284