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 <classes/resource.hrc>
21 #include <classes/fwkresid.hxx>
23 #include <cppuhelper/implbase1.hxx>
24 #include <cppuhelper/supportsservice.hxx>
25 #include <osl/file.hxx>
26 #include <osl/mutex.hxx>
27 #include <rtl/ref.hxx>
28 #include <svtools/popupmenucontrollerbase.hxx>
29 #include <tools/urlobj.hxx>
30 #include <unotools/historyoptions.hxx>
31 #include <vcl/menu.hxx>
32 #include <vcl/svapp.hxx>
35 using namespace com::sun::star::uno
;
36 using namespace com::sun::star::lang
;
37 using namespace com::sun::star::frame
;
38 using namespace com::sun::star::beans
;
39 using namespace com::sun::star::util
;
40 using namespace framework
;
42 #define MAX_MENU_ITEMS 99
46 static const char CMD_CLEAR_LIST
[] = ".uno:ClearRecentFileList";
47 static const char CMD_PREFIX
[] = "vnd.sun.star.popup:RecentFileList?entry=";
48 static const char MENU_SHORTCUT
[] = "~N. ";
53 uno::Sequence
< beans::PropertyValue
> aArgSeq
;
54 uno::Reference
< frame::XDispatch
> xDispatch
;
57 class RecentFilesMenuController
: public svt::PopupMenuControllerBase
59 using svt::PopupMenuControllerBase::disposing
;
62 RecentFilesMenuController( const uno::Reference
< uno::XComponentContext
>& xContext
);
63 virtual ~RecentFilesMenuController();
66 virtual OUString SAL_CALL
getImplementationName()
67 throw (css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
69 return OUString("com.sun.star.comp.framework.RecentFilesMenuController");
72 virtual sal_Bool SAL_CALL
supportsService(OUString
const & ServiceName
)
73 throw (css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
75 return cppu::supportsService(this, ServiceName
);
78 virtual css::uno::Sequence
<OUString
> SAL_CALL
getSupportedServiceNames()
79 throw (css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
81 css::uno::Sequence
< OUString
> aSeq(1);
82 aSeq
[0] = "com.sun.star.frame.PopupMenuController";
87 virtual void SAL_CALL
statusChanged( const frame::FeatureStateEvent
& Event
) throw ( uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
90 virtual void SAL_CALL
itemSelected( const awt::MenuEvent
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
91 virtual void SAL_CALL
itemActivated( const awt::MenuEvent
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
94 virtual uno::Reference
< frame::XDispatch
> SAL_CALL
queryDispatch( const util::URL
& aURL
, const OUString
& sTarget
, sal_Int32 nFlags
) throw( uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
97 virtual void SAL_CALL
dispatch( const util::URL
& aURL
, const uno::Sequence
< beans::PropertyValue
>& seqProperties
) throw( uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
100 virtual void SAL_CALL
disposing( const com::sun::star::lang::EventObject
& Source
) throw ( uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
102 DECL_STATIC_LINK( RecentFilesMenuController
, ExecuteHdl_Impl
, LoadRecentFile
* );
105 virtual void impl_setPopupMenu() SAL_OVERRIDE
;
112 void fillPopupMenu( com::sun::star::uno::Reference
< com::sun::star::awt::XPopupMenu
>& rPopupMenu
);
113 void executeEntry( sal_Int32 nIndex
);
115 std::vector
< RecentFile
> m_aRecentFilesItems
;
116 bool m_bDisabled
: 1;
119 RecentFilesMenuController::RecentFilesMenuController( const uno::Reference
< uno::XComponentContext
>& xContext
) :
120 svt::PopupMenuControllerBase( xContext
),
125 RecentFilesMenuController::~RecentFilesMenuController()
130 void RecentFilesMenuController::fillPopupMenu( Reference
< css::awt::XPopupMenu
>& rPopupMenu
)
132 VCLXPopupMenu
* pPopupMenu
= static_cast<VCLXPopupMenu
*>(VCLXMenu::GetImplementation( rPopupMenu
));
133 PopupMenu
* pVCLPopupMenu
= 0;
135 SolarMutexGuard aSolarMutexGuard
;
137 resetPopupMenu( rPopupMenu
);
139 pVCLPopupMenu
= static_cast<PopupMenu
*>(pPopupMenu
->GetMenu());
143 Sequence
< Sequence
< PropertyValue
> > aHistoryList
= SvtHistoryOptions().GetList( ePICKLIST
);
145 int nPickListMenuItems
= ( aHistoryList
.getLength() > MAX_MENU_ITEMS
) ? MAX_MENU_ITEMS
: aHistoryList
.getLength();
146 m_aRecentFilesItems
.clear();
147 if (( nPickListMenuItems
> 0 ) && !m_bDisabled
)
149 for ( int i
= 0; i
< nPickListMenuItems
; i
++ )
151 Sequence
< PropertyValue
>& rPickListEntry
= aHistoryList
[i
];
152 RecentFile aRecentFile
;
154 for ( int j
= 0; j
< rPickListEntry
.getLength(); j
++ )
156 Any a
= rPickListEntry
[j
].Value
;
158 if ( rPickListEntry
[j
].Name
== HISTORY_PROPERTYNAME_URL
)
159 a
>>= aRecentFile
.aURL
;
160 else if ( rPickListEntry
[j
].Name
== HISTORY_PROPERTYNAME_TITLE
)
161 a
>>= aRecentFile
.aTitle
;
164 m_aRecentFilesItems
.push_back( aRecentFile
);
168 if ( !m_aRecentFilesItems
.empty() )
170 const sal_uInt32 nCount
= m_aRecentFilesItems
.size();
171 for ( sal_uInt32 i
= 0; i
< nCount
; i
++ )
174 OUStringBuffer aMenuShortCut
;
178 aMenuShortCut
.append( "1~0. " );
181 aMenuShortCut
.append( MENU_SHORTCUT
);
182 aMenuShortCut
[ 1 ] = sal_Unicode( i
+ '1' );
187 aMenuShortCut
.append( sal_Int32( i
+ 1 ) );
188 aMenuShortCut
.append( ". " );
191 OUStringBuffer aStrBuffer
;
192 aStrBuffer
.append( CMD_PREFIX
);
193 aStrBuffer
.append( sal_Int32( i
) );
194 OUString
aURLString( aStrBuffer
.makeStringAndClear() );
198 INetURLObject
aURL( m_aRecentFilesItems
[i
].aURL
);
199 OUString
aTipHelpText( aURL
.getFSysPath( INetURLObject::FSYS_DETECT
) );
201 if ( aURL
.GetProtocol() == INetProtocol::File
)
203 // Do handle file URL differently: don't show the protocol, just the file name
204 aMenuTitle
= aURL
.GetLastName(INetURLObject::DECODE_WITH_CHARSET
, RTL_TEXTENCODING_UTF8
);
208 // In all other URLs show the protocol name before the file name
209 aMenuTitle
= INetURLObject::GetSchemeName(aURL
.GetProtocol()) + ": " + aURL
.getName();
212 aMenuShortCut
.append( aMenuTitle
);
214 pVCLPopupMenu
->InsertItem( sal_uInt16( i
+1 ), aMenuShortCut
.makeStringAndClear() );
215 pVCLPopupMenu
->SetTipHelpText( sal_uInt16( i
+1 ), aTipHelpText
);
216 pVCLPopupMenu
->SetItemCommand( sal_uInt16( i
+1 ), aURLString
);
219 pVCLPopupMenu
->InsertSeparator();
220 // Clear List menu entry
221 pVCLPopupMenu
->InsertItem( sal_uInt16( nCount
+ 1 ),
222 FWK_RESSTR(STR_CLEAR_RECENT_FILES
) );
223 pVCLPopupMenu
->SetItemCommand( sal_uInt16( nCount
+ 1 ),
224 OUString( CMD_CLEAR_LIST
) );
225 pVCLPopupMenu
->SetHelpText( sal_uInt16( nCount
+ 1 ),
226 FWK_RESSTR(STR_CLEAR_RECENT_FILES_HELP
) );
230 // No recent documents => insert "no document" string
231 pVCLPopupMenu
->InsertItem( 1, FWK_RESSTR(STR_NODOCUMENT
) );
232 // Do not disable it, otherwise the Toolbar controller and MenuButton
233 // will display SV_RESID_STRING_NOSELECTIONPOSSIBLE instead of STR_NODOCUMENT
234 pVCLPopupMenu
->SetItemBits( 1, pVCLPopupMenu
->GetItemBits( 1 ) | MenuItemBits::NOSELECT
);
239 void RecentFilesMenuController::executeEntry( sal_Int32 nIndex
)
241 Reference
< XDispatch
> xDispatch
;
242 Reference
< XDispatchProvider
> xDispatchProvider
;
243 css::util::URL aTargetURL
;
244 Sequence
< PropertyValue
> aArgsList
;
246 osl::ClearableMutexGuard
aLock( m_aMutex
);
247 xDispatchProvider
= Reference
< XDispatchProvider
>( m_xFrame
, UNO_QUERY
);
250 if (( nIndex
>= 0 ) &&
251 ( nIndex
< sal::static_int_cast
<sal_Int32
>( m_aRecentFilesItems
.size() )))
253 const RecentFile
& rRecentFile
= m_aRecentFilesItems
[ nIndex
];
255 aTargetURL
.Complete
= rRecentFile
.aURL
;
256 m_xURLTransformer
->parseStrict( aTargetURL
);
259 aArgsList
.realloc( nSize
);
260 aArgsList
[0].Name
= "Referer";
261 aArgsList
[0].Value
= makeAny( OUString( "private:user" ) );
263 // documents in the picklist will never be opened as templates
264 aArgsList
[1].Name
= "AsTemplate";
265 aArgsList
[1].Value
= makeAny( sal_False
);
267 if (!m_aModuleName
.isEmpty())
269 // Type detection needs to know which app we are opening it from.
270 aArgsList
.realloc(++nSize
);
271 aArgsList
[nSize
-1].Name
= "DocumentService";
272 aArgsList
[nSize
-1].Value
<<= m_aModuleName
;
275 xDispatch
= xDispatchProvider
->queryDispatch( aTargetURL
, "_default", 0 );
278 if ( xDispatch
.is() )
280 // Call dispatch asychronously as we can be destroyed while dispatch is
281 // executed. VCL is not able to survive this as it wants to call listeners
283 LoadRecentFile
* pLoadRecentFile
= new LoadRecentFile
;
284 pLoadRecentFile
->xDispatch
= xDispatch
;
285 pLoadRecentFile
->aTargetURL
= aTargetURL
;
286 pLoadRecentFile
->aArgSeq
= aArgsList
;
288 Application::PostUserEvent( LINK(0, RecentFilesMenuController
, ExecuteHdl_Impl
), pLoadRecentFile
);
293 void SAL_CALL
RecentFilesMenuController::disposing( const EventObject
& ) throw ( RuntimeException
, std::exception
)
295 Reference
< css::awt::XMenuListener
> xHolder(( OWeakObject
*)this, UNO_QUERY
);
297 osl::MutexGuard
aLock( m_aMutex
);
301 if ( m_xPopupMenu
.is() )
302 m_xPopupMenu
->removeMenuListener( Reference
< css::awt::XMenuListener
>(( OWeakObject
*)this, UNO_QUERY
));
303 m_xPopupMenu
.clear();
307 void SAL_CALL
RecentFilesMenuController::statusChanged( const FeatureStateEvent
& Event
) throw ( RuntimeException
, std::exception
)
309 osl::MutexGuard
aLock( m_aMutex
);
310 m_bDisabled
= !Event
.IsEnabled
;
313 void SAL_CALL
RecentFilesMenuController::itemSelected( const css::awt::MenuEvent
& rEvent
) throw (RuntimeException
, std::exception
)
315 Reference
< css::awt::XPopupMenu
> xPopupMenu
;
317 osl::ClearableMutexGuard
aLock( m_aMutex
);
318 xPopupMenu
= m_xPopupMenu
;
321 if ( xPopupMenu
.is() )
323 const OUString
aCommand( xPopupMenu
->getCommand( rEvent
.MenuId
) );
324 OSL_TRACE( "RecentFilesMenuController::itemSelected() - Command : %s",
325 OUStringToOString( aCommand
, RTL_TEXTENCODING_UTF8
).getStr() );
327 if ( aCommand
== CMD_CLEAR_LIST
)
329 SvtHistoryOptions().Clear( ePICKLIST
);
331 "vnd.org.libreoffice.recentdocs:ClearRecentFileList",
332 ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::PropertyValue
>() );
335 executeEntry( rEvent
.MenuId
-1 );
339 void SAL_CALL
RecentFilesMenuController::itemActivated( const css::awt::MenuEvent
& ) throw (RuntimeException
, std::exception
)
341 osl::MutexGuard
aLock( m_aMutex
);
345 // XPopupMenuController
346 void RecentFilesMenuController::impl_setPopupMenu()
348 if ( m_xPopupMenu
.is() )
349 fillPopupMenu( m_xPopupMenu
);
353 Reference
< XDispatch
> SAL_CALL
RecentFilesMenuController::queryDispatch(
355 const OUString
& /*sTarget*/,
356 sal_Int32
/*nFlags*/ )
357 throw( RuntimeException
, std::exception
)
359 osl::MutexGuard
aLock( m_aMutex
);
363 if ( aURL
.Complete
.startsWith( m_aBaseURL
) )
364 return Reference
< XDispatch
>( static_cast< OWeakObject
* >( this ), UNO_QUERY
);
366 return Reference
< XDispatch
>();
370 void SAL_CALL
RecentFilesMenuController::dispatch(
372 const Sequence
< PropertyValue
>& /*seqProperties*/ )
373 throw( RuntimeException
, std::exception
)
375 osl::MutexGuard
aLock( m_aMutex
);
379 if ( aURL
.Complete
.startsWith( m_aBaseURL
) )
381 // Parse URL to retrieve entry argument and its value
382 sal_Int32 nQueryPart
= aURL
.Complete
.indexOf( '?', m_aBaseURL
.getLength() );
383 if ( nQueryPart
> 0 )
385 const OUString
aEntryArgStr( "entry=" );
386 sal_Int32 nEntryArg
= aURL
.Complete
.indexOf( aEntryArgStr
, nQueryPart
);
387 sal_Int32 nEntryPos
= nEntryArg
+ aEntryArgStr
.getLength();
388 if (( nEntryArg
> 0 ) && ( nEntryPos
< aURL
.Complete
.getLength() ))
390 sal_Int32 nAddArgs
= aURL
.Complete
.indexOf( '&', nEntryPos
);
394 aEntryArg
= aURL
.Complete
.copy( nEntryPos
);
396 aEntryArg
= aURL
.Complete
.copy( nEntryPos
, nAddArgs
-nEntryPos
);
398 sal_Int32 nEntry
= aEntryArg
.toInt32();
399 executeEntry( nEntry
);
405 IMPL_STATIC_LINK( RecentFilesMenuController
, ExecuteHdl_Impl
, LoadRecentFile
*, pLoadRecentFile
)
409 // Asynchronous execution as this can lead to our own destruction!
410 // Framework can recycle our current frame and the layout manager disposes all user interface
411 // elements if a component gets detached from its frame!
412 pLoadRecentFile
->xDispatch
->dispatch( pLoadRecentFile
->aTargetURL
, pLoadRecentFile
->aArgSeq
);
414 catch ( const Exception
& )
418 delete pLoadRecentFile
;
424 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
425 com_sun_star_comp_framework_RecentFilesMenuController_get_implementation(
426 css::uno::XComponentContext
*context
,
427 css::uno::Sequence
<css::uno::Any
> const &)
429 return cppu::acquire(new RecentFilesMenuController(context
));
432 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */