865 lines
32 KiB
C++
865 lines
32 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* 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 (const com::sun::star::container::NoSuchElementException&)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|