/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
using ::com::sun::star::uno::Reference; using ::com::sun::star::uno::XComponentContext; using ::com::sun::star::uno::Any; using ::com::sun::star::uno::TypeClass_STRING; using ::com::sun::star::uno::Type; using ::com::sun::star::beans::theIntrospection; using ::com::sun::star::beans::XPropertyChangeListener; using ::com::sun::star::beans::Property; using ::com::sun::star::beans::PropertyState; using ::com::sun::star::beans::PropertyState_DIRECT_VALUE; using ::com::sun::star::uno::Sequence; using ::com::sun::star::script::ScriptEventDescriptor; using ::com::sun::star::script::XScriptEventsSupplier; using ::com::sun::star::lang::NullPointerException; using ::com::sun::star::uno::Exception; using ::com::sun::star::container::XChild; using ::com::sun::star::container::XIndexAccess; using ::com::sun::star::script::XEventAttacherManager; using ::com::sun::star::uno::UNO_QUERY; using ::com::sun::star::uno::UNO_QUERY_THROW; using ::com::sun::star::uno::XInterface; using ::com::sun::star::beans::XIntrospection; using ::com::sun::star::beans::XIntrospectionAccess; using ::com::sun::star::container::XNameContainer; using ::com::sun::star::awt::XTabControllerModel; using ::com::sun::star::form::XForm; using ::com::sun::star::form::runtime::FormController; using ::com::sun::star::form::runtime::XFormController; using ::com::sun::star::beans::UnknownPropertyException; using ::com::sun::star::container::NoSuchElementException; using ::com::sun::star::beans::XPropertySetInfo; using ::com::sun::star::container::XNameReplace; using ::com::sun::star::beans::PropertyValue; using ::com::sun::star::inspection::LineDescriptor; using ::com::sun::star::inspection::XPropertyControlFactory; using ::com::sun::star::inspection::InteractiveSelectionResult; using ::com::sun::star::inspection::InteractiveSelectionResult_Cancelled; using ::com::sun::star::inspection::InteractiveSelectionResult_Success; using ::com::sun::star::inspection::XObjectInspectorUI; using ::com::sun::star::beans::PropertyChangeEvent; using ::com::sun::star::frame::XFrame; using ::com::sun::star::frame::XModel; using ::com::sun::star::frame::XController; using ::com::sun::star::uno::UNO_SET_THROW; using com::sun::star::uri::UriReferenceFactory; using com::sun::star::uri::XUriReferenceFactory; using com::sun::star::uri::XVndSunStarScriptUrlReference;
ScriptEventDescriptor lcl_getAssignedScriptEvent( const EventDescription& _rEvent, const std::vector< ScriptEventDescriptor >& _rAllAssignedMacros )
{
ScriptEventDescriptor aScriptEvent; // for the case there is actually no event assigned, initialize at least ListenerType and MethodName, // so this ScriptEventDescriptor properly describes the given event
aScriptEvent.ListenerType = _rEvent.sListenerClassName;
aScriptEvent.EventMethod = _rEvent.sListenerMethodName;
for ( const ScriptEventDescriptor& rSED : _rAllAssignedMacros )
{ if ( rSED.ListenerType != _rEvent.sListenerClassName
|| rSED.EventMethod != _rEvent.sListenerMethodName
) continue;
if ( rSED.ScriptCode.isEmpty()
|| rSED.ScriptType.isEmpty()
)
{
OSL_FAIL( "lcl_getAssignedScriptEvent: me thinks this should not happen!" ); continue;
}
aScriptEvent = rSED;
if ( aScriptEvent.ScriptType != "StarBasic" ) continue;
// this is an old-style macro specification: // [document|application]:Library.Module.Function // we need to translate this to the new-style macro specification // vnd.sun.star.script:Library.Module.Function?language=Basic&location=[document|application]
// also, this new-style spec requires the script code to be "Script" instead of "StarBasic"
aScriptEvent.ScriptType = "Script";
} return aScriptEvent;
}
OUString lcl_getQualifiedKnownListenerName( const ScriptEventDescriptor& _rFormComponentEventDescriptor )
{
EventDescription aKnownEvent; if ( lcl_getEventDescriptionForMethod( _rFormComponentEventDescriptor.EventMethod, aKnownEvent ) ) return aKnownEvent.sListenerClassName;
OSL_FAIL( "lcl_getQualifiedKnownListenerName: unknown method name!" ); // somebody assigned an script to a form component event which we don't know // Speaking strictly, this is not really an error - it is possible to do // this programmatically -, but it should rarely happen, since it's not possible // via UI return _rFormComponentEventDescriptor.ListenerType;
}
typedef std::set< Type, TypeLessByName > TypeBag;
void lcl_addListenerTypesFor_throw( const Reference< XInterface >& _rxComponent, const Reference< XIntrospection >& _rxIntrospection, TypeBag& _out_rTypes )
{ if ( !_rxComponent.is() ) return;
OSL_PRECOND( _rxIntrospection.is(), "lcl_addListenerTypesFor_throw: this will crash!" );
/* A UNO component holding assigned event descriptions, for use with a SvxMacroAssignDlg */ class EventHolder : public EventHolder_Base
{ private: typedef std::unordered_map< OUString, ScriptEventDescriptor > EventMap; typedef std::map< EventId, OUString > EventMapIndexAccess;
/** effectively the same as getByName, but instead of converting the ScriptEventDescriptor to the weird format used by the macro assignment dialog, it is returned directly
*/
ScriptEventDescriptor getNormalizedDescriptorByName( const OUString& _rEventName ) const;
// SvxMacroAssignDlg has a weird API: It expects a XNameReplace, means a container whose // main access method is by name. In its UI, it shows the possible events in exactly the // order in which XNameAccess::getElementNames returns them. // However, SvxMacroAssignDlg *also* takes an index for the initial selection, which is // relative to the sequence returned by XNameAccess::getElementNames. // This is IMO weird, since it mixes index access with name access, which decreases efficiency // of the implementation. // Well, it means we're forced to return the events in getElementNames in exactly the same as they // appear in the property browser UI. for (autoconst& elem : m_aEventIndexAccess)
{
*pReturn = elem.second;
++pReturn;
} return aReturn;
}
OSL_ENSURE( sNewScriptCode.isEmpty(), "EventHandler::convertToPropertyValue: cannot convert a non-empty display name!" ); // Usually, there is no possibility for the user to change the content of an event binding directly in the // input field, this instead is done with the macro assignment dialog. // The only exception is the user pressing "DEL" while the control has the focus, in this case, we reset the // control content to an empty string. So this is the only scenario where this method is allowed to be called.
// Strictly, we would be able to convert the display value to a property value, // using the "name (location, language)" format we used in convertToControlValue. However, // there is no need for this code...
// loop through all listeners and all methods, and see which we can present at the UI for ( const Type& rListener : aListeners )
{ // the programmatic name of the listener, to be used as "property" name
sListenerClassName = rListener.getTypeName();
OSL_ENSURE( !sListenerClassName.isEmpty(), "EventHandler::getSupportedProperties: strange - no listener name ..." ); if ( sListenerClassName.isEmpty() ) continue;
// loop through all methods const Sequence<OUString> aEventMethods = comphelper::getEventMethodsForType( rListener ); for (const OUString& rMethod : aEventMethods)
{
EventDescription aEvent; if ( !lcl_getEventDescriptionForMethod( rMethod, aEvent ) ) continue;
if ( !impl_filterMethod_nothrow( aEvent ) ) continue;
// sort them by ID - this is the relative ordering in the UI
std::map< EventId, Property > aOrderedProperties; for (autoconst& event : m_aEvents)
{
aOrderedProperties[ event.second.nId ] = Property(
event.first, event.second.nId,
::cppu::UnoType<OUString>::get(),
PropertyAttribute::BOUND );
}
// SvxMacroAssignDlg-compatible structure holding all event/assignments
::rtl::Reference< EventHolder > pEventHolder( new EventHolder );
for (autoconst& event : m_aEvents)
{ // the script which is assigned to the current event (if any)
ScriptEventDescriptor aAssignedScript = lcl_getAssignedScriptEvent( event.second, aAllAssignedEvents );
pEventHolder->addEvent( event.second.nId, event.second.sListenerMethodName, aAssignedScript );
}
if ( !pDialog ) return InteractiveSelectionResult_Cancelled;
// DF definite problem here // OK & Cancel seem to be both returning 0 if ( pDialog->Execute() == RET_CANCEL ) return InteractiveSelectionResult_Cancelled;
// get the index of the inspected object within its parent container
sal_Int32 nElements = xParentAsIndexAccess->getCount(); for ( sal_Int32 i=0; i<nElements; ++i )
{
Reference< XInterface > xElement( xParentAsIndexAccess->getByIndex( i ), UNO_QUERY_THROW ); if ( xElement == m_xComponent ) return i;
} throw NoSuchElementException();
}
// the form component script API has unqualified listener names, but for normalization // purpose, we want fully qualified ones for ( ScriptEventDescriptor& rSED : _out_rEvents)
{
rSED.ListenerType = lcl_getQualifiedKnownListenerName( rSED );
}
} catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
}
}
void EventHandler::impl_getComponentListenerTypes_nothrow( std::vector< Type >& _out_rTypes ) const
{
_out_rTypes.clear(); try
{ // we use a set to avoid duplicates
TypeBag aListeners;
// now that they're disambiguated, copy these types into our member
_out_rTypes.insert( _out_rTypes.end(), aListeners.begin(), aListeners.end() );
} catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
}
}
bool EventHandler::impl_filterMethod_nothrow( const EventDescription& _rEvent ) const
{ // some (control-triggered) events do not make sense for certain grid control columns. However, // our mechanism to retrieve control-triggered events does not know about this, so we do some // late filtering here. switch ( m_nGridColumnType )
{ case FormComponentType::COMBOBOX: if ( UID_BRWEVT_ACTIONPERFORMED == _rEvent.sUniqueBrowseId ) returnfalse; break; case FormComponentType::LISTBOX: if ( ( UID_BRWEVT_CHANGED == _rEvent.sUniqueBrowseId )
|| ( UID_BRWEVT_ACTIONPERFORMED == _rEvent.sUniqueBrowseId )
) returnfalse; break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.