diff options
Diffstat (limited to 'framework/source/lomenubar/FrameHelper.cxx')
-rw-r--r-- | framework/source/lomenubar/FrameHelper.cxx | 862 |
1 files changed, 862 insertions, 0 deletions
diff --git a/framework/source/lomenubar/FrameHelper.cxx b/framework/source/lomenubar/FrameHelper.cxx new file mode 100644 index 000000000000..7b749648d34c --- /dev/null +++ b/framework/source/lomenubar/FrameHelper.cxx @@ -0,0 +1,862 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * A LibreOffice extension to send the menubar structure through DBusMenu + * + * Copyright 2011 Canonical, Ltd. + * Authors: + * Alberto Ruiz <alberto.ruiz@codethink.co.uk> + * + * This program is free software: you can redistribute it and/or modify it under + * the the GNU Lesser General Public License version 3, as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, + * SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR PURPOSE. See the applicable + * version of the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include "FrameHelper.hxx" +#include "AwtKeyToDbusmenuString.hxx" +#include "MenuItemInfo.hxx" +#include "MenuItemStatusListener.hxx" + +#include <boost/foreach.hpp> + +#include <com/sun/star/awt/KeyEvent.hpp> +#include <com/sun/star/awt/SystemDependentXWindow.hpp> +#include <com/sun/star/awt/XSystemDependentWindowPeer.hpp> +#include <com/sun/star/awt/XWindow2.hpp> +#include <com/sun/star/awt/Key.hpp> +#include <com/sun/star/awt/KeyModifier.hpp> +#include <com/sun/star/awt/MenuEvent.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/awt/XPopupMenu.hpp> +#include <com/sun/star/awt/XMenuExtended.hpp> +#include <com/sun/star/awt/XMenuListener.hpp> +#include <com/sun/star/awt/XPopupMenuExtended.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchHelper.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XModuleManager.hpp> +#include <com/sun/star/frame/XPopupMenuController.hpp> +#include <com/sun/star/frame/FrameAction.hpp> +#include <com/sun/star/frame/FrameActionEvent.hpp> +#include <com/sun/star/lang/SystemDependent.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/util/URL.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/ui/XUIElement.hpp> +#include <com/sun/star/ui/XUIConfigurationManager.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XAcceleratorConfiguration.hpp> +#include <com/sun/star/ui/XModuleUIConfigurationManagerSupplier.hpp> +#include <rtl/process.h> + +#include <gio/gio.h> +//#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#include <libdbusmenu-glib/client.h> +#pragma GCC diagnostic error "-Wignored-qualifiers" +//#pragma GCC diagnostic pop +#include <libdbusmenu-gtk/menuitem.h> + +using rtl::OUString; +using rtl::OString; +using rtl::OUStringToOString; + +using namespace ::com::sun::star; + +using com::sun::star::awt::KeyEvent; +using com::sun::star::awt::MenuEvent; +using com::sun::star::awt::SystemDependentXWindow; +using com::sun::star::awt::XMenuListener; +using com::sun::star::awt::XMenuExtended; +using com::sun::star::awt::XMenuListener; +using com::sun::star::awt::MenuEvent; +using com::sun::star::awt::XPopupMenu; +using com::sun::star::awt::XPopupMenuExtended; +using com::sun::star::awt::XSystemDependentWindowPeer; +using com::sun::star::awt::XWindow2; +using com::sun::star::beans::XPropertySet; +using com::sun::star::beans::PropertyValue; +using com::sun::star::container::XNameAccess; +using com::sun::star::container::NoSuchElementException; +using com::sun::star::frame::XController; +using com::sun::star::frame::XComponentLoader; +using com::sun::star::frame::XDispatch; +using com::sun::star::frame::XDispatchProvider; +using com::sun::star::frame::XDispatchHelper; +using com::sun::star::frame::XModel; +using com::sun::star::frame::XModuleManager; +using com::sun::star::frame::XLayoutManager; +using com::sun::star::frame::XPopupMenuController; +using com::sun::star::lang::SystemDependent::SYSTEM_XWINDOW; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::UNO_QUERY_THROW; +using com::sun::star::uno::Sequence; +using com::sun::star::uno::XComponentContext; +using com::sun::star::uno::XInterface; +using com::sun::star::ui::XUIElement; +using com::sun::star::ui::XUIConfigurationManager; +using com::sun::star::ui::XUIConfigurationManagerSupplier; +using com::sun::star::ui::XAcceleratorConfiguration; +using com::sun::star::ui::XModuleUIConfigurationManagerSupplier; +using com::sun::star::util::URL; +using com::sun::star::util::XURLTransformer; + + +namespace +{ + static Sequence<Any> lcl_initArgs(const OUString& sModuleName, const Reference<XFrame> xFrame) + { + // These are the arguments needed for the XPopupMenuController + Sequence<Any> aResult(2); + PropertyValue item; + + item.Name = OUString(RTL_CONSTASCII_USTRINGPARAM("ModuleName")); + item.Value <<= sModuleName; + aResult[0] <<= item; + + item.Name = OUString(RTL_CONSTASCII_USTRINGPARAM("Frame")); + item.Value <<= xFrame; + aResult[1] <<= item; + return aResult; + }; + + struct DispatchConnection + { + Reference<XDispatch> m_xDispatch; + URL m_aUrl; + DispatchConnection(Reference<XDispatch> xDispatch, URL aUrl) + : m_xDispatch(xDispatch), m_aUrl(aUrl) + {} + }; +} + +namespace framework { namespace lomenubar +{ + class DispatchRegistry + { + private: + ::std::vector<DispatchConnection> m_vDispatchConnections; + const Reference<XStatusListener> m_xStatusListener; + public: + DispatchRegistry(const Reference<XStatusListener> xStatusListener) + : m_xStatusListener(xStatusListener) + {} + ~DispatchRegistry() + { + BOOST_FOREACH(const DispatchConnection& rConnection, m_vDispatchConnections) + { + rConnection.m_xDispatch->removeStatusListener(m_xStatusListener, rConnection.m_aUrl); + } + } + void Connect(Reference<XDispatch> xDispatch, URL aURL) + { + const DispatchConnection connection(xDispatch, aURL); + m_vDispatchConnections.push_back(connection); + xDispatch->addStatusListener(m_xStatusListener, aURL); + } + }; +}} + +// ------------------------ Item callbacks --------------------------- +// Item activated. It distpatches the command associated to a given menu item. +void +item_activated (DbusmenuMenuitem *item, guint /*timestamp*/, gpointer user_data) +{ + FrameHelper *helper = (FrameHelper*)user_data; + OUString command = OUString::createFromAscii(dbusmenu_menuitem_property_get (item, "CommandURL")); + helper->dispatchCommand (command); +} + +// Rebuilds the submenu +gboolean +item_about_to_show (DbusmenuMenuitem *item, gpointer user_data) +{ + //Get the XMenu interface for the MenuBar UIElement + FrameHelper *helper = (FrameHelper*)user_data; + Reference < XFrame > xFrame = helper->getFrame (); + Reference< XPropertySet > frameProps (xFrame, UNO_QUERY); + Reference < XLayoutManager > xLayoutManager(frameProps->getPropertyValue(OUString(RTL_CONSTASCII_USTRINGPARAM("LayoutManager"))), + UNO_QUERY); + Reference < XUIElement > menuBar(xLayoutManager->getElement (OUString(RTL_CONSTASCII_USTRINGPARAM("private:resource/menubar/menubar"))), + UNO_QUERY); + Reference < XPropertySet > menuPropSet (menuBar, UNO_QUERY); + + if (!menuPropSet.is ()) + { + return FALSE; + } + + Reference < XMenu > xMenu(menuPropSet->getPropertyValue(OUString(RTL_CONSTASCII_USTRINGPARAM("XMenuBar"))), + UNO_QUERY); + if (!xMenu.is()) + { + return FALSE; + } + + //Find xMenu for the first level item + Reference < XMenu > xSubMenu; + Reference < XMenuExtended > xMenuEx (xMenu, UNO_QUERY); + guint16 root_count = xMenu->getItemCount(); + for (guint16 i = 0; i<root_count ;i++) + { + + guint16 id = xMenu->getItemId (i); + if (id == 0) + continue; + + OUString command = xMenuEx->getCommand (id); + + //We must find the element with the same command URL + if (! OUString::createFromAscii (dbusmenu_menuitem_property_get (item, "CommandURL")).equals (command)) + continue; + + Reference <XPopupMenu> subPopup (xMenu->getPopupMenu (id), UNO_QUERY); + xSubMenu = Reference <XMenu> (subPopup, UNO_QUERY); + break; + } + + //We only do this for toplevel items + if (xSubMenu.is ()) + { + helper->rebuildMenu (xSubMenu, item); + return FALSE; + } + + //If it is not a toplevel item we stop trying to rebuild + return TRUE; +} + +void +destroy_menuitem (gpointer data) +{ + g_object_unref (G_OBJECT (data)); +} + +void +destroy_menu_item_info (gpointer data) +{ + delete (MenuItemInfo*)data; +} + +// ------------------------ FrameHelper Class ------------------------------- +FrameHelper::FrameHelper(const Reference< XMultiServiceFactory >& rServiceManager, + const Reference< XFrame >& xFrame, + DbusmenuServer* server) + : m_xStatusListener(new MenuItemStatusListener(this)) + , m_pDispatchRegistry(new framework::lomenubar::DispatchRegistry(m_xStatusListener)) + , m_xMSF(rServiceManager) + , m_xTrans(m_xMSF->createInstance( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer" ))), UNO_QUERY) + , m_xMM(m_xMSF->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.ModuleManager"))),UNO_QUERY) + , m_xPCF(m_xMSF->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.PopupMenuControllerFactory"))), UNO_QUERY) + , m_xFrame(xFrame) + , m_xdp(xFrame, UNO_QUERY) + , m_args(lcl_initArgs(m_xMM->identify(xFrame), xFrame)) + , m_server(server) + , m_root(NULL) + , m_watcher_set(FALSE) + , m_blockDetach(FALSE) +{ + + //Get xUICommands database (to retrieve labels, see FrameJob::getLabelFromCommandURL ()) + Reference < XNameAccess > xNameAccess (m_xMSF->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.UICommandDescription"))), + UNO_QUERY); + xNameAccess->getByName(m_xMM->identify(xFrame)) >>= m_xUICommands; + + + // This initializes the shortcut database + getAcceleratorConfigurations (xFrame->getController()->getModel (), m_xMM); + + // This is a hash table that maps Command URLs to MenuItemInfo classes + // to cache command information + m_commandsInfo = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + destroy_menu_item_info); + +} + +void SAL_CALL +FrameHelper::disposing (const EventObject& /*aEvent*/ ) throw (RuntimeException) +{} + +FrameHelper::~FrameHelper() +{ + ::boost::scoped_ptr< ::framework::lomenubar::DispatchRegistry>().swap(m_pDispatchRegistry); + if (m_server) + g_object_unref (m_server); + + if (m_watcher_set) + g_bus_unwatch_name (m_watcher); + + g_hash_table_destroy (m_commandsInfo); +} + +void +FrameHelper::setRootItem (DbusmenuMenuitem *root) +{ + this->m_root = root; +} + +void +FrameHelper::setRegistrarWatcher (guint watcher) +{ + m_watcher_set = TRUE; + this->m_watcher = watcher; +} + +void +FrameHelper::setServer (DbusmenuServer *server) +{ + this->m_server = server; +} + +//Getters +Reference < XFrame > +FrameHelper::getFrame () +{ + return m_xFrame; +} + +GHashTable* +FrameHelper::getCommandsInfo () +{ + return m_commandsInfo; +} + +unsigned long +FrameHelper::getXID () +{ + Reference< XSystemDependentWindowPeer > xWin( m_xFrame->getContainerWindow(), UNO_QUERY ); + + if (!xWin.is()) + return 0; + + sal_Int8 processID[16]; + rtl_getGlobalProcessId( (sal_uInt8*)processID ); + Sequence <signed char> pidSeq (processID, 16); + + SystemDependentXWindow xWindow; + xWin->getWindowHandle (pidSeq, SYSTEM_XWINDOW) >>= xWindow; + + return xWindow.WindowHandle; +} + +void SAL_CALL +FrameHelper::frameAction(const FrameActionEvent& action) throw (RuntimeException) +{ + //If theh component is detached from the frame, remove this action listener, + //it is then disposed and destroyed by the frame. We deregister the window + //from the AppMenu Registrar + + //This is a special case, .uno:printPreview detaches the component but we are + //not actually switching to another document. + if (m_blockDetach) + { + m_blockDetach = TRUE; + return; + } + + if (action.Action == frame::FrameAction_COMPONENT_DETACHING) + { + GError *error = NULL; + + + m_xFrame->removeFrameActionListener (this); + Reference< XPropertySet > frameProps (m_xFrame, UNO_QUERY); + Reference < XLayoutManager > xLayoutManager(frameProps->getPropertyValue(OUString(RTL_CONSTASCII_USTRINGPARAM("LayoutManager"))), + UNO_QUERY); + xLayoutManager->showElement (OUString(RTL_CONSTASCII_USTRINGPARAM("private:resource/menubar/menubar"))); + + unsigned long xid = getXID(); + + GDBusProxy *proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "com.canonical.AppMenu.Registrar", + "/com/canonical/AppMenu/Registrar", + "com.canonical.AppMenu.Registrar", + NULL, + &error); + if (error) + { + g_warning ("Couldn't get /com/canonical/AppMenu/Registrar proxy"); + g_error_free (error); + return; + } + + //TODO: Check if window is registered already + g_dbus_proxy_call_sync (proxy, + "UnregisterWindow", + g_variant_new ("(u)", (guint32)xid), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + if (error) + { + g_warning ("Couldn't call /com/canonical/AppMenu/Registrar.UnregisterWindow"); + g_error_free (error); + } + + if (m_server) + { + g_object_unref (m_server); + m_server = NULL; + m_root = NULL; + } + + if (m_watcher_set) + { + g_bus_unwatch_name (m_watcher); + m_watcher_set = FALSE; + } + + return; + } +} + +//This function rebuilds (or builds from scratch) a DbusmenuMenuitem structure +//from a given pair of XMenu/Dbusmenuitem. +void +FrameHelper::rebuildMenu (Reference < XMenu > xMenu, + DbusmenuMenuitem *parent) +{ + g_return_if_fail (parent != NULL); + GList *items = dbusmenu_menuitem_get_children (parent); + guint nitems = g_list_length (items); //number of available Dbusmenuitems + guint16 count = xMenu->getItemCount (); //number of real menu items + + // One item does not represent always the same command. + // We do this for performance reasons, as it's really hard to match a command with + // a single dbusmenuitem given the lack of information provided by the status listener + if (count > nitems) + { + // Add enough Dbusmenuitems to replicate all + for (guint16 i = 0; i < (count - nitems); i++) + { + DbusmenuMenuitem *item = dbusmenu_menuitem_new (); + dbusmenu_menuitem_child_append (parent, item); + g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(item_activated), this); + } + items = dbusmenu_menuitem_get_children (parent); + } + if (count < nitems) + { + // If there is an excess of Dbusmenuitems we make them invisible + for (guint16 i = nitems - 1; i >= count; i--) + { + DbusmenuMenuitem *item = DBUSMENU_MENUITEM (g_list_nth_data(items, i)); + dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); + } + } + + for (guint16 i = 0; i<count; i++) + { + Reference < XMenuExtended > xMenuEx (xMenu, UNO_QUERY); + guint16 id = xMenu->getItemId (i); + OUString oUCommand = xMenuEx->getCommand (id); + OString command = OUStringToOString (oUCommand, RTL_TEXTENCODING_ASCII_US); + + DbusmenuMenuitem *item = DBUSMENU_MENUITEM(g_list_nth_data(items, i)); + + if (!item) + continue; + + if (!DBUSMENU_IS_MENUITEM (item)) + continue; + + // We drop the WindowList, doesn't work properly and it's useless anyhow + if (oUCommand.equals (OUString(RTL_CONSTASCII_USTRINGPARAM(".uno:WindowList")))) + continue; + + //We set the default properties (in case it was not visible or a separator) + dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_DEFAULT); + dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); + + if (id == 0) + { + dbusmenu_menuitem_property_set (item, "CommandURL", "slot:0"); + dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); + //Getting rid of any possible children + g_list_free_full (dbusmenu_menuitem_take_children (item), destroy_menuitem); + continue; + } + + //Setting the command + dbusmenu_menuitem_property_set (item, "CommandURL", command.getStr()); + + //Getting a shortcut + KeyEvent kev = findShortcutForCommand (oUCommand); + + if (kev.KeyCode != 0) //KeyCode must have a value + { + GVariantBuilder builder; + const gchar* keystring = AwtKeyToDbusmenuString(kev.KeyCode); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + + //We map KeyEvent.Modifiers with Dbusmenu modifiers strings + if (awt::KeyModifier::SHIFT & kev.Modifiers) + g_variant_builder_add(&builder, "s", DBUSMENU_MENUITEM_SHORTCUT_SHIFT); + if (awt::KeyModifier::MOD2 & kev.Modifiers) + g_variant_builder_add(&builder, "s", DBUSMENU_MENUITEM_SHORTCUT_ALT); + if (awt::KeyModifier::MOD1 & kev.Modifiers || awt::KeyModifier::MOD3 & kev.Modifiers) + g_variant_builder_add(&builder, "s", DBUSMENU_MENUITEM_SHORTCUT_CONTROL); + + g_variant_builder_add(&builder, "s", keystring); + + GVariant * inside = g_variant_builder_end(&builder); + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value(&builder, inside); + + GVariant * outsidevariant = g_variant_builder_end(&builder); + dbusmenu_menuitem_property_set_variant(item, DBUSMENU_MENUITEM_PROP_SHORTCUT, outsidevariant); + } + + // Lookup for a MenuItemInfo object for this menuitem, create one if it doesn't exist + // this object caches the values that change on status updates. + MenuItemInfo* commInfo = (MenuItemInfo*)g_hash_table_lookup (m_commandsInfo, (gconstpointer)command.getStr()); + if (!commInfo) + { + commInfo = new MenuItemInfo (); + g_hash_table_insert (m_commandsInfo, g_strdup (command.getStr()), commInfo); + + OUString oULabel = getLabelFromCommandURL(oUCommand); + if (oULabel.getLength() == 0) + { + oULabel = xMenu->getItemText (id); + } + + //Replace tilde with underscore for Dbusmenu Alt accelerators + oULabel = oULabel.replace ((sal_Unicode)0x007e, (sal_Unicode)0x005f); + // GLib behaves better than OUStringToOString wrt encoding transformation + gchar* label = g_utf16_to_utf8 (oULabel.getStr(), + oULabel.getLength(), + NULL, NULL, NULL); + commInfo->setLabel (label); + g_free (label); + } + + //Update the check state directly from the data, this is more reliable + Reference < XPopupMenu > popUp (xMenu, UNO_QUERY); + if (popUp.is() && popUp->isItemChecked (id)) + { + commInfo->setCheckState (DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED); + } + + dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_LABEL, commInfo->getLabel ()); + dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, commInfo->getEnabled ()); + + //TODO: Find a selection of which commands are radio toggle type + if (commInfo->getCheckState () != DBUSMENU_MENUITEM_TOGGLE_STATE_UNKNOWN) + { + dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, commInfo->getCheckType ()); + dbusmenu_menuitem_property_set_int (item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, commInfo->getCheckState ()); + } + + // Adding status listener + URL commandURL; + commandURL.Complete = oUCommand; + m_xTrans->parseStrict (commandURL); + + Reference < XDispatch > xDispatch = m_xdp->queryDispatch (commandURL, OUString(), 0); + if(xDispatch.is()) + m_pDispatchRegistry->Connect(xDispatch, commandURL); + + Reference < XPopupMenu > subPopMenu (xMenu->getPopupMenu (id), UNO_QUERY); + + //Some menus do not provide the information available through the normal XMenu interface, + //we need to access that info through a special XPopupMenuController + if (isSpecialSubmenu (oUCommand)) + { + Reference < XPropertySet > xMSFProps (m_xMSF, UNO_QUERY); + Reference <XComponentContext> xContext (xMSFProps->getPropertyValue (OUString(RTL_CONSTASCII_USTRINGPARAM("DefaultContext"))), + UNO_QUERY); + + Reference < XPopupMenuController > xRFC (m_xPCF->createInstanceWithArgumentsAndContext(oUCommand, + m_args, + xContext), + UNO_QUERY); + + Reference < XPopupMenu > xPO (m_xMSF->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("stardiv.Toolkit.VCLXPopupMenu"))), + UNO_QUERY); + + if (xRFC.is () && xPO.is ()) + { + xRFC->setPopupMenu (xPO); + xRFC->updatePopupMenu (); + Reference < XMenu > subMenu (xPO, UNO_QUERY); + rebuildMenu (subMenu, item); + } + else if (subPopMenu.is ()) + { + Reference <XMenu> subMenu (subPopMenu, UNO_QUERY); + rebuildMenu (subMenu, item); + } + } + + // Introspect submenus + else if (subPopMenu.is ()) + { + Reference <XMenu> subMenu (subPopMenu, UNO_QUERY); + g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW, G_CALLBACK(item_about_to_show), this); + rebuildMenu (subMenu, item); + } + else + { + //Getting rid of any possible children + g_list_free_full (dbusmenu_menuitem_take_children (item), destroy_menuitem); + } + } + + return; +} + +//Gets the menu item Label given a CommandURL +//This is a work around for bug: https://bugs.freedesktop.org/show_bug.cgi?id=34127 +OUString +FrameHelper::getLabelFromCommandURL (OUString commandURL) +{ + OUString label; + + Sequence < PropertyValue > commandProps; + + if (commandURL.getLength () < 1) + return label; + + if (!m_xUICommands.is()) + return label; + + try + { + m_xUICommands->getByName (commandURL) >>= commandProps; + } + catch (com::sun::star::container::NoSuchElementException e) + { + return label; + } + + for (sal_Int32 i = 0; i < commandProps.getLength(); i++) + { + if ( commandProps[i].Name.equalsAsciiL (RTL_CONSTASCII_STRINGPARAM ("Label"))) + { + commandProps[i].Value >>= label; + label = label.replace ((sal_Unicode)0x007e, (sal_Unicode)0x005f); + //break; + } + } + + return label; +} + +//This method is a facility to bootstrap the Dbusmenuitem strcuture from the menubar +void +FrameHelper::rebuildMenuFromRoot () +{ + Reference < XFrame > xFrame = getFrame (); + Reference < XPropertySet > frameProps (xFrame, UNO_QUERY); + Reference < XLayoutManager > xLayoutManager (frameProps->getPropertyValue(OUString(RTL_CONSTASCII_USTRINGPARAM("LayoutManager"))), + UNO_QUERY); + Reference < XUIElement > menuBar (xLayoutManager->getElement (OUString(RTL_CONSTASCII_USTRINGPARAM("private:resource/menubar/menubar"))), + UNO_QUERY); + Reference < XPropertySet > menuPropSet (menuBar, UNO_QUERY); + + if (!menuPropSet.is ()) + return; + + Reference < XMenu > xMenu (menuPropSet->getPropertyValue(OUString(RTL_CONSTASCII_USTRINGPARAM("XMenuBar"))), + UNO_QUERY); + if (!xMenu.is ()) + return; + + rebuildMenu (xMenu, m_root); +} + +//Some menus are special, this is the list of them +gboolean +FrameHelper::isSpecialSubmenu (OUString command) +{ + const gchar * specialSubmenus[11] = {".uno:CharFontName", + ".uno:FontHeight", + ".uno:ObjectMenue", + ".uno:InsertPageHeader", + ".uno:InsertPageFooter", + ".uno:ChangeControlType", + ".uno:AvailableToolbars", + ".uno:ScriptOrganizer", + ".uno:RecentFileList", + ".uno:AddDirect", + ".uno:AutoPilotMenu"}; + + for (gint i = 0; i < 11; i++) + { + if (command.equals (OUString::createFromAscii (specialSubmenus[i]))) + return TRUE; + } + return FALSE; +} + +void +FrameHelper::dispatchCommand (OUString command) +{ + OUString target = OUString(RTL_CONSTASCII_USTRINGPARAM("")); + Reference < XDispatchHelper > xdh (m_xMSF->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.DispatchHelper"))), + UNO_QUERY); + + // This is a special case, we don't want the helper to be disconnected from the frame + // when PrintPreview dettaches. See the frameAction method. + if (command.equals (OUString(RTL_CONSTASCII_USTRINGPARAM(".uno:PrintPreview")))) + { + m_blockDetach = TRUE; + } + + // This is a special case for the recentfilelist + if (command.matchAsciiL ("vnd.sun.star.popup:RecentFileList", 33, 0)) + { + target = OUString(RTL_CONSTASCII_USTRINGPARAM("_default")); + + Reference < XPropertySet > xMSFProps (m_xMSF, UNO_QUERY); + Reference <XComponentContext> xContext (xMSFProps->getPropertyValue (OUString(RTL_CONSTASCII_USTRINGPARAM("DefaultContext"))), + UNO_QUERY); + Reference < XPopupMenuController > xRFC (m_xPCF->createInstanceWithArgumentsAndContext(OUString(RTL_CONSTASCII_USTRINGPARAM(".uno:RecentFileList")), + m_args, + xContext), + UNO_QUERY); + Reference < XMenuListener > xML (xRFC, UNO_QUERY); + + Reference < XPopupMenu > xPO (m_xMSF->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("stardiv.Toolkit.VCLXPopupMenu"))), + UNO_QUERY); + + if (xRFC.is () && xPO.is ()) + { + xRFC->setPopupMenu (xPO); + xRFC->updatePopupMenu (); + Reference < XMenu > subMenu (xPO, UNO_QUERY); + Reference < XMenuExtended > subMenuEx (xPO, UNO_QUERY); + + //We need to find the item idd + for (int i = 0; i < subMenu->getItemCount (); i++) + { + int id = subMenu->getItemId (i); + + if (subMenuEx->getCommand (id).equals (command)) + { + MenuEvent mev; + mev.MenuId = id; + + xML->select (mev); + } + } + } + + return; + } + + if (command.matchAsciiL ("private:factory/", 16, 0)) + target = OUString(RTL_CONSTASCII_USTRINGPARAM("_blank")); + + xdh->executeDispatch (Reference < XDispatchProvider > (m_xFrame, UNO_QUERY), + command, + target, + 0, + Sequence < PropertyValue > ()); +} + +//Set all the accelerator configuration sources +void +FrameHelper::getAcceleratorConfigurations (Reference < XModel > xModel, + Reference < XModuleManager> xModuleManager) +{ + //Get document shortcut database + Reference< XUIConfigurationManagerSupplier > docUISupplier(xModel, UNO_QUERY); + Reference< XUIConfigurationManager > docUIManager = docUISupplier->getUIConfigurationManager(); + Reference< XAcceleratorConfiguration > docAccelConf(docUIManager->getShortCutManager(), UNO_QUERY); + this->m_docAccelConf = docAccelConf; + + //Get module shurtcut database + Reference< XModuleUIConfigurationManagerSupplier > modUISupplier(m_xMSF->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.ui.ModuleUIConfigurationManagerSupplier"))), + UNO_QUERY); + Reference< XUIConfigurationManager > modUIManager = modUISupplier->getUIConfigurationManager(xModuleManager->identify(m_xFrame)); + Reference< XAcceleratorConfiguration > modAccelConf(modUIManager->getShortCutManager(), UNO_QUERY); + this->m_modAccelConf = modAccelConf; + + //Get global shortcut database + Reference< XAcceleratorConfiguration > globAccelConf(m_xMSF->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.ui.GlobalAcceleratorConfiguration"))), + UNO_QUERY); + this->m_globAccelConf = globAccelConf; +} + + +//This function finds a KeyEvent with the shortcut information for each command +KeyEvent +FrameHelper::findShortcutForCommand (OUString command) + +{ + KeyEvent kev; + + Sequence < OUString > commands (1); + commands[0] = command; + + try + { + Sequence < Any > evs = m_docAccelConf->getPreferredKeyEventsForCommandList (commands); + + for (int j = 0; j < evs.getLength (); j++) + { + KeyEvent ev; + if (evs[j] >>= ev) + return ev; + } + } + catch (...) + {} + try + { + Sequence < Any > evs = m_modAccelConf->getPreferredKeyEventsForCommandList (commands); + + for (int j = 0; j < evs.getLength (); j++) + { + KeyEvent ev; + if (evs[j] >>= ev) + return ev; + } + } + catch (...) + {} + try + { + Sequence < Any > evs = m_globAccelConf->getPreferredKeyEventsForCommandList (commands); + + for (int j = 0; j < evs.getLength (); j++) + { + KeyEvent ev; + if (evs[j] >>= ev) + return ev; + } + } + catch (...) + {} + + //NOTE: For some reason this item does not return its shortcut. Setting manually: + if (command.equals (OUString(RTL_CONSTASCII_USTRINGPARAM(".uno:HelpIndex")))) + { + kev.KeyCode = awt::Key::F1; + } + + return kev; +} |