LibreOffice Module sfx2 (master) 1
recentdocsviewitem.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
10#include <com/sun/star/beans/XPropertySet.hpp>
11#include <com/sun/star/embed/ElementModes.hpp>
12#include <com/sun/star/embed/StorageFactory.hpp>
13#include <com/sun/star/frame/Desktop.hpp>
14#include <com/sun/star/util/URLTransformer.hpp>
15
16#include <comphelper/base64.hxx>
20#include <i18nutil/paper.hxx>
21#include <officecfg/Office/Common.hxx>
22#include <recentdocsview.hxx>
25#include <tools/stream.hxx>
26#include <tools/urlobj.hxx>
28#include <vcl/event.hxx>
30#include <vcl/ptrstyle.hxx>
31#include <vcl/virdev.hxx>
32
33#include <map>
34
35#include <bitmaps.hlst>
37
38using namespace basegfx;
39using namespace com::sun::star;
40using namespace com::sun::star::uno;
41using namespace drawinglayer::primitive2d;
42using namespace drawinglayer::processor2d;
43
44namespace
45{
46bool IsDocEncrypted(const OUString& rURL)
47{
48 bool bIsEncrypted = false;
49
50 try
51 {
52 auto xFactory = embed::StorageFactory::create(comphelper::getProcessComponentContext());
53 auto xStorage(xFactory->createInstanceWithArguments(
54 { uno::Any(rURL), uno::Any(embed::ElementModes::READ) }));
55 if (uno::Reference<beans::XPropertySet> xStorageProps{ xStorage, uno::UNO_QUERY })
56 {
57 try
58 {
59 xStorageProps->getPropertyValue("HasEncryptedEntries") >>= bIsEncrypted;
60 }
61 catch (uno::Exception&)
62 {
63 }
64 }
65 }
66 catch (const uno::Exception&)
67 {
68 TOOLS_WARN_EXCEPTION("sfx", "caught exception trying to find out if doc <"
69 << rURL << "> is encrypted:");
70 }
71
72 return bIsEncrypted;
73}
74
75using Ext2IconMap = std::map<sfx2::ApplicationType, OUString>;
76BitmapEx Url2Icon(std::u16string_view rURL, const Ext2IconMap& rExtToIcon, const OUString& sDefault)
77{
78 auto it = std::find_if(rExtToIcon.begin(), rExtToIcon.end(),
79 [aExt = INetURLObject(rURL).getExtension()](const auto& r)
80 { return sfx2::RecentDocsView::typeMatchesExtension(r.first, aExt); });
81
82 return BitmapEx(it != rExtToIcon.end() ? it->second : sDefault);
83};
84
85BitmapEx getDefaultThumbnail(const OUString& rURL)
86{
87 static const Ext2IconMap BitmapForExtension
88 = { { sfx2::ApplicationType::TYPE_WRITER, SFX_FILE_THUMBNAIL_TEXT },
89 { sfx2::ApplicationType::TYPE_CALC, SFX_FILE_THUMBNAIL_SHEET },
90 { sfx2::ApplicationType::TYPE_IMPRESS, SFX_FILE_THUMBNAIL_PRESENTATION },
91 { sfx2::ApplicationType::TYPE_DRAW, SFX_FILE_THUMBNAIL_DRAWING },
92 { sfx2::ApplicationType::TYPE_DATABASE, SFX_FILE_THUMBNAIL_DATABASE },
93 { sfx2::ApplicationType::TYPE_MATH, SFX_FILE_THUMBNAIL_MATH } };
94
95 static const Ext2IconMap EncryptedBitmapForExtension
96 = { { sfx2::ApplicationType::TYPE_WRITER, BMP_128X128_WRITER_DOC },
97 { sfx2::ApplicationType::TYPE_CALC, BMP_128X128_CALC_DOC },
98 { sfx2::ApplicationType::TYPE_IMPRESS, BMP_128X128_IMPRESS_DOC },
99 { sfx2::ApplicationType::TYPE_DRAW, BMP_128X128_DRAW_DOC },
100 // You can't save a database file with encryption -> no respective icon
101 { sfx2::ApplicationType::TYPE_MATH, BMP_128X128_MATH_DOC } };
102
103 const std::map<sfx2::ApplicationType, OUString>& rWhichMap
104 = IsDocEncrypted(rURL) ? EncryptedBitmapForExtension : BitmapForExtension;
105
106 return Url2Icon(rURL, rWhichMap, SFX_FILE_THUMBNAIL_DEFAULT);
107}
108
109BitmapEx getModuleOverlay(std::u16string_view rURL)
110{
111 static const Ext2IconMap OverlayBitmapForExtension
112 = { { sfx2::ApplicationType::TYPE_WRITER, SFX_FILE_OVERLAY_TEXT },
113 { sfx2::ApplicationType::TYPE_CALC, SFX_FILE_OVERLAY_SHEET },
114 { sfx2::ApplicationType::TYPE_IMPRESS, SFX_FILE_OVERLAY_PRESENTATION },
115 { sfx2::ApplicationType::TYPE_DRAW, SFX_FILE_OVERLAY_DRAWING },
116 { sfx2::ApplicationType::TYPE_DATABASE, SFX_FILE_OVERLAY_DATABASE },
117 { sfx2::ApplicationType::TYPE_MATH, SFX_FILE_OVERLAY_MATH } };
118
119 return Url2Icon(rURL, OverlayBitmapForExtension, SFX_FILE_OVERLAY_DEFAULT);
120}
121};
122
124 const OUString &rTitle, std::u16string_view const sThumbnailBase64,
125 sal_uInt16 const nId, tools::Long const nThumbnailSize,
126 bool const isReadOnly, bool const isPinned)
127 : ThumbnailViewItem(rView, nId),
128 mrParentView(rView),
129 maURL(rURL),
130 m_isReadOnly(isReadOnly),
131 m_bRemoveIconHighlighted(false),
132 m_aRemoveRecentBitmap(BMP_RECENTDOC_REMOVE),
133 m_aRemoveRecentBitmapHighlighted(BMP_RECENTDOC_REMOVE_HIGHLIGHTED)
134{
135 OUString aTitle(rTitle);
136 INetURLObject aURLObj(rURL);
137
138 if( aURLObj.GetProtocol() == INetProtocol::File )
139 m_sHelpText = aURLObj.getFSysPath(FSysStyle::Detect);
140 if( m_sHelpText.isEmpty() )
141 m_sHelpText = aURLObj.GetURLNoPass();
142
143 if (aTitle.isEmpty())
145
146 BitmapEx aThumbnail;
147
148 //fdo#74834: only load thumbnail if the corresponding option is not disabled in the configuration
149 if (officecfg::Office::Common::History::RecentDocsThumbnail::get())
150 {
151 if (!sThumbnailBase64.empty())
152 {
153 Sequence<sal_Int8> aDecoded;
154 comphelper::Base64::decode(aDecoded, sThumbnailBase64);
155
156 SvMemoryStream aStream(aDecoded.getArray(), aDecoded.getLength(), StreamMode::READ);
157 vcl::PngImageReader aReader(aStream);
158 aThumbnail = aReader.read();
159 }
161 aURLObj.getExtension()))
162 {
163 aThumbnail
164 = BitmapEx(nThumbnailSize > 192 ? SFX_THUMBNAIL_BASE_256 : SFX_THUMBNAIL_BASE_192);
165 }
166 }
167
168 if (aThumbnail.IsEmpty())
169 {
170 // 1. Thumbnail absent: get the default thumbnail, checking for encryption.
171 BitmapEx aExt(getDefaultThumbnail(rURL));
172 Size aExtSize(aExt.GetSizePixel());
173
174 // attempt to make it appear as if it is on a piece of paper
175 tools::Long nPaperHeight;
176 tools::Long nPaperWidth;
179 {
180 // Swap width and height (PAPER_SCREEN_4_3 definition make it needed)
182 nPaperHeight = aInfo.getWidth();
183 nPaperWidth = aInfo.getHeight();
184 }
185 else
186 {
188 nPaperHeight = aInfo.getHeight();
189 nPaperWidth = aInfo.getWidth();
190 }
191 double ratio = double(nThumbnailSize) / double(std::max(nPaperHeight, nPaperWidth));
192 Size aThumbnailSize(std::round(nPaperWidth * ratio), std::round(nPaperHeight * ratio));
193
194 if (aExtSize.Width() > aThumbnailSize.Width() || aExtSize.Height() > aThumbnailSize.Height())
195 {
196 aExt = TemplateLocalView::scaleImg(aExt, aThumbnailSize.Width(), aThumbnailSize.Height());
197 aExtSize = aExt.GetSizePixel();
198 }
199
200 // create empty, and copy the default thumbnail in
201 sal_uInt8 nAlpha = 255;
202 aThumbnail = BitmapEx(Bitmap(aThumbnailSize, vcl::PixelFormat::N24_BPP), AlphaMask(aThumbnailSize, &nAlpha));
203
204 aThumbnail.CopyPixel(
205 ::tools::Rectangle(Point((aThumbnailSize.Width() - aExtSize.Width()) / 2, (aThumbnailSize.Height() - aExtSize.Height()) / 2), aExtSize),
206 ::tools::Rectangle(Point(0, 0), aExtSize),
207 &aExt);
208 }
209 else
210 {
211 // 2. Thumbnail present: it's unencrypted document -> add a module overlay.
212 // Pre-scale the thumbnail to the final size before applying the overlay
213 aThumbnail = TemplateLocalView::scaleImg(aThumbnail, nThumbnailSize, nThumbnailSize);
214
215 BitmapEx aModule = getModuleOverlay(rURL);
216 aModule.Scale(Size(48,48)); //tdf#155200: Thumbnails don't change their size so overlay must not too
217 if (!aModule.IsEmpty())
218 {
219 const Size aSize(aThumbnail.GetSizePixel());
220 const Size aOverlaySize(aModule.GetSizePixel());
222 pVirDev->SetOutputSizePixel(aSize);
223 pVirDev->DrawBitmapEx(Point(), aThumbnail);
224 pVirDev->DrawBitmapEx(Point(aSize.Width() - aOverlaySize.Width() - 5,
225 aSize.Height() - aOverlaySize.Height() - 5),
226 aModule);
227 aThumbnail = pVirDev->GetBitmapEx(Point(), aSize);
228 }
229 }
230
231 maTitle = aTitle;
232 maPreview1 = aThumbnail;
234}
235
237{
239
240 if (bVisible && getRemoveIconArea().Contains(rPoint))
241 {
243 aRect.Union(getRemoveIconArea());
244
246 }
247 else
248 {
250 aRect.Union(getRemoveIconArea());
251
253 }
254
255 return aRect;
256}
257
259{
262
263 return ::tools::Rectangle(
265 aSize);
266}
267
269{
270 return m_sHelpText;
271}
272
274{
275 ThumbnailViewItem::Paint(pProcessor, pAttrs);
276
277 // paint the remove icon when hovered
278 if (isHighlighted())
279 {
281
282 Point aIconPos(getRemoveIconArea().TopLeft());
283
286 B2DPoint(aIconPos.X(), aIconPos.Y())));
287
288 pProcessor->process(aSeq);
289 }
290}
291
293{
294 if (rMEvt.IsLeft())
295 {
296 if (getRemoveIconArea().Contains(rMEvt.GetPosPixel()))
297 {
298 SvtHistoryOptions::DeleteItem(EHistoryType::PickList, maURL);
300 return;
301 }
302
303 const ::tools::Rectangle aPinPosRectangle(maPinPos, maPinnedDocumentBitmap.GetSizePixel());
304 if (aPinPosRectangle.Contains(rMEvt.GetPosPixel()))
305 {
306 SvtHistoryOptions::TogglePinItem(EHistoryType::PickList, maURL);
308 return;
309 }
310
311 OpenDocument();
312 return;
313 }
314}
315
317{
318 // show busy mouse pointer
319 mrParentView.SetPointer(PointerStyle::Wait);
321
323 css::util::URL aTargetURL;
325
326 uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(::comphelper::getProcessComponentContext());
327
328 aTargetURL.Complete = maURL;
329 Reference<util::XURLTransformer> xTrans(util::URLTransformer::create(::comphelper::getProcessComponentContext()));
330 xTrans->parseStrict(aTargetURL);
331
332 aArgsList = { comphelper::makePropertyValue("Referer", OUString("private:user")),
333 // documents will never be opened as templates
334 comphelper::makePropertyValue("AsTemplate", false) };
335 if (m_isReadOnly) // tdf#149170 only add if true
336 {
337 aArgsList.realloc(aArgsList.size()+1);
338 aArgsList.getArray()[aArgsList.size()-1] = comphelper::makePropertyValue("ReadOnly", true);
339 }
340
341 xDispatch = xDesktop->queryDispatch(aTargetURL, "_default", 0);
342
343 if (!xDispatch.is())
344 return;
345
346 // Call dispatch asynchronously as we can be destroyed while dispatch is
347 // executed. VCL is not able to survive this as it wants to call listeners
348 // after select!!!
349 sfx2::LoadRecentFile *const pLoadRecentFile = new sfx2::LoadRecentFile;
350 pLoadRecentFile->xDispatch = xDispatch;
351 pLoadRecentFile->aTargetURL = aTargetURL;
352 pLoadRecentFile->aArgSeq = aArgsList;
353 pLoadRecentFile->pView = &mrParentView;
354
355 mrParentView.PostLoadRecentUsedFile(pLoadRecentFile);
356}
357
358/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool Scale(const Size &rNewSize, BmpScaleFlag nScaleFlag=BmpScaleFlag::Default)
bool CopyPixel(const tools::Rectangle &rRectDst, const tools::Rectangle &rRectSrc, const BitmapEx *pBmpExSrc)
bool IsEmpty() const
const Size & GetSizePixel() const
OUString getExtension(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
OUString GetLastName(DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
INetProtocol GetProtocol() const
OUString getFSysPath(FSysStyle eStyle, sal_Unicode *pDelimiter=nullptr) const
OUString GetURLNoPass(DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
const Point & GetPosPixel() const
bool IsLeft() const
static PaperInfo getSystemDefaultPaper()
tools::Long getWidth() const
tools::Long getHeight() const
constexpr tools::Long Y() const
constexpr tools::Long X() const
BitmapEx m_aRemoveRecentBitmapHighlighted
virtual tools::Rectangle updateHighlight(bool bVisible, const Point &rPoint) override
Updates own highlight status based on the aPoint position.
virtual void MouseButtonUp(const MouseEvent &rMEvt) override
RecentDocsViewItem(sfx2::RecentDocsView &rView, const OUString &rURL, const OUString &rTitle, std::u16string_view sThumbnailBase64, sal_uInt16 nId, tools::Long nThumbnailSize, bool isReadOnly, bool isPinned)
tools::Rectangle getRemoveIconArea() const
Return area where is the icon to remove document from the recent documents.
virtual OUString getHelpText() const override
Text to be used for the tooltip.
bool m_bRemoveIconHighlighted
Is the icon that the user can click to remove the document from the recent documents highlighted?
void OpenDocument()
Called when the user clicks a document - it will open it.
virtual void Paint(drawinglayer::processor2d::BaseProcessor2D *pProcessor, const ThumbnailItemAttributes *pAttrs) override
sfx2::RecentDocsView & mrParentView
constexpr tools::Long Height() const
constexpr tools::Long Width() const
static BitmapEx scaleImg(const BitmapEx &rImg, tools::Long width, tools::Long height)
bool isHighlighted() const
ThumbnailView & mrParent
virtual tools::Rectangle updateHighlight(bool bVisible, const Point &rPoint)
Updates own highlight status based on the aPoint position.
const tools::Rectangle & getDrawArea() const
virtual void Paint(drawinglayer::processor2d::BaseProcessor2D *pProcessor, const ThumbnailItemAttributes *pAttrs)
virtual void Reload()
Updates information in the view; used only in RecentDocsView ATM.
static void decode(css::uno::Sequence< sal_Int8 > &aPass, std::u16string_view sBuffer)
void process(const primitive2d::Primitive2DContainer &rSource)
void PostLoadRecentUsedFile(LoadRecentFile *pLoadRecentFile)
static bool typeMatchesExtension(ApplicationType type, std::u16string_view rExt)
constexpr tools::Long Top() const
constexpr tools::Long Right() const
tools::Rectangle & Union(const tools::Rectangle &rRect)
bool read(BitmapEx &rBitmap)
void SetPointer(PointerStyle ePointerStyle)
#define TOOLS_WARN_EXCEPTION(area, stream)
Reference< XDispatch > xDispatch
Reference< XSingleServiceFactory > xFactory
Sequence< sal_Int8 > aSeq
Definition: lnkbase2.cxx:83
void DeleteItem(EHistoryType eHistory, const OUString &sURL)
void TogglePinItem(EHistoryType eHistory, const OUString &sURL)
Reference< XComponentContext > getProcessComponentContext()
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
rtl::Reference< BasePrimitive2D > Primitive2DReference
long Long
sal_Int16 nId
PAPER_SCREEN_4_3
const URL maURL
css::uno::Sequence< css::beans::PropertyValue > aArgSeq
css::uno::Reference< css::frame::XDispatch > xDispatch
RecentDocsView * pView
css::util::URL aTargetURL
const int THUMBNAILVIEW_ITEM_CORNER
bool bVisible
unsigned char sal_uInt8
OUString aTargetURL