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 0 : StringLength() {}
69 0 : 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 0 : void SfxPickList::RemovePickListEntries()
144 : {
145 0 : ::osl::MutexGuard aGuard( thePickListMutex::get() );
146 0 : for ( sal_uInt32 i = 0; i < m_aPicklistVector.size(); i++ )
147 0 : delete m_aPicklistVector[i];
148 0 : m_aPicklistVector.clear();
149 0 : }
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 0 : void SfxPickList::AddDocumentToPickList( SfxObjectShell* pDocSh )
162 : {
163 0 : SfxMedium *pMed = pDocSh->GetMedium();
164 0 : if( !pMed )
165 0 : return;
166 :
167 : // Unnamed Documents and embedded-Documents not in Picklist
168 0 : if ( !pDocSh->HasName() ||
169 0 : SFX_CREATE_MODE_STANDARD != pDocSh->GetCreateMode() )
170 0 : return;
171 :
172 : // Help not in History
173 0 : INetURLObject aURL( pDocSh->IsDocShared() ? pDocSh->GetSharedFileURL() : OUString( pMed->GetOrigURL() ) );
174 0 : if ( aURL.GetProtocol() == INET_PROT_VND_SUN_STAR_HELP )
175 0 : return;
176 :
177 0 : if ( !pMed->IsUpdatePickList() )
178 0 : return;
179 :
180 : // add no document that forbids this (for example Message-Body)
181 0 : SFX_ITEMSET_ARG( pMed->GetItemSet(), pPicklistItem, SfxBoolItem, SID_PICKLIST, false );
182 0 : if ( pPicklistItem && !pPicklistItem->GetValue() )
183 0 : return;
184 :
185 : // ignore hidden documents
186 0 : if ( !SfxViewFrame::GetFirst( pDocSh, true ) )
187 0 : return;
188 :
189 0 : OUString aTitle = pDocSh->GetTitle(SFX_TITLE_PICKLIST);
190 0 : OUString aFilter;
191 0 : const SfxFilter* pFilter = pMed->GetOrigFilter();
192 0 : if ( pFilter )
193 0 : aFilter = pFilter->GetFilterName();
194 :
195 : // generate a thumbnail
196 0 : OUString aThumbnail;
197 : // don't generate thumbnail when in headless mode, or on non-desktop (?)
198 : #if HAVE_FEATURE_DESKTOP
199 0 : if (!pDocSh->IsModified() && !Application::IsHeadlessModeRequested())
200 : {
201 : // not modified => the document matches what is in the shell
202 0 : boost::shared_ptr<GDIMetaFile> pMetaFile = pDocSh->GetPreviewMetaFile();
203 0 : BitmapEx aResultBitmap;
204 0 : if (pMetaFile->CreateThumbnail(aResultBitmap))
205 : {
206 0 : SvMemoryStream aStream(65535, 65535);
207 0 : vcl::PNGWriter aWriter(aResultBitmap);
208 0 : if (aWriter.Write(aStream))
209 : {
210 0 : Sequence<sal_Int8> aSequence(static_cast<const sal_Int8*>(aStream.GetData()), aStream.Tell());
211 0 : OUStringBuffer aBuffer;
212 0 : ::sax::Converter::encodeBase64(aBuffer, aSequence);
213 0 : aThumbnail = aBuffer.makeStringAndClear();
214 0 : }
215 0 : }
216 : }
217 : #endif
218 : // add to svtool history options
219 : SvtHistoryOptions().AppendItem( ePICKLIST,
220 : aURL.GetURLNoPass( INetURLObject::NO_DECODE ),
221 : aFilter,
222 : aTitle,
223 : OUString(),
224 0 : aThumbnail);
225 :
226 0 : if ( aURL.GetProtocol() == INET_PROT_FILE )
227 : Application::AddToRecentDocumentList( aURL.GetURLNoPass( INetURLObject::NO_DECODE ),
228 0 : (pFilter) ? pFilter->GetMimeType() : OUString(),
229 0 : (pFilter) ? pFilter->GetServiceName() : OUString() );
230 : }
231 :
232 0 : SfxPickList& SfxPickList::Get()
233 : {
234 0 : static SfxPickList aUniqueInstance(SvtHistoryOptions().GetSize(ePICKLIST));
235 0 : return aUniqueInstance;
236 : }
237 :
238 0 : SfxPickList::SfxPickList( sal_uInt32 nAllowedMenuSize ) :
239 0 : m_nAllowedMenuSize( nAllowedMenuSize )
240 : {
241 0 : m_xStringLength = new StringLength;
242 0 : m_nAllowedMenuSize = ::std::min( m_nAllowedMenuSize, (sal_uInt32)PICKLIST_MAXSIZE );
243 0 : StartListening( *SFX_APP() );
244 0 : }
245 :
246 0 : SfxPickList::~SfxPickList()
247 : {
248 0 : RemovePickListEntries();
249 0 : }
250 :
251 0 : void SfxPickList::CreatePickListEntries()
252 : {
253 0 : RemovePickListEntries();
254 :
255 : // Reading the pick list
256 0 : Sequence< Sequence< PropertyValue > > seqPicklist = SvtHistoryOptions().GetList( ePICKLIST );
257 :
258 0 : sal_uInt32 nCount = seqPicklist.getLength();
259 0 : sal_uInt32 nEntries = ::std::min( m_nAllowedMenuSize, nCount );
260 :
261 0 : for( sal_uInt32 nItem=0; nItem < nEntries; ++nItem )
262 : {
263 0 : Sequence< PropertyValue > seqPropertySet = seqPicklist[ nItem ];
264 :
265 0 : INetURLObject aURL;
266 0 : OUString sURL;
267 0 : OUString sFilter;
268 0 : OUString sTitle;
269 :
270 0 : sal_uInt32 nPropertyCount = seqPropertySet.getLength();
271 0 : for( sal_uInt32 nProperty=0; nProperty<nPropertyCount; ++nProperty )
272 : {
273 0 : if( seqPropertySet[nProperty].Name == HISTORY_PROPERTYNAME_URL )
274 : {
275 0 : seqPropertySet[nProperty].Value >>= sURL;
276 : }
277 0 : else if( seqPropertySet[nProperty].Name == HISTORY_PROPERTYNAME_FILTER )
278 : {
279 0 : seqPropertySet[nProperty].Value >>= sFilter;
280 : }
281 0 : else if( seqPropertySet[nProperty].Name == HISTORY_PROPERTYNAME_TITLE )
282 : {
283 0 : seqPropertySet[nProperty].Value >>= sTitle;
284 : }
285 : }
286 :
287 0 : aURL.SetSmartURL( sURL );
288 0 : aURL.SetPass( OUString() );
289 :
290 0 : PickListEntry *pPick = new PickListEntry( aURL.GetMainURL( INetURLObject::NO_DECODE ), sFilter, sTitle );
291 0 : m_aPicklistVector.push_back( pPick );
292 0 : }
293 0 : }
294 :
295 0 : void SfxPickList::CreateMenuEntries( Menu* pMenu )
296 : {
297 0 : ::osl::MutexGuard aGuard( thePickListMutex::get() );
298 :
299 : static bool bPickListMenuInitializing = false;
300 :
301 0 : if ( bPickListMenuInitializing ) // method is not reentrant!
302 0 : return;
303 :
304 0 : bPickListMenuInitializing = true;
305 0 : CreatePickListEntries();
306 :
307 0 : for ( sal_uInt16 nId = START_ITEMID_PICKLIST; nId <= END_ITEMID_PICKLIST; ++nId )
308 0 : pMenu->RemoveItem( pMenu->GetItemPos( nId ) );
309 :
310 0 : if ( pMenu->GetItemType( pMenu->GetItemCount()-1 ) == MENUITEM_SEPARATOR )
311 0 : pMenu->RemoveItem( pMenu->GetItemCount()-1 );
312 :
313 0 : if ( m_aPicklistVector.size() > 0 &&
314 0 : pMenu->GetItemType( pMenu->GetItemCount()-1 )
315 0 : != MENUITEM_SEPARATOR && m_nAllowedMenuSize )
316 0 : pMenu->InsertSeparator();
317 :
318 0 : OUString aEmptyString;
319 0 : for ( sal_uInt32 i = 0; i < m_aPicklistVector.size(); i++ )
320 : {
321 0 : PickListEntry* pEntry = GetPickListEntry( i );
322 :
323 0 : pMenu->InsertItem( (sal_uInt16)(START_ITEMID_PICKLIST + i), aEmptyString );
324 0 : CreatePicklistMenuTitle( pMenu, (sal_uInt16)(START_ITEMID_PICKLIST + i), pEntry->aName, i );
325 : }
326 :
327 0 : bPickListMenuInitializing = false;
328 : }
329 :
330 0 : void SfxPickList::ExecuteEntry( sal_uInt32 nIndex )
331 : {
332 0 : ::osl::ClearableMutexGuard aGuard( thePickListMutex::get() );
333 :
334 0 : PickListEntry *pPick = SfxPickList::Get().GetPickListEntry( nIndex );
335 :
336 0 : if ( pPick )
337 : {
338 0 : SfxRequest aReq( SID_OPENDOC, SFX_CALLMODE_ASYNCHRON, SFX_APP()->GetPool() );
339 0 : aReq.AppendItem( SfxStringItem( SID_FILE_NAME, pPick->aName ));
340 0 : aReq.AppendItem( SfxStringItem( SID_REFERER, "private:user" ) );
341 0 : aReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_default" ) );
342 0 : OUString aFilter( pPick->aFilter );
343 0 : aGuard.clear();
344 :
345 0 : sal_Int32 nPos = aFilter.indexOf('|');
346 0 : if( nPos != -1 )
347 : {
348 0 : OUString aOptions(aFilter.copy(nPos+1));
349 0 : aFilter = aFilter.copy( 0, nPos );
350 0 : aReq.AppendItem( SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions));
351 : }
352 :
353 0 : aReq.AppendItem(SfxStringItem( SID_FILTER_NAME, aFilter ));
354 0 : aReq.AppendItem( SfxBoolItem( SID_TEMPLATE, false ) );
355 0 : SFX_APP()->ExecuteSlot( aReq );
356 0 : }
357 0 : }
358 :
359 0 : void SfxPickList::ExecuteMenuEntry( sal_uInt16 nId )
360 : {
361 0 : ExecuteEntry( (sal_uInt32)( nId - START_ITEMID_PICKLIST ) );
362 0 : }
363 :
364 0 : void SfxPickList::Notify( SfxBroadcaster&, const SfxHint& rHint )
365 : {
366 0 : if ( rHint.IsA( TYPE( SfxStringHint )))
367 : {
368 0 : SfxStringHint* pStringHint = (SfxStringHint*) &rHint;
369 :
370 0 : if ( pStringHint->GetId() == SID_OPENURL )
371 0 : INetURLHistory::GetOrCreate()->PutUrl( INetURLObject( pStringHint->GetObject() ));
372 : }
373 :
374 0 : if ( rHint.IsA( TYPE( SfxEventHint )))
375 : {
376 0 : SfxEventHint* pEventHint = PTR_CAST(SfxEventHint,&rHint);
377 : // only ObjectShell-related events with media interest
378 0 : SfxObjectShell* pDocSh = pEventHint->GetObjShell();
379 0 : if( !pDocSh )
380 0 : return;
381 :
382 0 : switch ( pEventHint->GetEventId() )
383 : {
384 : case SFX_EVENT_CREATEDOC:
385 : {
386 0 : bool bAllowModif = pDocSh->IsEnableSetModified();
387 0 : if ( bAllowModif )
388 0 : pDocSh->EnableSetModified( false );
389 :
390 : using namespace ::com::sun::star;
391 : uno::Reference<document::XDocumentProperties> xDocProps(
392 0 : pDocSh->getDocProperties());
393 0 : if (xDocProps.is()) {
394 0 : xDocProps->setAuthor( SvtUserOptions().GetFullName() );
395 0 : ::DateTime now( ::DateTime::SYSTEM );
396 0 : xDocProps->setCreationDate( util::DateTime(
397 0 : now.GetNanoSec(), now.GetSec(), now.GetMin(),
398 0 : now.GetHour(), now.GetDay(), now.GetMonth(),
399 0 : now.GetYear(), false) );
400 : }
401 :
402 0 : if ( bAllowModif )
403 0 : pDocSh->EnableSetModified( bAllowModif );
404 : }
405 0 : break;
406 :
407 : case SFX_EVENT_OPENDOC:
408 : {
409 0 : AddDocumentToPickList(pDocSh);
410 : }
411 0 : break;
412 :
413 : case SFX_EVENT_SAVEDOCDONE:
414 : case SFX_EVENT_SAVEASDOCDONE:
415 : case SFX_EVENT_SAVETODOCDONE:
416 : case SFX_EVENT_CLOSEDOC:
417 : {
418 0 : AddDocumentToPickList(pDocSh);
419 : }
420 0 : break;
421 :
422 : case SFX_EVENT_SAVEASDOC:
423 : {
424 0 : SfxMedium *pMedium = pDocSh->GetMedium();
425 0 : if (!pMedium)
426 0 : return;
427 :
428 : // We're starting a "Save As". Add the current document (if it's
429 : // not a "new" document) to the "Recent Documents" list before we
430 : // switch to the new path.
431 : // If the current document is new, path will be empty.
432 0 : OUString path = pMedium->GetOrigURL();
433 0 : if (!path.isEmpty())
434 : {
435 0 : AddDocumentToPickList(pDocSh);
436 0 : }
437 : }
438 0 : break;
439 : }
440 : }
441 : }
442 :
443 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|