1 /*
2 * Copyright (C) 2016 Alberts Muktupāvels
3 * Copyright (C) 2017 Colomban Wendling <cwendling@hypra.fr>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include <math.h>
22
23 #include "sn-item.h"
24 #include "sn-item-v0.h"
25 #include "sn-item-v0-gen.h"
26
27 #define SN_ITEM_INTERFACE "org.kde.StatusNotifierItem"
28
29 typedef struct
30 {
31 cairo_surface_t *surface;
32 gint width;
33 gint height;
34 } SnIconPixmap;
35
36 typedef struct
37 {
38 gchar *icon_name;
39 SnIconPixmap **icon_pixmap;
40 gchar *title;
41 gchar *text;
42 } SnTooltip;
43
44 struct _SnItemV0
45 {
46 SnItem parent;
47
48 GtkWidget *image;
49 gint icon_size;
50 gint effective_icon_size;
51
52 GCancellable *cancellable;
53 SnItemV0Gen *proxy;
54
55 gchar *id;
56 gchar *category;
57 gchar *status;
58
59 gchar *title;
60 gint32 window_id;
61 gchar *icon_name;
62 gchar *label;
63 SnIconPixmap **icon_pixmap;
64 gchar *overlay_icon_name;
65 SnIconPixmap **overlay_icon_pixmap;
66 gchar *attention_icon_name;
67 SnIconPixmap **attention_icon_pixmap;
68 gchar *attention_movie_name;
69 SnTooltip *tooltip;
70 gchar *icon_theme_path;
71 gchar *menu;
72 gboolean item_is_menu;
73
74 guint update_id;
75 };
76
77 enum
78 {
79 PROP_0,
80
81 PROP_ICON_SIZE,
82 PROP_ICON_PADDING,
83
84 LAST_PROP
85 };
86
87 static GParamSpec *obj_properties[LAST_PROP] = { NULL };
88
G_DEFINE_TYPE(SnItemV0,sn_item_v0,SN_TYPE_ITEM)89 G_DEFINE_TYPE (SnItemV0, sn_item_v0, SN_TYPE_ITEM)
90
91 static cairo_surface_t *
92 scale_surface (SnIconPixmap *pixmap,
93 GtkOrientation orientation,
94 gint size)
95 {
96 gdouble ratio;
97 gdouble new_width;
98 gdouble new_height;
99 gdouble scale_x;
100 gdouble scale_y;
101 gint width;
102 gint height;
103 cairo_content_t content;
104 cairo_surface_t *scaled;
105 cairo_t *cr;
106
107 g_return_val_if_fail (pixmap != NULL, NULL);
108
109 ratio = pixmap->width / (gdouble) pixmap->height;
110 if (orientation == GTK_ORIENTATION_HORIZONTAL)
111 {
112 new_height = (gdouble) size;
113 new_width = new_height * ratio;
114 }
115 else
116 {
117 new_width = (gdouble) size;
118 new_height = new_width * ratio;
119 }
120
121 scale_x = new_width / pixmap->width;
122 scale_y = new_height / pixmap->height;
123
124 width = ceil (new_width);
125 height = ceil (new_height);
126
127 content = CAIRO_CONTENT_COLOR_ALPHA;
128 scaled = cairo_surface_create_similar (pixmap->surface, content, width, height);
129 cr = cairo_create (scaled);
130
131 cairo_scale (cr, scale_x, scale_y);
132 cairo_set_source_surface (cr, pixmap->surface, 0, 0);
133 cairo_paint (cr);
134
135 cairo_destroy (cr);
136 return scaled;
137 }
138
139 static gint
compare_size(gconstpointer a,gconstpointer b,gpointer user_data)140 compare_size (gconstpointer a,
141 gconstpointer b,
142 gpointer user_data)
143 {
144 SnIconPixmap *p1;
145 SnIconPixmap *p2;
146 GtkOrientation orientation;
147
148 p1 = (SnIconPixmap *) a;
149 p2 = (SnIconPixmap *) b;
150 orientation = GPOINTER_TO_UINT (user_data);
151
152 if (orientation == GTK_ORIENTATION_HORIZONTAL)
153 return p1->height - p2->height;
154 else
155 return p1->width - p2->width;
156 }
157
158 static cairo_surface_t *
get_surface(SnItemV0 * v0,GtkOrientation orientation,gint size)159 get_surface (SnItemV0 *v0,
160 GtkOrientation orientation,
161 gint size)
162 {
163 gint i;
164 GList *pixmaps = NULL;
165 SnIconPixmap *pixmap = NULL;
166 GList *l;
167
168 g_assert (v0->icon_pixmap != NULL && v0->icon_pixmap[0] != NULL);
169
170 for (i = 0; v0->icon_pixmap[i] != NULL; i++)
171 pixmaps = g_list_prepend (pixmaps, v0->icon_pixmap[i]);
172
173 pixmaps = g_list_sort_with_data (pixmaps, compare_size,
174 GUINT_TO_POINTER (orientation));
175
176 pixmap = (SnIconPixmap *) pixmaps->data;
177 for (l = pixmaps; l != NULL; l = l->next)
178 {
179 SnIconPixmap *p = (SnIconPixmap *) l->data;
180
181 if (p->height > size && p->width > size)
182 {
183 break;
184 }
185 pixmap = p;
186 }
187
188 g_list_free (pixmaps);
189
190 if (pixmap == NULL || pixmap->surface == NULL)
191 return NULL;
192 else if (pixmap->height > size || pixmap->width > size)
193 return scale_surface (pixmap, orientation, size);
194 else
195 return cairo_surface_reference (pixmap->surface);
196 }
197
198 static cairo_surface_t *
get_icon_by_name(const gchar * icon_name,gint requested_size,gint scale)199 get_icon_by_name (const gchar *icon_name,
200 gint requested_size,
201 gint scale)
202 {
203 GtkIconTheme *icon_theme;
204 gint *sizes;
205 gint i;
206 gint chosen_size = 0;
207
208 g_return_val_if_fail (icon_name != NULL && icon_name[0] != '\0', NULL);
209 g_return_val_if_fail (requested_size > 0, NULL);
210
211 icon_theme = gtk_icon_theme_get_default ();
212 gtk_icon_theme_rescan_if_needed (icon_theme);
213
214 sizes = gtk_icon_theme_get_icon_sizes (icon_theme, icon_name);
215 for (i = 0; sizes[i] != 0; i++)
216 {
217 if (sizes[i] == requested_size ||
218 sizes[i] == -1) /* scalable */
219 {
220 /* perfect match, stop here */
221 chosen_size = requested_size;
222 break;
223 }
224 else if (sizes[i] < requested_size && sizes[i] > chosen_size)
225 chosen_size = sizes[i];
226 }
227 g_free (sizes);
228
229 if (chosen_size == 0)
230 chosen_size = requested_size;
231
232 return gtk_icon_theme_load_surface (icon_theme, icon_name,
233 chosen_size, scale,
234 NULL, GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
235 }
236
237 static void
update(SnItemV0 * v0)238 update (SnItemV0 *v0)
239 {
240 AtkObject *accessible;
241 GtkImage *image;
242 SnTooltip *tip;
243 gint icon_size;
244 gboolean visible;
245 g_return_if_fail (SN_IS_ITEM_V0 (v0));
246
247 image = GTK_IMAGE (v0->image);
248
249 if (v0->icon_size > 0)
250 icon_size = v0->icon_size;
251 else
252 icon_size = MAX (1, v0->effective_icon_size);
253
254 if (v0->icon_name != NULL && v0->icon_name[0] != '\0')
255 {
256 cairo_surface_t *surface;
257 gint scale;
258
259 scale = gtk_widget_get_scale_factor (GTK_WIDGET (image));
260 surface = get_icon_by_name (v0->icon_name, icon_size, scale);
261
262 if (!surface)
263 {
264 GdkPixbuf *pixbuf;
265 /*try to find icons specified by path and filename*/
266 pixbuf = gdk_pixbuf_new_from_file (v0->icon_name, NULL);
267 if (pixbuf && icon_size > 1)
268 {
269 /*An icon specified by path and filename may be the wrong size for the tray */
270 pixbuf = gdk_pixbuf_scale_simple (pixbuf, icon_size-2, icon_size-2, GDK_INTERP_BILINEAR);
271 surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
272 }
273 if (pixbuf)
274 g_object_unref (pixbuf);
275 }
276 if (!surface)
277 {
278 /*deal with missing icon or failure to load icon*/
279 surface = get_icon_by_name ("image-missing", icon_size, scale);
280 }
281 gtk_image_set_from_surface (image, surface);
282 cairo_surface_destroy (surface);
283 }
284 else if (v0->icon_pixmap != NULL && v0->icon_pixmap[0] != NULL)
285 {
286 cairo_surface_t *surface;
287
288 surface = get_surface (v0,
289 gtk_orientable_get_orientation (GTK_ORIENTABLE (v0)),
290 icon_size);
291 if (surface != NULL)
292 {
293 gtk_image_set_from_surface (image, surface);
294 cairo_surface_destroy (surface);
295 }
296 }
297 else
298 {
299 gtk_image_set_from_icon_name (image, "image-missing", GTK_ICON_SIZE_MENU);
300 gtk_image_set_pixel_size (image, icon_size);
301 }
302
303 tip = v0->tooltip;
304
305 if (tip != NULL)
306 {
307 gchar *markup;
308
309 markup = NULL;
310
311 if ((tip->title != NULL && *tip->title != '\0') &&
312 (tip->text != NULL && *tip->text != '\0'))
313 {
314 markup = g_strdup_printf ("%s\n%s", tip->title, tip->text);
315 }
316 else if (tip->title != NULL && *tip->title != '\0')
317 {
318 markup = g_strdup (tip->title);
319 }
320 else if (tip->text != NULL && *tip->text != '\0')
321 {
322 markup = g_strdup (tip->text);
323 }
324
325 gtk_widget_set_tooltip_markup (GTK_WIDGET (v0), markup);
326 g_free (markup);
327 }
328 else
329 {
330 gtk_widget_set_tooltip_markup (GTK_WIDGET (v0), NULL);
331 }
332
333 gtk_button_set_label (GTK_BUTTON (v0), v0->label);
334 accessible = gtk_widget_get_accessible (GTK_WIDGET (v0));
335
336 if (v0->title != NULL && *v0->title != '\0')
337 atk_object_set_name (accessible, v0->title);
338 else
339 atk_object_set_name (accessible, v0->id);
340
341 /* TODO: hide "Passive" items with a setting? */
342 /*Special case mate-polkit*/
343 if (g_strcmp0 (v0->status, "password-dialog") != 0){
344 visible = g_strcmp0 (v0->status, "Passive") != 0;
345 gtk_widget_set_visible (GTK_WIDGET (v0), visible);
346 }
347 else
348 gtk_widget_set_visible (GTK_WIDGET (v0), TRUE);
349 }
350
351 static gboolean
update_cb(gpointer user_data)352 update_cb (gpointer user_data)
353 {
354 SnItemV0 *v0;
355
356 v0 = SN_ITEM_V0 (user_data);
357
358 v0->update_id = 0;
359 update (v0);
360
361 return G_SOURCE_REMOVE;
362 }
363
364 static void
queue_update(SnItemV0 * v0)365 queue_update (SnItemV0 *v0)
366 {
367 if (v0->update_id != 0)
368 return;
369
370 v0->update_id = g_timeout_add (10, update_cb, v0);
371 g_source_set_name_by_id (v0->update_id, "[status-notifier] update_cb");
372 }
373
374 static cairo_surface_t *
surface_from_variant(GVariant * variant,gint width,gint height)375 surface_from_variant (GVariant *variant,
376 gint width,
377 gint height)
378 {
379 cairo_format_t format;
380 gint stride;
381 guint32 *data;
382 gint x;
383 gint y;
384 guchar *p;
385
386 format = CAIRO_FORMAT_ARGB32;
387 stride = cairo_format_stride_for_width (format, width);
388 data = (guint32 *) g_variant_get_data (variant);
389
390 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
391 {
392 gint i;
393
394 for (i = 0; i < width * height; i++)
395 data[i] = GUINT32_FROM_BE (data[i]);
396 }
397 #endif
398
399 p = (guchar *) data;
400 /* premultiply alpha for Cairo */
401 for (y = 0; y < height; y++)
402 {
403 for (x = 0; x < width; x++)
404 {
405 guchar alpha = p[x * 4 + 3];
406
407 p[x * 4 + 0] = (p[x * 4 + 0] * alpha) / 255;
408 p[x * 4 + 1] = (p[x * 4 + 1] * alpha) / 255;
409 p[x * 4 + 2] = (p[x * 4 + 2] * alpha) / 255;
410 }
411
412 p += stride;
413 }
414
415 return cairo_image_surface_create_for_data ((guchar *) data, format,
416 width, height, stride);
417 }
418
419 static cairo_surface_t *
icon_surface_new(GVariant * variant,gint width,gint height)420 icon_surface_new (GVariant *variant,
421 gint width,
422 gint height)
423 {
424 cairo_surface_t *surface;
425 cairo_surface_t *tmp;
426 cairo_t *cr;
427
428 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
429 if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
430 return NULL;
431
432 tmp = surface_from_variant (variant, width, height);
433 if (cairo_surface_status (tmp) != CAIRO_STATUS_SUCCESS)
434 {
435 cairo_surface_destroy (surface);
436 return NULL;
437 }
438
439 cr = cairo_create (surface);
440 if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
441 {
442 cairo_surface_destroy (surface);
443 cairo_surface_destroy (tmp);
444 return NULL;
445 }
446
447 cairo_set_source_surface (cr, tmp, 0, 0);
448 cairo_paint (cr);
449
450 cairo_surface_destroy (tmp);
451 cairo_destroy (cr);
452
453 return surface;
454 }
455
456 static SnIconPixmap **
icon_pixmap_new(GVariant * variant)457 icon_pixmap_new (GVariant *variant)
458 {
459 GPtrArray *array;
460 GVariantIter iter;
461 gint width;
462 gint height;
463 GVariant *value;
464
465 if (variant == NULL || g_variant_iter_init (&iter, variant) == 0)
466 return NULL;
467
468 array = g_ptr_array_new ();
469 while (g_variant_iter_next (&iter, "(ii@ay)", &width, &height, &value))
470 {
471 cairo_surface_t *surface;
472
473 if (width == 0 || height == 0)
474 {
475 g_variant_unref (value);
476 continue;
477 }
478
479 surface = icon_surface_new (value, width, height);
480 g_variant_unref (value);
481
482 if (surface != NULL)
483 {
484 SnIconPixmap *pixmap;
485
486 pixmap = g_new0 (SnIconPixmap, 1);
487
488 pixmap->surface = surface;
489 pixmap->width = width;
490 pixmap->height = height;
491
492 g_ptr_array_add (array, pixmap);
493 }
494 }
495
496 g_ptr_array_add (array, NULL);
497 return (SnIconPixmap **) g_ptr_array_free (array, FALSE);
498 }
499
500 static void
icon_pixmap_free(SnIconPixmap ** data)501 icon_pixmap_free (SnIconPixmap **data)
502 {
503 gint i;
504
505 if (data == NULL)
506 return;
507
508 for (i = 0; data[i] != NULL; i++)
509 {
510 cairo_surface_destroy (data[i]->surface);
511 g_free (data[i]);
512 }
513
514 g_free (data);
515 }
516
517 static SnTooltip *
sn_tooltip_new(GVariant * variant)518 sn_tooltip_new (GVariant *variant)
519 {
520 const gchar *icon_name;
521 GVariant *icon_pixmap;
522 const gchar *title;
523 const gchar *text;
524 SnTooltip *tooltip;
525
526 if (variant == NULL)
527 return NULL;
528
529 if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("(sa(iiay)ss)")))
530 {
531 g_warning ("Type for 'ToolTip' property should be '(sa(iiay)ss)' "
532 "but got '%s'", g_variant_get_type_string (variant));
533
534 return NULL;
535 }
536
537 g_variant_get (variant, "(&s@a(iiay)&s&s)",
538 &icon_name, &icon_pixmap,
539 &title, &text);
540
541 tooltip = g_new0 (SnTooltip, 1);
542
543 tooltip->icon_name = g_strdup (icon_name);
544 tooltip->icon_pixmap = icon_pixmap_new (icon_pixmap);
545 tooltip->title = g_strdup (title);
546 tooltip->text = g_strdup (text);
547
548 g_variant_unref (icon_pixmap);
549 return tooltip;
550 }
551
552 static void
sn_tooltip_free(SnTooltip * tooltip)553 sn_tooltip_free (SnTooltip *tooltip)
554 {
555 if (tooltip == NULL)
556 return;
557
558 g_free (tooltip->icon_name);
559 icon_pixmap_free (tooltip->icon_pixmap);
560 g_free (tooltip->title);
561 g_free (tooltip->text);
562
563 g_free (tooltip);
564 }
565
566 static GVariant *
get_property(GObject * source_object,GAsyncResult * res,gpointer user_data,gboolean * cancelled)567 get_property (GObject *source_object,
568 GAsyncResult *res,
569 gpointer user_data,
570 gboolean *cancelled)
571 {
572 GVariant *variant;
573 GError *error;
574 GVariant *property;
575
576 error = NULL;
577 variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
578 res, &error);
579
580 *cancelled = FALSE;
581 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
582 {
583 *cancelled = TRUE;
584 g_error_free (error);
585 return NULL;
586 }
587
588 if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS))
589 {
590 g_error_free (error);
591 return NULL;
592 }
593
594 if (error)
595 {
596 g_warning ("%s", error->message);
597 g_error_free (error);
598 return NULL;
599 }
600
601 g_variant_get (variant, "(v)", &property);
602 g_variant_unref (variant);
603
604 return property;
605 }
606
607 static void
update_property(SnItemV0 * v0,const gchar * property,GAsyncReadyCallback callback)608 update_property (SnItemV0 *v0,
609 const gchar *property,
610 GAsyncReadyCallback callback)
611 {
612 GDBusProxy *proxy;
613 SnItem *item;
614
615 proxy = G_DBUS_PROXY (v0->proxy);
616 item = SN_ITEM (v0);
617
618 g_dbus_connection_call (g_dbus_proxy_get_connection (proxy),
619 sn_item_get_bus_name (item),
620 sn_item_get_object_path (item),
621 "org.freedesktop.DBus.Properties", "Get",
622 g_variant_new ("(ss)", SN_ITEM_INTERFACE, property),
623 G_VARIANT_TYPE ("(v)"),
624 G_DBUS_CALL_FLAGS_NONE, -1,
625 v0->cancellable, callback, v0);
626 }
627
628 static void
update_title(GObject * source_object,GAsyncResult * res,gpointer user_data)629 update_title (GObject *source_object,
630 GAsyncResult *res,
631 gpointer user_data)
632 {
633 SnItemV0 *v0;
634 GVariant *variant;
635 gboolean cancelled;
636
637 variant = get_property (source_object, res, user_data, &cancelled);
638 if (cancelled)
639 return;
640
641 v0 = SN_ITEM_V0 (user_data);
642
643 g_clear_pointer (&v0->title, g_free);
644 v0->title = g_variant_dup_string (variant, NULL);
645 g_clear_pointer (&variant, g_variant_unref);
646
647 queue_update (v0);
648 }
649
650 static void
new_title_cb(SnItemV0 * v0)651 new_title_cb (SnItemV0 *v0)
652 {
653 update_property (v0, "Title", update_title);
654 }
655
656 static void
update_icon_name(GObject * source_object,GAsyncResult * res,gpointer user_data)657 update_icon_name (GObject *source_object,
658 GAsyncResult *res,
659 gpointer user_data)
660 {
661 SnItemV0 *v0;
662 GVariant *variant;
663 gboolean cancelled;
664
665 variant = get_property (source_object, res, user_data, &cancelled);
666 if (cancelled)
667 return;
668
669 v0 = SN_ITEM_V0 (user_data);
670
671 g_clear_pointer (&v0->icon_name, g_free);
672 v0->icon_name = g_variant_dup_string (variant, NULL);
673 g_clear_pointer (&variant, g_variant_unref);
674
675 queue_update (v0);
676 }
677
678 static void
update_icon_pixmap(GObject * source_object,GAsyncResult * res,gpointer user_data)679 update_icon_pixmap (GObject *source_object,
680 GAsyncResult *res,
681 gpointer user_data)
682 {
683 SnItemV0 *v0;
684 GVariant *variant;
685 gboolean cancelled;
686
687 variant = get_property (source_object, res, user_data, &cancelled);
688 if (cancelled)
689 return;
690
691 v0 = SN_ITEM_V0 (user_data);
692
693 g_clear_pointer (&v0->icon_pixmap, icon_pixmap_free);
694 v0->icon_pixmap = icon_pixmap_new (variant);
695 g_clear_pointer (&variant, g_variant_unref);
696
697 queue_update (v0);
698 }
699
700 static void
new_icon_cb(SnItemV0 * v0)701 new_icon_cb (SnItemV0 *v0)
702 {
703 update_property (v0, "IconName", update_icon_name);
704 update_property (v0, "IconPixmap", update_icon_pixmap);
705 }
706
707 static void
update_overlay_icon_name(GObject * source_object,GAsyncResult * res,gpointer user_data)708 update_overlay_icon_name (GObject *source_object,
709 GAsyncResult *res,
710 gpointer user_data)
711 {
712 SnItemV0 *v0;
713 GVariant *variant;
714 gboolean cancelled;
715
716 variant = get_property (source_object, res, user_data, &cancelled);
717 if (cancelled)
718 return;
719
720 v0 = SN_ITEM_V0 (user_data);
721
722 g_clear_pointer (&v0->overlay_icon_name, g_free);
723 v0->overlay_icon_name = g_variant_dup_string (variant, NULL);
724 g_clear_pointer (&variant, g_variant_unref);
725
726 queue_update (v0);
727 }
728
729 static void
update_overlay_icon_pixmap(GObject * source_object,GAsyncResult * res,gpointer user_data)730 update_overlay_icon_pixmap (GObject *source_object,
731 GAsyncResult *res,
732 gpointer user_data)
733 {
734 SnItemV0 *v0;
735 GVariant *variant;
736 gboolean cancelled;
737
738 variant = get_property (source_object, res, user_data, &cancelled);
739 if (cancelled)
740 return;
741
742 v0 = SN_ITEM_V0 (user_data);
743
744 g_clear_pointer (&v0->overlay_icon_pixmap, icon_pixmap_free);
745 v0->overlay_icon_pixmap = icon_pixmap_new (variant);
746 g_clear_pointer (&variant, g_variant_unref);
747
748 queue_update (v0);
749 }
750
751 static void
new_overlay_icon_cb(SnItemV0 * v0)752 new_overlay_icon_cb (SnItemV0 *v0)
753 {
754 update_property (v0, "OverlayIconName", update_overlay_icon_name);
755 update_property (v0, "OverlayIconPixmap", update_overlay_icon_pixmap);
756 }
757
758 static void
update_attention_icon_name(GObject * source_object,GAsyncResult * res,gpointer user_data)759 update_attention_icon_name (GObject *source_object,
760 GAsyncResult *res,
761 gpointer user_data)
762 {
763 SnItemV0 *v0;
764 GVariant *variant;
765 gboolean cancelled;
766
767 variant = get_property (source_object, res, user_data, &cancelled);
768 if (cancelled)
769 return;
770
771 v0 = SN_ITEM_V0 (user_data);
772
773 g_clear_pointer (&v0->attention_icon_name, g_free);
774 v0->attention_icon_name = g_variant_dup_string (variant, NULL);
775 g_clear_pointer (&variant, g_variant_unref);
776
777 queue_update (v0);
778 }
779
780 static void
update_attention_icon_pixmap(GObject * source_object,GAsyncResult * res,gpointer user_data)781 update_attention_icon_pixmap (GObject *source_object,
782 GAsyncResult *res,
783 gpointer user_data)
784 {
785 SnItemV0 *v0;
786 GVariant *variant;
787 gboolean cancelled;
788
789 variant = get_property (source_object, res, user_data, &cancelled);
790 if (cancelled)
791 return;
792
793 v0 = SN_ITEM_V0 (user_data);
794
795 g_clear_pointer (&v0->attention_icon_pixmap, icon_pixmap_free);
796 v0->attention_icon_pixmap = icon_pixmap_new (variant);
797 g_clear_pointer (&variant, g_variant_unref);
798
799 queue_update (v0);
800 }
801
802 static void
new_attention_icon_cb(SnItemV0 * v0)803 new_attention_icon_cb (SnItemV0 *v0)
804 {
805 update_property (v0, "AttentionIconName", update_attention_icon_name);
806 update_property (v0, "AttentionIconPixmap", update_attention_icon_pixmap);
807 }
808
809 static void
update_tooltip(GObject * source_object,GAsyncResult * res,gpointer user_data)810 update_tooltip (GObject *source_object,
811 GAsyncResult *res,
812 gpointer user_data)
813 {
814 SnItemV0 *v0;
815 GVariant *variant;
816 gboolean cancelled;
817
818 variant = get_property (source_object, res, user_data, &cancelled);
819 if (cancelled)
820 return;
821
822 v0 = SN_ITEM_V0 (user_data);
823
824 g_clear_pointer (&v0->tooltip, sn_tooltip_free);
825 v0->tooltip = sn_tooltip_new (variant);
826 g_clear_pointer (&variant, g_variant_unref);
827
828 queue_update (v0);
829 }
830
831 static void
new_tooltip_cb(SnItemV0 * v0)832 new_tooltip_cb (SnItemV0 *v0)
833 {
834 update_property (v0, "ToolTip", update_tooltip);
835 }
836
837 static void
new_status_cb(SnItemV0 * v0,GVariant * parameters)838 new_status_cb (SnItemV0 *v0,
839 GVariant *parameters)
840 {
841 GVariant *variant;
842
843 variant = g_variant_get_child_value (parameters, 0);
844
845 g_free (v0->status);
846 v0->status = g_variant_dup_string (variant, NULL);
847 g_variant_unref (variant);
848
849 queue_update (v0);
850 }
851
852 static void
new_label_cb(SnItemV0 * v0,GVariant * parameters)853 new_label_cb (SnItemV0 *v0,
854 GVariant *parameters)
855 {
856 GVariant *variant;
857
858 variant = g_variant_get_child_value (parameters, 0);
859
860 g_free (v0->label);
861 v0->label = g_variant_dup_string (variant, NULL);
862 g_variant_unref (variant);
863
864 queue_update (v0);
865 }
866
867 static void
new_icon_theme_path_cb(SnItemV0 * v0,GVariant * parameters)868 new_icon_theme_path_cb (SnItemV0 *v0,
869 GVariant *parameters)
870 {
871 GVariant *variant;
872
873 variant = g_variant_get_child_value (parameters, 0);
874
875 g_free (v0->icon_theme_path);
876 v0->icon_theme_path = g_variant_dup_string (variant, NULL);
877 g_variant_unref (variant);
878
879 if (v0->icon_theme_path != NULL)
880 {
881 GtkIconTheme *icon_theme;
882
883 icon_theme = gtk_icon_theme_get_default ();
884
885 gtk_icon_theme_append_search_path (icon_theme, v0->icon_theme_path);
886 }
887
888 queue_update (v0);
889 }
890
891 static void
g_properties_changed_cb(GDBusProxy * proxy,GVariant * changed_properties,GStrv invalidated_properties,SnItemV0 * v0)892 g_properties_changed_cb (GDBusProxy *proxy,
893 GVariant *changed_properties,
894 GStrv invalidated_properties,
895 SnItemV0 *v0)
896 {
897 gchar *debug;
898
899 debug = g_variant_print (changed_properties, FALSE);
900 g_debug ("g_properties_changed_cb: %s", debug);
901 g_free (debug);
902 }
903
904 static void
g_signal_cb(GDBusProxy * proxy,gchar * sender_name,gchar * signal_name,GVariant * parameters,SnItemV0 * v0)905 g_signal_cb (GDBusProxy *proxy,
906 gchar *sender_name,
907 gchar *signal_name,
908 GVariant *parameters,
909 SnItemV0 *v0)
910 {
911 if (g_strcmp0 (signal_name, "NewTitle") == 0)
912 new_title_cb (v0);
913 else if (g_strcmp0 (signal_name, "NewIcon") == 0)
914 new_icon_cb (v0);
915 else if (g_strcmp0 (signal_name, "NewOverlayIcon") == 0)
916 new_overlay_icon_cb (v0);
917 else if (g_strcmp0 (signal_name, "NewAttentionIcon") == 0)
918 new_attention_icon_cb (v0);
919 else if (g_strcmp0 (signal_name, "NewToolTip") == 0)
920 new_tooltip_cb (v0);
921 else if (g_strcmp0 (signal_name, "NewStatus") == 0)
922 new_status_cb (v0, parameters);
923 else if (g_strcmp0 (signal_name, "NewIconThemePath") == 0)
924 new_icon_theme_path_cb (v0, parameters);
925 else if (g_strcmp0 (signal_name, "XAyatanaNewLabel") == 0)
926 new_label_cb (v0, parameters);
927 else
928 g_debug ("signal '%s' not handled!", signal_name);
929 }
930
931 static void
get_all_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)932 get_all_cb (GObject *source_object,
933 GAsyncResult *res,
934 gpointer user_data)
935 {
936 SnItemV0 *v0;
937 GVariant *properties;
938 GError *error;
939 GVariantIter *iter;
940 gchar *key;
941 GVariant *value;
942
943 error = NULL;
944 properties = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
945 res, &error);
946
947 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
948 {
949 g_error_free (error);
950 return;
951 }
952
953 v0 = SN_ITEM_V0 (user_data);
954
955 if (error)
956 {
957 g_warning ("%s", error->message);
958 g_error_free (error);
959 return;
960 }
961
962 g_variant_get (properties, "(a{sv})", &iter);
963 while (g_variant_iter_next (iter, "{sv}", &key, &value))
964 {
965 if (g_strcmp0 (key, "Category") == 0)
966 v0->category = g_variant_dup_string (value, NULL);
967 else if (g_strcmp0 (key, "Id") == 0)
968 v0->id = g_variant_dup_string (value, NULL);
969 else if (g_strcmp0 (key, "Title") == 0)
970 v0->title = g_variant_dup_string (value, NULL);
971 else if (g_strcmp0 (key, "Status") == 0)
972 v0->status = g_variant_dup_string (value, NULL);
973 else if (g_strcmp0 (key, "WindowId") == 0)
974 v0->window_id = g_variant_get_int32 (value);
975 else if (g_strcmp0 (key, "IconName") == 0)
976 v0->icon_name = g_variant_dup_string (value, NULL);
977 else if (g_strcmp0 (key, "IconPixmap") == 0)
978 v0->icon_pixmap = icon_pixmap_new (value);
979 else if (g_strcmp0 (key, "OverlayIconName") == 0)
980 v0->overlay_icon_name = g_variant_dup_string (value, NULL);
981 else if (g_strcmp0 (key, "OverlayIconPixmap") == 0)
982 v0->overlay_icon_pixmap = icon_pixmap_new (value);
983 else if (g_strcmp0 (key, "AttentionIconName") == 0)
984 v0->attention_icon_name = g_variant_dup_string (value, NULL);
985 else if (g_strcmp0 (key, "AttentionIconPixmap") == 0)
986 v0->attention_icon_pixmap = icon_pixmap_new (value);
987 else if (g_strcmp0 (key, "AttentionMovieName") == 0)
988 v0->attention_movie_name = g_variant_dup_string (value, NULL);
989 else if (g_strcmp0 (key, "ToolTip") == 0)
990 v0->tooltip = sn_tooltip_new (value);
991 else if (g_strcmp0 (key, "IconThemePath") == 0)
992 v0->icon_theme_path = g_variant_dup_string (value, NULL);
993 else if (g_strcmp0 (key, "Menu") == 0)
994 v0->menu = g_variant_dup_string (value, NULL);
995 else if (g_strcmp0 (key, "ItemIsMenu") == 0)
996 v0->item_is_menu = g_variant_get_boolean (value);
997 else if (g_strcmp0 (key, "XAyatanaLabel") == 0)
998 v0->label = g_variant_dup_string (value, NULL);
999 else
1000 g_debug ("property '%s' not handled!", key);
1001
1002 g_variant_unref (value);
1003 g_free (key);
1004 }
1005
1006 g_variant_iter_free (iter);
1007 g_variant_unref (properties);
1008
1009 if (v0->id == NULL || v0->category == NULL || v0->status == NULL)
1010 {
1011 SnItem *item;
1012 const gchar *bus_name;
1013 const gchar *object_path;
1014
1015 item = SN_ITEM (v0);
1016 bus_name = sn_item_get_bus_name (item);
1017 object_path = sn_item_get_object_path (item);
1018
1019 g_warning ("Invalid Status Notifier Item (%s, %s)",
1020 bus_name, object_path);
1021
1022 return;
1023 }
1024
1025 if (v0->icon_theme_path != NULL)
1026 {
1027 GtkIconTheme *icon_theme;
1028
1029 icon_theme = gtk_icon_theme_get_default ();
1030
1031 gtk_icon_theme_append_search_path (icon_theme, v0->icon_theme_path);
1032 }
1033
1034 g_signal_connect (v0->proxy, "g-properties-changed",
1035 G_CALLBACK (g_properties_changed_cb), v0);
1036
1037 g_signal_connect (v0->proxy, "g-signal",
1038 G_CALLBACK (g_signal_cb), v0);
1039
1040 update (v0);
1041 sn_item_emit_ready (SN_ITEM (v0));
1042 }
1043
1044 static void
proxy_ready_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)1045 proxy_ready_cb (GObject *source_object,
1046 GAsyncResult *res,
1047 gpointer user_data)
1048 {
1049 SnItemV0 *v0;
1050 SnItemV0Gen *proxy;
1051 GError *error;
1052
1053 error = NULL;
1054 proxy = sn_item_v0_gen_proxy_new_for_bus_finish (res, &error);
1055
1056 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1057 {
1058 g_error_free (error);
1059 return;
1060 }
1061
1062 v0 = SN_ITEM_V0 (user_data);
1063 v0->proxy = proxy;
1064
1065 if (error)
1066 {
1067 g_warning ("%s", error->message);
1068 g_error_free (error);
1069 return;
1070 }
1071
1072 g_dbus_connection_call (g_dbus_proxy_get_connection (G_DBUS_PROXY (proxy)),
1073 sn_item_get_bus_name (SN_ITEM (v0)),
1074 sn_item_get_object_path (SN_ITEM (v0)),
1075 "org.freedesktop.DBus.Properties", "GetAll",
1076 g_variant_new ("(s)", SN_ITEM_INTERFACE),
1077 G_VARIANT_TYPE ("(a{sv})"),
1078 G_DBUS_CALL_FLAGS_NONE, -1,
1079 v0->cancellable, get_all_cb, v0);
1080 }
1081
1082 static void
sn_item_v0_constructed(GObject * object)1083 sn_item_v0_constructed (GObject *object)
1084 {
1085 SnItemV0 *v0;
1086 SnItem *item;
1087
1088 v0 = SN_ITEM_V0 (object);
1089 item = SN_ITEM (v0);
1090
1091 G_OBJECT_CLASS (sn_item_v0_parent_class)->constructed (object);
1092
1093 v0->cancellable = g_cancellable_new ();
1094 sn_item_v0_gen_proxy_new_for_bus (G_BUS_TYPE_SESSION,
1095 G_DBUS_PROXY_FLAGS_NONE,
1096 sn_item_get_bus_name (item),
1097 sn_item_get_object_path (item),
1098 v0->cancellable,
1099 proxy_ready_cb, v0);
1100 }
1101
1102 static void
sn_item_v0_dispose(GObject * object)1103 sn_item_v0_dispose (GObject *object)
1104 {
1105 SnItemV0 *v0;
1106
1107 v0 = SN_ITEM_V0 (object);
1108
1109 g_cancellable_cancel (v0->cancellable);
1110 g_clear_object (&v0->cancellable);
1111 g_clear_object (&v0->proxy);
1112
1113 if (v0->update_id != 0)
1114 {
1115 g_source_remove (v0->update_id);
1116 v0->update_id = 0;
1117 }
1118
1119 G_OBJECT_CLASS (sn_item_v0_parent_class)->dispose (object);
1120 }
1121
1122 static void
sn_item_v0_finalize(GObject * object)1123 sn_item_v0_finalize (GObject *object)
1124 {
1125 SnItemV0 *v0;
1126
1127 v0 = SN_ITEM_V0 (object);
1128
1129 g_clear_pointer (&v0->id, g_free);
1130 g_clear_pointer (&v0->category, g_free);
1131 g_clear_pointer (&v0->status, g_free);
1132
1133 g_clear_pointer (&v0->title, g_free);
1134 g_clear_pointer (&v0->icon_name, g_free);
1135 g_clear_pointer (&v0->label, g_free);
1136 g_clear_pointer (&v0->icon_pixmap, icon_pixmap_free);
1137 g_clear_pointer (&v0->overlay_icon_name, g_free);
1138 g_clear_pointer (&v0->overlay_icon_pixmap, icon_pixmap_free);
1139 g_clear_pointer (&v0->attention_icon_name, g_free);
1140 g_clear_pointer (&v0->attention_icon_pixmap, icon_pixmap_free);
1141 g_clear_pointer (&v0->attention_movie_name, g_free);
1142 g_clear_pointer (&v0->tooltip, sn_tooltip_free);
1143 g_clear_pointer (&v0->icon_theme_path, g_free);
1144 g_clear_pointer (&v0->menu, g_free);
1145
1146 G_OBJECT_CLASS (sn_item_v0_parent_class)->finalize (object);
1147 }
1148
1149 static const gchar *
sn_item_v0_get_id(SnItem * item)1150 sn_item_v0_get_id (SnItem *item)
1151 {
1152 SnItemV0 *v0;
1153
1154 v0 = SN_ITEM_V0 (item);
1155
1156 return v0->id;
1157 }
1158
1159 static const gchar *
sn_item_v0_get_category(SnItem * item)1160 sn_item_v0_get_category (SnItem *item)
1161 {
1162 SnItemV0 *v0;
1163
1164 v0 = SN_ITEM_V0 (item);
1165
1166 return v0->category;
1167 }
1168
1169 static const gchar *
sn_item_v0_get_menu(SnItem * item)1170 sn_item_v0_get_menu (SnItem *item)
1171 {
1172 SnItemV0 *v0;
1173
1174 v0 = SN_ITEM_V0 (item);
1175
1176 return v0->menu;
1177 }
1178
1179 static void
context_menu_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)1180 context_menu_cb (GObject *source_object,
1181 GAsyncResult *res,
1182 gpointer user_data)
1183 {
1184 SnItemV0 *v0;
1185
1186 v0 = SN_ITEM_V0 (user_data);
1187
1188 sn_item_v0_gen_call_context_menu_finish (v0->proxy, res, NULL);
1189 }
1190
1191 static void
sn_item_v0_context_menu(SnItem * item,gint x,gint y)1192 sn_item_v0_context_menu (SnItem *item,
1193 gint x,
1194 gint y)
1195 {
1196 SnItemV0 *v0;
1197
1198 v0 = SN_ITEM_V0 (item);
1199
1200 sn_item_v0_gen_call_context_menu (v0->proxy, x, y, NULL,
1201 context_menu_cb, v0);
1202 }
1203
1204 static void
activate_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)1205 activate_cb (GObject *source_object,
1206 GAsyncResult *res,
1207 gpointer user_data)
1208 {
1209 SnItemV0 *v0;
1210
1211 v0 = SN_ITEM_V0 (user_data);
1212
1213 sn_item_v0_gen_call_activate_finish (v0->proxy, res, NULL);
1214 }
1215
1216 static void
sn_item_v0_activate(SnItem * item,gint x,gint y)1217 sn_item_v0_activate (SnItem *item,
1218 gint x,
1219 gint y)
1220 {
1221 SnItemV0 *v0;
1222
1223 v0 = SN_ITEM_V0 (item);
1224
1225 sn_item_v0_gen_call_activate (v0->proxy, x, y, NULL,
1226 activate_cb, v0);
1227 }
1228
1229 static void
secondary_activate_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)1230 secondary_activate_cb (GObject *source_object,
1231 GAsyncResult *res,
1232 gpointer user_data)
1233 {
1234 SnItemV0 *v0;
1235
1236 v0 = SN_ITEM_V0 (user_data);
1237
1238 sn_item_v0_gen_call_secondary_activate_finish (v0->proxy, res, NULL);
1239 }
1240
1241 static void
sn_item_v0_secondary_activate(SnItem * item,gint x,gint y)1242 sn_item_v0_secondary_activate (SnItem *item,
1243 gint x,
1244 gint y)
1245 {
1246 SnItemV0 *v0;
1247
1248 v0 = SN_ITEM_V0 (item);
1249
1250 sn_item_v0_gen_call_secondary_activate (v0->proxy, x, y, NULL,
1251 secondary_activate_cb, v0);
1252 }
1253
1254 static void
scroll_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)1255 scroll_cb (GObject *source_object,
1256 GAsyncResult *res,
1257 gpointer user_data)
1258 {
1259 SnItemV0 *v0;
1260
1261 v0 = SN_ITEM_V0 (user_data);
1262
1263 sn_item_v0_gen_call_scroll_finish (v0->proxy, res, NULL);
1264 }
1265
1266 static void
sn_item_v0_scroll(SnItem * item,gint delta,SnItemOrientation orientation)1267 sn_item_v0_scroll (SnItem *item,
1268 gint delta,
1269 SnItemOrientation orientation)
1270 {
1271 SnItemV0 *v0;
1272 const gchar *tmp;
1273
1274 v0 = SN_ITEM_V0 (item);
1275
1276 switch (orientation)
1277 {
1278 case SN_ITEM_ORIENTATION_VERTICAL:
1279 tmp = "Vertical";
1280 break;
1281
1282 case SN_ITEM_ORIENTATION_HORIZONTAL:
1283 default:
1284 tmp = "Horizontal";
1285 break;
1286 }
1287
1288 sn_item_v0_gen_call_scroll (v0->proxy, delta, tmp, NULL, scroll_cb, v0);
1289 }
1290
1291 static void
sn_item_v0_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1292 sn_item_v0_size_allocate (GtkWidget *widget,
1293 GtkAllocation *allocation)
1294 {
1295 SnItemV0 *v0 = SN_ITEM_V0 (widget);
1296
1297 GTK_WIDGET_CLASS (sn_item_v0_parent_class)->size_allocate (widget, allocation);
1298
1299 /* FIXME: this leads to grow-only size, unless there's underallocation.
1300 * not a problem in the panel, but one in the test app. */
1301 if (v0->icon_size <= 0)
1302 {
1303 gint prev_effective_icon_size = v0->effective_icon_size;
1304
1305 if (gtk_orientable_get_orientation (GTK_ORIENTABLE (v0)) == GTK_ORIENTATION_HORIZONTAL)
1306 v0->effective_icon_size = allocation->height;
1307 else
1308 v0->effective_icon_size = allocation->width;
1309
1310 if (v0->effective_icon_size != prev_effective_icon_size)
1311 queue_update (SN_ITEM_V0 (widget));
1312 }
1313 }
1314
1315 static void
sn_item_v0_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1316 sn_item_v0_get_property (GObject *object,
1317 guint property_id,
1318 GValue *value,
1319 GParamSpec *pspec)
1320 {
1321 SnItemV0 *v0;
1322
1323 v0 = SN_ITEM_V0 (object);
1324
1325 switch (property_id)
1326 {
1327 case PROP_ICON_SIZE:
1328 g_value_set_uint (value, v0->icon_size);
1329 break;
1330
1331 case PROP_ICON_PADDING:
1332 g_value_set_int (value, sn_item_v0_get_icon_padding (v0));
1333 break;
1334
1335 default:
1336 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1337 break;
1338 }
1339 }
1340
1341 static void
sn_item_v0_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1342 sn_item_v0_set_property (GObject *object,
1343 guint property_id,
1344 const GValue *value,
1345 GParamSpec *pspec)
1346 {
1347 SnItemV0 *v0;
1348
1349 v0 = SN_ITEM_V0 (object);
1350
1351 switch (property_id)
1352 {
1353 case PROP_ICON_SIZE:
1354 sn_item_v0_set_icon_size (v0, g_value_get_int (value));
1355 break;
1356
1357 case PROP_ICON_PADDING:
1358 sn_item_v0_set_icon_padding (v0, g_value_get_int (value));
1359 break;
1360
1361 default:
1362 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1363 break;
1364 }
1365 }
1366
1367 static void
install_properties(GObjectClass * object_class)1368 install_properties (GObjectClass *object_class)
1369 {
1370 obj_properties[PROP_ICON_SIZE] =
1371 g_param_spec_int ("icon-size", "Icon size", "Icon size", 0, G_MAXINT, 16,
1372 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1373
1374 obj_properties[PROP_ICON_PADDING] =
1375 g_param_spec_int ("icon-padding", "Icon padding", "Icon padding", 0,
1376 G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1377
1378 g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
1379 }
1380
1381 static void
sn_item_v0_class_init(SnItemV0Class * v0_class)1382 sn_item_v0_class_init (SnItemV0Class *v0_class)
1383 {
1384 GObjectClass *object_class;
1385 GtkWidgetClass *widget_class;
1386 SnItemClass *item_class;
1387
1388 object_class = G_OBJECT_CLASS (v0_class);
1389 widget_class = GTK_WIDGET_CLASS (v0_class);
1390 item_class = SN_ITEM_CLASS (v0_class);
1391
1392 object_class->constructed = sn_item_v0_constructed;
1393 object_class->dispose = sn_item_v0_dispose;
1394 object_class->finalize = sn_item_v0_finalize;
1395 object_class->get_property = sn_item_v0_get_property;
1396 object_class->set_property = sn_item_v0_set_property;
1397
1398 item_class->get_id = sn_item_v0_get_id;
1399 item_class->get_category = sn_item_v0_get_category;
1400 item_class->get_menu = sn_item_v0_get_menu;
1401
1402 item_class->context_menu = sn_item_v0_context_menu;
1403 item_class->activate = sn_item_v0_activate;
1404 item_class->secondary_activate = sn_item_v0_secondary_activate;
1405 item_class->scroll = sn_item_v0_scroll;
1406
1407 widget_class->size_allocate = sn_item_v0_size_allocate;
1408
1409 gtk_widget_class_set_css_name (widget_class, "sn-item");
1410
1411 install_properties (object_class);
1412 }
1413
1414 static void
sn_item_v0_init(SnItemV0 * v0)1415 sn_item_v0_init (SnItemV0 *v0)
1416 {
1417 v0->icon_size = 16;
1418 v0->effective_icon_size = 0;
1419 v0->image = gtk_image_new ();
1420 gtk_button_set_image (GTK_BUTTON (v0), v0->image);
1421 gtk_widget_show (v0->image);
1422 }
1423
1424 SnItem *
sn_item_v0_new(const gchar * bus_name,const gchar * object_path)1425 sn_item_v0_new (const gchar *bus_name,
1426 const gchar *object_path)
1427 {
1428 return g_object_new (SN_TYPE_ITEM_V0,
1429 "bus-name", bus_name,
1430 "object-path", object_path,
1431 NULL);
1432 }
1433
1434 gint
sn_item_v0_get_icon_padding(SnItemV0 * v0)1435 sn_item_v0_get_icon_padding (SnItemV0 *v0)
1436 {
1437 GtkOrientation orientation;
1438 gint a, b;
1439
1440 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (v0));
1441
1442 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1443 {
1444 a = gtk_widget_get_margin_start (v0->image);
1445 b = gtk_widget_get_margin_end (v0->image);
1446 }
1447 else
1448 {
1449 a = gtk_widget_get_margin_top (v0->image);
1450 b = gtk_widget_get_margin_bottom (v0->image);
1451 }
1452
1453 return (a + b) / 2;
1454 }
1455
1456 void
sn_item_v0_set_icon_padding(SnItemV0 * v0,gint padding)1457 sn_item_v0_set_icon_padding (SnItemV0 *v0,
1458 gint padding)
1459 {
1460 GtkOrientation orientation;
1461 gint padding_x = 0;
1462 gint padding_y = 0;
1463
1464 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (v0));
1465
1466 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1467 padding_x = padding;
1468 else
1469 padding_y = padding;
1470
1471 gtk_widget_set_margin_start (v0->image, padding_x);
1472 gtk_widget_set_margin_end (v0->image, padding_x);
1473 gtk_widget_set_margin_top (v0->image, padding_y);
1474 gtk_widget_set_margin_bottom (v0->image, padding_y);
1475 }
1476
1477 gint
sn_item_v0_get_icon_size(SnItemV0 * v0)1478 sn_item_v0_get_icon_size (SnItemV0 *v0)
1479 {
1480 return v0->icon_size;
1481 }
1482
1483 void
sn_item_v0_set_icon_size(SnItemV0 * v0,gint size)1484 sn_item_v0_set_icon_size (SnItemV0 *v0,
1485 gint size)
1486 {
1487 if (v0->icon_size != size)
1488 {
1489 v0->icon_size = size;
1490 g_object_notify_by_pspec (G_OBJECT (v0), obj_properties[PROP_ICON_SIZE]);
1491
1492 if (v0->id != NULL)
1493 queue_update (v0);
1494 }
1495 }
1496