1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: recentfilesmenucontroller.cxx,v $
10 * $Revision: 1.9.40.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_framework.hxx"
33 #include <uielement/recentfilesmenucontroller.hxx>
35 //_________________________________________________________________________________________________________________
37 //_________________________________________________________________________________________________________________
38 #include <threadhelp/resetableguard.hxx>
41 #ifndef __FRAMEWORK_CLASSES_RESOURCE_HRC_
42 #include <classes/resource.hrc>
44 #include <classes/fwkresid.hxx>
46 //_________________________________________________________________________________________________________________
48 //_________________________________________________________________________________________________________________
49 #include <com/sun/star/awt/XDevice.hpp>
50 #include <com/sun/star/beans/PropertyValue.hpp>
51 #include <com/sun/star/awt/MenuItemStyle.hpp>
52 #include <com/sun/star/util/XStringWidth.hpp>
53 #include <com/sun/star/lang/DisposedException.hpp>
54 //_________________________________________________________________________________________________________________
55 // includes of other projects
56 //_________________________________________________________________________________________________________________
58 #ifndef _VCL_MENU_HXX_
59 #include <vcl/menu.hxx>
61 #include <vcl/svapp.hxx>
62 #include <vcl/i18nhelp.hxx>
63 #include <tools/urlobj.hxx>
64 #include <rtl/ustrbuf.hxx>
65 #include <svtools/historyoptions.hxx>
66 #include <cppuhelper/implbase1.hxx>
67 #include <osl/file.hxx>
69 #include <tools/prewin.h>
70 #include <tools/postwin.h>
71 #include <odma_lib.hxx>
73 //#include <tools/solar.hrc>
74 #include <dispatch/uieventloghelper.hxx>
76 //_________________________________________________________________________________________________________________
78 //_________________________________________________________________________________________________________________
81 using namespace com::sun::star::uno
;
82 using namespace com::sun::star::lang
;
83 using namespace com::sun::star::frame
;
84 using namespace com::sun::star::beans
;
85 using namespace com::sun::star::util
;
86 using namespace com::sun::star::container
;
88 static const char SFX_REFERER_USER
[] = "private:user";
93 class RecentFilesStringLength
: public ::cppu::WeakImplHelper1
< ::com::sun::star::util::XStringWidth
>
96 RecentFilesStringLength() {}
97 virtual ~RecentFilesStringLength() {}
100 sal_Int32 SAL_CALL
queryStringWidth( const ::rtl::OUString
& aString
)
101 throw (::com::sun::star::uno::RuntimeException
)
103 return aString
.getLength();
107 DEFINE_XSERVICEINFO_MULTISERVICE ( RecentFilesMenuController
,
109 SERVICENAME_POPUPMENUCONTROLLER
,
110 IMPLEMENTATIONNAME_RECENTFILESMENUCONTROLLER
113 DEFINE_INIT_SERVICE ( RecentFilesMenuController
, {} )
115 RecentFilesMenuController::RecentFilesMenuController( const ::com::sun::star::uno::Reference
< ::com::sun::star::lang::XMultiServiceFactory
>& xServiceManager
) :
116 PopupMenuControllerBase( xServiceManager
),
117 m_bDisabled( sal_False
)
121 RecentFilesMenuController::~RecentFilesMenuController()
126 void RecentFilesMenuController::fillPopupMenu( Reference
< css::awt::XPopupMenu
>& rPopupMenu
)
128 VCLXPopupMenu
* pPopupMenu
= (VCLXPopupMenu
*)VCLXMenu::GetImplementation( rPopupMenu
);
129 PopupMenu
* pVCLPopupMenu
= 0;
131 vos::OGuard
aSolarMutexGuard( Application::GetSolarMutex() );
133 resetPopupMenu( rPopupMenu
);
135 pVCLPopupMenu
= (PopupMenu
*)pPopupMenu
->GetMenu();
139 Sequence
< Sequence
< PropertyValue
> > aHistoryList
= SvtHistoryOptions().GetList( ePICKLIST
);
140 Reference
< XStringWidth
> xStringLength( new RecentFilesStringLength
);
142 int nPickListMenuItems
= ( aHistoryList
.getLength() > 99 ) ? 99 : aHistoryList
.getLength();
144 // New vnd.sun.star.popup: command URL to support direct dispatches
145 const rtl::OUString
aCmdPrefix( RTL_CONSTASCII_USTRINGPARAM( "vnd.sun.star.popup:RecentFileList?entry=" ));
147 m_aRecentFilesItems
.clear();
148 if (( nPickListMenuItems
> 0 ) && !m_bDisabled
)
150 for ( int i
= 0; i
< nPickListMenuItems
; i
++ )
152 Sequence
< PropertyValue
>& rPickListEntry
= aHistoryList
[i
];
153 RecentFile aRecentFile
;
155 for ( int j
= 0; j
< rPickListEntry
.getLength(); j
++ )
157 Any a
= rPickListEntry
[j
].Value
;
159 if ( rPickListEntry
[j
].Name
== HISTORY_PROPERTYNAME_URL
)
160 a
>>= aRecentFile
.aURL
;
161 else if ( rPickListEntry
[j
].Name
== HISTORY_PROPERTYNAME_FILTER
)
162 a
>>= aRecentFile
.aFilter
;
163 else if ( rPickListEntry
[j
].Name
== HISTORY_PROPERTYNAME_TITLE
)
164 a
>>= aRecentFile
.aTitle
;
165 else if ( rPickListEntry
[j
].Name
== HISTORY_PROPERTYNAME_PASSWORD
)
166 a
>>= aRecentFile
.aPassword
;
169 m_aRecentFilesItems
.push_back( aRecentFile
);
173 if ( !m_aRecentFilesItems
.empty() )
177 const sal_uInt32 nCount
= m_aRecentFilesItems
.size();
178 for ( sal_uInt32 i
= 0; i
< nCount
; i
++ )
180 char menuShortCut
[5] = "~n: ";
182 ::rtl::OUString aMenuShortCut
;
186 aMenuShortCut
= rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "1~0: " ));
189 menuShortCut
[1] = (char)( '1' + i
);
190 aMenuShortCut
= rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( menuShortCut
));
195 aMenuShortCut
= rtl::OUString::valueOf((sal_Int32
)( i
+ 1 ));
196 aMenuShortCut
+= rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ": " ));
200 rtl::OUString
aURLString( aCmdPrefix
+ rtl::OUString::valueOf( sal_Int32( i
)));
201 rtl::OUString aTipHelpText
;
202 rtl::OUString aMenuTitle
;
203 INetURLObject
aURL( m_aRecentFilesItems
[i
].aURL
);
205 if ( aURL
.GetProtocol() == INET_PROT_FILE
)
207 // Do handle file URL differently => convert it to a system
208 // path and abbreviate it with a special function:
209 String
aFileSystemPath( aURL
.getFSysPath( INetURLObject::FSYS_DETECT
) );
211 ::rtl::OUString
aSystemPath( aFileSystemPath
);
212 ::rtl::OUString aCompactedSystemPath
;
214 aTipHelpText
= aSystemPath
;
215 oslFileError nError
= osl_abbreviateSystemPath( aSystemPath
.pData
, &aCompactedSystemPath
.pData
, 46, NULL
);
217 aMenuTitle
= String( aCompactedSystemPath
);
219 aMenuTitle
= aSystemPath
;
222 else if ( aURL
.GetProtocol() == INET_PROT_VND_SUN_STAR_ODMA
&& ::odma::DMSsAvailable ())
224 String aShortTitle
= m_aRecentFilesItems
.at( i
).aTitle
;
226 // This is against all rules for using
227 // proper abstraction layers and whatnot.
228 // But figuring out how to do it "right"
229 // would have taken the whole week.
230 // So just call the odma_lib functions...
231 // (odma_lib is a thin layer on
232 // top of the ODMA32 DLL)
234 static ODMHANDLE handle
= NULL
;
235 static sal_Bool beenhere
= sal_False
;
240 status
= NODMRegisterApp( &handle
, ODM_API_VERSION
, "sodma", NULL
, NULL
);
244 if ( handle
!= NULL
)
246 rtl::OUString s
= aURL
.GetMainURL( INetURLObject::DECODE_WITH_CHARSET
, RTL_TEXTENCODING_MS_1252
);
247 s
= s
.copy( strlen ( "vnd.sun.star.odma:/" ) );
249 status
= NODMGetDocInfo( handle
, rtl::OUStringToOString( s
, RTL_TEXTENCODING_MS_1252
).pData
->buffer
, ODM_NAME
, title
, sizeof ( title
) );
250 aShortTitle
= String::CreateFromAscii( title
);
252 aMenuTitle
+= aShortTitle
;
253 aTipHelpText
= aURLString
;
258 // Use INetURLObject to abbreviate all other URLs
260 aShortURL
= aURL
.getAbbreviated( xStringLength
, 46, INetURLObject::DECODE_UNAMBIGUOUS
);
261 aMenuTitle
+= aShortURL
;
262 aTipHelpText
= aURLString
;
265 ::rtl::OUString
aTitle( aMenuShortCut
+ aMenuTitle
);
267 pVCLPopupMenu
->InsertItem( USHORT( i
+1 ), aTitle
);
268 pVCLPopupMenu
->SetTipHelpText( USHORT( i
+1 ), aTipHelpText
);
269 pVCLPopupMenu
->SetItemCommand( USHORT( i
+1 ), aURLString
);
274 // No recent documents => insert "no document" string
275 String aNoDocumentStr
= String( FwkResId( STR_NODOCUMENT
));
276 pVCLPopupMenu
->InsertItem( 1, aNoDocumentStr
);
277 pVCLPopupMenu
->EnableItem( 1, FALSE
);
282 void RecentFilesMenuController::executeEntry( sal_Int32 nIndex
)
284 static int NUM_OF_PICKLIST_ARGS
= 3;
286 Reference
< css::awt::XPopupMenu
> xPopupMenu
;
287 Reference
< XDispatch
> xDispatch
;
288 Reference
< XDispatchProvider
> xDispatchProvider
;
289 Reference
< XMultiServiceFactory
> xServiceManager
;
291 ResetableGuard
aLock( m_aLock
);
292 xPopupMenu
= m_xPopupMenu
;
293 xDispatchProvider
= Reference
< XDispatchProvider
>( m_xFrame
, UNO_QUERY
);
294 xServiceManager
= m_xServiceManager
;
297 css::util::URL aTargetURL
;
298 Sequence
< PropertyValue
> aArgsList
;
300 if (( nIndex
>= 0 ) &&
301 ( nIndex
< sal::static_int_cast
<sal_Int32
>( m_aRecentFilesItems
.size() )))
303 const RecentFile
& rRecentFile
= m_aRecentFilesItems
[ nIndex
];
305 aTargetURL
.Complete
= rRecentFile
.aURL
;
306 m_xURLTransformer
->parseStrict( aTargetURL
);
308 aArgsList
.realloc( NUM_OF_PICKLIST_ARGS
);
309 aArgsList
[0].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Referer" ));
310 aArgsList
[0].Value
= makeAny( ::rtl::OUString::createFromAscii( SFX_REFERER_USER
));
312 // documents in the picklist will never be opened as templates
313 aArgsList
[1].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AsTemplate" ));
314 aArgsList
[1].Value
= makeAny( (sal_Bool
) sal_False
);
316 ::rtl::OUString
aFilter( rRecentFile
.aFilter
);
317 sal_Int32 nPos
= aFilter
.indexOf( '|' );
320 ::rtl::OUString aFilterOptions
;
322 if ( nPos
< ( aFilter
.getLength() - 1 ) )
323 aFilterOptions
= aFilter
.copy( nPos
+1 );
325 aArgsList
[2].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterOptions" ));
326 aArgsList
[2].Value
<<= aFilterOptions
;
328 aFilter
= aFilter
.copy( 0, nPos
-1 );
329 aArgsList
.realloc( ++NUM_OF_PICKLIST_ARGS
);
332 aArgsList
[NUM_OF_PICKLIST_ARGS
-1].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterName" ));
333 aArgsList
[NUM_OF_PICKLIST_ARGS
-1].Value
<<= aFilter
;
335 xDispatch
= xDispatchProvider
->queryDispatch( aTargetURL
, ::rtl::OUString::createFromAscii("_default"), 0 );
338 if ( xDispatch
.is() )
340 // Call dispatch asychronously as we can be destroyed while dispatch is
341 // executed. VCL is not able to survive this as it wants to call listeners
343 LoadRecentFile
* pLoadRecentFile
= new LoadRecentFile
;
344 pLoadRecentFile
->xDispatch
= xDispatch
;
345 pLoadRecentFile
->aTargetURL
= aTargetURL
;
346 pLoadRecentFile
->aArgSeq
= aArgsList
;
347 if(::comphelper::UiEventsLogger::isEnabled()) //#i88653#
348 UiEventLogHelper(::rtl::OUString::createFromAscii("RecentFilesMenuController")).log(m_xServiceManager
, m_xFrame
, aTargetURL
, aArgsList
);
349 Application::PostUserEvent( STATIC_LINK(0, RecentFilesMenuController
, ExecuteHdl_Impl
), pLoadRecentFile
);
354 void SAL_CALL
RecentFilesMenuController::disposing( const EventObject
& ) throw ( RuntimeException
)
356 Reference
< css::awt::XMenuListener
> xHolder(( OWeakObject
*)this, UNO_QUERY
);
358 ResetableGuard
aLock( m_aLock
);
361 m_xServiceManager
.clear();
363 if ( m_xPopupMenu
.is() )
364 m_xPopupMenu
->removeMenuListener( Reference
< css::awt::XMenuListener
>(( OWeakObject
*)this, UNO_QUERY
));
365 m_xPopupMenu
.clear();
369 void SAL_CALL
RecentFilesMenuController::statusChanged( const FeatureStateEvent
& Event
) throw ( RuntimeException
)
371 ResetableGuard
aLock( m_aLock
);
372 m_bDisabled
= !Event
.IsEnabled
;
375 void SAL_CALL
RecentFilesMenuController::select( const css::awt::MenuEvent
& rEvent
) throw (RuntimeException
)
377 Reference
< css::awt::XPopupMenu
> xPopupMenu
;
378 Reference
< XDispatch
> xDispatch
;
379 Reference
< XDispatchProvider
> xDispatchProvider
;
380 Reference
< XMultiServiceFactory
> xServiceManager
;
382 ResetableGuard
aLock( m_aLock
);
383 xPopupMenu
= m_xPopupMenu
;
384 xDispatchProvider
= Reference
< XDispatchProvider
>( m_xFrame
, UNO_QUERY
);
385 xServiceManager
= m_xServiceManager
;
388 css::util::URL aTargetURL
;
389 Sequence
< PropertyValue
> aArgsList
;
391 if ( xPopupMenu
.is() && xDispatchProvider
.is() )
393 VCLXPopupMenu
* pPopupMenu
= (VCLXPopupMenu
*)VCLXPopupMenu::GetImplementation( xPopupMenu
);
395 executeEntry( rEvent
.MenuId
-1 );
399 void SAL_CALL
RecentFilesMenuController::activate( const css::awt::MenuEvent
& ) throw (RuntimeException
)
401 ResetableGuard
aLock( m_aLock
);
405 // XPopupMenuController
406 void RecentFilesMenuController::impl_setPopupMenu()
408 if ( m_xPopupMenu
.is() )
409 fillPopupMenu( m_xPopupMenu
);
412 void SAL_CALL
RecentFilesMenuController::updatePopupMenu() throw (RuntimeException
)
414 ResetableGuard
aLock( m_aLock
);
417 throw DisposedException();
419 Reference
< XStatusListener
> xStatusListener( static_cast< OWeakObject
* >( this ), UNO_QUERY
);
420 Reference
< XDispatch
> xDispatch( m_xDispatch
);
421 com::sun::star::util::URL aTargetURL
;
422 aTargetURL
.Complete
= m_aCommandURL
;
423 m_xURLTransformer
->parseStrict( aTargetURL
);
426 // Add/remove status listener to get a status update once
427 if ( xDispatch
.is() )
429 xDispatch
->addStatusListener( xStatusListener
, aTargetURL
);
430 xDispatch
->removeStatusListener( xStatusListener
, aTargetURL
);
435 Reference
< XDispatch
> SAL_CALL
RecentFilesMenuController::queryDispatch(
437 const ::rtl::OUString
& /*sTarget*/,
438 sal_Int32
/*nFlags*/ )
439 throw( RuntimeException
)
441 ResetableGuard
aLock( m_aLock
);
444 throw DisposedException();
446 if ( aURL
.Complete
.indexOf( m_aBaseURL
) == 0 )
447 return Reference
< XDispatch
>( static_cast< OWeakObject
* >( this ), UNO_QUERY
);
449 return Reference
< XDispatch
>();
453 void SAL_CALL
RecentFilesMenuController::dispatch(
455 const Sequence
< PropertyValue
>& /*seqProperties*/ )
456 throw( RuntimeException
)
458 ResetableGuard
aLock( m_aLock
);
461 throw DisposedException();
463 if ( aURL
.Complete
.indexOf( m_aBaseURL
) == 0 )
465 // Parse URL to retrieve entry argument and its value
466 sal_Int32 nQueryPart
= aURL
.Complete
.indexOf( '?', m_aBaseURL
.getLength() );
467 if ( nQueryPart
> 0 )
469 const rtl::OUString
aEntryArgStr( RTL_CONSTASCII_USTRINGPARAM( "entry=" ));
470 sal_Int32 nEntryArg
= aURL
.Complete
.indexOf( aEntryArgStr
, nQueryPart
);
471 sal_Int32 nEntryPos
= nEntryArg
+ aEntryArgStr
.getLength();
472 if (( nEntryArg
> 0 ) && ( nEntryPos
< aURL
.Complete
.getLength() ))
474 sal_Int32 nAddArgs
= aURL
.Complete
.indexOf( '&', nEntryPos
);
475 rtl::OUString aEntryArg
;
478 aEntryArg
= aURL
.Complete
.copy( nEntryPos
);
480 aEntryArg
= aURL
.Complete
.copy( nEntryPos
, nAddArgs
-nEntryPos
);
482 sal_Int32 nEntry
= aEntryArg
.toInt32();
483 executeEntry( nEntry
);
489 void SAL_CALL
RecentFilesMenuController::addStatusListener(
490 const Reference
< XStatusListener
>& xControl
,
492 throw( RuntimeException
)
494 ResetableGuard
aLock( m_aLock
);
497 throw DisposedException();
499 PopupMenuControllerBase::addStatusListener( xControl
, aURL
);
502 void SAL_CALL
RecentFilesMenuController::removeStatusListener(
503 const Reference
< XStatusListener
>& xControl
,
505 throw( RuntimeException
)
507 PopupMenuControllerBase::removeStatusListener( xControl
, aURL
);
510 IMPL_STATIC_LINK_NOINSTANCE( RecentFilesMenuController
, ExecuteHdl_Impl
, LoadRecentFile
*, pLoadRecentFile
)
514 // Asynchronous execution as this can lead to our own destruction!
515 // Framework can recycle our current frame and the layout manager disposes all user interface
516 // elements if a component gets detached from its frame!
517 pLoadRecentFile
->xDispatch
->dispatch( pLoadRecentFile
->aTargetURL
, pLoadRecentFile
->aArgSeq
);
523 delete pLoadRecentFile
;