Merge lp:~ted/libdbusmenu/imageitem into lp:libdbusmenu/0.5

Proposed by Ted Gould
Status: Merged
Merged at revision: not available
Proposed branch: lp:~ted/libdbusmenu/imageitem
Merge into: lp:libdbusmenu/0.5
Diff against target: None lines
To merge this branch: bzr merge lp:~ted/libdbusmenu/imageitem
Reviewer Review Type Date Requested Status
Cody Russell (community) Approve
Review via email: mp+11141@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Ted Gould (ted) wrote :

Adds support for image menu items. They're fun!

Revision history for this message
Cody Russell (bratsche) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2009-07-01 05:34:05 +0000
3+++ .bzrignore 2009-09-02 18:29:01 +0000
4@@ -41,3 +41,4 @@
5 dbusmenu.py
6 mago.results
7 test-glib-simple-items
8+libdbusmenu-gtk/libdbusmenu_gtk_la-menuitem.lo
9
10=== modified file 'libdbusmenu-gtk/Makefile.am'
11--- libdbusmenu-gtk/Makefile.am 2009-06-24 04:05:44 +0000
12+++ libdbusmenu-gtk/Makefile.am 2009-09-02 18:29:01 +0000
13@@ -9,13 +9,16 @@
14
15 libdbusmenu_gtkinclude_HEADERS = \
16 client.h \
17- menu.h
18+ menu.h \
19+ menuitem.h
20
21 libdbusmenu_gtk_la_SOURCES = \
22 client.h \
23 client.c \
24 menu.h \
25- menu.c
26+ menu.c \
27+ menuitem.h \
28+ menuitem.c
29
30 libdbusmenu_gtk_la_LDFLAGS = \
31 -version-info $(LIBDBUSMENU_CURRENT):$(LIBDBUSMENU_REVISION):$(LIBDBUSMENU_AGE) \
32
33=== modified file 'libdbusmenu-gtk/client.c'
34--- libdbusmenu-gtk/client.c 2009-08-27 13:45:49 +0000
35+++ libdbusmenu-gtk/client.c 2009-09-03 17:12:18 +0000
36@@ -33,6 +33,7 @@
37 #include <gtk/gtk.h>
38
39 #include "client.h"
40+#include "menuitem.h"
41
42 /* Prototypes */
43 static void dbusmenu_gtkclient_class_init (DbusmenuGtkClientClass *klass);
44@@ -284,6 +285,8 @@
45 return mi;
46 }
47
48+/* The base type handler that builds a plain ol'
49+ GtkMenuItem to represent, well, the GtkMenuItem */
50 static gboolean
51 new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
52 {
53@@ -303,6 +306,8 @@
54 return TRUE;
55 }
56
57+/* Type handler for the seperators where it builds
58+ a GtkSeparator to act as the GtkMenuItem */
59 static gboolean
60 new_item_seperator (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
61 {
62@@ -322,9 +327,124 @@
63 return TRUE;
64 }
65
66+/* This handler looks at property changes for items that are
67+ image menu items. */
68+static void
69+image_property_handle (DbusmenuMenuitem * item, const gchar * property, const gchar * value, gpointer userdata)
70+{
71+ /* We're only looking at these two properties here */
72+ g_return_if_fail(!g_strcmp0(property, DBUSMENU_MENUITEM_PROP_ICON) || !g_strcmp0(property, DBUSMENU_MENUITEM_PROP_ICON_DATA));
73+
74+ if (value == NULL || value[0] == '\0') {
75+ /* This means that we're unsetting a value. */
76+ /* Try to use the other one */
77+ if (g_strcmp0(property, DBUSMENU_MENUITEM_PROP_ICON)) {
78+ property = DBUSMENU_MENUITEM_PROP_ICON_DATA;
79+ } else {
80+ property = DBUSMENU_MENUITEM_PROP_ICON;
81+ }
82+ }
83+
84+ /* Grab the data of the items that we've got, so that
85+ we can know how things need to change. */
86+ GtkMenuItem * gimi = dbusmenu_gtkclient_menuitem_get (DBUSMENU_GTKCLIENT(userdata), item);
87+ if (gimi == NULL) {
88+ g_warning("Oddly we're handling image properties on a menuitem that doesn't have any GTK structures associated with it.");
89+ return;
90+ }
91+ GtkWidget * gtkimage = gtk_image_menu_item_get_image(GTK_IMAGE_MENU_ITEM(gimi));
92+
93+ if (!g_strcmp0(property, DBUSMENU_MENUITEM_PROP_ICON_DATA)) {
94+ /* If we have an image already built from a name that is
95+ way better than a pixbuf. Keep it. */
96+ if (gtk_image_get_storage_type(GTK_IMAGE(gtkimage)) == GTK_IMAGE_ICON_NAME) {
97+ return;
98+ }
99+ }
100+
101+ /* Now figure out what to change */
102+ if (!g_strcmp0(property, DBUSMENU_MENUITEM_PROP_ICON)) {
103+ const gchar * iconname = dbusmenu_menuitem_property_get(item, property);
104+ if (iconname == NULL) {
105+ /* If there is no name, by golly we want no
106+ icon either. */
107+ gtkimage = NULL;
108+ } else {
109+ /* If we don't have an image, we need to build
110+ one so that we can set the name. Otherwise we
111+ can just convert it to this name. */
112+ if (gtkimage == NULL) {
113+ gtkimage = gtk_image_new_from_icon_name(iconname, GTK_ICON_SIZE_MENU);
114+ } else {
115+ gtk_image_set_from_icon_name(GTK_IMAGE(gtkimage), iconname, GTK_ICON_SIZE_MENU);
116+ }
117+ }
118+ } else {
119+ GdkPixbuf * image = dbusmenu_menuitem_property_get_image(item, property);
120+ if (image == NULL) {
121+ /* If there is no pixbuf, by golly we want no
122+ icon either. */
123+ gtkimage = NULL;
124+ } else {
125+ /* Resize the pixbuf */
126+ gint width, height;
127+ gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
128+ if (gdk_pixbuf_get_width(image) > width ||
129+ gdk_pixbuf_get_height(image) > height) {
130+ GdkPixbuf * newimage = gdk_pixbuf_scale_simple(image,
131+ width,
132+ height,
133+ GDK_INTERP_BILINEAR);
134+ g_object_unref(image);
135+ image = newimage;
136+ }
137+
138+ /* If we don't have an image, we need to build
139+ one so that we can set the pixbuf. */
140+ if (gtkimage == NULL) {
141+ gtkimage = gtk_image_new_from_pixbuf(image);
142+ } else {
143+ gtk_image_set_from_pixbuf(GTK_IMAGE(gtkimage), image);
144+ }
145+ }
146+
147+ }
148+
149+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(gimi), gtkimage);
150+
151+ return;
152+}
153+
154+/* This is a type call back for the image type where
155+ it uses the GtkImageMenuitem to create the menu item. */
156 static gboolean
157 new_item_image (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
158 {
159+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
160+ g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
161+ /* Note: not checking parent, it's reasonable for it to be NULL */
162+
163+ GtkMenuItem * gmi;
164+ gmi = GTK_MENU_ITEM(gtk_image_menu_item_new_with_label(dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_LABEL)));
165+
166+ if (gmi != NULL) {
167+ dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
168+ } else {
169+ return FALSE;
170+ }
171+
172+ image_property_handle(newitem,
173+ DBUSMENU_MENUITEM_PROP_ICON,
174+ dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_ICON),
175+ client);
176+ image_property_handle(newitem,
177+ DBUSMENU_MENUITEM_PROP_ICON_DATA,
178+ dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_ICON_DATA),
179+ client);
180+ g_signal_connect(G_OBJECT(newitem),
181+ DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED,
182+ G_CALLBACK(image_property_handle),
183+ client);
184
185 return TRUE;
186 }
187
188=== added file 'libdbusmenu-gtk/menuitem.c'
189--- libdbusmenu-gtk/menuitem.c 1970-01-01 00:00:00 +0000
190+++ libdbusmenu-gtk/menuitem.c 2009-09-02 18:50:29 +0000
191@@ -0,0 +1,130 @@
192+/*
193+A library to take the object model made consistent by libdbusmenu-glib
194+and visualize it in GTK.
195+
196+Copyright 2009 Canonical Ltd.
197+
198+Authors:
199+ Ted Gould <ted@canonical.com>
200+
201+This program is free software: you can redistribute it and/or modify it
202+under the terms of either or both of the following licenses:
203+
204+1) the GNU Lesser General Public License version 3, as published by the
205+Free Software Foundation; and/or
206+2) the GNU Lesser General Public License version 2.1, as published by
207+the Free Software Foundation.
208+
209+This program is distributed in the hope that it will be useful, but
210+WITHOUT ANY WARRANTY; without even the implied warranties of
211+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
212+PURPOSE. See the applicable version of the GNU Lesser General Public
213+License for more details.
214+
215+You should have received a copy of both the GNU Lesser General Public
216+License version 3 and version 2.1 along with this program. If not, see
217+<http://www.gnu.org/licenses/>
218+*/
219+
220+#include "menuitem.h"
221+
222+/**
223+ dbusmenu_menuitem_property_set_image:
224+ @menuitem: The #DbusmenuMenuitem to set the property on.
225+ @property: Name of the property to set.
226+ @data: The image to place on the property.
227+
228+ This function takes the pixbuf that is stored in @data and
229+ turns it into a base64 encoded PNG so that it can be placed
230+ onto a standard #DbusmenuMenuitem property.
231+
232+ Return value: Whether the function was able to set the property
233+ or not.
234+*/
235+gboolean
236+dbusmenu_menuitem_property_set_image (DbusmenuMenuitem * menuitem, const gchar * property, const GdkPixbuf * data)
237+{
238+ g_return_val_if_fail(GDK_IS_PIXBUF(data), FALSE);
239+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(menuitem), FALSE);
240+ g_return_val_if_fail(property != NULL && property[0] != '\0', FALSE);
241+
242+ GError * error = NULL;
243+ gchar * png_data;
244+ gsize png_data_len;
245+
246+ if (!gdk_pixbuf_save_to_buffer((GdkPixbuf *)data, &png_data, &png_data_len, "png", &error, NULL)) {
247+ if (error == NULL) {
248+ g_warning("Unable to create pixbuf data stream: %d", png_data_len);
249+ } else {
250+ g_warning("Unable to create pixbuf data stream: %s", error->message);
251+ g_error_free(error);
252+ error = NULL;
253+ }
254+
255+ return FALSE;
256+ }
257+
258+ gchar * prop_str = g_base64_encode((guchar *)png_data, png_data_len);
259+ gboolean propreturn = FALSE;
260+ propreturn = dbusmenu_menuitem_property_set(menuitem, property, prop_str);
261+
262+ g_free(prop_str);
263+ g_free(png_data);
264+
265+ return propreturn;
266+}
267+
268+/**
269+ dbusmenu_menuitem_property_get_image:
270+ @menuitem: The #DbusmenuMenuite to look for the property on
271+ @property: The name of the property to look for.
272+
273+ This function looks on the menu item for a property by the
274+ name of @property. If one exists it tries to turn it into
275+ a #GdkPixbuf. It assumes that the property is a base64 encoded
276+ PNG file like the one created by #dbusmenu_menuite_property_set_image.
277+
278+ Return value: A pixbuf or #NULL to signal error.
279+*/
280+GdkPixbuf *
281+dbusmenu_menuitem_property_get_image (DbusmenuMenuitem * menuitem, const gchar * property)
282+{
283+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(menuitem), NULL);
284+ g_return_val_if_fail(property != NULL && property[0] != '\0', NULL);
285+
286+ const gchar * value = dbusmenu_menuitem_property_get(menuitem, property);
287+
288+ /* There is no icon */
289+ if (value == NULL || value[0] == '\0') {
290+ return NULL;
291+ }
292+
293+ gsize length = 0;
294+ guchar * icondata = g_base64_decode(value, &length);
295+
296+ GInputStream * input = g_memory_input_stream_new_from_data(icondata, length, NULL);
297+ if (input == NULL) {
298+ g_warning("Cound not create input stream from icon property data");
299+ g_free(icondata);
300+ return NULL;
301+ }
302+
303+ GError * error = NULL;
304+ GdkPixbuf * icon = gdk_pixbuf_new_from_stream(input, NULL, &error);
305+
306+ if (error != NULL) {
307+ g_warning("Unable to build Pixbuf from icon data: %s", error->message);
308+ g_error_free(error);
309+ }
310+
311+ error = NULL;
312+ g_input_stream_close(input, NULL, &error);
313+ if (error != NULL) {
314+ g_warning("Unable to close input stream: %s", error->message);
315+ g_error_free(error);
316+ }
317+ g_free(icondata);
318+
319+ return icon;
320+}
321+
322
323=== added file 'libdbusmenu-gtk/menuitem.h'
324--- libdbusmenu-gtk/menuitem.h 1970-01-01 00:00:00 +0000
325+++ libdbusmenu-gtk/menuitem.h 2009-09-02 18:29:01 +0000
326@@ -0,0 +1,39 @@
327+/*
328+A library to take the object model made consistent by libdbusmenu-glib
329+and visualize it in GTK.
330+
331+Copyright 2009 Canonical Ltd.
332+
333+Authors:
334+ Ted Gould <ted@canonical.com>
335+
336+This program is free software: you can redistribute it and/or modify it
337+under the terms of either or both of the following licenses:
338+
339+1) the GNU Lesser General Public License version 3, as published by the
340+Free Software Foundation; and/or
341+2) the GNU Lesser General Public License version 2.1, as published by
342+the Free Software Foundation.
343+
344+This program is distributed in the hope that it will be useful, but
345+WITHOUT ANY WARRANTY; without even the implied warranties of
346+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
347+PURPOSE. See the applicable version of the GNU Lesser General Public
348+License for more details.
349+
350+You should have received a copy of both the GNU Lesser General Public
351+License version 3 and version 2.1 along with this program. If not, see
352+<http://www.gnu.org/licenses/>
353+*/
354+
355+#ifndef __DBUSMENU_GTKMENUITEM_H__
356+#define __DBUSMENU_GTKMENUITEM_H__ 1
357+
358+#include <glib.h>
359+#include <gdk-pixbuf/gdk-pixbuf.h>
360+#include <libdbusmenu-glib/menuitem.h>
361+
362+gboolean dbusmenu_menuitem_property_set_image (DbusmenuMenuitem * menuitem, const gchar * property, const GdkPixbuf * data);
363+GdkPixbuf * dbusmenu_menuitem_property_get_image (DbusmenuMenuitem * menuitem, const gchar * property);
364+
365+#endif
366
367=== modified file 'tests/test-gtk-label-server.c'
368--- tests/test-gtk-label-server.c 2009-07-20 21:04:00 +0000
369+++ tests/test-gtk-label-server.c 2009-09-02 20:13:15 +0000
370@@ -156,7 +156,7 @@
371 server = dbusmenu_server_new("/org/test");
372
373 timer_func(NULL);
374- g_timeout_add_seconds(15, timer_func, NULL);
375+ g_timeout_add_seconds(5, timer_func, NULL);
376
377 mainloop = g_main_loop_new(NULL, FALSE);
378 g_main_loop_run(mainloop);
379
380=== modified file 'tests/test-gtk-label.json'
381--- tests/test-gtk-label.json 2009-08-26 20:32:37 +0000
382+++ tests/test-gtk-label.json 2009-09-03 17:33:32 +0000
383@@ -154,5 +154,123 @@
384 ]
385 },
386 ]
387- }
388+ },
389+ {"id": 8, "type": "menuitem",
390+ "label": "value1",
391+ "submenu": [
392+ {"id": 80,
393+ "type": "imageitem",
394+ "icon": "face-angel",
395+ "label": "angel"},
396+ {"id": 81,
397+ "type": "imageitem",
398+ "icon": "face-angry",
399+ "label": "angry"},
400+ {"id": 82,
401+ "type": "imageitem",
402+ "icon": "face-cool",
403+ "label": "cool"},
404+ {"id": 83,
405+ "type":"imageitem",
406+ "icon": "face-devilish",
407+ "label": "devilish"},
408+ {"id": 84,
409+ "type": "imageitem",
410+ "icon": "face-embarrassed",
411+ "label": "embarrassed"},
412+ {"id": 85,
413+ "type": "imageitem",
414+ "icon": "face-kiss",
415+ "label": "kiss"},
416+ {"id": 86,
417+ "type": "imageitem",
418+ "icon": "face-laugh",
419+ "label": "laugh"},
420+ {"id": 87,
421+ "type": "imageitem",
422+ "icon": "face-monkey",
423+ "label": "monkey"},
424+ {"id": 88,
425+ "type": "imageitem",
426+ "icon": "face-sad",
427+ "label": "sad"},
428+ {"id": 89,
429+ "type": "imageitem",
430+ "icon": "face-sick",
431+ "label": "sick"}
432+ ]
433+ },
434+ {"id": 9, "type": "menuitem",
435+ "label": "value1",
436+ "submenu": [
437+ {"id": 90,
438+ "type": "imageitem",
439+ "icon-data":
440+"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACPUlEQVR4nGJgoBAAAAAA///Ch1gW
441+BzK0LQ5iaGNgYGDBpQgAAAD//8KpeY4/Q9+DCV7/H/S4/p8byDABlyEAAAAA///CqnluAMOEx5O8
442+/3/f0/P/+47m/4967P7PC2GYysDAwIquGAAAAP//YkLXPC+AodfNyStfRMuegenvdwamP98ZhJWM
443+GJzNzLIWhGMaAgAAAP//YkLT3Odq75InomrBwPT7CwPDr68M//98Y2D+951BREadwdFAL3VxJMNM
444+ZEMAAAAA//9ihPs5gKHPzdo2V0TNkoGZ4T/D//9/GRj+/WJg+PeT4f/vHwwMf78z/P31jeHN89sM
445++6/dXpS4lCGFgYHhNwAAAP//ghkguDqC4Z0CNwMDLwsDgxAHAwOfdQrD/1+fGP5/e83w/9tbhq/P
446+LjG8+8PA8PkPA8PDbwwMIXMYhBkYGN4BAAAA//80zDEOQDAAAMBLZ2MnITEafEziK2YfM/QBLDoX
447+g4jJPeD+IGJAgypNlrbuvNfBcxLYb/rZiIINK/IHAAD//zTRoRHAIBAAsMAU3X8iZCUSy5W7R0EN
448+rJDcmoEXFU8sVq/ko5SIDQoaJj74AQAA//+CGfADKoEIGZhmZiQ+RM0D5FgAAAAA//9Cj0aEKNRm
449+uGZmrCoZAAAAAP//wm4AskZkg7AAAAAAAP//wpo8n/6A2siEoJ/+wG4AAAAA///CZq4EAwODAZRG
450+Bi8YEIEIBwAAAAD//8JmAAcDA4MAlEYGPxgYGD5AaTgAAAAA//8DAD6xjTP5Y+A7AAAAAElFTkSu
451+QmCC",
452+ "label": "up"},
453+ {"id": 91,
454+ "type": "imageitem",
455+ "icon-data":
456+"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACl0lEQVR4nGJgoBAAAAAA//9ixCLG
457+sSWS4bs0B1QWip/+YGDwWcLAycDA8ANZMQAAAP//YsFigIA0JwODdvIsBob/fxgY/vxk+P/7OwPD
458+6ioGBgYGAQYGhhfIigEAAAD//8JmAAcDEwMDw5/vDAz/fjP8//uLgeHvDwYGJqgcGgAAAAD//8Jm
459+AAMDIwPD/99fGBj+/WVg+PeLgeHvTwYGZqwqGQAAAAD//8JuADMDA8PPLwwMDH8Z/v//AzGACbsB
460+AAAAAP//wmoAJwsDw/8/XxgY/v9l+P/rM8P/ry8ZONmwGwAAAAD//4LFAsfqaIbvCtwMDLxsDAxC
461+7AwMPMqWDP+/Pmf4//01w//fXxm+/2NgePebgeHLHwaGR98ZGIKmQ2IEAAAA//80zisSQFAAAMAd
462+wczzKRSvCIqrO4XqGoIkGTOv+SV7gv1j1XqY2jiI/agoSve2eM7VeyUyQk5X0gT2ZEYNHwAAAP//
463+gnnhb+lWhuk//90TD1dncBThYGBgYWGA+JsRQv9lYGB4/5uBYcdDhotZSxhaGRgY/jEwMDAAAAAA
464+//+CueALAwPDnZrtDK3r7jIcefuHgeEvI5JmRgaG938YGHY9YriatYShkoGB4TYDA8MnBgYGBgAA
465+AAD//4IZ8JuBgeE1AwPDjfLNDE2b7jGcfPsLYutfRgaGD78ZGPY+ZrievpChjIGB4SoDA8NLqB4G
466+AAAAAP//QkesDAwMMgwMDG6TwxnO3q5i+H+nhuH/nESGawwMDF4MDAzyDAwMKPEBAAAA///CZYgs
467+AwODR18kw4UJ0QyX8WkGAAAA///ClpkYoAolGBgYFKBqHjAwMDxnYGD4ha4QAAAA///CZQDMEG4o
468++ys2zQwMDAwAAAAA//8DAAF5nhyE7tENAAAAAElFTkSuQmCC",
469+ "label": "down"},
470+ {"id": 92,
471+ "type": "imageitem",
472+ "icon": "up",
473+ "icon-data":
474+"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACl0lEQVR4nGJgoBAAAAAA//9ixCLG
475+sSWS4bs0B1QWip/+YGDwWcLAycDA8ANZMQAAAP//YsFigIA0JwODdvIsBob/fxgY/vxk+P/7OwPD
476+6ioGBgYGAQYGhhfIigEAAAD//8JmAAcDEwMDw5/vDAz/fjP8//uLgeHvDwYGJqgcGgAAAAD//8Jm
477+AAMDIwPD/99fGBj+/WVg+PeLgeHvTwYGZqwqGQAAAAD//8JuADMDA8PPLwwMDH8Z/v//AzGACbsB
478+AAAAAP//wmoAJwsDw/8/XxgY/v9l+P/rM8P/ry8ZONmwGwAAAAD//4LFAsfqaIbvCtwMDLxsDAxC
479+7AwMPMqWDP+/Pmf4//01w//fXxm+/2NgePebgeHLHwaGR98ZGIKmQ2IEAAAA//80zisSQFAAAMAd
480+wczzKRSvCIqrO4XqGoIkGTOv+SV7gv1j1XqY2jiI/agoSve2eM7VeyUyQk5X0gT2ZEYNHwAAAP//
481+gnnhb+lWhuk//90TD1dncBThYGBgYWGA+JsRQv9lYGB4/5uBYcdDhotZSxhaGRgY/jEwMDAAAAAA
482+//+CueALAwPDnZrtDK3r7jIcefuHgeEvI5JmRgaG938YGHY9YriatYShkoGB4TYDA8MnBgYGBgAA
483+AAD//4IZ8JuBgeE1AwPDjfLNDE2b7jGcfPsLYutfRgaGD78ZGPY+ZrievpChjIGB4SoDA8NLqB4G
484+AAAAAP//QkesDAwMMgwMDG6TwxnO3q5i+H+nhuH/nESGawwMDF4MDAzyDAwMKPEBAAAA///CZYgs
485+AwODR18kw4UJ0QyX8WkGAAAA///ClpkYoAolGBgYFKBqHjAwMDxnYGD4ha4QAAAA///CZQDMEG4o
486++ys2zQwMDAwAAAAA//8DAAF5nhyE7tENAAAAAElFTkSuQmCC",
487+ "label": "up"},
488+ {"id": 93,
489+ "type": "imageitem",
490+ "icon": "down",
491+ "icon-data":
492+"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACPUlEQVR4nGJgoBAAAAAA///Ch1gW
493+BzK0LQ5iaGNgYGDBpQgAAAD//8KpeY4/Q9+DCV7/H/S4/p8byDABlyEAAAAA///CqnluAMOEx5O8
494+/3/f0/P/+47m/4967P7PC2GYysDAwIquGAAAAP//YkLXPC+AodfNyStfRMuegenvdwamP98ZhJWM
495+GJzNzLIWhGMaAgAAAP//YkLT3Odq75InomrBwPT7CwPDr68M//98Y2D+951BREadwdFAL3VxJMNM
496+ZEMAAAAA//9ihPs5gKHPzdo2V0TNkoGZ4T/D//9/GRj+/WJg+PeT4f/vHwwMf78z/P31jeHN89sM
497++6/dXpS4lCGFgYHhNwAAAP//ghkguDqC4Z0CNwMDLwsDgxAHAwOfdQrD/1+fGP5/e83w/9tbhq/P
498+LjG8+8PA8PkPA8PDbwwMIXMYhBkYGN4BAAAA//80zDEOQDAAAMBLZ2MnITEafEziK2YfM/QBLDoX
499+g4jJPeD+IGJAgypNlrbuvNfBcxLYb/rZiIINK/IHAAD//zTRoRHAIBAAsMAU3X8iZCUSy5W7R0EN
500+rJDcmoEXFU8sVq/ko5SIDQoaJj74AQAA//+CGfADKoEIGZhmZiQ+RM0D5FgAAAAA//9Cj0aEKNRm
501+uGZmrCoZAAAAAP//wm4AskZkg7AAAAAAAP//wpo8n/6A2siEoJ/+wG4AAAAA///CZq4EAwODAZRG
502+Bi8YEIEIBwAAAAD//8JmAAcDA4MAlEYGPxgYGD5AaTgAAAAA//8DAD6xjTP5Y+A7AAAAAElFTkSu
503+QmCC",
504+ "label": "down"}
505+ ]
506+ },
507 ]

Subscribers

People subscribed via source and target branches