bump product version to 5.0.4.1
[LibreOffice.git] / sfx2 / source / control / thumbnailview.cxx
blobff050df4a699f6424bb6fb805b696f430255f0e6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <sfx2/thumbnailview.hxx>
11 #include <sfx2/thumbnailviewitem.hxx>
13 #include <utility>
15 #include "thumbnailviewacc.hxx"
17 #include <basegfx/color/bcolortools.hxx>
18 #include <basegfx/matrix/b2dhommatrixtools.hxx>
19 #include <basegfx/range/b2drectangle.hxx>
20 #include <basegfx/polygon/b2dpolygon.hxx>
21 #include <basegfx/vector/b2dsize.hxx>
22 #include <basegfx/vector/b2dvector.hxx>
23 #include <drawinglayer/attribute/fillgraphicattribute.hxx>
24 #include <drawinglayer/attribute/fontattribute.hxx>
25 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
26 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
28 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
29 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
30 #include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
31 #include <rtl/ustring.hxx>
32 #include <svtools/optionsdrawinglayer.hxx>
33 #include <vcl/decoview.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/scrbar.hxx>
36 #include <vcl/help.hxx>
37 #include <vcl/settings.hxx>
39 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
40 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
42 #include <boost/scoped_ptr.hpp>
44 using namespace basegfx;
45 using namespace basegfx::tools;
46 using namespace drawinglayer::attribute;
47 using namespace drawinglayer::primitive2d;
49 enum
51 ITEM_OFFSET = 4,
52 ITEM_OFFSET_DOUBLE = 6,
53 NAME_LINE_OFF_X = 2,
54 NAME_LINE_OFF_Y = 2,
55 NAME_LINE_HEIGHT = 2,
56 NAME_OFFSET = 2,
57 SCROLL_OFFSET = 4
60 ThumbnailView::ThumbnailView (vcl::Window *pParent, WinBits nWinStyle, bool bDisableTransientChildren)
61 : Control( pParent, nWinStyle )
62 , mpItemAttrs(new ThumbnailItemAttributes)
64 ImplInit();
65 mbIsTransientChildrenDisabled = bDisableTransientChildren;
68 ThumbnailView::~ThumbnailView()
70 disposeOnce();
73 void ThumbnailView::dispose()
75 com::sun::star::uno::Reference< ::com::sun::star::lang::XComponent>
76 xComponent(GetAccessible(false),
77 com::sun::star::uno::UNO_QUERY);
79 if (xComponent.is())
80 xComponent->dispose ();
82 mpScrBar.disposeAndClear();
83 delete mpItemAttrs;
85 ImplDeleteItems();
86 Control::dispose();
89 void ThumbnailView::MouseMove(const MouseEvent& rMEvt)
91 size_t nItemCount = mFilteredItemList.size();
92 Point aPoint = rMEvt.GetPosPixel();
93 OUString aHelp;
95 for (size_t i = 0; i < nItemCount; i++)
97 ThumbnailViewItem *pItem = mFilteredItemList[i];
99 if (pItem->mbVisible && !rMEvt.IsLeaveWindow() && pItem->getDrawArea().IsInside(aPoint))
101 aHelp = pItem->getHelpText();
104 Rectangle aToInvalidate(pItem->updateHighlight(pItem->mbVisible && !rMEvt.IsLeaveWindow(), aPoint));
106 if (!aToInvalidate.IsEmpty() && IsReallyVisible() && IsUpdateMode())
107 Invalidate(aToInvalidate);
110 if (mbShowTooltips)
111 SetQuickHelpText(aHelp);
114 void ThumbnailView::AppendItem(ThumbnailViewItem *pItem)
116 if (maFilterFunc(pItem))
118 // Save current start,end range, iterator might get invalidated
119 size_t nSelStartPos = 0;
120 ThumbnailViewItem *pSelStartItem = NULL;
122 if (mpStartSelRange != mFilteredItemList.end())
124 pSelStartItem = *mpStartSelRange;
125 nSelStartPos = mpStartSelRange - mFilteredItemList.begin();
128 mFilteredItemList.push_back(pItem);
129 mpStartSelRange = pSelStartItem != NULL ? mFilteredItemList.begin() + nSelStartPos : mFilteredItemList.end();
132 mItemList.push_back(pItem);
135 void ThumbnailView::ImplInit()
137 mpScrBar = NULL;
138 mnHeaderHeight = 0;
139 mnItemWidth = 0;
140 mnItemHeight = 0;
141 mnItemPadding = 0;
142 mnVisLines = 0;
143 mnLines = 0;
144 mnFineness = 5;
145 mnFirstLine = 0;
146 mnCols = 0;
147 mbScroll = false;
148 mbHasVisibleItems = false;
149 mbShowTooltips = false;
150 maFilterFunc = ViewFilterAll();
151 maFillColor = GetSettings().GetStyleSettings().GetFieldColor();
152 maTextColor = GetSettings().GetStyleSettings().GetWindowTextColor();
153 maHighlightColor = GetSettings().GetStyleSettings().GetHighlightColor();
154 maHighlightTextColor = GetSettings().GetStyleSettings().GetWindowTextColor();
156 const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer;
157 mfHighlightTransparence = aSvtOptionsDrawinglayer.GetTransparentSelectionPercent() * 0.01;
159 mpStartSelRange = mFilteredItemList.end();
161 ApplySettings(*this);
164 void ThumbnailView::ImplDeleteItems()
166 const size_t n = mItemList.size();
168 for ( size_t i = 0; i < n; ++i )
170 ThumbnailViewItem *const pItem = mItemList[i];
172 // deselect all current selected items and fire events
173 if (pItem->isSelected())
175 pItem->setSelection(false);
176 maItemStateHdl.Call(pItem);
178 // fire accessible event???
181 if ( pItem->isVisible() && ImplHasAccessibleListeners() )
183 ::com::sun::star::uno::Any aOldAny, aNewAny;
185 aOldAny <<= pItem->GetAccessible( mbIsTransientChildrenDisabled );
186 ImplFireAccessibleEvent( ::com::sun::star::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
189 delete pItem;
192 mItemList.clear();
193 mFilteredItemList.clear();
195 mpStartSelRange = mFilteredItemList.end();
198 void ThumbnailView::ApplySettings(vcl::RenderContext& rRenderContext)
200 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
202 ApplyControlFont(*this, rStyleSettings.GetAppFont());
203 ApplyControlForeground(*this, rStyleSettings.GetButtonTextColor());
204 rRenderContext.SetTextFillColor();
205 rRenderContext.SetBackground(maFillColor);
207 mpItemAttrs->aFillColor = maFillColor.getBColor();
208 mpItemAttrs->aTextColor = maTextColor.getBColor();
209 mpItemAttrs->aHighlightColor = maHighlightColor.getBColor();
210 mpItemAttrs->aHighlightTextColor = maHighlightTextColor.getBColor();
211 mpItemAttrs->fHighlightTransparence = mfHighlightTransparence;
212 mpItemAttrs->aFontAttr = getFontAttributeFromVclFont(mpItemAttrs->aFontSize,GetFont(),false,true);
213 mpItemAttrs->nMaxTextLength = 0;
216 void ThumbnailView::ImplInitScrollBar()
218 if ( GetStyle() & WB_VSCROLL )
220 if ( !mpScrBar )
222 mpScrBar = VclPtr<ScrollBar>::Create( this, WB_VSCROLL | WB_DRAG );
223 mpScrBar->SetScrollHdl( LINK( this, ThumbnailView, ImplScrollHdl ) );
225 else
227 // adapt the width because of the changed settings
228 long nScrBarWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
229 mpScrBar->setPosSizePixel( 0, 0, nScrBarWidth, 0, PosSizeFlags::Width );
234 void ThumbnailView::DrawItem(ThumbnailViewItem *pItem)
236 if (pItem->isVisible())
238 Rectangle aRect = pItem->getDrawArea();
240 if ((aRect.GetHeight() > 0) && (aRect.GetWidth() > 0))
241 Invalidate(aRect);
245 void ThumbnailView::OnItemDblClicked (ThumbnailViewItem*)
249 ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > ThumbnailView::CreateAccessible()
251 return new ThumbnailViewAcc( this, mbIsTransientChildrenDisabled );
254 void ThumbnailView::CalculateItemPositions (bool bScrollBarUsed)
256 if (!mnItemHeight || !mnItemWidth)
257 return;
259 Size aWinSize = GetOutputSizePixel();
260 size_t nItemCount = mFilteredItemList.size();
261 WinBits nStyle = GetStyle();
262 VclPtr<ScrollBar> pDelScrBar;
264 // consider the scrolling
265 if ( nStyle & WB_VSCROLL )
266 ImplInitScrollBar();
267 else
269 if ( mpScrBar )
271 // delete ScrollBar not until later, to prevent recursive calls
272 pDelScrBar = mpScrBar;
273 mpScrBar = NULL;
277 // calculate window scroll ratio
278 float nScrollRatio;
279 if( bScrollBarUsed && mpScrBar )
280 nScrollRatio = static_cast<float>(mpScrBar->GetThumbPos()) /
281 static_cast<float>(mpScrBar->GetRangeMax()-2);
282 else
283 nScrollRatio = 0;
285 // calculate ScrollBar width
286 long nScrBarWidth = 0;
287 if ( mpScrBar )
288 nScrBarWidth = mpScrBar->GetSizePixel().Width();
290 // calculate maximum number of visible columns
291 mnCols = (sal_uInt16)((aWinSize.Width()-nScrBarWidth) / (mnItemWidth));
293 if (!mnCols)
294 mnCols = 1;
296 // calculate maximum number of visible rows
297 mnVisLines = (sal_uInt16)((aWinSize.Height()-mnHeaderHeight) / (mnItemHeight));
299 // calculate empty space
300 long nHSpace = aWinSize.Width()-nScrBarWidth - mnCols*mnItemWidth;
301 long nVSpace = aWinSize.Height()-mnHeaderHeight - mnVisLines*mnItemHeight;
302 long nHItemSpace = nHSpace / (mnCols+1);
303 long nVItemSpace = nVSpace / (mnVisLines+1);
305 // calculate maximum number of rows
306 // Floor( (M+N-1)/N )==Ceiling( M/N )
307 mnLines = (static_cast<long>(nItemCount)+mnCols-1) / mnCols;
309 if ( !mnLines )
310 mnLines = 1;
312 if ( mnLines <= mnVisLines )
313 mnFirstLine = 0;
314 else if ( mnFirstLine > (sal_uInt16)(mnLines-mnVisLines) )
315 mnFirstLine = (sal_uInt16)(mnLines-mnVisLines);
317 mbHasVisibleItems = true;
319 long nItemHeightOffset = mnItemHeight + nVItemSpace;
320 long nHiddenLines = (static_cast<long>(
321 ( mnLines - 1 ) * nItemHeightOffset * nScrollRatio ) -
322 nVItemSpace - mnHeaderHeight) /
323 nItemHeightOffset;
325 // calculate offsets
326 long nStartX = nHItemSpace;
327 long nStartY = nVItemSpace + mnHeaderHeight;
329 // calculate and draw items
330 long x = nStartX;
331 long y = nStartY - ( mnLines - 1 ) * nItemHeightOffset * nScrollRatio +
332 nHiddenLines * nItemHeightOffset;
334 // draw items
335 // Unless we are scrolling (via scrollbar) we just use the precalculated
336 // mnFirstLine -- our nHiddenLines calculation takes into account only
337 // what the user has done with the scrollbar but not any changes of selection
338 // using the keyboard, meaning we could accidentally hide the selected item
339 // if we believe the scrollbar (fdo#72287).
340 size_t nFirstItem = (bScrollBarUsed ? nHiddenLines : mnFirstLine) * mnCols;
341 size_t nLastItem = nFirstItem + (mnVisLines + 1) * mnCols;
343 // If want also draw parts of items in the last line,
344 // then we add one more line if parts of these line are
345 // visible
347 size_t nCurCount = 0;
348 for ( size_t i = 0; i < nItemCount; i++ )
350 ThumbnailViewItem *const pItem = mFilteredItemList[i];
352 if ((nCurCount >= nFirstItem) && (nCurCount < nLastItem))
354 if( !pItem->isVisible())
356 if ( ImplHasAccessibleListeners() )
358 ::com::sun::star::uno::Any aOldAny, aNewAny;
360 aNewAny <<= pItem->GetAccessible( mbIsTransientChildrenDisabled );
361 ImplFireAccessibleEvent( ::com::sun::star::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
364 pItem->show(true);
366 maItemStateHdl.Call(pItem);
369 pItem->setDrawArea(Rectangle( Point(x,y), Size(mnItemWidth, mnItemHeight) ));
370 pItem->calculateItemsPosition(mnThumbnailHeight,mnDisplayHeight,mnItemPadding,mpItemAttrs->nMaxTextLength,mpItemAttrs);
372 if ( !((nCurCount+1) % mnCols) )
374 x = nStartX;
375 y += mnItemHeight+nVItemSpace;
377 else
378 x += mnItemWidth+nHItemSpace;
380 else
382 if( pItem->isVisible())
384 if ( ImplHasAccessibleListeners() )
386 ::com::sun::star::uno::Any aOldAny, aNewAny;
388 aOldAny <<= pItem->GetAccessible( mbIsTransientChildrenDisabled );
389 ImplFireAccessibleEvent( ::com::sun::star::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
392 pItem->show(false);
394 maItemStateHdl.Call(pItem);
399 ++nCurCount;
402 // arrange ScrollBar, set values and show it
403 if ( mpScrBar )
405 mnLines = (nCurCount+mnCols-1)/mnCols;
407 // check if scroll is needed
408 mbScroll = mnLines > mnVisLines;
411 Point aPos( aWinSize.Width() - nScrBarWidth, mnHeaderHeight );
412 Size aSize( nScrBarWidth, aWinSize.Height() - mnHeaderHeight );
414 mpScrBar->SetPosSizePixel( aPos, aSize );
415 mpScrBar->SetRangeMax( (nCurCount+mnCols-1)*mnFineness/mnCols);
416 mpScrBar->SetVisibleSize( mnVisLines );
417 if (!bScrollBarUsed)
418 mpScrBar->SetThumbPos( (long)mnFirstLine*mnFineness );
419 long nPageSize = mnVisLines;
420 if ( nPageSize < 1 )
421 nPageSize = 1;
422 mpScrBar->SetPageSize( nPageSize );
423 mpScrBar->Show( mbScroll );
426 // delete ScrollBar
427 pDelScrBar.disposeAndClear();
430 size_t ThumbnailView::ImplGetItem( const Point& rPos ) const
432 if ( !mbHasVisibleItems )
434 return THUMBNAILVIEW_ITEM_NOTFOUND;
437 for (size_t i = 0; i < mFilteredItemList.size(); ++i)
439 if (mFilteredItemList[i]->isVisible() && mFilteredItemList[i]->getDrawArea().IsInside(rPos))
440 return i;
443 return THUMBNAILVIEW_ITEM_NOTFOUND;
446 ThumbnailViewItem* ThumbnailView::ImplGetItem( size_t nPos )
448 return ( nPos < mFilteredItemList.size() ) ? mFilteredItemList[nPos] : NULL;
451 sal_uInt16 ThumbnailView::ImplGetVisibleItemCount() const
453 sal_uInt16 nRet = 0;
454 const size_t nItemCount = mItemList.size();
456 for ( size_t n = 0; n < nItemCount; ++n )
458 if ( mItemList[n]->isVisible() )
459 ++nRet;
462 return nRet;
465 ThumbnailViewItem* ThumbnailView::ImplGetVisibleItem( sal_uInt16 nVisiblePos )
467 const size_t nItemCount = mItemList.size();
469 for ( size_t n = 0; n < nItemCount; ++n )
471 ThumbnailViewItem *const pItem = mItemList[n];
473 if ( pItem->isVisible() && !nVisiblePos-- )
474 return pItem;
477 return NULL;
480 void ThumbnailView::ImplFireAccessibleEvent( short nEventId, const ::com::sun::star::uno::Any& rOldValue, const ::com::sun::star::uno::Any& rNewValue )
482 ThumbnailViewAcc* pAcc = ThumbnailViewAcc::getImplementation( GetAccessible( false ) );
484 if( pAcc )
485 pAcc->FireAccessibleEvent( nEventId, rOldValue, rNewValue );
488 bool ThumbnailView::ImplHasAccessibleListeners()
490 ThumbnailViewAcc* pAcc = ThumbnailViewAcc::getImplementation( GetAccessible( false ) );
491 return( pAcc && pAcc->HasAccessibleListeners() );
494 IMPL_LINK( ThumbnailView,ImplScrollHdl, ScrollBar*, pScrollBar )
496 if ( pScrollBar->GetDelta() )
498 CalculateItemPositions(true);
500 if ( IsReallyVisible() && IsUpdateMode() )
501 Invalidate();
503 return 0;
506 IMPL_LINK (ThumbnailView, OnItemSelected, ThumbnailViewItem*, pItem)
508 maItemStateHdl.Call(pItem);
509 return 0;
512 void ThumbnailView::KeyInput( const KeyEvent& rKEvt )
514 // Get the last selected item in the list
515 size_t nLastPos = 0;
516 bool bFoundLast = false;
517 for ( long i = mFilteredItemList.size() - 1; !bFoundLast && i >= 0; --i )
519 ThumbnailViewItem* pItem = mFilteredItemList[i];
520 if ( pItem->isSelected() )
522 nLastPos = i;
523 bFoundLast = true;
527 bool bValidRange = false;
528 bool bHasSelRange = mpStartSelRange != mFilteredItemList.end();
529 size_t nNextPos = nLastPos;
530 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
531 ThumbnailViewItem* pNext = NULL;
533 if (aKeyCode.IsShift() && bHasSelRange)
535 //If the last elemented selected is the start range position
536 //search for the first selected item
537 size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
539 if (nLastPos == nSelPos)
541 while (nLastPos && mFilteredItemList[nLastPos-1]->isSelected())
542 --nLastPos;
546 switch ( aKeyCode.GetCode() )
548 case KEY_RIGHT:
549 if (!mFilteredItemList.empty())
551 if ( bFoundLast && nLastPos + 1 < mFilteredItemList.size() )
553 bValidRange = true;
554 nNextPos = nLastPos + 1;
557 pNext = mFilteredItemList[nNextPos];
559 break;
560 case KEY_LEFT:
561 if (!mFilteredItemList.empty())
563 if ( nLastPos > 0 )
565 bValidRange = true;
566 nNextPos = nLastPos - 1;
569 pNext = mFilteredItemList[nNextPos];
571 break;
572 case KEY_DOWN:
573 if (!mFilteredItemList.empty())
575 if ( bFoundLast )
577 //If we are in the second last row just go the one in
578 //the row below, if theres not row below just go to the
579 //last item but for the last row dont do anything.
580 if ( nLastPos + mnCols < mFilteredItemList.size( ) )
582 bValidRange = true;
583 nNextPos = nLastPos + mnCols;
585 else
587 int curRow = nLastPos/mnCols;
589 if (curRow < mnLines-1)
590 nNextPos = mFilteredItemList.size()-1;
594 pNext = mFilteredItemList[nNextPos];
596 break;
597 case KEY_UP:
598 if (!mFilteredItemList.empty())
600 if ( nLastPos >= mnCols )
602 bValidRange = true;
603 nNextPos = nLastPos - mnCols;
606 pNext = mFilteredItemList[nNextPos];
608 break;
609 case KEY_RETURN:
611 if ( bFoundLast )
612 OnItemDblClicked( mFilteredItemList[nLastPos] );
614 //fall-through
615 default:
616 Control::KeyInput( rKEvt );
619 if ( pNext )
621 if (aKeyCode.IsShift() && bValidRange)
623 std::pair<size_t,size_t> aRange;
624 size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
626 if (nLastPos < nSelPos)
628 if (nNextPos > nLastPos)
630 if ( nNextPos > nSelPos)
631 aRange = std::make_pair(nLastPos,nNextPos);
632 else
633 aRange = std::make_pair(nLastPos,nNextPos-1);
635 else
636 aRange = std::make_pair(nNextPos,nLastPos-1);
638 else if (nLastPos == nSelPos)
640 if (nNextPos > nLastPos)
641 aRange = std::make_pair(nLastPos+1,nNextPos);
642 else
643 aRange = std::make_pair(nNextPos,nLastPos-1);
645 else
647 if (nNextPos > nLastPos)
648 aRange = std::make_pair(nLastPos+1,nNextPos);
649 else
651 if ( nNextPos < nSelPos)
652 aRange = std::make_pair(nNextPos,nLastPos);
653 else
654 aRange = std::make_pair(nNextPos+1,nLastPos);
658 for (size_t i = aRange.first; i <= aRange.second; ++i)
660 if (i != nSelPos)
662 ThumbnailViewItem *pCurItem = mFilteredItemList[i];
664 pCurItem->setSelection(!pCurItem->isSelected());
666 if (pCurItem->isVisible())
667 DrawItem(pCurItem);
669 maItemStateHdl.Call(pCurItem);
673 else if (!aKeyCode.IsShift())
675 deselectItems();
676 SelectItem(pNext->mnId);
678 //Mark it as the selection range start position
679 mpStartSelRange = mFilteredItemList.begin() + nNextPos;
682 MakeItemVisible(pNext->mnId);
686 void ThumbnailView::MakeItemVisible( sal_uInt16 nItemId )
688 // Get the item row
689 size_t nPos = 0;
690 bool bFound = false;
691 for ( size_t i = 0; !bFound && i < mFilteredItemList.size(); ++i )
693 ThumbnailViewItem* pItem = mFilteredItemList[i];
694 if ( pItem->mnId == nItemId )
696 nPos = i;
697 bFound = true;
700 sal_uInt16 nRow = mnCols ? nPos / mnCols : 0;
702 // Move the visible rows as little as possible to include that one
703 if ( nRow < mnFirstLine )
704 mnFirstLine = nRow;
705 else if ( nRow > mnFirstLine + mnVisLines )
706 mnFirstLine = nRow - mnVisLines;
708 CalculateItemPositions();
709 Invalidate();
712 void ThumbnailView::MouseButtonDown( const MouseEvent& rMEvt )
714 if ( !rMEvt.IsLeft() )
716 Control::MouseButtonDown( rMEvt );
717 return;
720 size_t nPos = ImplGetItem(rMEvt.GetPosPixel());
721 ThumbnailViewItem* pItem = ImplGetItem(nPos);
723 if ( !pItem )
725 deselectItems();
726 Control::MouseButtonDown( rMEvt );
727 return;
730 if ( rMEvt.GetClicks() == 2 )
732 OnItemDblClicked(pItem);
733 return;
736 if ( rMEvt.GetClicks() == 1 )
738 if (rMEvt.IsMod1())
740 //Keep selected item group state and just invert current desired one state
741 pItem->setSelection(!pItem->isSelected());
743 //This one becomes the selection range start position if it changes its state to selected otherwise resets it
744 mpStartSelRange = pItem->isSelected() ? mFilteredItemList.begin() + nPos : mFilteredItemList.end();
746 else if (rMEvt.IsShift() && mpStartSelRange != mFilteredItemList.end())
748 std::pair<size_t,size_t> aNewRange;
749 aNewRange.first = mpStartSelRange - mFilteredItemList.begin();
750 aNewRange.second = nPos;
752 if (aNewRange.first > aNewRange.second)
753 std::swap(aNewRange.first,aNewRange.second);
755 //Deselect the ones outside of it
756 for (size_t i = 0, n = mFilteredItemList.size(); i < n; ++i)
758 ThumbnailViewItem *pCurItem = mFilteredItemList[i];
760 if (pCurItem->isSelected() && (i < aNewRange.first || i > aNewRange.second))
762 pCurItem->setSelection(false);
764 if (pCurItem->isVisible())
765 DrawItem(pCurItem);
767 maItemStateHdl.Call(pCurItem);
771 size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
773 //Select the items between start range and the selected item
774 if (nSelPos != nPos)
776 int dir = nSelPos < nPos ? 1 : -1;
777 size_t nCurPos = nSelPos + dir;
779 while (nCurPos != nPos)
781 ThumbnailViewItem *pCurItem = mFilteredItemList[nCurPos];
783 if (!pCurItem->isSelected())
785 pCurItem->setSelection(true);
787 if (pCurItem->isVisible())
788 DrawItem(pCurItem);
790 maItemStateHdl.Call(pCurItem);
793 nCurPos += dir;
797 pItem->setSelection(true);
799 else
801 //If we got a group of selected items deselect the rest and only keep the desired one
802 //mark items as not selected to not fire unnecessary change state events.
803 pItem->setSelection(false);
804 deselectItems();
805 pItem->setSelection(true);
807 //Mark as initial selection range position and reset end one
808 mpStartSelRange = mFilteredItemList.begin() + nPos;
811 if (pItem->isSelected())
813 bool bClickOnTitle = pItem->getTextArea().IsInside(rMEvt.GetPosPixel());
814 pItem->setEditTitle(bClickOnTitle);
817 if (!pItem->isHighlighted())
818 DrawItem(pItem);
820 maItemStateHdl.Call(pItem);
822 //fire accessible event??
826 void ThumbnailView::MouseButtonUp( const MouseEvent& rMEvt )
828 Control::MouseButtonUp( rMEvt );
831 void ThumbnailView::Command( const CommandEvent& rCEvt )
833 if ( (rCEvt.GetCommand() == CommandEventId::Wheel) ||
834 (rCEvt.GetCommand() == CommandEventId::StartAutoScroll) ||
835 (rCEvt.GetCommand() == CommandEventId::AutoScroll) )
837 if ( HandleScrollCommand( rCEvt, NULL, mpScrBar ) )
838 return;
841 Control::Command( rCEvt );
844 void ThumbnailView::Paint(vcl::RenderContext& rRenderContext, const Rectangle& rRect)
846 size_t nItemCount = mItemList.size();
848 // Draw background
849 drawinglayer::primitive2d::Primitive2DSequence aSeq(1);
850 aSeq[0] = drawinglayer::primitive2d::Primitive2DReference(new PolyPolygonColorPrimitive2D(
851 B2DPolyPolygon(Polygon(Rectangle(Point(), GetOutputSizePixel()), 0, 0).getB2DPolygon()),
852 maFillColor.getBColor()));
854 // Create the processor and process the primitives
855 const drawinglayer::geometry::ViewInformation2D aNewViewInfos;
857 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(
858 drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(rRenderContext, aNewViewInfos));
859 pProcessor->process(aSeq);
861 // draw items
862 for (size_t i = 0; i < nItemCount; i++)
864 ThumbnailViewItem *const pItem = mItemList[i];
866 if (pItem->isVisible())
868 pItem->Paint(pProcessor.get(), mpItemAttrs);
872 if (mpScrBar && mpScrBar->IsVisible())
873 mpScrBar->Invalidate(rRect);
876 void ThumbnailView::GetFocus()
878 // Select the first item if nothing selected
879 int nSelected = -1;
880 for (size_t i = 0, n = mItemList.size(); i < n && nSelected == -1; ++i)
882 if (mItemList[i]->isSelected())
883 nSelected = i;
886 if (nSelected == -1 && mItemList.size() > 0)
888 SelectItem(1);
891 // Tell the accessible object that we got the focus.
892 ThumbnailViewAcc* pAcc = ThumbnailViewAcc::getImplementation( GetAccessible( false ) );
893 if( pAcc )
894 pAcc->GetFocus();
896 Control::GetFocus();
899 void ThumbnailView::LoseFocus()
901 Control::LoseFocus();
903 // Tell the accessible object that we lost the focus.
904 ThumbnailViewAcc* pAcc = ThumbnailViewAcc::getImplementation( GetAccessible( false ) );
905 if( pAcc )
906 pAcc->LoseFocus();
909 void ThumbnailView::Resize()
911 Control::Resize();
912 CalculateItemPositions();
914 if ( IsReallyVisible() && IsUpdateMode() )
915 Invalidate();
918 void ThumbnailView::StateChanged( StateChangedType nType )
920 Control::StateChanged( nType );
922 if ( nType == StateChangedType::InitShow )
924 if ( IsReallyVisible() && IsUpdateMode() )
925 Invalidate();
927 else if ( nType == StateChangedType::UpdateMode )
929 if ( IsReallyVisible() && IsUpdateMode() )
930 Invalidate();
932 else if ( nType == StateChangedType::Text )
935 else if ( (nType == StateChangedType::Zoom) ||
936 (nType == StateChangedType::ControlFont) )
938 Invalidate();
940 else if ( nType == StateChangedType::ControlForeground )
942 Invalidate();
944 else if ( nType == StateChangedType::ControlBackground )
946 Invalidate();
948 else if ( (nType == StateChangedType::Style) || (nType == StateChangedType::Enable) )
950 Invalidate();
954 void ThumbnailView::DataChanged( const DataChangedEvent& rDCEvt )
956 Control::DataChanged( rDCEvt );
958 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
959 (rDCEvt.GetType() == DataChangedEventType::DISPLAY) ||
960 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
961 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
962 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
964 Invalidate();
968 void ThumbnailView::RemoveItem( sal_uInt16 nItemId )
970 size_t nPos = GetItemPos( nItemId );
972 if ( nPos == THUMBNAILVIEW_ITEM_NOTFOUND )
973 return;
975 if ( nPos < mFilteredItemList.size() ) {
977 // delete item from the thumbnail list
978 for (size_t i = 0, n = mItemList.size(); i < n; ++i)
980 if (mItemList[i]->mnId == nItemId)
982 mItemList.erase(mItemList.begin()+i);
983 break;
987 // delete item from the filter item list
988 ThumbnailValueItemList::iterator it = mFilteredItemList.begin();
989 ::std::advance( it, nPos );
991 if ((*it)->isSelected())
993 (*it)->setSelection(false);
994 maItemStateHdl.Call(*it);
997 delete *it;
998 mFilteredItemList.erase( it );
999 mpStartSelRange = mFilteredItemList.end();
1002 CalculateItemPositions();
1004 if ( IsReallyVisible() && IsUpdateMode() )
1005 Invalidate();
1008 void ThumbnailView::Clear()
1010 ImplDeleteItems();
1012 // reset variables
1013 mnFirstLine = 0;
1015 CalculateItemPositions();
1017 if ( IsReallyVisible() && IsUpdateMode() )
1018 Invalidate();
1021 void ThumbnailView::updateItems (const std::vector<ThumbnailViewItem*> &items)
1023 ImplDeleteItems();
1025 // reset variables
1026 mnFirstLine = 0;
1028 mItemList = items;
1030 filterItems(maFilterFunc);
1033 size_t ThumbnailView::GetItemPos( sal_uInt16 nItemId ) const
1035 for ( size_t i = 0, n = mFilteredItemList.size(); i < n; ++i ) {
1036 if ( mFilteredItemList[i]->mnId == nItemId ) {
1037 return i;
1040 return THUMBNAILVIEW_ITEM_NOTFOUND;
1043 sal_uInt16 ThumbnailView::GetItemId( size_t nPos ) const
1045 return ( nPos < mFilteredItemList.size() ) ? mFilteredItemList[nPos]->mnId : 0 ;
1048 sal_uInt16 ThumbnailView::GetItemId( const Point& rPos ) const
1050 size_t nItemPos = ImplGetItem( rPos );
1051 if ( nItemPos != THUMBNAILVIEW_ITEM_NOTFOUND )
1052 return GetItemId( nItemPos );
1054 return 0;
1057 sal_uInt16 ThumbnailView::getNextItemId() const
1059 return mItemList.empty() ? 1 : mItemList.back()->mnId + 1;
1062 void ThumbnailView::setItemMaxTextLength(sal_uInt32 nLength)
1064 mpItemAttrs->nMaxTextLength = nLength;
1067 void ThumbnailView::setItemDimensions(long itemWidth, long thumbnailHeight, long displayHeight, int itemPadding)
1069 mnItemWidth = itemWidth + 2*itemPadding;
1070 mnThumbnailHeight = thumbnailHeight;
1071 mnDisplayHeight = displayHeight;
1072 mnItemPadding = itemPadding;
1073 mnItemHeight = mnDisplayHeight + mnThumbnailHeight + 2*itemPadding;
1076 void ThumbnailView::SelectItem( sal_uInt16 nItemId )
1078 size_t nItemPos = GetItemPos( nItemId );
1079 if ( nItemPos == THUMBNAILVIEW_ITEM_NOTFOUND )
1080 return;
1082 ThumbnailViewItem* pItem = mFilteredItemList[nItemPos];
1083 if (!pItem->isSelected())
1085 pItem->setSelection(true);
1086 maItemStateHdl.Call(pItem);
1088 if (IsReallyVisible() && IsUpdateMode())
1089 Invalidate();
1091 bool bNewOut = IsReallyVisible() && IsUpdateMode();
1093 // if necessary scroll to the visible area
1094 if ( mbScroll && nItemId )
1096 sal_uInt16 nNewLine = (sal_uInt16)(nItemPos / mnCols);
1097 if ( nNewLine < mnFirstLine )
1099 mnFirstLine = nNewLine;
1101 else if ( nNewLine > (sal_uInt16)(mnFirstLine+mnVisLines-1) )
1103 mnFirstLine = (sal_uInt16)(nNewLine-mnVisLines+1);
1107 if ( bNewOut )
1109 if ( IsReallyVisible() && IsUpdateMode() )
1110 Invalidate();
1113 if( ImplHasAccessibleListeners() )
1115 // focus event (select)
1116 ThumbnailViewItemAcc* pItemAcc = ThumbnailViewItemAcc::getImplementation( pItem->GetAccessible( mbIsTransientChildrenDisabled ) );
1118 if( pItemAcc )
1120 ::com::sun::star::uno::Any aOldAny, aNewAny;
1121 if( !mbIsTransientChildrenDisabled )
1123 aNewAny <<= ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >(
1124 static_cast< ::cppu::OWeakObject* >( pItemAcc ));
1125 ImplFireAccessibleEvent( ::com::sun::star::accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny );
1127 else
1129 aNewAny <<= ::com::sun::star::accessibility::AccessibleStateType::FOCUSED;
1130 pItemAcc->FireAccessibleEvent( ::com::sun::star::accessibility::AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny );
1134 // selection event
1135 ::com::sun::star::uno::Any aOldAny, aNewAny;
1136 ImplFireAccessibleEvent( ::com::sun::star::accessibility::AccessibleEventId::SELECTION_CHANGED, aOldAny, aNewAny );
1141 bool ThumbnailView::IsItemSelected( sal_uInt16 nItemId ) const
1143 size_t nItemPos = GetItemPos( nItemId );
1144 if ( nItemPos == THUMBNAILVIEW_ITEM_NOTFOUND )
1145 return false;
1147 ThumbnailViewItem* pItem = mFilteredItemList[nItemPos];
1148 return pItem->isSelected();
1151 void ThumbnailView::deselectItems()
1153 for (size_t i = 0, n = mItemList.size(); i < n; ++i)
1155 if (mItemList[i]->isSelected())
1157 mItemList[i]->setEditTitle(false);
1158 mItemList[i]->setSelection(false);
1160 maItemStateHdl.Call(mItemList[i]);
1164 if (IsReallyVisible() && IsUpdateMode())
1165 Invalidate();
1168 void ThumbnailView::ShowTooltips( bool bShowTooltips )
1170 mbShowTooltips = bShowTooltips;
1173 void ThumbnailView::filterItems (const boost::function<bool (const ThumbnailViewItem*) > &func)
1175 mnFirstLine = 0; // start at the top of the list instead of the current position
1176 maFilterFunc = func;
1178 size_t nSelPos = 0;
1179 bool bHasSelRange = false;
1180 ThumbnailViewItem *curSel = mpStartSelRange != mFilteredItemList.end() ? *mpStartSelRange : NULL;
1182 mFilteredItemList.clear();
1184 for (size_t i = 0, n = mItemList.size(); i < n; ++i)
1186 ThumbnailViewItem *const pItem = mItemList[i];
1188 if (maFilterFunc(pItem))
1190 if (curSel == pItem)
1192 nSelPos = i;
1193 bHasSelRange = true;
1196 mFilteredItemList.push_back(pItem);
1198 else
1200 if( pItem->isVisible())
1202 if ( ImplHasAccessibleListeners() )
1204 ::com::sun::star::uno::Any aOldAny, aNewAny;
1206 aOldAny <<= pItem->GetAccessible( mbIsTransientChildrenDisabled );
1207 ImplFireAccessibleEvent( ::com::sun::star::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny );
1210 pItem->show(false);
1211 pItem->setSelection(false);
1213 maItemStateHdl.Call(pItem);
1218 mpStartSelRange = bHasSelRange ? mFilteredItemList.begin() + nSelPos : mFilteredItemList.end();
1219 CalculateItemPositions();
1221 Invalidate();
1224 void ThumbnailView::sortItems (const boost::function<bool (const ThumbnailViewItem*, const ThumbnailViewItem*) > &func)
1226 std::sort(mItemList.begin(),mItemList.end(),func);
1228 CalculateItemPositions();
1230 Invalidate();
1233 bool ThumbnailView::renameItem(ThumbnailViewItem*, const OUString&)
1235 // Do nothing by default
1236 return false;
1239 BitmapEx ThumbnailView::readThumbnail(const OUString &msURL)
1241 using namespace ::com::sun::star;
1242 using namespace ::com::sun::star::uno;
1244 // Load the thumbnail from a template document.
1245 uno::Reference<io::XInputStream> xIStream;
1247 uno::Reference< uno::XComponentContext > xContext(::comphelper::getProcessComponentContext());
1250 uno::Reference<lang::XSingleServiceFactory> xStorageFactory = embed::StorageFactory::create(xContext);
1252 uno::Sequence<uno::Any> aArgs (2);
1253 aArgs[0] <<= msURL;
1254 aArgs[1] <<= embed::ElementModes::READ;
1255 uno::Reference<embed::XStorage> xDocStorage (
1256 xStorageFactory->createInstanceWithArguments(aArgs),
1257 uno::UNO_QUERY);
1261 if (xDocStorage.is())
1263 uno::Reference<embed::XStorage> xStorage (
1264 xDocStorage->openStorageElement(
1265 "Thumbnails",
1266 embed::ElementModes::READ));
1267 if (xStorage.is())
1269 uno::Reference<io::XStream> xThumbnailCopy (
1270 xStorage->cloneStreamElement("thumbnail.png"));
1271 if (xThumbnailCopy.is())
1272 xIStream = xThumbnailCopy->getInputStream();
1276 catch (const uno::Exception& rException)
1278 OSL_TRACE (
1279 "caught exception while trying to access Thumbnail/thumbnail.png of %s: %s",
1280 OUStringToOString(msURL,
1281 RTL_TEXTENCODING_UTF8).getStr(),
1282 OUStringToOString(rException.Message,
1283 RTL_TEXTENCODING_UTF8).getStr());
1288 // An (older) implementation had a bug - The storage
1289 // name was "Thumbnail" instead of "Thumbnails". The
1290 // old name is still used as fallback but this code can
1291 // be removed soon.
1292 if ( ! xIStream.is())
1294 uno::Reference<embed::XStorage> xStorage (
1295 xDocStorage->openStorageElement( "Thumbnail",
1296 embed::ElementModes::READ));
1297 if (xStorage.is())
1299 uno::Reference<io::XStream> xThumbnailCopy (
1300 xStorage->cloneStreamElement("thumbnail.png"));
1301 if (xThumbnailCopy.is())
1302 xIStream = xThumbnailCopy->getInputStream();
1306 catch (const uno::Exception& rException)
1308 OSL_TRACE (
1309 "caught exception while trying to access Thumbnails/thumbnail.png of %s: %s",
1310 OUStringToOString(msURL,
1311 RTL_TEXTENCODING_UTF8).getStr(),
1312 OUStringToOString(rException.Message,
1313 RTL_TEXTENCODING_UTF8).getStr());
1316 catch (const uno::Exception& rException)
1318 OSL_TRACE (
1319 "caught exception while trying to access tuhmbnail of %s: %s",
1320 OUStringToOString(msURL,
1321 RTL_TEXTENCODING_UTF8).getStr(),
1322 OUStringToOString(rException.Message,
1323 RTL_TEXTENCODING_UTF8).getStr());
1326 // Extract the image from the stream.
1327 BitmapEx aThumbnail;
1328 if (xIStream.is())
1330 boost::scoped_ptr<SvStream> pStream (
1331 ::utl::UcbStreamHelper::CreateStream (xIStream));
1332 vcl::PNGReader aReader (*pStream);
1333 aThumbnail = aReader.Read ();
1336 // Note that the preview is returned without scaling it to the desired
1337 // width. This gives the caller the chance to take advantage of a
1338 // possibly larger resolution then was asked for.
1339 return aThumbnail;
1342 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */