Line data Source code
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 <config_features.h>
21 :
22 : #include <com/sun/star/document/XDocumentProperties.hpp>
23 : #include <unotools/historyoptions.hxx>
24 : #include <unotools/useroptions.hxx>
25 : #include <tools/urlobj.hxx>
26 : #include <framework/menuconfiguration.hxx>
27 : #include <sax/tools/converter.hxx>
28 : #include <svl/inethist.hxx>
29 : #include <svl/stritem.hxx>
30 : #include <svl/eitem.hxx>
31 : #include <vcl/gdimtf.hxx>
32 : #include <vcl/pngwrite.hxx>
33 : #include <osl/file.hxx>
34 : #include <unotools/localfilehelper.hxx>
35 : #include <cppuhelper/implbase1.hxx>
36 :
37 :
38 :
39 : #include <sfx2/app.hxx>
40 : #include "sfxpicklist.hxx"
41 : #include <sfx2/sfxuno.hxx>
42 : #include "sfxtypes.hxx"
43 : #include <sfx2/request.hxx>
44 : #include <sfx2/sfxsids.hrc>
45 : #include <sfx2/sfx.hrc>
46 : #include <sfx2/event.hxx>
47 : #include <sfx2/objsh.hxx>
48 : #include <sfx2/bindings.hxx>
49 : #include <sfx2/docfile.hxx>
50 : #include "objshimp.hxx"
51 : #include <stringhint.hxx>
52 : #include <sfx2/docfilt.hxx>
53 :
54 : #include <rtl/instance.hxx>
55 :
56 : #include <algorithm>
57 :
58 :
59 :
60 : using namespace ::com::sun::star::uno;
61 : using namespace ::com::sun::star::beans;
62 : using namespace ::com::sun::star::util;
63 :
64 :
65 :
66 : class StringLength : public ::cppu::WeakImplHelper1< XStringWidth >
67 : {
68 : public:
69 208 : StringLength() {}
70 416 : virtual ~StringLength() {}
71 :
72 : // XStringWidth
73 0 : sal_Int32 SAL_CALL queryStringWidth( const OUString& aString )
74 : throw (::com::sun::star::uno::RuntimeException, std::exception) SAL_OVERRIDE
75 : {
76 0 : return aString.getLength();
77 : }
78 : };
79 :
80 0 : void SfxPickList::CreatePicklistMenuTitle( Menu* pMenu, sal_uInt16 nItemId, const OUString& aURLString, sal_uInt32 nNo )
81 : {
82 0 : OUStringBuffer aPickEntry;
83 :
84 0 : if ( nNo < 9 )
85 : {
86 0 : aPickEntry.append('~');
87 0 : aPickEntry.append(OUString::number(nNo + 1));
88 : }
89 0 : else if ( nNo == 9 )
90 0 : aPickEntry.append("1~0");
91 : else
92 0 : aPickEntry.append(OUString::number(nNo + 1));
93 0 : aPickEntry.append(": ");
94 :
95 0 : INetURLObject aURL( aURLString );
96 0 : OUString aTipHelpText;
97 0 : OUString aAccessibleName = aPickEntry.toString();
98 :
99 0 : if ( aURL.GetProtocol() == INetProtocol::File )
100 : {
101 : // Do handle file URL differently => convert it to a system
102 : // path and abbreviate it with a special function:
103 0 : OUString aFileSystemPath( aURL.getFSysPath( INetURLObject::FSYS_DETECT ) );
104 :
105 0 : OUString aSystemPath( aFileSystemPath );
106 0 : OUString aCompactedSystemPath;
107 :
108 0 : aTipHelpText = aSystemPath;
109 0 : aAccessibleName += aSystemPath;
110 0 : oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, NULL );
111 0 : if ( !nError )
112 0 : aPickEntry.append( aCompactedSystemPath );
113 : else
114 0 : aPickEntry.append( aFileSystemPath );
115 :
116 0 : if ( aPickEntry.getLength() > 50 )
117 : {
118 0 : aPickEntry.setLength( 47 );
119 0 : aPickEntry.append("...");
120 0 : }
121 : }
122 : else
123 : {
124 : // Use INetURLObject to abbreviate all other URLs
125 0 : OUString aShortURL;
126 0 : aShortURL = aURL.getAbbreviated( m_xStringLength, 46, INetURLObject::DECODE_UNAMBIGUOUS );
127 0 : aPickEntry.append(aShortURL);
128 0 : aTipHelpText = aURLString;
129 0 : aAccessibleName += aURLString;
130 : }
131 :
132 : // Set menu item text, tip help and accessible name
133 0 : pMenu->SetItemText( nItemId, aPickEntry.toString() );
134 0 : pMenu->SetTipHelpText( nItemId, aTipHelpText );
135 0 : pMenu->SetAccessibleName( nItemId, aAccessibleName );
136 0 : }
137 :
138 : namespace
139 : {
140 : class thePickListMutex
141 : : public rtl::Static<osl::Mutex, thePickListMutex> {};
142 : }
143 :
144 208 : void SfxPickList::RemovePickListEntries()
145 : {
146 208 : ::osl::MutexGuard aGuard( thePickListMutex::get() );
147 208 : for ( size_t i = 0; i < m_aPicklistVector.size(); i++ )
148 0 : delete m_aPicklistVector[i];
149 208 : m_aPicklistVector.clear();
150 208 : }
151 :
152 0 : SfxPickList::PickListEntry* SfxPickList::GetPickListEntry( sal_uInt32 nIndex )
153 : {
154 : OSL_ASSERT( m_aPicklistVector.size() > nIndex );
155 :
156 0 : if ( nIndex < m_aPicklistVector.size() )
157 0 : return m_aPicklistVector[ nIndex ];
158 : else
159 0 : return 0;
160 : }
161 :
162 4006 : void SfxPickList::AddDocumentToPickList( SfxObjectShell* pDocSh )
163 : {
164 4006 : SfxMedium *pMed = pDocSh->GetMedium();
165 4006 : if( !pMed )
166 856 : return;
167 :
168 : // Unnamed Documents and embedded-Documents not in Picklist
169 7212 : if ( !pDocSh->HasName() ||
170 3206 : SfxObjectCreateMode::STANDARD != pDocSh->GetCreateMode() )
171 811 : return;
172 :
173 : // Help not in History
174 3195 : INetURLObject aURL( pDocSh->IsDocShared() ? pDocSh->GetSharedFileURL() : OUString( pMed->GetOrigURL() ) );
175 3195 : if ( aURL.GetProtocol() == INetProtocol::VndSunStarHelp )
176 0 : return;
177 :
178 3195 : if ( !pMed->IsUpdatePickList() )
179 25 : return;
180 :
181 : // add no document that forbids this (for example Message-Body)
182 3170 : SFX_ITEMSET_ARG( pMed->GetItemSet(), pPicklistItem, SfxBoolItem, SID_PICKLIST, false );
183 3170 : if ( pPicklistItem && !pPicklistItem->GetValue() )
184 0 : return;
185 :
186 : // ignore hidden documents
187 3170 : if ( !SfxViewFrame::GetFirst( pDocSh, true ) )
188 20 : return;
189 :
190 6300 : OUString aTitle = pDocSh->GetTitle(SFX_TITLE_PICKLIST);
191 6300 : OUString aFilter;
192 3150 : const SfxFilter* pFilter = pMed->GetOrigFilter();
193 3150 : if ( pFilter )
194 3150 : aFilter = pFilter->GetFilterName();
195 :
196 : // generate a thumbnail
197 6300 : OUString aThumbnail;
198 : // don't generate thumbnail when in headless mode, or on non-desktop (?)
199 : #if HAVE_FEATURE_DESKTOP
200 3150 : SFX_ITEMSET_ARG( pMed->GetItemSet(), pEncryptionDataItem, SfxUnoAnyItem, SID_ENCRYPTIONDATA, false );
201 :
202 3150 : if (!pDocSh->IsModified() && !pEncryptionDataItem && !Application::IsHeadlessModeEnabled())
203 : {
204 : // not modified => the document matches what is in the shell
205 1 : std::shared_ptr<GDIMetaFile> xMetaFile = pDocSh->GetPreviewMetaFile();
206 2 : BitmapEx aResultBitmap;
207 1 : if (xMetaFile->CreateThumbnail(aResultBitmap))
208 : {
209 1 : SvMemoryStream aStream(65535, 65535);
210 2 : vcl::PNGWriter aWriter(aResultBitmap);
211 1 : if (aWriter.Write(aStream))
212 : {
213 1 : Sequence<sal_Int8> aSequence(static_cast<const sal_Int8*>(aStream.GetData()), aStream.Tell());
214 2 : OUStringBuffer aBuffer;
215 1 : ::sax::Converter::encodeBase64(aBuffer, aSequence);
216 2 : aThumbnail = aBuffer.makeStringAndClear();
217 1 : }
218 1 : }
219 : }
220 : #endif
221 : // add to svtool history options
222 : SvtHistoryOptions().AppendItem( ePICKLIST,
223 : aURL.GetURLNoPass( INetURLObject::NO_DECODE ),
224 : aFilter,
225 : aTitle,
226 : OUString(),
227 3150 : aThumbnail);
228 :
229 3150 : if ( aURL.GetProtocol() == INetProtocol::File )
230 : Application::AddToRecentDocumentList( aURL.GetURLNoPass( INetURLObject::NO_DECODE ),
231 3150 : (pFilter) ? pFilter->GetMimeType() : OUString(),
232 9450 : (pFilter) ? pFilter->GetServiceName() : OUString() );
233 : }
234 :
235 208 : SfxPickList& SfxPickList::Get()
236 : {
237 208 : static SfxPickList aUniqueInstance(SvtHistoryOptions().GetSize(ePICKLIST));
238 208 : return aUniqueInstance;
239 : }
240 :
241 208 : SfxPickList::SfxPickList( sal_uInt32 nAllowedMenuSize ) :
242 208 : m_nAllowedMenuSize( nAllowedMenuSize )
243 : {
244 208 : m_xStringLength = new StringLength;
245 208 : m_nAllowedMenuSize = ::std::min( m_nAllowedMenuSize, (sal_uInt32)PICKLIST_MAXSIZE );
246 208 : StartListening( *SfxGetpApp() );
247 208 : }
248 :
249 416 : SfxPickList::~SfxPickList()
250 : {
251 208 : RemovePickListEntries();
252 208 : }
253 :
254 0 : void SfxPickList::CreatePickListEntries()
255 : {
256 0 : RemovePickListEntries();
257 :
258 : // Reading the pick list
259 0 : Sequence< Sequence< PropertyValue > > seqPicklist = SvtHistoryOptions().GetList( ePICKLIST );
260 :
261 0 : sal_uInt32 nCount = seqPicklist.getLength();
262 0 : sal_uInt32 nEntries = ::std::min( m_nAllowedMenuSize, nCount );
263 :
264 0 : for( sal_uInt32 nItem=0; nItem < nEntries; ++nItem )
265 : {
266 0 : Sequence< PropertyValue > seqPropertySet = seqPicklist[ nItem ];
267 :
268 0 : INetURLObject aURL;
269 0 : OUString sURL;
270 0 : OUString sFilter;
271 0 : OUString sTitle;
272 :
273 0 : sal_uInt32 nPropertyCount = seqPropertySet.getLength();
274 0 : for( sal_uInt32 nProperty=0; nProperty<nPropertyCount; ++nProperty )
275 : {
276 0 : if( seqPropertySet[nProperty].Name == HISTORY_PROPERTYNAME_URL )
277 : {
278 0 : seqPropertySet[nProperty].Value >>= sURL;
279 : }
280 0 : else if( seqPropertySet[nProperty].Name == HISTORY_PROPERTYNAME_FILTER )
281 : {
282 0 : seqPropertySet[nProperty].Value >>= sFilter;
283 : }
284 0 : else if( seqPropertySet[nProperty].Name == HISTORY_PROPERTYNAME_TITLE )
285 : {
286 0 : seqPropertySet[nProperty].Value >>= sTitle;
287 : }
288 : }
289 :
290 0 : aURL.SetSmartURL( sURL );
291 0 : aURL.SetPass( OUString() );
292 :
293 0 : PickListEntry *pPick = new PickListEntry( aURL.GetMainURL( INetURLObject::NO_DECODE ), sFilter, sTitle );
294 0 : m_aPicklistVector.push_back( pPick );
295 0 : }
296 0 : }
297 :
298 0 : void SfxPickList::CreateMenuEntries( Menu* pMenu )
299 : {
300 0 : ::osl::MutexGuard aGuard( thePickListMutex::get() );
301 :
302 : static bool bPickListMenuInitializing = false;
303 :
304 0 : if ( bPickListMenuInitializing ) // method is not reentrant!
305 0 : return;
306 :
307 0 : bPickListMenuInitializing = true;
308 0 : CreatePickListEntries();
309 :
310 0 : for ( sal_uInt16 nId = START_ITEMID_PICKLIST; nId <= END_ITEMID_PICKLIST; ++nId )
311 0 : pMenu->RemoveItem( pMenu->GetItemPos( nId ) );
312 :
313 0 : if ( pMenu->GetItemType( pMenu->GetItemCount()-1 ) == MenuItemType::SEPARATOR )
314 0 : pMenu->RemoveItem( pMenu->GetItemCount()-1 );
315 :
316 0 : if ( m_aPicklistVector.size() > 0 &&
317 0 : pMenu->GetItemType( pMenu->GetItemCount()-1 )
318 0 : != MenuItemType::SEPARATOR && m_nAllowedMenuSize )
319 0 : pMenu->InsertSeparator();
320 :
321 0 : OUString aEmptyString;
322 0 : for ( size_t i = 0; i < m_aPicklistVector.size(); i++ )
323 : {
324 0 : PickListEntry* pEntry = GetPickListEntry( i );
325 :
326 0 : pMenu->InsertItem( (sal_uInt16)(START_ITEMID_PICKLIST + i), aEmptyString );
327 0 : CreatePicklistMenuTitle( pMenu, (sal_uInt16)(START_ITEMID_PICKLIST + i), pEntry->aName, i );
328 : }
329 :
330 0 : bPickListMenuInitializing = false;
331 : }
332 :
333 0 : void SfxPickList::ExecuteEntry( sal_uInt32 nIndex )
334 : {
335 0 : ::osl::ClearableMutexGuard aGuard( thePickListMutex::get() );
336 :
337 0 : PickListEntry *pPick = SfxPickList::Get().GetPickListEntry( nIndex );
338 :
339 0 : if ( pPick )
340 : {
341 0 : SfxRequest aReq( SID_OPENDOC, SfxCallMode::ASYNCHRON, SfxGetpApp()->GetPool() );
342 0 : aReq.AppendItem( SfxStringItem( SID_FILE_NAME, pPick->aName ));
343 0 : aReq.AppendItem( SfxStringItem( SID_REFERER, "private:user" ) );
344 0 : aReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_default" ) );
345 0 : OUString aFilter( pPick->aFilter );
346 0 : aGuard.clear();
347 :
348 0 : sal_Int32 nPos = aFilter.indexOf('|');
349 0 : if( nPos != -1 )
350 : {
351 0 : OUString aOptions(aFilter.copy(nPos+1));
352 0 : aFilter = aFilter.copy( 0, nPos );
353 0 : aReq.AppendItem( SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions));
354 : }
355 :
356 0 : aReq.AppendItem(SfxStringItem( SID_FILTER_NAME, aFilter ));
357 0 : aReq.AppendItem( SfxBoolItem( SID_TEMPLATE, false ) );
358 0 : SfxGetpApp()->ExecuteSlot( aReq );
359 0 : }
360 0 : }
361 :
362 0 : void SfxPickList::ExecuteMenuEntry( sal_uInt16 nId )
363 : {
364 0 : ExecuteEntry( (sal_uInt32)( nId - START_ITEMID_PICKLIST ) );
365 0 : }
366 :
367 43350 : void SfxPickList::Notify( SfxBroadcaster&, const SfxHint& rHint )
368 : {
369 43350 : const SfxStringHint* pStringHint = dynamic_cast<const SfxStringHint*>(&rHint);
370 43350 : if ( pStringHint )
371 : {
372 2442 : if ( pStringHint->GetId() == SID_OPENURL )
373 2442 : INetURLHistory::GetOrCreate()->PutUrl( INetURLObject( pStringHint->GetObject() ));
374 : }
375 :
376 43350 : const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint);
377 43350 : if ( pEventHint )
378 : {
379 : // only ObjectShell-related events with media interest
380 28011 : SfxObjectShell* pDocSh = pEventHint->GetObjShell();
381 28011 : if( !pDocSh )
382 0 : return;
383 :
384 28011 : switch ( pEventHint->GetEventId() )
385 : {
386 : case SFX_EVENT_CREATEDOC:
387 : {
388 564 : bool bAllowModif = pDocSh->IsEnableSetModified();
389 564 : if ( bAllowModif )
390 564 : pDocSh->EnableSetModified( false );
391 :
392 : using namespace ::com::sun::star;
393 : uno::Reference<document::XDocumentProperties> xDocProps(
394 564 : pDocSh->getDocProperties());
395 564 : if (xDocProps.is()) {
396 564 : xDocProps->setAuthor( SvtUserOptions().GetFullName() );
397 564 : ::DateTime now( ::DateTime::SYSTEM );
398 564 : xDocProps->setCreationDate( now.GetUNODateTime() );
399 : }
400 :
401 564 : if ( bAllowModif )
402 564 : pDocSh->EnableSetModified( bAllowModif );
403 : }
404 564 : break;
405 :
406 : case SFX_EVENT_OPENDOC:
407 : {
408 54 : AddDocumentToPickList(pDocSh);
409 : }
410 54 : break;
411 :
412 : case SFX_EVENT_SAVEDOCDONE:
413 : case SFX_EVENT_SAVEASDOCDONE:
414 : case SFX_EVENT_SAVETODOCDONE:
415 : case SFX_EVENT_CLOSEDOC:
416 : {
417 3944 : AddDocumentToPickList(pDocSh);
418 : }
419 3944 : break;
420 :
421 : case SFX_EVENT_SAVEASDOC:
422 : {
423 15 : SfxMedium *pMedium = pDocSh->GetMedium();
424 15 : if (!pMedium)
425 0 : return;
426 :
427 : // We're starting a "Save As". Add the current document (if it's
428 : // not a "new" document) to the "Recent Documents" list before we
429 : // switch to the new path.
430 : // If the current document is new, path will be empty.
431 15 : OUString path = pMedium->GetOrigURL();
432 15 : if (!path.isEmpty())
433 : {
434 8 : AddDocumentToPickList(pDocSh);
435 15 : }
436 : }
437 15 : break;
438 : }
439 : }
440 : }
441 :
442 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|