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  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <comphelper/lok.hxx>
21 #include <comphelper/base64.hxx>
22 #include <com/sun/star/document/XDocumentProperties.hpp>
23 #include <unotools/historyoptions.hxx>
24 #include <unotools/useroptions.hxx>
25 #include <tools/datetime.hxx>
26 #include <tools/urlobj.hxx>
27 #include <svl/inethist.hxx>
28 #include <vcl/pngwrite.hxx>
29 #include <vcl/svapp.hxx>
30 #include <officecfg/Office/Common.hxx>
31 
32 
33 #include <sfx2/app.hxx>
34 #include <sfxpicklist.hxx>
35 #include <sfx2/sfxsids.hrc>
36 #include <sfx2/event.hxx>
37 #include <sfx2/objsh.hxx>
38 #include <sfx2/docfile.hxx>
39 #include <openurlhint.hxx>
40 #include <sfx2/docfilt.hxx>
41 #include <sfx2/viewfrm.hxx>
42 
43 #include <rtl/instance.hxx>
44 
45 
46 using namespace ::com::sun::star::uno;
47 using namespace ::com::sun::star::beans;
48 using namespace ::com::sun::star::util;
49 
50 
51 namespace
52 {
53     class thePickListMutex
54         : public rtl::Static<osl::Mutex, thePickListMutex> {};
55 }
56 
57 class SfxPickListImpl : public SfxListener
58 {
59     /**
60      * Adds the given document to the pick list (recent documents) if it satisfies
61        certain requirements, e.g. being writable. Check implementation for requirement
62        details.
63      */
64     static void         AddDocumentToPickList( const SfxObjectShell* pDocShell );
65 
66 public:
67     SfxPickListImpl(SfxApplication& rApp);
68     virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
69 };
70 
AddDocumentToPickList(const SfxObjectShell * pDocSh)71 void SfxPickListImpl::AddDocumentToPickList( const SfxObjectShell* pDocSh )
72 {
73     if (pDocSh->IsAvoidRecentDocs() || comphelper::LibreOfficeKit::isActive())
74         return;
75 
76     SfxMedium *pMed = pDocSh->GetMedium();
77     if( !pMed )
78         return;
79 
80     // Unnamed Documents and embedded-Documents not in Picklist
81     if ( !pDocSh->HasName() ||
82             SfxObjectCreateMode::STANDARD != pDocSh->GetCreateMode() )
83         return;
84 
85     // Help not in History
86     INetURLObject aURL( pDocSh->IsDocShared() ? pDocSh->GetSharedFileURL() : pMed->GetOrigURL() );
87     if ( aURL.GetProtocol() == INetProtocol::VndSunStarHelp )
88         return;
89 
90     // add no document that forbids this
91     if ( !pMed->IsUpdatePickList() )
92         return;
93 
94     // ignore hidden documents
95     if ( !SfxViewFrame::GetFirst( pDocSh ) )
96         return;
97 
98     OUString  aTitle = pDocSh->GetTitle(SFX_TITLE_PICKLIST);
99     OUString  aFilter;
100     std::shared_ptr<const SfxFilter> pFilter = pMed->GetFilter();
101     if ( pFilter )
102         aFilter = pFilter->GetFilterName();
103 
104     std::optional<OUString> aThumbnail;
105 
106     // generate the thumbnail
107     //fdo#74834: only generate thumbnail for history if the corresponding option is not disabled in the configuration
108     if (!pDocSh->IsModified() && !Application::IsHeadlessModeEnabled() &&
109             officecfg::Office::Common::History::RecentDocsThumbnail::get())
110     {
111         // not modified => the document matches what is in the shell
112         const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pMed->GetItemSet(), SID_ENCRYPTIONDATA, false);
113         if ( pEncryptionDataItem )
114         {
115             // encrypted document, will show a generic document icon instead
116             aThumbnail = OUString();
117         }
118         else
119         {
120             BitmapEx aResultBitmap = pDocSh->GetPreviewBitmap();
121             if (!aResultBitmap.IsEmpty())
122             {
123                 SvMemoryStream aStream(65535, 65535);
124                 vcl::PNGWriter aWriter(aResultBitmap);
125                 if (aWriter.Write(aStream))
126                 {
127                     Sequence<sal_Int8> aSequence(static_cast<const sal_Int8*>(aStream.GetData()), aStream.Tell());
128                     OUStringBuffer aBuffer;
129                     ::comphelper::Base64::encode(aBuffer, aSequence);
130                     aThumbnail = aBuffer.makeStringAndClear();
131                 }
132             }
133         }
134     }
135 
136     // add to svtool history options
137     SvtHistoryOptions().AppendItem( EHistoryType::PickList,
138             aURL.GetURLNoPass( INetURLObject::DecodeMechanism::NONE ),
139             aFilter,
140             aTitle,
141             aThumbnail);
142 
143     if ( aURL.GetProtocol() == INetProtocol::File )
144         Application::AddToRecentDocumentList( aURL.GetURLNoPass( INetURLObject::DecodeMechanism::NONE ),
145                                                                  pFilter ? pFilter->GetMimeType() : OUString(),
146                                                                  pFilter ? pFilter->GetServiceName() : OUString() );
147 }
148 
SfxPickList(SfxApplication & rApp)149 SfxPickList::SfxPickList(SfxApplication& rApp)
150     : mxImpl(new SfxPickListImpl(rApp))
151 {
152 }
153 
~SfxPickList()154 SfxPickList::~SfxPickList()
155 {
156 }
157 
SfxPickListImpl(SfxApplication & rApp)158 SfxPickListImpl::SfxPickListImpl(SfxApplication& rApp)
159 {
160     StartListening(rApp);
161 }
162 
Notify(SfxBroadcaster &,const SfxHint & rHint)163 void SfxPickListImpl::Notify( SfxBroadcaster&, const SfxHint& rHint )
164 {
165     const SfxOpenUrlHint* pOpenUrlHint = dynamic_cast<const SfxOpenUrlHint*>(&rHint);
166     if ( pOpenUrlHint )
167     {
168         INetURLHistory::GetOrCreate()->PutUrl( INetURLObject( pOpenUrlHint->GetDocumentURL() ));
169     }
170 
171     const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint);
172     if ( !pEventHint )
173         return;
174 
175     // only ObjectShell-related events with media interest
176     SfxObjectShell* pDocSh = pEventHint->GetObjShell();
177     if( !pDocSh )
178         return;
179 
180     switch ( pEventHint->GetEventId() )
181     {
182         case SfxEventHintId::CreateDoc:
183         {
184             bool bAllowModif = pDocSh->IsEnableSetModified();
185             if ( bAllowModif )
186                 pDocSh->EnableSetModified( false );
187 
188             using namespace ::com::sun::star;
189             uno::Reference<document::XDocumentProperties> xDocProps(
190                 pDocSh->getDocProperties());
191             if (xDocProps.is()) {
192                 xDocProps->setAuthor( SvtUserOptions().GetFullName() );
193                 ::DateTime now( ::DateTime::SYSTEM );
194                 xDocProps->setCreationDate( now.GetUNODateTime() );
195             }
196 
197             if ( bAllowModif )
198                 pDocSh->EnableSetModified( bAllowModif );
199         }
200         break;
201 
202         case SfxEventHintId::OpenDoc:
203         case SfxEventHintId::SaveDocDone:
204         case SfxEventHintId::SaveAsDocDone:
205         case SfxEventHintId::SaveToDocDone:
206         case SfxEventHintId::CloseDoc:
207         {
208             AddDocumentToPickList(pDocSh);
209         }
210         break;
211 
212         case SfxEventHintId::SaveAsDoc:
213         {
214             SfxMedium *pMedium = pDocSh->GetMedium();
215             if (!pMedium)
216                 return;
217 
218             // We're starting a "Save As". Add the current document (if it's
219             // not a "new" document) to the "Recent Documents" list before we
220             // switch to the new path.
221             // If the current document is new, path will be empty.
222             OUString path = pMedium->GetOrigURL();
223             if (!path.isEmpty())
224             {
225                 AddDocumentToPickList(pDocSh);
226             }
227         }
228         break;
229         default: break;
230     }
231 }
232 
233 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
234