← Back to team overview

ayatana-commits team mailing list archive

[Merge] lp:~ted/indicator-appmenu/json-dbus-dump into lp:indicator-appmenu

 

Ted Gould has proposed merging lp:~ted/indicator-appmenu/json-dbus-dump into lp:indicator-appmenu.

Requested reviews:
  Indicator Applet Developers (indicator-applet-developers)


Adds a function to get a JSON dump from the widget hierarchy.  I still haven't figured out how to do the check/radio property, but I think this is in a good shape to merge and figure that out later.
-- 
https://code.launchpad.net/~ted/indicator-appmenu/json-dbus-dump/+merge/29625
Your team ayatana-commits is subscribed to branch lp:indicator-appmenu.
=== modified file 'configure.ac'
--- configure.ac	2010-06-29 14:50:54 +0000
+++ configure.ac	2010-07-10 04:46:39 +0000
@@ -1,11 +1,11 @@
 
-AC_INIT(indicator-appmenu, 0.0.7, ted@xxxxxxxxxxxxx)
+AC_INIT(indicator-appmenu, 0.0.8, ted@xxxxxxxxxxxxx)
 AC_COPYRIGHT([Copyright 2010 Canonical])
 
 AC_PREREQ(2.53)
 
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(indicator-appmenu, 0.0.7)
+AM_INIT_AUTOMAKE(indicator-appmenu, 0.0.8)
 
 AM_MAINTAINER_MODE
 

=== modified file 'src/application-menu-debug.xml'
--- src/application-menu-debug.xml	2010-06-01 16:38:23 +0000
+++ src/application-menu-debug.xml	2010-07-10 04:46:39 +0000
@@ -11,6 +11,10 @@
 		<method name="AllMenus">
 			<arg type="a(uos)" name="menus" direction="out" />
 		</method>
+		<method name="JSONDump">
+			<arg type="u" name="windowID" direction="in" />
+			<arg type="s" name="menu-json" direction="out" />
+		</method>
 
 	</interface>
 </node>

=== modified file 'src/indicator-appmenu.c'
--- src/indicator-appmenu.c	2010-07-01 04:51:36 +0000
+++ src/indicator-appmenu.c	2010-07-10 04:46:39 +0000
@@ -31,6 +31,9 @@
 #include <libindicator/indicator.h>
 #include <libindicator/indicator-object.h>
 
+#include <libdbusmenu-glib/menuitem.h>
+#include <libdbusmenu-glib/client.h>
+
 #include <libbamf/bamf-matcher.h>
 
 #include "indicator-appmenu-marshal.h"
@@ -148,6 +151,10 @@
 static gboolean _application_menu_debug_server_all_menus             (IndicatorAppmenuDebug * iappd,
                                                                       GPtrArray ** entries,
                                                                       GError ** error);
+static gboolean _application_menu_debug_server_j_so_ndump            (IndicatorAppmenuDebug * iappd,
+                                                                      guint windowid,
+                                                                      gchar ** jsondata,
+                                                                      GError ** error);
 
 /**********************
   DBus Interfaces
@@ -759,7 +766,8 @@
 /* Unique error codes for debug interface */
 enum {
 	ERROR_NO_APPLICATIONS,
-	ERROR_NO_DEFAULT_APP
+	ERROR_NO_DEFAULT_APP,
+	ERROR_WINDOW_NOT_FOUND
 };
 
 /* Get the current menu */
@@ -824,3 +832,280 @@
 	return TRUE;
 }
 
