Branch libreoffice-5-0-4
[LibreOffice.git] / sfx2 / source / appl / sfxpicklist.cxx
blob0f3196df45ccc2d4efe5b97b87c4c6c11c1f7052
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
56 #include <algorithm>
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 >
68 public:
69 StringLength() {}
70 virtual ~StringLength() {}
72 // XStringWidth
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;
84 if ( nNo < 9 )
86 aPickEntry.append('~');
87 aPickEntry.append(OUString::number(nNo + 1));
89 else if ( nNo == 9 )
90 aPickEntry.append("1~0");
91 else
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 );
111 if ( !nError )
112 aPickEntry.append( aCompactedSystemPath );
113 else
114 aPickEntry.append( aFileSystemPath );
116 if ( aPickEntry.getLength() > 50 )
118 aPickEntry.setLength( 47 );
119 aPickEntry.append("...");
122 else
124 // Use INetURLObject to abbreviate all other URLs
125 OUString aShortURL;
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 );
138 namespace
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 ];
158 else
159 return 0;
162 void SfxPickList::AddDocumentToPickList( SfxObjectShell* pDocSh )
164 SfxMedium *pMed = pDocSh->GetMedium();
165 if( !pMed )
166 return;
168 // Unnamed Documents and embedded-Documents not in Picklist
169 if ( !pDocSh->HasName() ||
170 SfxObjectCreateMode::STANDARD != pDocSh->GetCreateMode() )
171 return;
173 // Help not in History
174 INetURLObject aURL( pDocSh->IsDocShared() ? pDocSh->GetSharedFileURL() : OUString( pMed->GetOrigURL() ) );
175 if ( aURL.GetProtocol() == INetProtocol::VndSunStarHelp )
176 return;
178 if ( !pMed->IsUpdatePickList() )
179 return;
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() )
184 return;
186 // ignore hidden documents
187 if ( !SfxViewFrame::GetFirst( pDocSh, true ) )
188 return;
190 OUString aTitle = pDocSh->GetTitle(SFX_TITLE_PICKLIST);
191 OUString aFilter;
192 const SfxFilter* pFilter = pMed->GetOrigFilter();
193 if ( pFilter )
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();
209 else
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();
227 #endif
228 // add to svtool history options
229 SvtHistoryOptions().AppendItem( ePICKLIST,
230 aURL.GetURLNoPass( INetURLObject::NO_DECODE ),
231 aFilter,
232 aTitle,
233 OUString(),
234 aThumbnail);
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 ];
275 INetURLObject aURL;
276 OUString sURL;
277 OUString sFilter;
278 OUString sTitle;
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!
312 return;
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 );
346 if ( pPick )
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 );
353 aGuard.clear();
355 sal_Int32 nPos = aFilter.indexOf('|');
356 if( nPos != -1 )
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);
377 if ( pStringHint )
379 if ( pStringHint->GetId() == SID_OPENURL )
380 INetURLHistory::GetOrCreate()->PutUrl( INetURLObject( pStringHint->GetObject() ));
383 const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint);
384 if ( pEventHint )
386 // only ObjectShell-related events with media interest
387 SfxObjectShell* pDocSh = pEventHint->GetObjShell();
388 if( !pDocSh )
389 return;
391 switch ( pEventHint->GetEventId() )
393 case SFX_EVENT_CREATEDOC:
395 bool bAllowModif = pDocSh->IsEnableSetModified();
396 if ( bAllowModif )
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() );
408 if ( bAllowModif )
409 pDocSh->EnableSetModified( bAllowModif );
411 break;
413 case SFX_EVENT_OPENDOC:
415 AddDocumentToPickList(pDocSh);
417 break;
419 case SFX_EVENT_SAVEDOCDONE:
420 case SFX_EVENT_SAVEASDOCDONE:
421 case SFX_EVENT_SAVETODOCDONE:
422 case SFX_EVENT_CLOSEDOC:
424 AddDocumentToPickList(pDocSh);
426 break;
428 case SFX_EVENT_SAVEASDOC:
430 SfxMedium *pMedium = pDocSh->GetMedium();
431 if (!pMedium)
432 return;
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();
439 if (!path.isEmpty())
441 AddDocumentToPickList(pDocSh);
444 break;
449 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */