1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_features.h>
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>
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>
54 #include <rtl/instance.hxx>
60 using namespace ::com::sun::star::uno
;
61 using namespace ::com::sun::star::beans
;
62 using namespace ::com::sun::star::util
;
66 class StringLength
: public ::cppu::WeakImplHelper1
< XStringWidth
>
70 virtual ~StringLength() {}
73 sal_Int32 SAL_CALL
queryStringWidth( const OUString
& aString
)
74 throw (::com::sun::star::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
76 return aString
.getLength();
80 void SfxPickList::CreatePicklistMenuTitle( Menu
* pMenu
, sal_uInt16 nItemId
, const OUString
& aURLString
, sal_uInt32 nNo
)
82 OUStringBuffer aPickEntry
;
86 aPickEntry
.append('~');
87 aPickEntry
.append(OUString::number(nNo
+ 1));
90 aPickEntry
.append("1~0");
92 aPickEntry
.append(OUString::number(nNo
+ 1));
93 aPickEntry
.append(": ");
95 INetURLObject
aURL( aURLString
);
96 OUString aTipHelpText
;
97 OUString aAccessibleName
= aPickEntry
.toString();
99 if ( aURL
.GetProtocol() == INetProtocol::File
)
101 // Do handle file URL differently => convert it to a system
102 // path and abbreviate it with a special function:
103 OUString
aFileSystemPath( aURL
.getFSysPath( INetURLObject::FSYS_DETECT
) );
105 OUString
aSystemPath( aFileSystemPath
);
106 OUString aCompactedSystemPath
;
108 aTipHelpText
= aSystemPath
;
109 aAccessibleName
+= aSystemPath
;
110 oslFileError nError
= osl_abbreviateSystemPath( aSystemPath
.pData
, &aCompactedSystemPath
.pData
, 46, NULL
);
112 aPickEntry
.append( aCompactedSystemPath
);
114 aPickEntry
.append( aFileSystemPath
);
116 if ( aPickEntry
.getLength() > 50 )
118 aPickEntry
.setLength( 47 );
119 aPickEntry
.append("...");
124 // Use INetURLObject to abbreviate all other URLs
126 aShortURL
= aURL
.getAbbreviated( m_xStringLength
, 46, INetURLObject::DECODE_UNAMBIGUOUS
);
127 aPickEntry
.append(aShortURL
);
128 aTipHelpText
= aURLString
;
129 aAccessibleName
+= aURLString
;
132 // Set menu item text, tip help and accessible name
133 pMenu
->SetItemText( nItemId
, aPickEntry
.toString() );
134 pMenu
->SetTipHelpText( nItemId
, aTipHelpText
);
135 pMenu
->SetAccessibleName( nItemId
, aAccessibleName
);
140 class thePickListMutex
141 : public rtl::Static
<osl::Mutex
, thePickListMutex
> {};
144 void SfxPickList::RemovePickListEntries()
146 ::osl::MutexGuard
aGuard( thePickListMutex::get() );
147 for ( sal_uInt32 i
= 0; i
< m_aPicklistVector
.size(); i
++ )
148 delete m_aPicklistVector
[i
];
149 m_aPicklistVector
.clear();
152 SfxPickList::PickListEntry
* SfxPickList::GetPickListEntry( sal_uInt32 nIndex
)
154 OSL_ASSERT( m_aPicklistVector
.size() > nIndex
);
156 if ( nIndex
< m_aPicklistVector
.size() )
157 return m_aPicklistVector
[ nIndex
];
162 void SfxPickList::AddDocumentToPickList( SfxObjectShell
* pDocSh
)
164 SfxMedium
*pMed
= pDocSh
->GetMedium();
168 // Unnamed Documents and embedded-Documents not in Picklist
169 if ( !pDocSh
->HasName() ||
170 SfxObjectCreateMode::STANDARD
!= pDocSh
->GetCreateMode() )
173 // Help not in History
174 INetURLObject
aURL( pDocSh
->IsDocShared() ? pDocSh
->GetSharedFileURL() : OUString( pMed
->GetOrigURL() ) );
175 if ( aURL
.GetProtocol() == INetProtocol::VndSunStarHelp
)
178 if ( !pMed
->IsUpdatePickList() )
181 // add no document that forbids this (for example Message-Body)
182 SFX_ITEMSET_ARG( pMed
->GetItemSet(), pPicklistItem
, SfxBoolItem
, SID_PICKLIST
, false );
183 if ( pPicklistItem
&& !pPicklistItem
->GetValue() )
186 // ignore hidden documents
187 if ( !SfxViewFrame::GetFirst( pDocSh
, true ) )
190 OUString aTitle
= pDocSh
->GetTitle(SFX_TITLE_PICKLIST
);
192 const SfxFilter
* pFilter
= pMed
->GetOrigFilter();
194 aFilter
= pFilter
->GetFilterName();
196 // generate a thumbnail
197 boost::optional
<OUString
> aThumbnail
;
198 // don't generate thumbnail when in headless mode, or on non-desktop (?)
199 #if HAVE_FEATURE_DESKTOP
200 if (!pDocSh
->IsModified() && !Application::IsHeadlessModeEnabled())
202 // not modified => the document matches what is in the shell
203 SFX_ITEMSET_ARG( pMed
->GetItemSet(), pEncryptionDataItem
, SfxUnoAnyItem
, SID_ENCRYPTIONDATA
, false );
204 if ( pEncryptionDataItem
)
206 // encrypted document, will show a generic document icon instead
207 aThumbnail
= OUString();
211 std::shared_ptr
<GDIMetaFile
> xMetaFile
= pDocSh
->GetPreviewMetaFile();
212 BitmapEx aResultBitmap
;
213 if (xMetaFile
->CreateThumbnail(aResultBitmap
))
215 SvMemoryStream
aStream(65535, 65535);
216 vcl::PNGWriter
aWriter(aResultBitmap
);
217 if (aWriter
.Write(aStream
))
219 Sequence
<sal_Int8
> aSequence(static_cast<const sal_Int8
*>(aStream
.GetData()), aStream
.Tell());
220 OUStringBuffer aBuffer
;
221 ::sax::Converter::encodeBase64(aBuffer
, aSequence
);
222 aThumbnail
= aBuffer
.makeStringAndClear();
228 // add to svtool history options
229 SvtHistoryOptions().AppendItem( ePICKLIST
,
230 aURL
.GetURLNoPass( INetURLObject::NO_DECODE
),
236 if ( aURL
.GetProtocol() == INetProtocol::File
)
237 Application::AddToRecentDocumentList( aURL
.GetURLNoPass( INetURLObject::NO_DECODE
),
238 (pFilter
) ? pFilter
->GetMimeType() : OUString(),
239 (pFilter
) ? pFilter
->GetServiceName() : OUString() );
242 SfxPickList
& SfxPickList::Get()
244 static SfxPickList
aUniqueInstance(SvtHistoryOptions().GetSize(ePICKLIST
));
245 return aUniqueInstance
;
248 SfxPickList::SfxPickList( sal_uInt32 nAllowedMenuSize
) :
249 m_nAllowedMenuSize( nAllowedMenuSize
)
251 m_xStringLength
= new StringLength
;
252 m_nAllowedMenuSize
= ::std::min( m_nAllowedMenuSize
, (sal_uInt32
)PICKLIST_MAXSIZE
);
253 StartListening( *SfxGetpApp() );
256 SfxPickList::~SfxPickList()
258 RemovePickListEntries();
261 void SfxPickList::CreatePickListEntries()
263 RemovePickListEntries();
265 // Reading the pick list
266 Sequence
< Sequence
< PropertyValue
> > seqPicklist
= SvtHistoryOptions().GetList( ePICKLIST
);
268 sal_uInt32 nCount
= seqPicklist
.getLength();
269 sal_uInt32 nEntries
= ::std::min( m_nAllowedMenuSize
, nCount
);
271 for( sal_uInt32 nItem
=0; nItem
< nEntries
; ++nItem
)
273 Sequence
< PropertyValue
> seqPropertySet
= seqPicklist
[ nItem
];
280 sal_uInt32 nPropertyCount
= seqPropertySet
.getLength();
281 for( sal_uInt32 nProperty
=0; nProperty
<nPropertyCount
; ++nProperty
)
283 if( seqPropertySet
[nProperty
].Name
== HISTORY_PROPERTYNAME_URL
)
285 seqPropertySet
[nProperty
].Value
>>= sURL
;
287 else if( seqPropertySet
[nProperty
].Name
== HISTORY_PROPERTYNAME_FILTER
)
289 seqPropertySet
[nProperty
].Value
>>= sFilter
;
291 else if( seqPropertySet
[nProperty
].Name
== HISTORY_PROPERTYNAME_TITLE
)
293 seqPropertySet
[nProperty
].Value
>>= sTitle
;
297 aURL
.SetSmartURL( sURL
);
298 aURL
.SetPass( OUString() );
300 PickListEntry
*pPick
= new PickListEntry( aURL
.GetMainURL( INetURLObject::NO_DECODE
), sFilter
, sTitle
);
301 m_aPicklistVector
.push_back( pPick
);
305 void SfxPickList::CreateMenuEntries( Menu
* pMenu
)
307 ::osl::MutexGuard
aGuard( thePickListMutex::get() );
309 static bool bPickListMenuInitializing
= false;
311 if ( bPickListMenuInitializing
) // method is not reentrant!
314 bPickListMenuInitializing
= true;
315 CreatePickListEntries();
317 for ( sal_uInt16 nId
= START_ITEMID_PICKLIST
; nId
<= END_ITEMID_PICKLIST
; ++nId
)
318 pMenu
->RemoveItem( pMenu
->GetItemPos( nId
) );
320 if ( pMenu
->GetItemType( pMenu
->GetItemCount()-1 ) == MenuItemType::SEPARATOR
)
321 pMenu
->RemoveItem( pMenu
->GetItemCount()-1 );
323 if ( m_aPicklistVector
.size() > 0 &&
324 pMenu
->GetItemType( pMenu
->GetItemCount()-1 )
325 != MenuItemType::SEPARATOR
&& m_nAllowedMenuSize
)
326 pMenu
->InsertSeparator();
328 OUString aEmptyString
;
329 for ( sal_uInt32 i
= 0; i
< m_aPicklistVector
.size(); i
++ )
331 PickListEntry
* pEntry
= GetPickListEntry( i
);
333 pMenu
->InsertItem( (sal_uInt16
)(START_ITEMID_PICKLIST
+ i
), aEmptyString
);
334 CreatePicklistMenuTitle( pMenu
, (sal_uInt16
)(START_ITEMID_PICKLIST
+ i
), pEntry
->aName
, i
);
337 bPickListMenuInitializing
= false;
340 void SfxPickList::ExecuteEntry( sal_uInt32 nIndex
)
342 ::osl::ClearableMutexGuard
aGuard( thePickListMutex::get() );
344 PickListEntry
*pPick
= SfxPickList::Get().GetPickListEntry( nIndex
);
348 SfxRequest
aReq( SID_OPENDOC
, SfxCallMode::ASYNCHRON
, SfxGetpApp()->GetPool() );
349 aReq
.AppendItem( SfxStringItem( SID_FILE_NAME
, pPick
->aName
));
350 aReq
.AppendItem( SfxStringItem( SID_REFERER
, "private:user" ) );
351 aReq
.AppendItem( SfxStringItem( SID_TARGETNAME
, "_default" ) );
352 OUString
aFilter( pPick
->aFilter
);
355 sal_Int32 nPos
= aFilter
.indexOf('|');
358 OUString
aOptions(aFilter
.copy(nPos
+1));
359 aFilter
= aFilter
.copy( 0, nPos
);
360 aReq
.AppendItem( SfxStringItem(SID_FILE_FILTEROPTIONS
, aOptions
));
363 aReq
.AppendItem(SfxStringItem( SID_FILTER_NAME
, aFilter
));
364 aReq
.AppendItem( SfxBoolItem( SID_TEMPLATE
, false ) );
365 SfxGetpApp()->ExecuteSlot( aReq
);
369 void SfxPickList::ExecuteMenuEntry( sal_uInt16 nId
)
371 ExecuteEntry( (sal_uInt32
)( nId
- START_ITEMID_PICKLIST
) );
374 void SfxPickList::Notify( SfxBroadcaster
&, const SfxHint
& rHint
)
376 const SfxStringHint
* pStringHint
= dynamic_cast<const SfxStringHint
*>(&rHint
);
379 if ( pStringHint
->GetId() == SID_OPENURL
)
380 INetURLHistory::GetOrCreate()->PutUrl( INetURLObject( pStringHint
->GetObject() ));
383 const SfxEventHint
* pEventHint
= dynamic_cast<const SfxEventHint
*>(&rHint
);
386 // only ObjectShell-related events with media interest
387 SfxObjectShell
* pDocSh
= pEventHint
->GetObjShell();
391 switch ( pEventHint
->GetEventId() )
393 case SFX_EVENT_CREATEDOC
:
395 bool bAllowModif
= pDocSh
->IsEnableSetModified();
397 pDocSh
->EnableSetModified( false );
399 using namespace ::com::sun::star
;
400 uno::Reference
<document::XDocumentProperties
> xDocProps(
401 pDocSh
->getDocProperties());
402 if (xDocProps
.is()) {
403 xDocProps
->setAuthor( SvtUserOptions().GetFullName() );
404 ::DateTime
now( ::DateTime::SYSTEM
);
405 xDocProps
->setCreationDate( now
.GetUNODateTime() );
409 pDocSh
->EnableSetModified( bAllowModif
);
413 case SFX_EVENT_OPENDOC
:
415 AddDocumentToPickList(pDocSh
);
419 case SFX_EVENT_SAVEDOCDONE
:
420 case SFX_EVENT_SAVEASDOCDONE
:
421 case SFX_EVENT_SAVETODOCDONE
:
422 case SFX_EVENT_CLOSEDOC
:
424 AddDocumentToPickList(pDocSh
);
428 case SFX_EVENT_SAVEASDOC
:
430 SfxMedium
*pMedium
= pDocSh
->GetMedium();
434 // We're starting a "Save As". Add the current document (if it's
435 // not a "new" document) to the "Recent Documents" list before we
436 // switch to the new path.
437 // If the current document is new, path will be empty.
438 OUString path
= pMedium
->GetOrigURL();
441 AddDocumentToPickList(pDocSh
);
449 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */