LibreOffice Module sfx2 (master) 1
thumbnailview.cxx
Go to the documentation of this file.
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
12
13#include <utility>
14
15#include "thumbnailviewacc.hxx"
16
25#include <o3tl/safeint.hxx>
26#include <rtl/ustring.hxx>
27#include <sal/log.hxx>
31#include <vcl/svapp.hxx>
32#include <vcl/settings.hxx>
33#include <vcl/event.hxx>
35#include <vcl/weldutils.hxx>
36
37#include <com/sun/star/accessibility/AccessibleEventId.hpp>
38#include <com/sun/star/embed/ElementModes.hpp>
39#include <com/sun/star/embed/StorageFactory.hpp>
40#include <com/sun/star/embed/XStorage.hpp>
41
42#include <memory>
43
44using namespace basegfx;
45using namespace basegfx::utils;
46using namespace drawinglayer::attribute;
47using namespace drawinglayer::primitive2d;
48
49constexpr int gnFineness = 5;
50
52{
53 // Do nothing by default
54 return false;
55}
56
58{
59 using namespace ::com::sun::star;
60 using namespace ::com::sun::star::uno;
61
62 // Load the thumbnail from a template document.
63 uno::Reference<io::XInputStream> xIStream;
64
65 uno::Reference< uno::XComponentContext > xContext(::comphelper::getProcessComponentContext());
66 try
67 {
68 uno::Reference<lang::XSingleServiceFactory> xStorageFactory = embed::StorageFactory::create(xContext);
69
70 uno::Sequence<uno::Any> aArgs{ uno::Any(msURL), uno::Any(embed::ElementModes::READ) };
71 uno::Reference<embed::XStorage> xDocStorage (
72 xStorageFactory->createInstanceWithArguments(aArgs),
73 uno::UNO_QUERY);
74
75 try
76 {
77 if (xDocStorage.is())
78 {
79 uno::Reference<embed::XStorage> xStorage (
80 xDocStorage->openStorageElement(
81 "Thumbnails",
82 embed::ElementModes::READ));
83 if (xStorage.is())
84 {
85 uno::Reference<io::XStream> xThumbnailCopy (
86 xStorage->cloneStreamElement("thumbnail.png"));
87 if (xThumbnailCopy.is())
88 xIStream = xThumbnailCopy->getInputStream();
89 }
90 }
91 }
92 catch (const uno::Exception&)
93 {
95 "caught exception while trying to access Thumbnail/thumbnail.png of " << msURL);
96 }
97
98 try
99 {
100 // An (older) implementation had a bug - The storage
101 // name was "Thumbnail" instead of "Thumbnails". The
102 // old name is still used as fallback but this code can
103 // be removed soon.
104 if ( ! xIStream.is())
105 {
106 uno::Reference<embed::XStorage> xStorage (
107 xDocStorage->openStorageElement( "Thumbnail",
108 embed::ElementModes::READ));
109 if (xStorage.is())
110 {
111 uno::Reference<io::XStream> xThumbnailCopy (
112 xStorage->cloneStreamElement("thumbnail.png"));
113 if (xThumbnailCopy.is())
114 xIStream = xThumbnailCopy->getInputStream();
115 }
116 }
117 }
118 catch (const uno::Exception&)
119 {
121 "caught exception while trying to access Thumbnails/thumbnail.png of " << msURL);
122 }
123 }
124 catch (const uno::Exception&)
125 {
127 "caught exception while trying to access thumbnail of "
128 << msURL);
129 }
130
131 // Extract the image from the stream.
132 BitmapEx aThumbnail;
133 if (xIStream.is())
134 {
135 std::unique_ptr<SvStream> pStream (
137 vcl::PngImageReader aReader (*pStream);
138 aThumbnail = aReader.read ();
139 }
140
141 // Note that the preview is returned without scaling it to the desired
142 // width. This gives the caller the chance to take advantage of a
143 // possibly larger resolution then was asked for.
144 return aThumbnail;
145}
146
147ThumbnailView::ThumbnailView(std::unique_ptr<weld::ScrolledWindow> xWindow, std::unique_ptr<weld::Menu> xMenu)
148 : mnThumbnailHeight(0)
149 , mnDisplayHeight(0)
150 , mnVItemSpace(-1)
151 , mbAllowVScrollBar(xWindow->get_vpolicy() != VclPolicyType::NEVER)
152 , mbSelectOnFocus(true)
153 , mpItemAttrs(new ThumbnailItemAttributes)
154 , mxScrolledWindow(std::move(xWindow))
155 , mxContextMenu(std::move(xMenu))
156{
157 ImplInit();
158 mxScrolledWindow->connect_vadjustment_changed(LINK(this, ThumbnailView, ImplScrollHdl));
159}
160
162{
163 css::uno::Reference< css::lang::XComponent> xComponent(mxAccessible, css::uno::UNO_QUERY);
164
165 if (xComponent.is())
166 xComponent->dispose();
167
168 mpItemAttrs.reset();
169
171}
172
174{
175 size_t nItemCount = mFilteredItemList.size();
176 Point aPoint = rMEvt.GetPosPixel();
177
178 for (size_t i = 0; i < nItemCount; i++)
179 {
181 ::tools::Rectangle aToInvalidate(pItem->updateHighlight(pItem->mbVisible && !rMEvt.IsLeaveWindow(), aPoint));
182 if (!aToInvalidate.IsEmpty() && IsReallyVisible() && IsUpdateMode())
183 Invalidate(aToInvalidate);
184 }
185
186 return true;
187}
188
190{
191 if (!mbShowTooltips)
192 return OUString();
193
194 Point aPos = rHelpRect.TopLeft();
195 size_t nItemCount = mFilteredItemList.size();
196 for (size_t i = 0; i < nItemCount; i++)
197 {
199 if (!pItem->mbVisible)
200 continue;
201 const tools::Rectangle& rDrawArea = pItem->getDrawArea();
202 if (pItem->mbVisible && rDrawArea.Contains(aPos))
203 {
204 rHelpRect = rDrawArea;
205 return pItem->getHelpText();
206 }
207 }
208
209 return OUString();
210}
211
212void ThumbnailView::AppendItem(std::unique_ptr<ThumbnailViewItem> pItem)
213{
214 if (maFilterFunc(pItem.get()))
215 {
216 // Save current start,end range, iterator might get invalidated
217 size_t nSelStartPos = 0;
218 ThumbnailViewItem *pSelStartItem = nullptr;
219
221 {
222 pSelStartItem = *mpStartSelRange;
223 nSelStartPos = mpStartSelRange - mFilteredItemList.begin();
224 }
225
226 mFilteredItemList.push_back(pItem.get());
227 mpStartSelRange = pSelStartItem != nullptr ? mFilteredItemList.begin() + nSelStartPos : mFilteredItemList.end();
228 }
229
230 mItemList.push_back(std::move(pItem));
231}
232
234{
235 mnItemWidth = 0;
236 mnItemHeight = 0;
237 mnItemPadding = 0;
238 mnVisLines = 0;
239 mnLines = 0;
240 mnFirstLine = 0;
241 mnCols = 0;
242 mbScroll = false;
243 mbHasVisibleItems = false;
244 mbShowTooltips = false;
245 mbDrawMnemonics = false;
247
249 maFillColor = rSettings.GetFieldColor();
250 maTextColor = rSettings.GetWindowTextColor();
255
257
259
260 UpdateColors();
261
262 mpItemAttrs->nMaxTextLength = 0;
263}
264
266{
267 mpItemAttrs->aFillColor = maFillColor.getBColor();
268 mpItemAttrs->aTextColor = maTextColor.getBColor();
269 mpItemAttrs->aHighlightColor = maHighlightColor.getBColor();
270 mpItemAttrs->aHighlightTextColor = maHighlightTextColor.getBColor();
271 mpItemAttrs->aSelectHighlightColor = maSelectHighlightColor.getBColor();
272 mpItemAttrs->aSelectHighlightTextColor = maSelectHighlightTextColor.getBColor();
273 mpItemAttrs->fHighlightTransparence = mfHighlightTransparence;
274}
275
277{
278 const size_t n = mItemList.size();
279
280 for ( size_t i = 0; i < n; ++i )
281 {
282 ThumbnailViewItem *const pItem = mItemList[i].get();
283
284 // deselect all current selected items and fire events
285 if (pItem->isSelected())
286 {
287 pItem->setSelection(false);
288 maItemStateHdl.Call(pItem);
289
290 // fire accessible event???
291 }
292
293 if ( pItem->isVisible() && ImplHasAccessibleListeners() )
294 {
295 css::uno::Any aOldAny, aNewAny;
296
297 aOldAny <<= pItem->GetAccessible( false );
298 ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
299 }
300
301 mItemList[i].reset();
302 }
303
304 mItemList.clear();
305 mFilteredItemList.clear();
306
308}
309
311{
312 if (pItem->isVisible())
313 {
314 ::tools::Rectangle aRect = pItem->getDrawArea();
315
316 if (!aRect.IsEmpty())
317 Invalidate(aRect);
318 }
319}
320
322{
323}
324
325css::uno::Reference< css::accessibility::XAccessible > ThumbnailView::CreateAccessible()
326{
327 mxAccessible.set(new ThumbnailViewAcc(this));
328 return mxAccessible;
329}
330
331const css::uno::Reference< css::accessibility::XAccessible > & ThumbnailView::getAccessible() const
332{
333 return mxAccessible;
334}
335
337{
338 if (!mnItemHeight || !mnItemWidth)
339 return;
340
341 Size aWinSize = GetOutputSizePixel();
342 size_t nItemCount = mFilteredItemList.size();
343
344 // calculate window scroll ratio
345 float nScrollRatio;
346 if (bScrollBarUsed)
347 {
348 nScrollRatio = static_cast<float>(mxScrolledWindow->vadjustment_get_value()) /
349 static_cast<float>(mxScrolledWindow->vadjustment_get_upper() -
350 mxScrolledWindow->vadjustment_get_page_size());
351 }
352 else
353 nScrollRatio = 0;
354
355 // calculate ScrollBar width
356 tools::Long nScrBarWidth = mbAllowVScrollBar ? mxScrolledWindow->get_scroll_thickness() : 0;
357
358 // calculate maximum number of visible columns
359 mnCols = static_cast<sal_uInt16>((aWinSize.Width()-nScrBarWidth) / mnItemWidth);
360
361 if (!mnCols)
362 mnCols = 1;
363
364 // calculate maximum number of visible rows
365 mnVisLines = static_cast<sal_uInt16>(aWinSize.Height() / mnItemHeight);
366
367 // calculate empty space
368 tools::Long nHSpace = aWinSize.Width()-nScrBarWidth - mnCols*mnItemWidth;
369 tools::Long nVSpace = aWinSize.Height() - mnVisLines*mnItemHeight;
370 tools::Long nHItemSpace = nHSpace / (mnCols+1);
371 tools::Long nVItemSpace = mnVItemSpace;
372 if (nVItemSpace == -1) // auto, split up extra space to use as vertical spacing
373 nVItemSpace = nVSpace / (mnVisLines+1);
374
375 // calculate maximum number of rows
376 // Floor( (M+N-1)/N )==Ceiling( M/N )
377 mnLines = (static_cast<tools::Long>(nItemCount)+mnCols-1) / mnCols;
378
379 if ( !mnLines )
380 mnLines = 1;
381
382 if ( mnLines <= mnVisLines )
383 mnFirstLine = 0;
385 mnFirstLine = static_cast<sal_uInt16>(mnLines-mnVisLines);
386
387 mbHasVisibleItems = true;
388
389 tools::Long nFullSteps = (mnLines > mnVisLines) ? mnLines - mnVisLines + 1 : 1;
390
391 tools::Long nItemHeightOffset = mnItemHeight + nVItemSpace;
392 tools::Long nHiddenLines = static_cast<tools::Long>((nFullSteps - 1) * nScrollRatio);
393
394 // calculate offsets
395 tools::Long nStartX = nHItemSpace;
396 tools::Long nStartY = nVItemSpace;
397
398 // calculate and draw items
399 tools::Long x = nStartX;
400 tools::Long y = nStartY - ((nFullSteps - 1) * nScrollRatio - nHiddenLines) * nItemHeightOffset;
401
402 // draw items
403 // Unless we are scrolling (via scrollbar) we just use the precalculated
404 // mnFirstLine -- our nHiddenLines calculation takes into account only
405 // what the user has done with the scrollbar but not any changes of selection
406 // using the keyboard, meaning we could accidentally hide the selected item
407 // if we believe the scrollbar (fdo#72287).
408 size_t nFirstItem = (bScrollBarUsed ? nHiddenLines : mnFirstLine) * mnCols;
409 size_t nLastItem = nFirstItem + (mnVisLines + 1) * mnCols;
410
411 // If want also draw parts of items in the last line,
412 // then we add one more line if parts of this line are visible
413
414 bool bPinnedItems = true;
415 size_t nCurCount = 0;
416 for ( size_t i = 0; i < nItemCount; i++ )
417 {
418 ThumbnailViewItem *const pItem = mFilteredItemList[i];
419
420 // tdf#38742 - show pinned items in a separate line
421 if (bPinnedItems && !pItem->isPinned())
422 {
423 bPinnedItems = false;
424 // Start a new line only if the entire line is not filled
425 if ((nCurCount + 1) % mnCols && nCurCount > nFirstItem)
426 {
427 x = nStartX;
428 y += mnItemHeight + nVItemSpace;
429 }
430 nCurCount = 0;
431 }
432
433 if ((nCurCount >= nFirstItem) && (nCurCount < nLastItem))
434 {
435 if( !pItem->isVisible())
436 {
438 {
439 css::uno::Any aOldAny, aNewAny;
440
441 aNewAny <<= pItem->GetAccessible( false );
442 ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
443 }
444
445 pItem->show(true);
446
447 maItemStateHdl.Call(pItem);
448 }
449
452
453 if ( !((nCurCount+1) % mnCols) )
454 {
455 x = nStartX;
456 y += mnItemHeight+nVItemSpace;
457 }
458 else
459 x += mnItemWidth+nHItemSpace;
460 }
461 else
462 {
463 if( pItem->isVisible())
464 {
466 {
467 css::uno::Any aOldAny, aNewAny;
468
469 aOldAny <<= pItem->GetAccessible( false );
470 ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
471 }
472
473 pItem->show(false);
474
475 maItemStateHdl.Call(pItem);
476 }
477
478 }
479
480 ++nCurCount;
481 }
482
483 // arrange ScrollBar, set values and show it
484 mnLines = (nCurCount+mnCols-1)/mnCols;
485
486 // check if scroll is needed
488
489 mxScrolledWindow->vadjustment_set_upper(mnLines * gnFineness);
490 mxScrolledWindow->vadjustment_set_page_size(mnVisLines * gnFineness);
491 if (!bScrollBarUsed)
492 mxScrolledWindow->vadjustment_set_value(static_cast<tools::Long>(mnFirstLine)*gnFineness);
493 tools::Long nPageSize = mnVisLines;
494 if ( nPageSize < 1 )
495 nPageSize = 1;
496 mxScrolledWindow->vadjustment_set_page_increment(nPageSize*gnFineness);
498 mxScrolledWindow->set_vpolicy(mbScroll ? VclPolicyType::ALWAYS : VclPolicyType::NEVER);
499}
500
501size_t ThumbnailView::ImplGetItem( const Point& rPos ) const
502{
503 if ( !mbHasVisibleItems )
504 {
506 }
507
508 for (size_t i = 0; i < mFilteredItemList.size(); ++i)
509 {
510 if (mFilteredItemList[i]->isVisible() && mFilteredItemList[i]->getDrawArea().Contains(rPos))
511 return i;
512 }
513
515}
516
518{
519 return ( nPos < mFilteredItemList.size() ) ? mFilteredItemList[nPos] : nullptr;
520}
521
523{
524 sal_uInt16 nRet = 0;
525 const size_t nItemCount = mItemList.size();
526
527 for ( size_t n = 0; n < nItemCount; ++n )
528 {
529 if ( mItemList[n]->isVisible() )
530 ++nRet;
531 }
532
533 return nRet;
534}
535
537{
538 const size_t nItemCount = mItemList.size();
539
540 for ( size_t n = 0; n < nItemCount; ++n )
541 {
542 ThumbnailViewItem *const pItem = mItemList[n].get();
543
544 if ( pItem->isVisible() && !nVisiblePos-- )
545 return pItem;
546 }
547
548 return nullptr;
549}
550
551void ThumbnailView::ImplFireAccessibleEvent( short nEventId, const css::uno::Any& rOldValue, const css::uno::Any& rNewValue )
552{
554
555 if( pAcc )
556 pAcc->FireAccessibleEvent( nEventId, rOldValue, rNewValue );
557}
558
560{
562 return( pAcc && pAcc->HasAccessibleListeners() );
563}
564
566{
567 CalculateItemPositions(true);
568 if (IsReallyVisible() && IsUpdateMode())
569 Invalidate();
570}
571
573{
574 bool bHandled = true;
575
576 // Get the last selected item in the list
577 size_t nLastPos = 0;
578 bool bFoundLast = false;
579 for ( tools::Long i = mFilteredItemList.size() - 1; !bFoundLast && i >= 0; --i )
580 {
582 if ( pItem->isSelected() )
583 {
584 nLastPos = i;
585 bFoundLast = true;
586 }
587 }
588
589 bool bValidRange = false;
590 bool bHasSelRange = mpStartSelRange != mFilteredItemList.end();
591 size_t nNextPos = nLastPos;
592 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
593 ThumbnailViewItem* pNext = nullptr;
594
595 if (aKeyCode.IsShift() && bHasSelRange)
596 {
597 //If the last element selected is the start range position
598 //search for the first selected item
599 size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
600
601 if (nLastPos == nSelPos)
602 {
603 while (nLastPos && mFilteredItemList[nLastPos-1]->isSelected())
604 --nLastPos;
605 }
606 }
607
608 switch ( aKeyCode.GetCode() )
609 {
610 case KEY_RIGHT:
611 if (!mFilteredItemList.empty())
612 {
613 if ( bFoundLast && nLastPos + 1 < mFilteredItemList.size() )
614 {
615 bValidRange = true;
616 nNextPos = nLastPos + 1;
617 }
618
619 pNext = mFilteredItemList[nNextPos];
620 }
621 break;
622 case KEY_LEFT:
623 if (!mFilteredItemList.empty())
624 {
625 if ( nLastPos > 0 )
626 {
627 bValidRange = true;
628 nNextPos = nLastPos - 1;
629 }
630
631 pNext = mFilteredItemList[nNextPos];
632 }
633 break;
634 case KEY_DOWN:
635 if (!mFilteredItemList.empty())
636 {
637 if ( bFoundLast )
638 {
639 //If we are in the second last row just go the one in
640 //the row below, if there's not row below just go to the
641 //last item but for the last row don't do anything.
642 if ( nLastPos + mnCols < mFilteredItemList.size( ) )
643 {
644 bValidRange = true;
645 nNextPos = nLastPos + mnCols;
646 }
647 else
648 {
649 int curRow = nLastPos/mnCols;
650
651 if (curRow < mnLines-1)
652 nNextPos = mFilteredItemList.size()-1;
653 }
654 }
655
656 pNext = mFilteredItemList[nNextPos];
657 }
658 break;
659 case KEY_UP:
660 if (!mFilteredItemList.empty())
661 {
662 if ( nLastPos >= mnCols )
663 {
664 bValidRange = true;
665 nNextPos = nLastPos - mnCols;
666 }
667
668 pNext = mFilteredItemList[nNextPos];
669 }
670 break;
671 case KEY_RETURN:
672 {
673 if ( bFoundLast )
675 }
676 [[fallthrough]];
677 default:
678 bHandled = CustomWidgetController::KeyInput(rKEvt);
679 }
680
681 if ( pNext )
682 {
683 if (aKeyCode.IsShift() && bValidRange)
684 {
685 std::pair<size_t,size_t> aRange;
686 size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
687
688 if (nLastPos < nSelPos)
689 {
690 if (nNextPos > nLastPos)
691 {
692 if ( nNextPos > nSelPos)
693 aRange = std::make_pair(nLastPos,nNextPos);
694 else
695 aRange = std::make_pair(nLastPos,nNextPos-1);
696 }
697 else
698 aRange = std::make_pair(nNextPos,nLastPos-1);
699 }
700 else if (nLastPos == nSelPos)
701 {
702 if (nNextPos > nLastPos)
703 aRange = std::make_pair(nLastPos+1,nNextPos);
704 else
705 aRange = std::make_pair(nNextPos,nLastPos-1);
706 }
707 else
708 {
709 if (nNextPos > nLastPos)
710 aRange = std::make_pair(nLastPos+1,nNextPos);
711 else
712 {
713 if ( nNextPos < nSelPos)
714 aRange = std::make_pair(nNextPos,nLastPos);
715 else
716 aRange = std::make_pair(nNextPos+1,nLastPos);
717 }
718 }
719
720 for (size_t i = aRange.first; i <= aRange.second; ++i)
721 {
722 if (i != nSelPos)
723 {
725
726 pCurItem->setSelection(!pCurItem->isSelected());
727
728 DrawItem(pCurItem);
729
730 maItemStateHdl.Call(pCurItem);
731 }
732 }
733 }
734 else if (!aKeyCode.IsShift())
735 {
737 SelectItem(pNext->mnId);
738
739 //Mark it as the selection range start position
740 mpStartSelRange = mFilteredItemList.begin() + nNextPos;
741 }
742
743 MakeItemVisible(pNext->mnId);
744 }
745 return bHandled;
746}
747
748void ThumbnailView::MakeItemVisible( sal_uInt16 nItemId )
749{
750 // Get the item row
751 size_t nPos = 0;
752 bool bFound = false;
753 for ( size_t i = 0; !bFound && i < mFilteredItemList.size(); ++i )
754 {
756 if ( pItem->mnId == nItemId )
757 {
758 nPos = i;
759 bFound = true;
760 }
761 }
762 sal_uInt16 nRow = mnCols ? nPos / mnCols : 0;
763
764 // Move the visible rows as little as possible to include that one
765 if ( nRow < mnFirstLine )
766 mnFirstLine = nRow;
767 else if ( nRow > mnFirstLine + mnVisLines )
768 mnFirstLine = nRow - mnVisLines;
769
771 Invalidate();
772}
773
775{
776 GrabFocus();
777
778 if (!rMEvt.IsLeft())
779 {
780 return CustomWidgetController::MouseButtonDown( rMEvt );
781 }
782
783 size_t nPos = ImplGetItem(rMEvt.GetPosPixel());
785
786 if ( !pItem )
787 {
789 return CustomWidgetController::MouseButtonDown( rMEvt );
790 }
791
792 if ( rMEvt.GetClicks() == 2 )
793 {
794 OnItemDblClicked(pItem);
795 return true;
796 }
797
798 if(rMEvt.GetClicks() == 1)
799 {
800 if (rMEvt.IsMod1())
801 {
802 //Keep selected item group state and just invert current desired one state
803 pItem->setSelection(!pItem->isSelected());
804
805 //This one becomes the selection range start position if it changes its state to selected otherwise resets it
807 }
808 else if (rMEvt.IsShift() && mpStartSelRange != mFilteredItemList.end())
809 {
810 std::pair<size_t,size_t> aNewRange;
811 aNewRange.first = mpStartSelRange - mFilteredItemList.begin();
812 aNewRange.second = nPos;
813
814 if (aNewRange.first > aNewRange.second)
815 std::swap(aNewRange.first,aNewRange.second);
816
817 //Deselect the ones outside of it
818 for (size_t i = 0, n = mFilteredItemList.size(); i < n; ++i)
819 {
821
822 if (pCurItem->isSelected() && (i < aNewRange.first || i > aNewRange.second))
823 {
824 pCurItem->setSelection(false);
825
826 DrawItem(pCurItem);
827
828 maItemStateHdl.Call(pCurItem);
829 }
830 }
831
832 size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
833
834 //Select the items between start range and the selected item
835 if (nSelPos != nPos)
836 {
837 int dir = nSelPos < nPos ? 1 : -1;
838 size_t nCurPos = nSelPos + dir;
839
840 while (nCurPos != nPos)
841 {
842 ThumbnailViewItem *pCurItem = mFilteredItemList[nCurPos];
843
844 if (!pCurItem->isSelected())
845 {
846 pCurItem->setSelection(true);
847
848 DrawItem(pCurItem);
849
850 maItemStateHdl.Call(pCurItem);
851 }
852
853 nCurPos += dir;
854 }
855 }
856
857 pItem->setSelection(true);
858 }
859 else
860 {
861 //If we got a group of selected items deselect the rest and only keep the desired one
862 //mark items as not selected to not fire unnecessary change state events.
863 pItem->setSelection(false);
865 pItem->setSelection(true);
866
867 //Mark as initial selection range position and reset end one
869 }
870
871 if (!pItem->isHighlighted())
872 DrawItem(pItem);
873
874 maItemStateHdl.Call(pItem);
875
876 //fire accessible event??
877 }
878 return true;
879}
880
882{
883 CustomWidgetController::SetDrawingArea(pDrawingArea);
884
885 OutputDevice& rDevice = pDrawingArea->get_ref_device();
886 weld::SetPointFont(rDevice, pDrawingArea->get_font());
887 mpItemAttrs->aFontAttr = getFontAttributeFromVclFont(mpItemAttrs->aFontSize, rDevice.GetFont(), false, true);
888
889 SetOutputSizePixel(pDrawingArea->get_preferred_size());
890}
891
892void ThumbnailView::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& /*rRect*/)
893{
894 rRenderContext.Push(vcl::PushFlags::ALL);
895
896 rRenderContext.SetTextFillColor();
897 rRenderContext.SetBackground(maFillColor);
898
899 size_t nItemCount = mItemList.size();
900
901 // Draw background
907
908 // Create the processor and process the primitives
910
911 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(
913 pProcessor->process(aSeq);
914
915 // draw items
916 for (size_t i = 0; i < nItemCount; i++)
917 {
918 ThumbnailViewItem *const pItem = mItemList[i].get();
919 if (!pItem->isVisible())
920 continue;
921 pItem->Paint(pProcessor.get(), mpItemAttrs.get());
922 }
923
924 rRenderContext.Pop();
925}
926
928{
929 if (mbSelectOnFocus)
930 {
931 // Select the first item if nothing selected
932 int nSelected = -1;
933 for (size_t i = 0, n = mItemList.size(); i < n && nSelected == -1; ++i)
934 {
935 if (mItemList[i]->isSelected())
936 nSelected = i;
937 }
938
939 if (nSelected == -1 && !mItemList.empty())
940 {
941 ThumbnailViewItem* pFirst = nullptr;
942 if (!mFilteredItemList.empty()) {
943 pFirst = mFilteredItemList[0];
944 } else {
945 pFirst = mItemList[0].get();
946 }
947
948 SelectItem(pFirst->mnId);
949 }
950 }
951
952 // Tell the accessible object that we got the focus.
954 if( pAcc )
955 pAcc->GetFocus();
956
957 CustomWidgetController::GetFocus();
958}
959
961{
962 CustomWidgetController::LoseFocus();
963
964 // Tell the accessible object that we lost the focus.
966 if( pAcc )
967 pAcc->LoseFocus();
968}
969
971{
972 CustomWidgetController::Resize();
974
975 if ( IsReallyVisible() && IsUpdateMode() )
976 Invalidate();
977}
978
979void ThumbnailView::RemoveItem( sal_uInt16 nItemId )
980{
981 size_t nPos = GetItemPos( nItemId );
982
984 return;
985
986 if ( nPos < mFilteredItemList.size() ) {
987
988 // keep it alive until after we have deleted it from the filter item list
989 std::unique_ptr<ThumbnailViewItem> xKeepAliveViewItem;
990
991 // delete item from the thumbnail list
992 for (auto it = mItemList.begin(); it != mItemList.end(); ++it)
993 {
994 if ((*it)->mnId == nItemId)
995 {
996 xKeepAliveViewItem = std::move(*it);
997 mItemList.erase(it);
998 break;
999 }
1000 }
1001
1002 // delete item from the filter item list
1003 ThumbnailValueItemList::iterator it = mFilteredItemList.begin();
1004 ::std::advance( it, nPos );
1005
1006 if ((*it)->isSelected())
1007 {
1008 (*it)->setSelection(false);
1009 maItemStateHdl.Call(*it);
1010 }
1011
1012 mFilteredItemList.erase( it );
1014 }
1015
1017
1018 if ( IsReallyVisible() && IsUpdateMode() )
1019 Invalidate();
1020}
1021
1023{
1025
1026 // reset variables
1027 mnFirstLine = 0;
1028
1030
1031 if ( IsReallyVisible() && IsUpdateMode() )
1032 Invalidate();
1033}
1034
1035void ThumbnailView::updateItems (std::vector<std::unique_ptr<ThumbnailViewItem>> items)
1036{
1038
1039 // reset variables
1040 mnFirstLine = 0;
1041
1042 mItemList = std::move(items);
1043
1045}
1046
1047size_t ThumbnailView::GetItemPos( sal_uInt16 nItemId ) const
1048{
1049 for ( size_t i = 0, n = mFilteredItemList.size(); i < n; ++i ) {
1050 if ( mFilteredItemList[i]->mnId == nItemId ) {
1051 return i;
1052 }
1053 }
1055}
1056
1057sal_uInt16 ThumbnailView::GetItemId( size_t nPos ) const
1058{
1059 return ( nPos < mFilteredItemList.size() ) ? mFilteredItemList[nPos]->mnId : 0 ;
1060}
1061
1062sal_uInt16 ThumbnailView::GetItemId( const Point& rPos ) const
1063{
1064 size_t nItemPos = ImplGetItem( rPos );
1065 if ( nItemPos != THUMBNAILVIEW_ITEM_NOTFOUND )
1066 return GetItemId( nItemPos );
1067
1068 return 0;
1069}
1070
1072{
1073 mpItemAttrs->nMaxTextLength = nLength;
1074}
1075
1076void ThumbnailView::setItemDimensions(tools::Long itemWidth, tools::Long thumbnailHeight, tools::Long displayHeight, int itemPadding)
1077{
1078 mnItemWidth = itemWidth + 2*itemPadding;
1079 mnThumbnailHeight = thumbnailHeight;
1080 mnDisplayHeight = displayHeight;
1081 mnItemPadding = itemPadding;
1083}
1084
1085void ThumbnailView::SelectItem( sal_uInt16 nItemId )
1086{
1087 size_t nItemPos = GetItemPos( nItemId );
1088 if ( nItemPos == THUMBNAILVIEW_ITEM_NOTFOUND )
1089 return;
1090
1091 ThumbnailViewItem* pItem = mFilteredItemList[nItemPos];
1092 if (pItem->isSelected())
1093 return;
1094
1095 pItem->setSelection(true);
1096 maItemStateHdl.Call(pItem);
1097
1098 if (IsReallyVisible() && IsUpdateMode())
1099 Invalidate();
1100
1101 bool bNewOut = IsReallyVisible() && IsUpdateMode();
1102
1103 // if necessary scroll to the visible area
1104 if (mbScroll && nItemId && mnCols)
1105 {
1106 sal_uInt16 nNewLine = static_cast<sal_uInt16>(nItemPos / mnCols);
1107 if ( nNewLine < mnFirstLine )
1108 {
1109 mnFirstLine = nNewLine;
1110 }
1111 else if ( mnVisLines != 0 && nNewLine > o3tl::make_unsigned(mnFirstLine+mnVisLines-1) )
1112 {
1113 mnFirstLine = static_cast<sal_uInt16>(nNewLine-mnVisLines+1);
1114 }
1115 }
1116
1117 if ( bNewOut )
1118 {
1119 if ( IsReallyVisible() && IsUpdateMode() )
1120 Invalidate();
1121 }
1122
1124 return;
1125
1126 // focus event (select)
1128
1129 if( pItemAcc )
1130 {
1131 css::uno::Any aOldAny, aNewAny;
1132 aNewAny <<= css::uno::Reference(getXWeak( pItemAcc ));
1133 ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny );
1134 }
1135
1136 // selection event
1137 css::uno::Any aOldAny, aNewAny;
1138 ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::SELECTION_CHANGED, aOldAny, aNewAny );
1139}
1140
1141bool ThumbnailView::IsItemSelected( sal_uInt16 nItemId ) const
1142{
1143 size_t nItemPos = GetItemPos( nItemId );
1144 if ( nItemPos == THUMBNAILVIEW_ITEM_NOTFOUND )
1145 return false;
1146
1147 ThumbnailViewItem* pItem = mFilteredItemList[nItemPos];
1148 return pItem->isSelected();
1149}
1150
1152{
1153 for (std::unique_ptr<ThumbnailViewItem>& p : mItemList)
1154 {
1155 if (p->isSelected())
1156 {
1157 p->setSelection(false);
1158
1159 maItemStateHdl.Call(p.get());
1160 }
1161 }
1162
1163 if (IsReallyVisible() && IsUpdateMode())
1164 Invalidate();
1165}
1166
1167void ThumbnailView::ShowTooltips( bool bShowTooltips )
1168{
1169 mbShowTooltips = bShowTooltips;
1170}
1171
1172void ThumbnailView::DrawMnemonics( bool bDrawMnemonics )
1173{
1174 mbDrawMnemonics = bDrawMnemonics;
1175}
1176
1177void ThumbnailView::filterItems(const std::function<bool (const ThumbnailViewItem*)> &func)
1178{
1179 mnFirstLine = 0; // start at the top of the list instead of the current position
1180 maFilterFunc = func;
1181
1182 size_t nSelPos = 0;
1183 bool bHasSelRange = false;
1185
1186 mFilteredItemList.clear();
1187
1188 for (size_t i = 0, n = mItemList.size(); i < n; ++i)
1189 {
1190 ThumbnailViewItem *const pItem = mItemList[i].get();
1191
1192 if (maFilterFunc(pItem))
1193 {
1194 if (curSel == pItem)
1195 {
1196 nSelPos = i;
1197 bHasSelRange = true;
1198 }
1199
1200 mFilteredItemList.push_back(pItem);
1201 }
1202 else
1203 {
1204 if( pItem->isVisible())
1205 {
1207 {
1208 css::uno::Any aOldAny, aNewAny;
1209
1210 aOldAny <<= pItem->GetAccessible( false );
1211 ImplFireAccessibleEvent( css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
1212 }
1213
1214 pItem->show(false);
1215 pItem->setSelection(false);
1216
1217 maItemStateHdl.Call(pItem);
1218 }
1219 }
1220 }
1221
1222 mpStartSelRange = bHasSelRange ? mFilteredItemList.begin() + nSelPos : mFilteredItemList.end();
1224
1225 Invalidate();
1226}
1227
1228/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const StyleSettings & GetStyleSettings() const
static const AllSettings & GetSettings()
basegfx::BColor getBColor() const
const vcl::KeyCode & GetKeyCode() const
bool IsMod1() const
bool IsLeaveWindow() const
sal_uInt16 GetClicks() const
const Point & GetPosPixel() const
bool IsLeft() const
bool IsShift() const
const vcl::Font & GetFont() const
void SetTextFillColor()
void Push(vcl::PushFlags nFlags=vcl::PushFlags::ALL)
void SetBackground()
constexpr tools::Long Height() const
constexpr tools::Long Width() const
const Color & GetFieldColor() const
const Color & GetWindowTextColor() const
const Color & GetActiveColor() const
const Color & GetHighlightColor() const
const Color & GetHighlightTextColor() const
const Color & GetActiveTextColor() const
void FireAccessibleEvent(short nEventId, const css::uno::Any &rOldValue, const css::uno::Any &rNewValue)
bool HasAccessibleListeners() const
static ThumbnailViewAcc * getImplementation(const css::uno::Reference< css::uno::XInterface > &rxData) noexcept
void GetFocus()
Called by the corresponding ValueSet when it gets the focus.
void LoseFocus()
Called by the corresponding ValueSet when it loses the focus.
static ThumbnailViewItemAcc * getImplementation(const css::uno::Reference< css::uno::XInterface > &rxData) noexcept
virtual OUString getHelpText() const
void show(bool bVisible)
bool isHighlighted() const
css::uno::Reference< css::accessibility::XAccessible > const & GetAccessible(bool bIsTransientChildrenDisabled)
void calculateItemsPosition(const tools::Long nThumbnailHeight, const tools::Long nPadding, sal_uInt32 nMaxTextLength, const ThumbnailItemAttributes *pAttrs)
virtual tools::Rectangle updateHighlight(bool bVisible, const Point &rPoint)
Updates own highlight status based on the aPoint position.
void setSelection(bool state)
const tools::Rectangle & getDrawArea() const
virtual void Paint(drawinglayer::processor2d::BaseProcessor2D *pProcessor, const ThumbnailItemAttributes *pAttrs)
void setDrawArea(const tools::Rectangle &area)
Class to display thumbnails with their names below their respective icons.
void updateItems(std::vector< std::unique_ptr< ThumbnailViewItem > > items)
sal_uInt16 mnCols
tools::Long mnThumbnailHeight
virtual ~ThumbnailView() override
size_t GetItemPos(sal_uInt16 nItemId) const
void CalculateItemPositions(bool bScrollBarUsed=false)
tools::Long mnVisLines
void deselectItems()
deselect all current selected items.
tools::Long mnVItemSpace
Link< const ThumbnailViewItem *, void > maItemStateHdl
virtual bool MouseButtonDown(const MouseEvent &rMEvt) override
tools::Long mnDisplayHeight
sal_uInt16 GetItemId(size_t nPos) const
size_t ImplGetItem(const Point &rPoint) const
virtual bool KeyInput(const KeyEvent &rKEvt) override
tools::Long mnLines
virtual void Resize() override
const css::uno::Reference< css::accessibility::XAccessible > & getAccessible() const
virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override
virtual bool renameItem(ThumbnailViewItem *pItem, const OUString &sNewTitle)
bool ImplHasAccessibleListeners() const
Color maHighlightColor
Color of the highlight (background) of the hovered item.
virtual void OnItemDblClicked(ThumbnailViewItem *pItem)
void AppendItem(std::unique_ptr< ThumbnailViewItem > pItem)
ThumbnailValueItemList::iterator mpStartSelRange
sal_uInt16 ImplGetVisibleItemCount() const
tools::Long mnItemPadding
virtual bool MouseMove(const MouseEvent &rMEvt) override
tools::Long mnItemHeight
double mfHighlightTransparence
Transparence of the highlight.
virtual void Clear()
sal_uInt16 mnFirstLine
void RemoveItem(sal_uInt16 nItemId)
friend class ThumbnailViewAcc
bool IsItemSelected(sal_uInt16 nItemId) const
ThumbnailViewItem * ImplGetVisibleItem(sal_uInt16 nVisiblePos)
virtual OUString RequestHelp(tools::Rectangle &rRect) override
void filterItems(const std::function< bool(const ThumbnailViewItem *) > &func)
Color maHighlightTextColor
Color of the text for the highlighted item.
void setItemDimensions(tools::Long ItemWidth, tools::Long ThumbnailHeight, tools::Long DisplayHeight, int itemPadding)
ThumbnailView(std::unique_ptr< weld::ScrolledWindow > xWindow, std::unique_ptr< weld::Menu > xMenu)
virtual void LoseFocus() override
void DrawItem(ThumbnailViewItem const *pItem)
std::unique_ptr< weld::ScrolledWindow > mxScrolledWindow
Color maTextColor
Text color.
css::uno::Reference< css::accessibility::XAccessible > mxAccessible
void ImplFireAccessibleEvent(short nEventId, const css::uno::Any &rOldValue, const css::uno::Any &rNewValue)
virtual void SetDrawingArea(weld::DrawingArea *pDrawingArea) override
virtual void GetFocus() override
void ShowTooltips(bool bShowTooltips)
void DrawMnemonics(bool bDrawMnemonics)
std::unique_ptr< ThumbnailItemAttributes > mpItemAttrs
std::function< bool(const ThumbnailViewItem *)> maFilterFunc
void ImplDeleteItems()
void setItemMaxTextLength(sal_uInt32 nLength)
Color maSelectHighlightTextColor
Color of the text of the selected and hovered item.
tools::Long mnItemWidth
static BitmapEx readThumbnail(const OUString &msURL)
void SelectItem(sal_uInt16 nItemId)
Color maSelectHighlightColor
Color of the highlight (background) of the selected and hovered item.
std::vector< std::unique_ptr< ThumbnailViewItem > > mItemList
Color maFillColor
Background color of the thumbnail view widget.
ThumbnailValueItemList mFilteredItemList
Cache to store the filtered items.
virtual void Paint(vcl::RenderContext &rRenderContext, const tools::Rectangle &rRect) override
void MakeItemVisible(sal_uInt16 nId)
bool Contains(const Point &rPOINT) const
constexpr Point TopLeft() const
constexpr bool IsEmpty() const
static std::unique_ptr< SvStream > CreateStream(const OUString &rFileName, StreamMode eOpenMode, css::uno::Reference< css::awt::XWindow > xParentWin=nullptr)
sal_uInt16 GetCode() const
bool IsShift() const
bool read(BitmapEx &rBitmap)
void SetOutputSizePixel(const Size &rSize)
Size const & GetOutputSizePixel() const
virtual OutputDevice & get_ref_device()=0
virtual vcl::Font get_font()=0
virtual Size get_preferred_size() const=0
sal_uInt16 mnId
#define TOOLS_WARN_EXCEPTION(area, stream)
float y
float x
void * p
sal_Int64 n
constexpr sal_uInt16 KEY_RETURN
constexpr sal_uInt16 KEY_LEFT
constexpr sal_uInt16 KEY_UP
constexpr sal_uInt16 KEY_RIGHT
constexpr sal_uInt16 KEY_DOWN
NEVER
sal_uInt16 nPos
Definition: linksrc.cxx:118
Sequence< sal_Int8 > aSeq
Definition: lnkbase2.cxx:83
sal_uInt16 GetTransparentSelectionPercent()
rtl::Reference< BasePrimitive2D > Primitive2DReference
attribute::FontAttribute getFontAttributeFromVclFont(basegfx::B2DVector &o_rSize, const vcl::Font &rFont, bool bRTL, bool bBiDiStrong)
std::unique_ptr< BaseProcessor2D > createProcessor2DFromOutputDevice(OutputDevice &rTargetOutDev, const drawinglayer::geometry::ViewInformation2D &rViewInformation2D)
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
long Long
void SetPointFont(OutputDevice &rDevice, const vcl::Font &rFont)
constexpr int gnFineness
IMPL_LINK_NOARG(ThumbnailView, ImplScrollHdl, weld::ScrolledWindow &, void)
#define THUMBNAILVIEW_ITEM_NOTFOUND
VclPolicyType
sal_Int32 nLength