/* -*- 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/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "recentdocsviewitem.hxx" using namespace basegfx; using namespace com::sun::star; using namespace com::sun::star::uno; using namespace drawinglayer::primitive2d; using namespace drawinglayer::processor2d; namespace { bool IsDocEncrypted(const OUString& rURL) { bool bIsEncrypted = false; try { auto xFactory = embed::StorageFactory::create(comphelper::getProcessComponentContext()); auto xStorage(xFactory->createInstanceWithArguments( { uno::Any(rURL), uno::Any(embed::ElementModes::READ) })); if (uno::Reference xStorageProps{ xStorage, uno::UNO_QUERY }) { try { xStorageProps->getPropertyValue("HasEncryptedEntries") >>= bIsEncrypted; } catch (uno::Exception&) { } } } catch (const uno::Exception&) { TOOLS_WARN_EXCEPTION("sfx", "caught exception trying to find out if doc <" << rURL << "> is encrypted:"); } return bIsEncrypted; } using Ext2IconMap = std::map; BitmapEx Url2Icon(const OUString& rURL, const Ext2IconMap& rExtToIcon, const OUString& sDefault) { auto it = std::find_if(rExtToIcon.begin(), rExtToIcon.end(), [aExt = INetURLObject(rURL).getExtension()](const auto& r) { return sfx2::RecentDocsView::typeMatchesExtension(r.first, aExt); }); return BitmapEx(it != rExtToIcon.end() ? it->second : sDefault); }; BitmapEx getDefaultThumbnail(const OUString& rURL) { static const Ext2IconMap BitmapForExtension = { { sfx2::ApplicationType::TYPE_WRITER, SFX_FILE_THUMBNAIL_TEXT }, { sfx2::ApplicationType::TYPE_CALC, SFX_FILE_THUMBNAIL_SHEET }, { sfx2::ApplicationType::TYPE_IMPRESS, SFX_FILE_THUMBNAIL_PRESENTATION }, { sfx2::ApplicationType::TYPE_DRAW, SFX_FILE_THUMBNAIL_DRAWING }, { sfx2::ApplicationType::TYPE_DATABASE, SFX_FILE_THUMBNAIL_DATABASE }, { sfx2::ApplicationType::TYPE_MATH, SFX_FILE_THUMBNAIL_MATH } }; static const Ext2IconMap EncryptedBitmapForExtension = { { sfx2::ApplicationType::TYPE_WRITER, BMP_128X128_WRITER_DOC }, { sfx2::ApplicationType::TYPE_CALC, BMP_128X128_CALC_DOC }, { sfx2::ApplicationType::TYPE_IMPRESS, BMP_128X128_IMPRESS_DOC }, { sfx2::ApplicationType::TYPE_DRAW, BMP_128X128_DRAW_DOC }, // You can't save a database file with encryption -> no respective icon { sfx2::ApplicationType::TYPE_MATH, BMP_128X128_MATH_DOC } }; const std::map& rWhichMap = IsDocEncrypted(rURL) ? EncryptedBitmapForExtension : BitmapForExtension; return Url2Icon(rURL, rWhichMap, SFX_FILE_THUMBNAIL_DEFAULT); } BitmapEx getModuleOverlay(const OUString& rURL) { static const Ext2IconMap OverlayBitmapForExtension = { { sfx2::ApplicationType::TYPE_WRITER, SFX_FILE_OVERLAY_TEXT }, { sfx2::ApplicationType::TYPE_CALC, SFX_FILE_OVERLAY_SHEET }, { sfx2::ApplicationType::TYPE_IMPRESS, SFX_FILE_OVERLAY_PRESENTATION }, { sfx2::ApplicationType::TYPE_DRAW, SFX_FILE_OVERLAY_DRAWING }, { sfx2::ApplicationType::TYPE_DATABASE, SFX_FILE_OVERLAY_DATABASE }, { sfx2::ApplicationType::TYPE_MATH, SFX_FILE_OVERLAY_MATH } }; return Url2Icon(rURL, OverlayBitmapForExtension, SFX_FILE_OVERLAY_DEFAULT); } }; RecentDocsViewItem::RecentDocsViewItem(sfx2::RecentDocsView &rView, const OUString &rURL, const OUString &rTitle, std::u16string_view const sThumbnailBase64, sal_uInt16 const nId, tools::Long const nThumbnailSize, bool const isReadOnly) : ThumbnailViewItem(rView, nId), mrParentView(rView), maURL(rURL), m_isReadOnly(isReadOnly), m_bRemoveIconHighlighted(false), m_aRemoveRecentBitmap(BMP_RECENTDOC_REMOVE), m_aRemoveRecentBitmapHighlighted(BMP_RECENTDOC_REMOVE_HIGHLIGHTED) { OUString aTitle(rTitle); INetURLObject aURLObj(rURL); if( aURLObj.GetProtocol() == INetProtocol::File ) m_sHelpText = aURLObj.getFSysPath(FSysStyle::Detect); if( m_sHelpText.isEmpty() ) m_sHelpText = aURLObj.GetURLNoPass(); if (aTitle.isEmpty()) aTitle = aURLObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset); BitmapEx aThumbnail; //fdo#74834: only load thumbnail if the corresponding option is not disabled in the configuration if (officecfg::Office::Common::History::RecentDocsThumbnail::get()) { if (!sThumbnailBase64.empty()) { Sequence aDecoded; comphelper::Base64::decode(aDecoded, sThumbnailBase64); SvMemoryStream aStream(aDecoded.getArray(), aDecoded.getLength(), StreamMode::READ); vcl::PngImageReader aReader(aStream); aThumbnail = aReader.read(); } else if (sfx2::RecentDocsView::typeMatchesExtension(sfx2::ApplicationType::TYPE_DATABASE, aURLObj.getExtension())) { aThumbnail = BitmapEx(nThumbnailSize > 192 ? SFX_THUMBNAIL_BASE_256 : SFX_THUMBNAIL_BASE_192); } } if (aThumbnail.IsEmpty()) { // 1. Thumbnail absent: get the default thumbnail, checking for encryption. BitmapEx aExt(getDefaultThumbnail(rURL)); Size aExtSize(aExt.GetSizePixel()); // attempt to make it appear as if it is on a piece of paper tools::Long nPaperHeight; tools::Long nPaperWidth; if (sfx2::RecentDocsView::typeMatchesExtension( sfx2::ApplicationType::TYPE_IMPRESS, aURLObj.getExtension())) { // Swap width and height (PAPER_SCREEN_4_3 definition make it needed) PaperInfo aInfo(PAPER_SCREEN_4_3); nPaperHeight = aInfo.getWidth(); nPaperWidth = aInfo.getHeight(); } else { PaperInfo aInfo(PaperInfo::getSystemDefaultPaper()); nPaperHeight = aInfo.getHeight(); nPaperWidth = aInfo.getWidth(); } double ratio = double(nThumbnailSize) / double(std::max(nPaperHeight, nPaperWidth)); Size aThumbnailSize(std::round(nPaperWidth * ratio), std::round(nPaperHeight * ratio)); if (aExtSize.Width() > aThumbnailSize.Width() || aExtSize.Height() > aThumbnailSize.Height()) { aExt = TemplateLocalView::scaleImg(aExt, aThumbnailSize.Width(), aThumbnailSize.Height()); aExtSize = aExt.GetSizePixel(); } // create empty, and copy the default thumbnail in sal_uInt8 nAlpha = 255; aThumbnail = BitmapEx(Bitmap(aThumbnailSize, vcl::PixelFormat::N24_BPP), AlphaMask(aThumbnailSize, &nAlpha)); aThumbnail.CopyPixel( ::tools::Rectangle(Point((aThumbnailSize.Width() - aExtSize.Width()) / 2, (aThumbnailSize.Height() - aExtSize.Height()) / 2), aExtSize), ::tools::Rectangle(Point(0, 0), aExtSize), &aExt); } else { // 2. Thumbnail present: it's unencrypted document -> add a module overlay. // Pre-scale the thumbnail to the final size before applying the overlay aThumbnail = TemplateLocalView::scaleImg(aThumbnail, nThumbnailSize, nThumbnailSize); BitmapEx aModule = getModuleOverlay(rURL); if (!aModule.IsEmpty()) { const Size aSize(aThumbnail.GetSizePixel()); const Size aOverlaySize(aModule.GetSizePixel()); ScopedVclPtr pVirDev(VclPtr::Create()); pVirDev->SetOutputSizePixel(aSize); pVirDev->DrawBitmapEx(Point(), aThumbnail); pVirDev->DrawBitmapEx(Point(aSize.Width() - aOverlaySize.Width() - 5, aSize.Height() - aOverlaySize.Height() - 5), aModule); aThumbnail = pVirDev->GetBitmapEx(Point(), aSize); } } maTitle = aTitle; maPreview1 = aThumbnail; } ::tools::Rectangle RecentDocsViewItem::updateHighlight(bool bVisible, const Point& rPoint) { ::tools::Rectangle aRect(ThumbnailViewItem::updateHighlight(bVisible, rPoint)); if (bVisible && getRemoveIconArea().Contains(rPoint)) { if (!m_bRemoveIconHighlighted) aRect.Union(getRemoveIconArea()); m_bRemoveIconHighlighted = true; } else { if (m_bRemoveIconHighlighted) aRect.Union(getRemoveIconArea()); m_bRemoveIconHighlighted = false; } return aRect; } ::tools::Rectangle RecentDocsViewItem::getRemoveIconArea() const { ::tools::Rectangle aArea(getDrawArea()); Size aSize(m_aRemoveRecentBitmap.GetSizePixel()); return ::tools::Rectangle( Point(aArea.Right() - aSize.Width() - THUMBNAILVIEW_ITEM_CORNER, aArea.Top() + THUMBNAILVIEW_ITEM_CORNER), aSize); } OUString RecentDocsViewItem::getHelpText() const { return m_sHelpText; } void RecentDocsViewItem::Paint(drawinglayer::processor2d::BaseProcessor2D *pProcessor, const ThumbnailItemAttributes *pAttrs) { ThumbnailViewItem::Paint(pProcessor, pAttrs); // paint the remove icon when highlighted if (isHighlighted()) { drawinglayer::primitive2d::Primitive2DContainer aSeq(1); Point aIconPos(getRemoveIconArea().TopLeft()); aSeq[0] = drawinglayer::primitive2d::Primitive2DReference(new DiscreteBitmapPrimitive2D( m_bRemoveIconHighlighted ? m_aRemoveRecentBitmapHighlighted : m_aRemoveRecentBitmap, B2DPoint(aIconPos.X(), aIconPos.Y()))); pProcessor->process(aSeq); } } void RecentDocsViewItem::MouseButtonUp(const MouseEvent& rMEvt) { if (rMEvt.IsLeft()) { if (getRemoveIconArea().Contains(rMEvt.GetPosPixel())) { SvtHistoryOptions::DeleteItem(EHistoryType::PickList, maURL); mrParent.Reload(); return; } OpenDocument(); return; } } void RecentDocsViewItem::OpenDocument() { // show busy mouse pointer mrParentView.SetPointer(PointerStyle::Wait); mrParentView.Disable(); Reference xDispatch; css::util::URL aTargetURL; Sequence aArgsList; uno::Reference xDesktop = frame::Desktop::create(::comphelper::getProcessComponentContext()); aTargetURL.Complete = maURL; Reference xTrans(util::URLTransformer::create(::comphelper::getProcessComponentContext())); xTrans->parseStrict(aTargetURL); aArgsList = { comphelper::makePropertyValue("Referer", OUString("private:user")), comphelper::makePropertyValue("ReadOnly", m_isReadOnly), // documents will never be opened as templates comphelper::makePropertyValue("AsTemplate", false) }; xDispatch = xDesktop->queryDispatch(aTargetURL, "_default", 0); if (!xDispatch.is()) return; // Call dispatch asynchronously as we can be destroyed while dispatch is // executed. VCL is not able to survive this as it wants to call listeners // after select!!! sfx2::LoadRecentFile *const pLoadRecentFile = new sfx2::LoadRecentFile; pLoadRecentFile->xDispatch = xDispatch; pLoadRecentFile->aTargetURL = aTargetURL; pLoadRecentFile->aArgSeq = aArgsList; pLoadRecentFile->pView = &mrParentView; mrParentView.PostLoadRecentUsedFile(pLoadRecentFile); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */