/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
static css::uno::Reference<css::io::XInputStream>
getFirstHierarchicalStream(const OUString& URL, sal_Int32 format,
std::initializer_list<OUString> names)
{ auto xStorage(getStorageAccess(URL, format)); for (constauto& name : names)
{ try
{ return getHierarchicalStream(xStorage, name);
} catch (const css::uno::Exception&)
{
TOOLS_WARN_EXCEPTION("sfx", "caught exception while trying to access " << name << " of "
<< URL);
}
} return {};
}
static css::uno::Reference<css::io::XInputStream>
getFirstStreamByRelType(const OUString& URL, std::initializer_list<OUString> types)
{ auto xStorage(getStorageAccess(URL, css::embed::StorageFormats::OFOPXML)); if (auto xRelationshipAccess = xStorage.query<css::embed::XRelationshipAccess>())
{ for (constauto& type : types)
{ auto rels = xRelationshipAccess->getRelationshipsByType(type); if (rels.hasElements())
{ // ISO/IEC 29500-1:2016(E) 15.2.16 Thumbnail Part: "Packages shall not contain // more than one thumbnail relationship associated with the package as a whole" for (constauto& [tag, value] : rels[0])
{ if (tag == "Id")
{ return getHierarchicalStream(xStorage,
xRelationshipAccess->getTargetByID(value));
}
}
}
}
} return {};
}
// Load the thumbnail from a template document.
uno::Reference<io::XInputStream> xIStream;
try
{ // An (older) implementation had a bug - The storage // name was "Thumbnail" instead of "Thumbnails". The // old name is still used as fallback but this code can // be removed soon.
xIStream = getFirstHierarchicalStream(
msURL, embed::StorageFormats::PACKAGE,
{ u"Thumbnails/thumbnail.png"_ustr, u"Thumbnail/thumbnail.png"_ustr });
} catch (const uno::Exception&)
{
TOOLS_WARN_EXCEPTION("sfx", "caught exception while trying to access thumbnail of "
<< msURL);
}
// Extract the image from the stream.
BitmapEx aThumbnail; if (auto pStream = utl::UcbStreamHelper::CreateStream(xIStream, /*CloseStream=*/true))
{
Graphic aGraphic = GraphicFilter::GetGraphicFilter().ImportUnloadedGraphic(*pStream);
aThumbnail = aGraphic.GetBitmapEx();
}
// Note that the preview is returned without scaling it to the desired // width. This gives the caller the chance to take advantage of a // possibly larger resolution then was asked for. return aThumbnail;
}
// calculate and draw items
tools::Long x = nStartX;
tools::Long y = nStartY - ((nFullSteps - 1) * nScrollRatio - nHiddenLines) * nItemHeightOffset;
// draw items // Unless we are scrolling (via scrollbar) we just use the precalculated // mnFirstLine -- our nHiddenLines calculation takes into account only // what the user has done with the scrollbar but not any changes of selection // using the keyboard, meaning we could accidentally hide the selected item // if we believe the scrollbar (fdo#72287).
size_t nFirstItem = (bScrollBarUsed ? nHiddenLines : mnFirstLine) * mnCols;
size_t nLastItem = nFirstItem + (mnVisLines + 1) * mnCols;
// tdf#162510 - helper for in order to handle accessibility events auto handleAccessibleEvent = [&](ThumbnailViewItem& rItem, bool bIsVisible)
{ if (ImplHasAccessibleListeners())
{
css::uno::Any aOldAny, aNewAny; if (bIsVisible)
aNewAny <<= css::uno::Reference<css::accessibility::XAccessible>(rItem.GetAccessible()); else
aOldAny <<= css::uno::Reference<css::accessibility::XAccessible>(rItem.GetAccessible());
ImplFireAccessibleEvent(css::accessibility::AccessibleEventId::CHILD, aOldAny, aNewAny);
}
};
// tdf#162510 - helper to set visibility and update layout auto updateItemLayout = [&](ThumbnailViewItem& rItem, bool bIsVisible, size_t& nVisibleCount)
{ if (bIsVisible != rItem.isVisible())
{
handleAccessibleEvent(rItem, bIsVisible);
rItem.show(bIsVisible);
maItemStateHdl.Call(&rItem);
}
if ((nVisibleCount + 1) % mnCols)
x += mnItemWidth + nHItemSpace; else
{
x = nStartX;
y += mnItemHeight + nVItemSpace;
}
++nVisibleCount;
}
};
size_t nCurCountVisible = 0; #if !ENABLE_WASM_STRIP_RECENT // tdf#162510 - process pinned items for (size_t i = 0; i < nItemCountPinned; i++)
updateItemLayout(*mFilteredItemList[i], nFirstItem <= i && i < nLastItem, nCurCountVisible);
// tdf#162510 - start a new line only if the entire line is not filled with pinned items if (nCurCountVisible && nCurCountVisible % mnCols)
{
x = nStartX;
y += mnItemHeight + nVItemSpace;
}
// tdf#164102 - adjust first item only if there are any pinned items if (constauto nRemainingPinnedSlots = nItemCountPinned % mnCols)
{ // tdf#162510 - adjust first item to take into account the new line after pinned items constauto nFirstItemAdjustment = mnCols - nRemainingPinnedSlots; if (nFirstItemAdjustment <= nFirstItem)
nFirstItem -= nFirstItemAdjustment;
}
#endif
// If want also draw parts of items in the last line, // then we add one more line if parts of this line are visible
nCurCountVisible = 0; for (size_t i = nItemCountPinned; i < nItemCount; i++)
updateItemLayout(*mFilteredItemList[i], nFirstItem <= i && i < nLastItem, nCurCountVisible);
// check if scroll is needed
mbScroll = mnLines > mnVisLines;
for (size_t i = 0; i < mFilteredItemList.size(); ++i)
{ if (mFilteredItemList[i]->isVisible() && mFilteredItemList[i]->getDrawArea().Contains(rPos)) return i;
}
// Get the last selected item in the list
size_t nLastPos = 0; bool bFoundLast = false; for ( tools::Long i = mFilteredItemList.size() - 1; !bFoundLast && i >= 0; --i )
{
ThumbnailViewItem* pItem = mFilteredItemList[i]; if ( pItem->isSelected() )
{
nLastPos = i;
bFoundLast = true;
}
}
if (aKeyCode.IsShift() && bHasSelRange)
{ //If the last element selected is the start range position //search for the first selected item
size_t nSelPos = mpStartSelRange - mFilteredItemList.begin();
if (nLastPos == nSelPos)
{ while (nLastPos && mFilteredItemList[nLastPos-1]->isSelected())
--nLastPos;
}
}
switch ( aKeyCode.GetCode() )
{ case KEY_RIGHT: if (!mFilteredItemList.empty())
{ if ( bFoundLast && nLastPos + 1 < mFilteredItemList.size() )
{
bValidRange = true;
nNextPos = nLastPos + 1;
}
pNext = mFilteredItemList[nNextPos];
} break; case KEY_LEFT: if (!mFilteredItemList.empty())
{ if ( nLastPos > 0 )
{
bValidRange = true;
nNextPos = nLastPos - 1;
}
pNext = mFilteredItemList[nNextPos];
} break; case KEY_DOWN: if (!mFilteredItemList.empty())
{ if ( bFoundLast )
{ //If we are in the second last row just go the one in //the row below, if there's not row below just go to the //last item but for the last row don't do anything. if ( nLastPos + mnCols < mFilteredItemList.size( ) )
{
bValidRange = true;
nNextPos = nLastPos + mnCols;
} else
{ int curRow = nLastPos/mnCols;
if (curRow < mnLines-1)
nNextPos = mFilteredItemList.size()-1;
}
}
pNext = mFilteredItemList[nNextPos];
} break; case KEY_UP: if (!mFilteredItemList.empty())
{ if ( nLastPos >= mnCols )
{
bValidRange = true;
nNextPos = nLastPos - mnCols;
}
// Move the visible rows as little as possible to include that one if ( nRow < mnFirstLine )
mnFirstLine = nRow; elseif ( nRow > mnFirstLine + mnVisLines )
mnFirstLine = nRow - mnVisLines;
if ( rMEvt.GetClicks() == 2 )
{
OnItemDblClicked(pItem); returntrue;
}
if(rMEvt.GetClicks() == 1)
{ if (rMEvt.IsMod1())
{ //Keep selected item group state and just invert current desired one state
pItem->setSelection(!pItem->isSelected());
//This one becomes the selection range start position if it changes its state to selected otherwise resets it
mpStartSelRange = pItem->isSelected() ? mFilteredItemList.begin() + nPos : mFilteredItemList.end();
} elseif (rMEvt.IsShift() && mpStartSelRange != mFilteredItemList.end())
{
std::pair<size_t,size_t> aNewRange;
aNewRange.first = mpStartSelRange - mFilteredItemList.begin();
aNewRange.second = nPos;
if (aNewRange.first > aNewRange.second)
std::swap(aNewRange.first,aNewRange.second);
//Deselect the ones outside of it for (size_t i = 0, n = mFilteredItemList.size(); i < n; ++i)
{
ThumbnailViewItem *pCurItem = mFilteredItemList[i];
if (pCurItem->isSelected() && (i < aNewRange.first || i > aNewRange.second))
{
pCurItem->setSelection(false);
//Select the items between start range and the selected item if (nSelPos != nPos)
{ int dir = nSelPos < nPos ? 1 : -1;
size_t nCurPos = nSelPos + dir;
while (nCurPos != nPos)
{
ThumbnailViewItem *pCurItem = mFilteredItemList[nCurPos];
if (!pCurItem->isSelected())
{
pCurItem->setSelection(true);
DrawItem(pCurItem);
maItemStateHdl.Call(pCurItem);
}
nCurPos += dir;
}
}
pItem->setSelection(true);
} else
{ //If we got a group of selected items deselect the rest and only keep the desired one //mark items as not selected to not fire unnecessary change state events.
pItem->setSelection(false);
deselectItems();
pItem->setSelection(true);
//Mark as initial selection range position and reset end one
mpStartSelRange = mFilteredItemList.begin() + nPos;
}
// draw items for (size_t i = 0; i < nItemCount; i++)
{
ThumbnailViewItem *const pItem = mItemList[i].get(); if (!pItem->isVisible()) continue;
pItem->Paint(pProcessor.get(), mpItemAttrs.get());
}
rRenderContext.Pop();
}
void ThumbnailView::GetFocus()
{ if (mbSelectOnFocus)
{ // Select the first item if nothing selected int nSelected = -1; for (size_t i = 0, n = mItemList.size(); i < n && nSelected == -1; ++i)
{ if (mItemList[i]->isSelected())
nSelected = i;
}
if ( nPos == THUMBNAILVIEW_ITEM_NOTFOUND ) return;
if ( nPos < mFilteredItemList.size() ) {
// keep it alive until after we have deleted it from the filter item list
std::unique_ptr<ThumbnailViewItem> xKeepAliveViewItem;
// delete item from the thumbnail list for (auto it = mItemList.begin(); it != mItemList.end(); ++it)
{ if ((*it)->mnId == nItemId)
{
xKeepAliveViewItem = std::move(*it);
mItemList.erase(it); break;
}
}
// delete item from the filter item list
ThumbnailValueItemList::iterator it = mFilteredItemList.begin();
::std::advance( it, nPos );
if ((*it)->isSelected())
{
(*it)->setSelection(false);
maItemStateHdl.Call(*it);
}
mFilteredItemList.erase( it );
mpStartSelRange = mFilteredItemList.end();
}
CalculateItemPositions();
if ( IsReallyVisible() && IsUpdateMode() )
Invalidate();
}
void ThumbnailView::Clear()
{
ImplDeleteItems();
// reset variables
mnFirstLine = 0;
CalculateItemPositions();
if ( IsReallyVisible() && IsUpdateMode() )
Invalidate();
}
void ThumbnailView::filterItems(const std::function<bool (const ThumbnailViewItem*)> &func)
{
mnFirstLine = 0; // start at the top of the list instead of the current position
maFilterFunc = func;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.