+/* Looks to see if we can find an accel label to steal the
+   closure from */
+static void
+find_closure (GtkWidget * widget, gpointer user_data)
+{
+	GClosure ** closure = (GClosure **)user_data;
+
+	/* If we have one quit */
+	if (*closure != NULL) {
+		return;
+	}
+	
+	/* If we've got a label, steal its */
+	if (GTK_IS_ACCEL_LABEL(widget)) {
+		g_object_get (widget,
+		              "accel-closure", closure,
+		              NULL);
+		return;
+	}
+
+	/* If we've got a container, dig deeper */
+	if (GTK_IS_CONTAINER(widget)) {
+		gtk_container_foreach(GTK_CONTAINER(widget), find_closure, user_data);
+	}
+
+	return;
+}
+
+/* Look at the closures in an accel group and find
+   the one that matches the one we've been passed */
+static gboolean
+find_group_closure (GtkAccelKey * key, GClosure * closure, gpointer user_data)
+{
+	return closure == user_data;
+}
+
+/* Turn the key codes into a string for the JSON output */
+static void
+key2string (GtkAccelKey * key, GArray * strings)
+{
+	gchar * temp = NULL;
+
+	temp = g_strdup(", \"" DBUSMENU_MENUITEM_PROP_SHORTCUT "\": [[");
+	g_array_append_val(strings, temp);
+
+	if (key->accel_mods & GDK_CONTROL_MASK) {
+		temp = g_strdup_printf("\"%s\", ", DBUSMENU_MENUITEM_SHORTCUT_CONTROL);
+		g_array_append_val(strings, temp);
+	}
+	if (key->accel_mods & GDK_MOD1_MASK) {
+		temp = g_strdup_printf("\"%s\", ", DBUSMENU_MENUITEM_SHORTCUT_ALT);
+		g_array_append_val(strings, temp);
+	}
+	if (key->accel_mods & GDK_SHIFT_MASK) {
+		temp = g_strdup_printf("\"%s\", ", DBUSMENU_MENUITEM_SHORTCUT_SHIFT);
+		g_array_append_val(strings, temp);
+	}
+	if (key->accel_mods & GDK_SUPER_MASK) {
+		temp = g_strdup_printf("\"%s\", ", DBUSMENU_MENUITEM_SHORTCUT_SUPER);
+		g_array_append_val(strings, temp);
+	}
+
+	temp = g_strdup_printf("\"%s\"]]", gdk_keyval_name(key->accel_key));
+	g_array_append_val(strings, temp);
+
+	return;
+}
+
+/* Do something for each menu item */
+static void
+menu_iterator (GtkWidget * widget, gpointer user_data)
+{
+	GArray * strings = (GArray *)user_data;
+
+	gchar * temp = g_strdup("{");
+	g_array_append_val(strings, temp);
+
+	/* TODO: We need some sort of useful ID, but for now  we're
+	   just ensuring it's unique. */
+	temp = g_strdup_printf("\"id\": %d", strings->len);
+	g_array_append_val(strings, temp);
+
+	if (GTK_IS_SEPARATOR_MENU_ITEM(widget)) {
+		temp = g_strdup_printf(", \"" DBUSMENU_MENUITEM_PROP_TYPE "\": \"%s\"", DBUSMENU_CLIENT_TYPES_SEPARATOR);
+		g_array_append_val(strings, temp);
+	} else {
+		temp = g_strdup_printf(", \"" DBUSMENU_MENUITEM_PROP_TYPE "\": \"%s\"", DBUSMENU_CLIENT_TYPES_DEFAULT);
+		g_array_append_val(strings, temp);
+	}
+
+	temp = g_strdup_printf(", \"" DBUSMENU_MENUITEM_PROP_ENABLED "\": %s", gtk_widget_get_sensitive(GTK_WIDGET(widget)) ? "true" : "false");
+	g_array_append_val(strings, temp);
+
+	temp = g_strdup_printf(", \"" DBUSMENU_MENUITEM_PROP_VISIBLE "\": %s", gtk_widget_get_visible(GTK_WIDGET(widget)) ? "true" : "false");
+	g_array_append_val(strings, temp);
+
+	const gchar * label = gtk_menu_item_get_label(GTK_MENU_ITEM(widget));
+	if (label != NULL) {
+		temp = g_strdup_printf(", \"" DBUSMENU_MENUITEM_PROP_LABEL "\": \"%s\"", label);
+		g_array_append_val(strings, temp);
+	}
+
+	/* Deal with shortcuts, find the accel closure if we can and then
+	   turn that into a string */
+	GClosure * closure = NULL;
+	find_closure(widget, &closure);
+
+	if (closure != NULL) {
+		GtkAccelGroup * group = gtk_accel_group_from_accel_closure(closure);
+		if (group != NULL) {
+			GtkAccelKey * key = gtk_accel_group_find(group, find_group_closure, closure);
+			if (key != NULL) {
+				key2string(key, strings);
+			}
+		}
+	}
+
+	/* TODO: Handle check/radio items */
+
+	GtkWidget * submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget));
+	if (submenu != NULL) {
+		temp = g_strdup(", \"" DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "\": \"" DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU "\"");
+		g_array_append_val(strings, temp);
+
+		temp = g_strdup(", \"submenu\": [ ");
+		g_array_append_val(strings, temp);
+
+		guint old_len = strings->len;
+
+		gtk_container_foreach(GTK_CONTAINER(submenu), menu_iterator, strings);
+
+		if (old_len == strings->len) {
+			temp = g_strdup("]");
+			g_array_append_val(strings, temp);
+		} else {
+			gchar * last_one = g_array_index(strings, gchar *, strings->len - 1);
+			guint lastlen = g_utf8_strlen(last_one, -1);
+
+			if (last_one[lastlen - 1] != ',') {
+				g_warning("Huh, this seems impossible.  Should be a comma at the end.");
+			}
+
+			last_one[lastlen - 1] = ']';
+		}
+	}
+
+	temp = g_strdup("},");
+	g_array_append_val(strings, temp);
+
+	return;
+}
+
+/* Takes an entry and outputs it into the ptrarray */
+static void
+entry2json (IndicatorObjectEntry * entry, GArray * strings)
+{
+	gchar * temp = g_strdup("{");
+	g_array_append_val(strings, temp);
+
+	/* TODO: We need some sort of useful ID, but for now  we're
+	   just ensuring it's unique. */
+	temp = g_strdup_printf("\"id\": %d", strings->len);
+	g_array_append_val(strings, temp);
+
+	if (entry->label != NULL) {
+		temp = g_strdup_printf(", \"" DBUSMENU_MENUITEM_PROP_LABEL "\": \"%s\"", gtk_label_get_label(entry->label));
+		g_array_append_val(strings, temp);
+
+		temp = g_strdup_printf(", \"" DBUSMENU_MENUITEM_PROP_ENABLED "\": %s", gtk_widget_get_sensitive(GTK_WIDGET(entry->label)) ? "true" : "false");
+		g_array_append_val(strings, temp);
+
+		temp = g_strdup_printf(", \"" DBUSMENU_MENUITEM_PROP_VISIBLE "\": %s", gtk_widget_get_visible(GTK_WIDGET(entry->label)) ? "true" : "false");
+		g_array_append_val(strings, temp);
+	}
+
+	if (entry->menu != NULL) {
+		temp = g_strdup(", \"" DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "\": \"" DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU "\"");
+		g_array_append_val(strings, temp);
+
+		temp = g_strdup(", \"submenu\": [ ");
+		g_array_append_val(strings, temp);
+
+		guint old_len = strings->len;
+
+		gtk_container_foreach(GTK_CONTAINER(entry->menu), menu_iterator, strings);
+
+		if (old_len == strings->len) {
+			temp = g_strdup("]");
+			g_array_append_val(strings, temp);
+		} else {
+			gchar * last_one = g_array_index(strings, gchar *, strings->len - 1);
+			guint lastlen = g_utf8_strlen(last_one, -1);
+
+			if (last_one[lastlen - 1] != ',') {
+				g_warning("Huh, this seems impossible.  Should be a comma at the end.");
+			}
+
+			last_one[lastlen - 1] = ']';
+		}
+	}
+
+
+	temp = g_strdup("},");
+	g_array_append_val(strings, temp);
+
+	return;
+}
+
+/* Make JSON out of our menus */
+static gboolean
+_application_menu_debug_server_j_so_ndump (IndicatorAppmenuDebug * iappd, guint windowid, gchar ** jsondata, GError ** error)
+{
+	IndicatorAppmenu * iapp = iappd->appmenu;
+	WindowMenus * wm = NULL;
+
+	if (windowid == 0) {
+		wm = iapp->default_app;
+	} else {
+		wm = WINDOW_MENUS(g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid)));
+	}
+
+	if (wm == NULL) {
+		g_set_error_literal(error, error_quark(), ERROR_WINDOW_NOT_FOUND, "Window not found");
+		return FALSE;
+	}
+
+	GArray * strings = g_array_new(TRUE, FALSE, sizeof(gchar *));
+	GList * entries = window_menus_get_entries(wm);
+	GList * entry;
+
+	gchar * temp = g_strdup("{");
+	g_array_append_val(strings, temp);
+
+	temp = g_strdup("\"id\": 0");
+	g_array_append_val(strings, temp);
+
+	if (entries != NULL) {
+		temp = g_strdup(", \"" DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "\": \"" DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU "\"");
+		g_array_append_val(strings, temp);
+
+		temp = g_strdup(", \"submenu\": [");
+		g_array_append_val(strings, temp);
+	}
+
+	guint old_len = strings->len;
+
+	for (entry = entries; entry != NULL; entry = g_list_next(entry)) {
+		entry2json(entry->data, strings);
+	}
+
+	if (entries != NULL) {
+		if (old_len == strings->len) {
+			temp = g_strdup("]");
+			g_array_append_val(strings, temp);
+		} else {
+			gchar * last_one = g_array_index(strings, gchar *, strings->len - 1);
+			guint lastlen = g_utf8_strlen(last_one, -1);
+
+			if (last_one[lastlen - 1] != ',') {
+				g_warning("Huh, this seems impossible.  Should be a comma at the end.");
+			}
+
+			last_one[lastlen - 1] = ']';
+		}
+	}
+
+	g_list_free(entries);
+
+	temp = g_strdup("}");
+	g_array_append_val(strings, temp);
+
+	*jsondata = g_strjoinv(NULL, (gchar **)strings->data);
+	g_strfreev((gchar **)strings->data);
+	g_array_free(strings, FALSE);
+
+	return TRUE;
+}


Follow ups