Coverage Report

Created: 2026-02-14 09:37

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