summaryrefslogtreecommitdiffstats
path: root/sfx2/source/control/recentdocsviewitem.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source/control/recentdocsviewitem.cxx')
-rw-r--r--sfx2/source/control/recentdocsviewitem.cxx348
1 files changed, 348 insertions, 0 deletions
diff --git a/sfx2/source/control/recentdocsviewitem.cxx b/sfx2/source/control/recentdocsviewitem.cxx
new file mode 100644
index 000000000..44f103dfb
--- /dev/null
+++ b/sfx2/source/control/recentdocsviewitem.cxx
@@ -0,0 +1,348 @@
+/* -*- 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 <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/StorageFactory.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+#include <comphelper/base64.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <i18nutil/paper.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <recentdocsview.hxx>
+#include <sfx2/templatelocalview.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/historyoptions.hxx>
+#include <vcl/event.hxx>
+#include <vcl/filter/PngImageReader.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/virdev.hxx>
+
+#include <map>
+
+#include <bitmaps.hlst>
+#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<beans::XPropertySet> 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<sfx2::ApplicationType, OUString>;
+BitmapEx Url2Icon(std::u16string_view 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<sfx2::ApplicationType, OUString>& rWhichMap
+ = IsDocEncrypted(rURL) ? EncryptedBitmapForExtension : BitmapForExtension;
+
+ return Url2Icon(rURL, rWhichMap, SFX_FILE_THUMBNAIL_DEFAULT);
+}
+
+BitmapEx getModuleOverlay(std::u16string_view 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<sal_Int8> 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<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::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<frame::XDispatch> xDispatch;
+ css::util::URL aTargetURL;
+ Sequence<beans::PropertyValue> aArgsList;
+
+ uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(::comphelper::getProcessComponentContext());
+
+ aTargetURL.Complete = maURL;
+ Reference<util::XURLTransformer> xTrans(util::URLTransformer::create(::comphelper::getProcessComponentContext()));
+ xTrans->parseStrict(aTargetURL);
+
+ aArgsList = { comphelper::makePropertyValue("Referer", OUString("private:user")),
+ // documents will never be opened as templates
+ comphelper::makePropertyValue("AsTemplate", false) };
+ if (m_isReadOnly) // tdf#149170 only add if true
+ {
+ aArgsList.realloc(aArgsList.size()+1);
+ aArgsList.getArray()[aArgsList.size()-1] = comphelper::makePropertyValue("ReadOnly", true);
+ }
+
+ 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: */