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