1 /*
2  * Copyright (C) 2010-2011 Canonical Ltd
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 3 as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Authored by: Jason Smith <jason.smith@canonical.com>
17  *              Marco Trevisan (Treviño) <3v1n0@ubuntu.com>
18  *
19  */
20 
21 #include "bamf-view.h"
22 
23 #define BAMF_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE(obj, \
24                                     BAMF_TYPE_VIEW, BamfViewPrivate))
25 
26 static void bamf_view_dbus_view_iface_init (BamfDBusItemViewIface *iface);
27 G_DEFINE_TYPE_WITH_CODE (BamfView, bamf_view, BAMF_DBUS_ITEM_TYPE_OBJECT_SKELETON,
28                          G_IMPLEMENT_INTERFACE (BAMF_DBUS_ITEM_TYPE_VIEW,
29                                                 bamf_view_dbus_view_iface_init));
30 
31 #define STARTING_MAX_WAIT 15
32 
33 enum
34 {
35   PROP_0,
36 
37   PROP_NAME,
38   PROP_ICON,
39   PROP_ACTIVE,
40   PROP_STARTING,
41   PROP_RUNNING,
42   PROP_URGENT,
43   PROP_USER_VISIBLE,
44 };
45 
46 enum
47 {
48   CLOSED_INTERNAL,
49   EXPORTED,
50 
51   LAST_SIGNAL,
52 };
53 
54 static guint view_signals[LAST_SIGNAL] = { 0 };
55 
56 typedef struct _BamfViewPropCache
57 {
58   /* FIXME: temporary cache these properties until we don't export the view
59    * to the bus, we need this until the skeleton won't be smart enough to emit
60    * signals as soon as the object is exported */
61   gboolean starting;
62   gboolean running;
63   gboolean user_visible;
64   gboolean urgent;
65   gboolean active;
66 
67   gchar *name;
68   gchar *icon;
69 } BamfViewPropCache;
70 
71 struct _BamfViewPrivate
72 {
73   BamfDBusItemView * dbus_iface;
74   BamfViewPropCache * props;
75   char * path;
76   GList * children;
77   GList * parents;
78   gboolean closed;
79   guint starting_timeout;
80 
81   SnStartupSequence *startup_sequence;
82 
83   /* FIXME: remove this as soon as we move to properties on library as well */
84   guint active_changed_idle;
85 };
86 
87 static gboolean
on_active_changed_idle(gpointer data)88 on_active_changed_idle (gpointer data)
89 {
90   g_return_val_if_fail (BAMF_IS_VIEW (data), FALSE);
91 
92   BamfView *self = BAMF_VIEW (data);
93   gboolean active = bamf_view_is_active (self);
94 
95   g_signal_emit_by_name (self, "active-changed", active);
96   self->priv->active_changed_idle = 0;
97 
98   return FALSE;
99 }
100 
101 static void
bamf_view_active_changed(BamfView * view,gboolean active)102 bamf_view_active_changed (BamfView *view, gboolean active)
103 {
104   g_return_if_fail (BAMF_IS_VIEW (view));
105 
106   gboolean emit = TRUE;
107   if (BAMF_VIEW_GET_CLASS (view)->active_changed)
108     {
109       emit = !BAMF_VIEW_GET_CLASS (view)->active_changed (view, active);
110     }
111 
112   if (emit)
113     {
114       if (view->priv->active_changed_idle)
115         g_source_remove (view->priv->active_changed_idle);
116 
117       guint idle = g_idle_add_full (G_PRIORITY_DEFAULT, on_active_changed_idle, view, NULL);
118       view->priv->active_changed_idle = idle;
119     }
120 
121   if (active)
122     bamf_view_set_starting (view, NULL, FALSE);
123 }
124 
125 static void
bamf_view_name_changed(BamfView * view,const gchar * new_name)126 bamf_view_name_changed (BamfView *view, const gchar *new_name)
127 {
128   g_return_if_fail (BAMF_IS_VIEW (view));
129 
130   const gchar *old_name = bamf_view_get_name (view);
131   g_signal_emit_by_name (view, "name-changed", old_name, new_name);
132 }
133 
134 static void
bamf_view_icon_changed(BamfView * view,const gchar * new_icon)135 bamf_view_icon_changed (BamfView *view, const gchar *new_icon)
136 {
137   g_object_notify (G_OBJECT (view), "icon");
138 }
139 
140 static void
bamf_view_user_visible_changed(BamfView * view,gboolean user_visible)141 bamf_view_user_visible_changed (BamfView *view, gboolean user_visible)
142 {
143   g_return_if_fail (BAMF_IS_VIEW (view));
144 
145   gboolean emit = TRUE;
146   if (BAMF_VIEW_GET_CLASS (view)->user_visible_changed)
147     {
148       emit = !BAMF_VIEW_GET_CLASS (view)->user_visible_changed (view, user_visible);
149     }
150 
151   if (emit)
152     g_signal_emit_by_name (view, "user-visible-changed", user_visible);
153 }
154 
155 static gboolean
on_starting_timeout(gpointer data)156 on_starting_timeout (gpointer data)
157 {
158   BamfView *view = data;
159 
160   bamf_view_set_starting (view, NULL, FALSE);
161   view->priv->starting_timeout = 0;
162 
163   return FALSE;
164 }
165 
166 static void
bamf_view_starting_changed(BamfView * view,gboolean starting)167 bamf_view_starting_changed (BamfView *view, gboolean starting)
168 {
169   BamfViewPrivate *priv;
170 
171   g_return_if_fail (BAMF_IS_VIEW (view));
172 
173   priv = view->priv;
174 
175   if (BAMF_VIEW_GET_CLASS (view)->starting_changed)
176     {
177       BAMF_VIEW_GET_CLASS (view)->starting_changed (view, starting);
178     }
179 
180   if (priv->starting_timeout)
181     {
182       g_source_remove (priv->starting_timeout);
183       priv->starting_timeout = 0;
184     }
185 
186   if (starting)
187     priv->starting_timeout = g_timeout_add_seconds (STARTING_MAX_WAIT, on_starting_timeout, view);
188 }
189 
190 static void
bamf_view_running_changed(BamfView * view,gboolean running)191 bamf_view_running_changed (BamfView *view, gboolean running)
192 {
193   g_return_if_fail (BAMF_IS_VIEW (view));
194 
195   gboolean emit = TRUE;
196   if (BAMF_VIEW_GET_CLASS (view)->running_changed)
197     {
198       emit = !BAMF_VIEW_GET_CLASS (view)->running_changed (view, running);
199     }
200 
201   if (emit)
202     g_signal_emit_by_name (view, "running-changed", running);
203 
204   if (running)
205     bamf_view_set_starting (view, NULL, FALSE);
206 }
207 
208 static void
bamf_view_urgent_changed(BamfView * view,gboolean urgent)209 bamf_view_urgent_changed (BamfView *view, gboolean urgent)
210 {
211   g_return_if_fail (BAMF_IS_VIEW (view));
212 
213   gboolean emit = TRUE;
214   if (BAMF_VIEW_GET_CLASS (view)->urgent_changed)
215     {
216       emit = !BAMF_VIEW_GET_CLASS (view)->urgent_changed (view, urgent);
217     }
218 
219   if (emit)
220     g_signal_emit_by_name (view, "urgent-changed", urgent);
221 
222   if (urgent)
223     bamf_view_set_starting (view, NULL, FALSE);
224 }
225 
226 void
bamf_view_close(BamfView * view)227 bamf_view_close (BamfView *view)
228 {
229   BamfViewPrivate *priv;
230   gboolean emit = TRUE;
231   GList *l;
232 
233   g_return_if_fail (BAMF_IS_VIEW (view));
234   priv = view->priv;
235 
236   if (priv->closed)
237     return;
238 
239   priv->closed = TRUE;
240 
241   if (BAMF_VIEW_GET_CLASS (view)->closed)
242     {
243       emit = !BAMF_VIEW_GET_CLASS (view)->closed (view);
244     }
245 
246   if (priv->children)
247     {
248       for (l = priv->children; l; l = l->next)
249         {
250           if (BAMF_IS_VIEW (l->data))
251             bamf_view_remove_child (view, l->data);
252         }
253       g_list_free (priv->children);
254       priv->children = NULL;
255     }
256 
257   if (emit)
258     {
259       g_object_ref (view);
260       g_signal_emit (view, view_signals[CLOSED_INTERNAL], 0);
261       g_signal_emit_by_name (view, "closed");
262       g_object_unref (view);
263     }
264 }
265 
266 const char *
bamf_view_get_path(BamfView * view)267 bamf_view_get_path (BamfView *view)
268 {
269   g_return_val_if_fail (BAMF_IS_VIEW (view), NULL);
270 
271   return view->priv->path;
272 }
273 
274 GVariant *
bamf_view_get_children_paths(BamfView * view)275 bamf_view_get_children_paths (BamfView *view)
276 {
277   GVariantBuilder b;
278   GList *l;
279 
280   g_return_val_if_fail (BAMF_IS_VIEW (view), NULL);
281 
282   g_variant_builder_init (&b, G_VARIANT_TYPE ("(as)"));
283   g_variant_builder_open (&b, G_VARIANT_TYPE ("as"));
284 
285   for (l = view->priv->children; l; l = l->next)
286     {
287       BamfView *child = l->data;
288       const char *path = bamf_view_get_path (child);
289 
290       if (!path)
291         continue;
292 
293       g_variant_builder_add (&b, "s", path);
294     }
295 
296   g_variant_builder_close (&b);
297 
298   return g_variant_builder_end (&b);
299 }
300 
301 GList *
bamf_view_get_children(BamfView * view)302 bamf_view_get_children (BamfView *view)
303 {
304   g_return_val_if_fail (BAMF_IS_VIEW (view), NULL);
305 
306   return view->priv->children;
307 }
308 
309 GVariant *
bamf_view_get_parent_paths(BamfView * view)310 bamf_view_get_parent_paths (BamfView *view)
311 {
312   GVariantBuilder b;
313   GList *l;
314 
315   g_return_val_if_fail (BAMF_IS_VIEW (view), NULL);
316 
317   g_variant_builder_init (&b, G_VARIANT_TYPE ("(as)"));
318   g_variant_builder_open (&b, G_VARIANT_TYPE ("as"));
319 
320   for (l = view->priv->parents; l; l = l->next)
321     {
322       BamfView *parent = l->data;
323       const char *path = bamf_view_get_path (parent);
324 
325       if (!path)
326         continue;
327 
328       g_variant_builder_add (&b, "s", path);
329     }
330 
331   g_variant_builder_close (&b);
332 
333   return g_variant_builder_end (&b);
334 }
335 
336 GList *
bamf_view_get_parents(BamfView * view)337 bamf_view_get_parents (BamfView *view)
338 {
339   g_return_val_if_fail (BAMF_IS_VIEW (view), NULL);
340 
341   return view->priv->parents;
342 }
343 
344 static void
bamf_view_handle_child_closed(BamfView * child,BamfView * view)345 bamf_view_handle_child_closed (BamfView *child,
346                                BamfView *view)
347 {
348   bamf_view_remove_child (view, child);
349 }
350 
351 static void
on_child_view_exported(BamfView * child,BamfView * view)352 on_child_view_exported (BamfView *child, BamfView *view)
353 {
354   g_signal_emit_by_name (view, "child-added", bamf_view_get_path (child));
355   g_signal_handlers_disconnect_by_func (child, on_child_view_exported, view);
356 }
357 
358 void
bamf_view_add_child(BamfView * view,BamfView * child)359 bamf_view_add_child (BamfView *view,
360                      BamfView *child)
361 {
362   const char * added;
363 
364   g_return_if_fail (BAMF_IS_VIEW (view));
365   g_return_if_fail (BAMF_IS_VIEW (child));
366 
367   g_signal_connect (G_OBJECT (child), "closed-internal",
368                     G_CALLBACK (bamf_view_handle_child_closed), view);
369 
370   /* Make sure our parent child lists are ok, pay attention to whose list you add parents to */
371   view->priv->children = g_list_prepend (view->priv->children, child);
372   child->priv->parents = g_list_prepend (child->priv->parents, view);
373 
374   if (bamf_view_is_on_bus (child))
375     {
376       added = bamf_view_get_path (child);
377       g_signal_emit_by_name (view, "child-added", added);
378     }
379   else
380     {
381       g_signal_connect (G_OBJECT (child), "exported",
382                         G_CALLBACK (on_child_view_exported), view);
383     }
384 
385   // Do this by hand so we can pass and object instead of a string
386   if (BAMF_VIEW_GET_CLASS (view)->child_added)
387     BAMF_VIEW_GET_CLASS (view)->child_added (view, child);
388 }
389 
390 void
bamf_view_remove_child(BamfView * view,BamfView * child)391 bamf_view_remove_child (BamfView *view, BamfView *child)
392 {
393   const char *removed;
394 
395   g_return_if_fail (BAMF_IS_VIEW (view));
396   g_return_if_fail (BAMF_IS_VIEW (child));
397 
398   g_signal_handlers_disconnect_by_data (child, view);
399 
400   /* Make sure our parent child lists are ok, pay attention to whose list you add parents to */
401   view->priv->children = g_list_remove (view->priv->children, child);
402   child->priv->parents = g_list_remove (child->priv->parents, view);
403 
404   removed = bamf_view_get_path (child);
405   g_signal_emit_by_name (view, "child-removed", removed);
406 
407   /* Do this by hand so we can pass and object instead of a string */
408   if (BAMF_VIEW_GET_CLASS (view)->child_removed)
409     BAMF_VIEW_GET_CLASS (view)->child_removed (view, child);
410 }
411 
412 #define BAMF_VIEW_GET_PROPERTY(v, property, ret_val)                          \
413   g_return_val_if_fail (BAMF_IS_VIEW (v), ret_val);                           \
414                                                                               \
415   if (v->priv->props)                                                         \
416     return v->priv->props->property;                                          \
417                                                                               \
418   return _bamf_dbus_item_view_get_##property (v->priv->dbus_iface);
419 
420 #define BAMF_VIEW_SET_BOOL_PROPERTY(v, property)                              \
421   g_return_if_fail (BAMF_IS_VIEW (v));                                        \
422                                                                               \
423   if (property == bamf_view_is_##property (v))                                \
424     return;                                                                   \
425                                                                               \
426   if (v->priv->props)                                                         \
427     {                                                                         \
428       v->priv->props->property = property;                                    \
429     }                                                                         \
430   else                                                                        \
431     {                                                                         \
432       _bamf_dbus_item_view_set_##property (v->priv->dbus_iface, property);    \
433     }                                                                         \
434                                                                               \
435   bamf_view_##property##_changed (v, property);
436 
437 #define BAMF_VIEW_SET_STRING_PROPERTY(v, property)                            \
438   g_return_if_fail (BAMF_IS_VIEW (v));                                        \
439                                                                               \
440   const gchar *current_value = bamf_view_get_##property (v);                  \
441                                                                               \
442   if (current_value == property || g_strcmp0 (current_value, property) == 0)  \
443     return;                                                                   \
444                                                                               \
445   bamf_view_##property##_changed (v, property);                               \
446                                                                               \
447   if (v->priv->props)                                                         \
448     {                                                                         \
449       g_free (v->priv->props->property);                                      \
450       v->priv->props->property = g_strdup (property);                         \
451     }                                                                         \
452   else                                                                        \
453     {                                                                         \
454       _bamf_dbus_item_view_set_##property (v->priv->dbus_iface, property);    \
455     }
456 
457 gboolean
bamf_view_is_active(BamfView * view)458 bamf_view_is_active (BamfView *view)
459 {
460   BAMF_VIEW_GET_PROPERTY (view, active, FALSE);
461 }
462 
463 void
bamf_view_set_active(BamfView * view,gboolean active)464 bamf_view_set_active (BamfView *view,
465                       gboolean active)
466 {
467   BAMF_VIEW_SET_BOOL_PROPERTY (view, active);
468 }
469 
470 gboolean
bamf_view_is_urgent(BamfView * view)471 bamf_view_is_urgent (BamfView *view)
472 {
473   BAMF_VIEW_GET_PROPERTY (view, urgent, FALSE);
474 }
475 
476 void
bamf_view_set_urgent(BamfView * view,gboolean urgent)477 bamf_view_set_urgent (BamfView *view,
478                        gboolean urgent)
479 {
480   BAMF_VIEW_SET_BOOL_PROPERTY (view, urgent);
481 }
482 
483 gboolean
bamf_view_is_starting(BamfView * view)484 bamf_view_is_starting (BamfView *view)
485 {
486  BAMF_VIEW_GET_PROPERTY (view, starting, FALSE);
487 }
488 
489 void
bamf_view_set_starting(BamfView * view,SnStartupSequence * startup_sequence,gboolean starting)490 bamf_view_set_starting (BamfView *view,
491                         SnStartupSequence *startup_sequence,
492                         gboolean starting)
493 {
494   if (!bamf_view_is_starting (view) && starting)
495     {
496       if (view->priv->startup_sequence)
497       {
498         sn_startup_sequence_unref (view->priv->startup_sequence);
499         view->priv->startup_sequence = NULL;
500       }
501 
502       if (startup_sequence)
503         {
504           view->priv->startup_sequence = startup_sequence;
505           sn_startup_sequence_ref (view->priv->startup_sequence);
506         }
507     }
508   else if (!starting)
509     {
510       if (view->priv->startup_sequence)
511         {
512           sn_startup_sequence_complete (view->priv->startup_sequence);
513           sn_startup_sequence_unref (view->priv->startup_sequence);
514           view->priv->startup_sequence = NULL;
515         }
516     }
517 
518   BAMF_VIEW_SET_BOOL_PROPERTY (view, starting);
519 }
520 
521 gboolean
bamf_view_is_running(BamfView * view)522 bamf_view_is_running (BamfView *view)
523 {
524  BAMF_VIEW_GET_PROPERTY (view, running, FALSE);
525 }
526 
527 void
bamf_view_set_running(BamfView * view,gboolean running)528 bamf_view_set_running (BamfView *view,
529                        gboolean running)
530 {
531   BAMF_VIEW_SET_BOOL_PROPERTY (view, running);
532 }
533 
534 gboolean
bamf_view_is_user_visible(BamfView * view)535 bamf_view_is_user_visible (BamfView *view)
536 {
537   BAMF_VIEW_GET_PROPERTY (view, user_visible, FALSE);
538 }
539 
540 void
bamf_view_set_user_visible(BamfView * view,gboolean user_visible)541 bamf_view_set_user_visible (BamfView *view, gboolean user_visible)
542 {
543   BAMF_VIEW_SET_BOOL_PROPERTY (view, user_visible);
544 }
545 
546 const char *
bamf_view_get_icon(BamfView * view)547 bamf_view_get_icon (BamfView *view)
548 {
549   BAMF_VIEW_GET_PROPERTY (view, icon, NULL);
550 }
551 
552 void
bamf_view_set_icon(BamfView * view,const char * icon)553 bamf_view_set_icon (BamfView *view, const char *icon)
554 {
555   BAMF_VIEW_SET_STRING_PROPERTY (view, icon);
556 }
557 
558 const char *
bamf_view_get_name(BamfView * view)559 bamf_view_get_name (BamfView *view)
560 {
561   BAMF_VIEW_GET_PROPERTY (view, name, NULL);
562 }
563 
564 void
bamf_view_set_name(BamfView * view,const char * name)565 bamf_view_set_name (BamfView *view, const char *name)
566 {
567   BAMF_VIEW_SET_STRING_PROPERTY (view, name);
568 }
569 
570 const char *
bamf_view_get_view_type(BamfView * view)571 bamf_view_get_view_type (BamfView *view)
572 {
573   g_return_val_if_fail (BAMF_IS_VIEW (view), NULL);
574 
575   if (BAMF_VIEW_GET_CLASS (view)->view_type)
576     return BAMF_VIEW_GET_CLASS (view)->view_type (view);
577 
578   return "view";
579 }
580 
581 static char *
bamf_view_get_stable_bus_name(BamfView * view)582 bamf_view_get_stable_bus_name (BamfView *view)
583 {
584   g_return_val_if_fail (BAMF_IS_VIEW (view), NULL);
585 
586   if (BAMF_VIEW_GET_CLASS (view)->stable_bus_name)
587     return BAMF_VIEW_GET_CLASS (view)->stable_bus_name (view);
588 
589   return g_strdup_printf ("view/%p", view);
590 }
591 
592 static void
bamf_view_cached_properties_clear(BamfView * view)593 bamf_view_cached_properties_clear (BamfView *view)
594 {
595   if (!view->priv->props)
596     return;
597 
598   g_free (view->priv->props->name);
599   g_free (view->priv->props->icon);
600   g_free (view->priv->props);
601   view->priv->props = NULL;
602 }
603 
604 static void
bamf_view_cached_properties_notify(BamfView * view)605 bamf_view_cached_properties_notify (BamfView *view)
606 {
607   if (!view->priv->props || !bamf_view_is_on_bus (view))
608     return;
609 
610   /* Temporary disable the cache so that cached values will be set on the skeleton */
611   BamfViewPropCache *cache = view->priv->props;
612   view->priv->props = NULL;
613 
614   bamf_view_set_name (view, cache->name);
615   bamf_view_set_icon (view, cache->icon);
616   bamf_view_set_active (view, cache->active);
617   bamf_view_set_starting (view, NULL, cache->starting);
618   bamf_view_set_running (view, cache->running);
619   bamf_view_set_user_visible (view, cache->user_visible);
620   bamf_view_set_urgent (view, cache->urgent);
621 
622   view->priv->props = cache;
623 }
624 
625 const char *
bamf_view_export_on_bus(BamfView * view,GDBusConnection * connection)626 bamf_view_export_on_bus (BamfView *view, GDBusConnection *connection)
627 {
628   char *path = NULL;
629   GList *ifaces, *l;
630   GError *error = NULL;
631 
632   g_return_val_if_fail (BAMF_IS_VIEW (view), NULL);
633   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
634 
635   if (!view->priv->path)
636     {
637       gboolean exported = TRUE;
638       char *stable_name = bamf_view_get_stable_bus_name (view);
639       path = g_strconcat (BAMF_DBUS_BASE_PATH, "/", stable_name, NULL);
640       g_free (stable_name);
641 
642       BAMF_VIEW_GET_CLASS (view)->names = g_list_prepend (BAMF_VIEW_GET_CLASS (view)->names, path);
643       view->priv->path = path;
644 
645       ifaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (view));
646 
647       /* The dbus object interface list is in reversed order, we try to export
648        * the interfaces in bottom to top order (BamfView should be the first) */
649       for (l = g_list_last (ifaces); l; l = l->prev)
650         {
651           g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (l->data),
652                                             connection, path, &error);
653           if (error)
654             {
655               g_critical ("Can't register BAMF view interface: %s", error->message);
656               g_clear_error (&error);
657               exported = FALSE;
658             }
659         }
660 
661       if (exported)
662         {
663           /* FIXME: if we change the properties before that the view has been
664            * exported, the skeleton doesn't emit the proper signals to notify
665            * the proxy that the values have been changed, and this causes
666            * the properties not to be updated on the client side.
667            * So we store the values locally until the proxy is not exported,
668            * then we notify our clients. */
669           bamf_view_cached_properties_notify (view);
670           bamf_view_cached_properties_clear (view);
671 
672           g_signal_emit (view, view_signals[EXPORTED], 0);
673         }
674 
675       g_list_free_full (ifaces, g_object_unref);
676     }
677 
678   return view->priv->path;
679 }
680 
681 gboolean
bamf_view_is_on_bus(BamfView * view)682 bamf_view_is_on_bus (BamfView *view)
683 {
684   g_return_val_if_fail (BAMF_IS_VIEW (view), FALSE);
685   GDBusInterfaceSkeleton *dbus_iface;
686   const gchar *exported_path;
687 
688   if (!view->priv->path)
689     return FALSE;
690 
691   dbus_iface = G_DBUS_INTERFACE_SKELETON (view->priv->dbus_iface);
692   exported_path = g_dbus_interface_skeleton_get_object_path (dbus_iface);
693 
694   return (exported_path != NULL);
695 }
696 
697 static void
on_view_active_changed(BamfView * view,gboolean active,gpointer _not_used)698 on_view_active_changed (BamfView *view, gboolean active, gpointer _not_used)
699 {
700   g_return_if_fail (BAMF_IS_VIEW (view));
701   g_signal_emit_by_name (view->priv->dbus_iface, "active-changed", active);
702 }
703 
704 static void
on_view_running_changed(BamfView * view,gboolean running,gpointer _not_used)705 on_view_running_changed (BamfView *view, gboolean running, gpointer _not_used)
706 {
707   g_return_if_fail (BAMF_IS_VIEW (view));
708   g_signal_emit_by_name (view->priv->dbus_iface, "running-changed", running);
709 }
710 
711 static void
on_view_urgent_changed(BamfView * view,gboolean urgent,gpointer _not_used)712 on_view_urgent_changed (BamfView *view, gboolean urgent, gpointer _not_used)
713 {
714   g_return_if_fail (BAMF_IS_VIEW (view));
715   g_signal_emit_by_name (view->priv->dbus_iface, "urgent-changed", urgent);
716 }
717 
718 static void
on_view_user_visible_changed(BamfView * view,gboolean user_visible,gpointer _not_used)719 on_view_user_visible_changed (BamfView *view, gboolean user_visible, gpointer _not_used)
720 {
721   g_return_if_fail (BAMF_IS_VIEW (view));
722   g_signal_emit_by_name (view->priv->dbus_iface, "user-visible-changed", user_visible);
723 }
724 
725 static void
on_view_name_changed(BamfView * view,const gchar * old_name,const gchar * new_name,gpointer _not_used)726 on_view_name_changed (BamfView *view, const gchar *old_name, const gchar *new_name, gpointer _not_used)
727 {
728   g_return_if_fail (BAMF_IS_VIEW (view));
729   g_signal_emit_by_name (view->priv->dbus_iface, "name-changed",
730                          old_name ? old_name : "", new_name ? new_name : "");
731 }
732 
733 static void
on_view_child_added(BamfView * view,const gchar * child_path,gpointer _not_used)734 on_view_child_added (BamfView *view, const gchar *child_path, gpointer _not_used)
735 {
736   g_return_if_fail (BAMF_IS_VIEW (view));
737   g_signal_emit_by_name (view->priv->dbus_iface, "child-added",
738                          child_path ? child_path : "");
739 }
740 
741 static void
on_view_child_removed(BamfView * view,const gchar * child_path,gpointer _not_used)742 on_view_child_removed (BamfView *view, const gchar *child_path, gpointer _not_used)
743 {
744   g_return_if_fail (BAMF_IS_VIEW (view));
745   g_signal_emit_by_name (view->priv->dbus_iface, "child-removed",
746                          child_path ? child_path : "");
747 }
748 
749 static void
on_view_closed(BamfView * view,gpointer _not_used)750 on_view_closed (BamfView *view, gpointer _not_used)
751 {
752   g_return_if_fail (BAMF_IS_VIEW (view));
753   g_dbus_object_skeleton_flush (G_DBUS_OBJECT_SKELETON (view));
754   g_signal_emit_by_name (view->priv->dbus_iface, "closed");
755 }
756 
757 static gboolean
on_dbus_handle_view_type(BamfDBusItemView * interface,GDBusMethodInvocation * invocation,BamfView * view)758 on_dbus_handle_view_type (BamfDBusItemView *interface,
759                           GDBusMethodInvocation *invocation,
760                           BamfView *view)
761 {
762   const char *type = bamf_view_get_view_type (view);
763   g_dbus_method_invocation_return_value (invocation,
764                                          g_variant_new ("(s)", type));
765 
766   return TRUE;
767 }
768 
769 static gboolean
on_dbus_handle_user_visible(BamfDBusItemView * interface,GDBusMethodInvocation * invocation,BamfView * view)770 on_dbus_handle_user_visible (BamfDBusItemView *interface,
771                              GDBusMethodInvocation *invocation,
772                              BamfView *view)
773 {
774   gboolean user_visible = bamf_view_is_user_visible (view);
775   g_dbus_method_invocation_return_value (invocation,
776                                          g_variant_new ("(b)", user_visible));
777 
778   return TRUE;
779 }
780 
781 static gboolean
on_dbus_handle_icon(BamfDBusItemView * interface,GDBusMethodInvocation * invocation,BamfView * view)782 on_dbus_handle_icon (BamfDBusItemView *interface,
783                      GDBusMethodInvocation *invocation,
784                      BamfView *view)
785 {
786   const char *icon = bamf_view_get_icon (view);
787   g_dbus_method_invocation_return_value (invocation,
788                                          g_variant_new ("(s)", icon ? icon : ""));
789 
790   return TRUE;
791 }
792 
793 static gboolean
on_dbus_handle_name(BamfDBusItemView * interface,GDBusMethodInvocation * invocation,BamfView * view)794 on_dbus_handle_name (BamfDBusItemView *interface,
795                      GDBusMethodInvocation *invocation,
796                      BamfView *view)
797 {
798   const char *name = bamf_view_get_name (view);
799   g_dbus_method_invocation_return_value (invocation,
800                                          g_variant_new ("(s)", name ? name : ""));
801 
802   return TRUE;
803 }
804 
805 static gboolean
on_dbus_handle_is_urgent(BamfDBusItemView * interface,GDBusMethodInvocation * invocation,BamfView * view)806 on_dbus_handle_is_urgent (BamfDBusItemView *interface,
807                           GDBusMethodInvocation *invocation,
808                           BamfView *view)
809 {
810   gboolean is_urgent = bamf_view_is_urgent (view);
811   g_dbus_method_invocation_return_value (invocation,
812                                          g_variant_new ("(b)", is_urgent));
813 
814   return TRUE;
815 }
816 
817 static gboolean
on_dbus_handle_is_running(BamfDBusItemView * interface,GDBusMethodInvocation * invocation,BamfView * view)818 on_dbus_handle_is_running (BamfDBusItemView *interface,
819                            GDBusMethodInvocation *invocation,
820                            BamfView *view)
821 {
822   gboolean is_running = bamf_view_is_running (view);
823   g_dbus_method_invocation_return_value (invocation,
824                                          g_variant_new ("(b)", is_running));
825 
826   return TRUE;
827 }
828 
829 static gboolean
on_dbus_handle_is_active(BamfDBusItemView * interface,GDBusMethodInvocation * invocation,BamfView * view)830 on_dbus_handle_is_active (BamfDBusItemView *interface,
831                           GDBusMethodInvocation *invocation,
832                           BamfView *view)
833 {
834   gboolean is_active = bamf_view_is_active (view);
835   g_dbus_method_invocation_return_value (invocation,
836                                          g_variant_new ("(b)", is_active));
837 
838   return TRUE;
839 }
840 
841 static gboolean
on_dbus_handle_parents(BamfDBusItemView * interface,GDBusMethodInvocation * invocation,BamfView * view)842 on_dbus_handle_parents (BamfDBusItemView *interface,
843                         GDBusMethodInvocation *invocation,
844                         BamfView *view)
845 {
846   GVariant *parents = bamf_view_get_parent_paths (view);
847   g_dbus_method_invocation_return_value (invocation, parents);
848 
849   return TRUE;
850 }
851 
852 static gboolean
on_dbus_handle_children(BamfDBusItemView * interface,GDBusMethodInvocation * invocation,BamfView * view)853 on_dbus_handle_children (BamfDBusItemView *interface,
854                          GDBusMethodInvocation *invocation,
855                          BamfView *view)
856 {
857   GVariant *children = bamf_view_get_children_paths (view);
858   g_dbus_method_invocation_return_value (invocation, children);
859 
860   return TRUE;
861 }
862 
863 static void
bamf_view_dispose(GObject * object)864 bamf_view_dispose (GObject *object)
865 {
866   BamfView *view = BAMF_VIEW (object);
867   BamfViewPrivate *priv = view->priv;
868 
869   if (priv->path)
870     {
871       g_free (priv->path);
872       priv->path = NULL;
873     }
874 
875   if (priv->children)
876     {
877       g_list_free (priv->children);
878       priv->children = NULL;
879     }
880 
881   if (priv->parents)
882     {
883       g_list_free (priv->parents);
884       priv->parents = NULL;
885     }
886 
887   if (priv->starting_timeout)
888     {
889       g_source_remove (priv->starting_timeout);
890       priv->starting_timeout = 0;
891     }
892 
893   if (priv->active_changed_idle)
894     {
895       g_source_remove (priv->active_changed_idle);
896       priv->active_changed_idle = 0;
897     }
898 
899   if (priv->startup_sequence)
900     {
901       sn_startup_sequence_unref (priv->startup_sequence);
902       priv->startup_sequence = NULL;
903     }
904 
905   bamf_view_cached_properties_clear (view);
906   g_dbus_object_skeleton_flush (G_DBUS_OBJECT_SKELETON (view));
907 
908   G_OBJECT_CLASS (bamf_view_parent_class)->dispose (object);
909 }
910 
911 static void
bamf_view_finalize(GObject * object)912 bamf_view_finalize (GObject *object)
913 {
914   BamfView *view = BAMF_VIEW (object);
915 
916   g_object_unref (view->priv->dbus_iface);
917 
918   G_OBJECT_CLASS (bamf_view_parent_class)->finalize (object);
919 }
920 
921 static void
bamf_view_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)922 bamf_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
923 {
924   BamfView *view = BAMF_VIEW (object);
925 
926   switch (property_id)
927     {
928       case PROP_NAME:
929         g_value_set_string (value, bamf_view_get_name (view));
930         break;
931       case PROP_ICON:
932         g_value_set_string (value, bamf_view_get_icon (view));
933         break;
934       case PROP_ACTIVE:
935         g_value_set_boolean (value, bamf_view_is_active (view));
936         break;
937       case PROP_URGENT:
938         g_value_set_boolean (value, bamf_view_is_urgent (view));
939         break;
940       case PROP_USER_VISIBLE:
941         g_value_set_boolean (value, bamf_view_is_user_visible (view));
942         break;
943       case PROP_STARTING:
944         g_value_set_boolean (value, bamf_view_is_starting (view));
945         break;
946       case PROP_RUNNING:
947         g_value_set_boolean (value, bamf_view_is_running (view));
948         break;
949       default:
950         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
951     }
952 }
953 
954 static void
bamf_view_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)955 bamf_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
956 {
957   switch (property_id)
958     {
959       default:
960         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
961     }
962 }
963 
964 static void
bamf_view_init(BamfView * self)965 bamf_view_init (BamfView * self)
966 {
967   self->priv = BAMF_VIEW_GET_PRIVATE (self);
968 
969   /* Initializing the dbus interface */
970   self->priv->dbus_iface = _bamf_dbus_item_view_skeleton_new ();
971   self->priv->props = g_new0 (BamfViewPropCache, 1);
972 
973   self->priv->startup_sequence = NULL;
974 
975   /* We need to connect to the object own signals to redirect them to the dbus
976    * interface                                                                */
977   g_signal_connect (self, "active-changed", G_CALLBACK (on_view_active_changed), NULL);
978   g_signal_connect (self, "running-changed", G_CALLBACK (on_view_running_changed), NULL);
979   g_signal_connect (self, "urgent-changed", G_CALLBACK (on_view_urgent_changed), NULL);
980   g_signal_connect (self, "user-visible-changed", G_CALLBACK (on_view_user_visible_changed), NULL);
981   g_signal_connect (self, "name-changed", G_CALLBACK (on_view_name_changed), NULL);
982   g_signal_connect (self, "child-added", G_CALLBACK (on_view_child_added), NULL);
983   g_signal_connect (self, "child-removed", G_CALLBACK (on_view_child_removed), NULL);
984   g_signal_connect (self, "closed", G_CALLBACK (on_view_closed), NULL);
985 
986   /* Registering signal callbacks to reply to dbus method calls */
987   g_signal_connect (self->priv->dbus_iface, "handle-view-type",
988                     G_CALLBACK (on_dbus_handle_view_type), self);
989 
990   g_signal_connect (self->priv->dbus_iface, "handle-user-visible",
991                     G_CALLBACK (on_dbus_handle_user_visible), self);
992 
993   g_signal_connect (self->priv->dbus_iface, "handle-icon",
994                     G_CALLBACK (on_dbus_handle_icon), self);
995 
996   g_signal_connect (self->priv->dbus_iface, "handle-name",
997                     G_CALLBACK (on_dbus_handle_name), self);
998 
999   g_signal_connect (self->priv->dbus_iface, "handle-is-urgent",
1000                     G_CALLBACK (on_dbus_handle_is_urgent), self);
1001 
1002   g_signal_connect (self->priv->dbus_iface, "handle-is-running",
1003                     G_CALLBACK (on_dbus_handle_is_running), self);
1004 
1005   g_signal_connect (self->priv->dbus_iface, "handle-is-active",
1006                     G_CALLBACK (on_dbus_handle_is_active), self);
1007 
1008   g_signal_connect (self->priv->dbus_iface, "handle-parents",
1009                     G_CALLBACK (on_dbus_handle_parents), self);
1010 
1011   g_signal_connect (self->priv->dbus_iface, "handle-children",
1012                     G_CALLBACK (on_dbus_handle_children), self);
1013 
1014   /* Setting the interface for the dbus object */
1015   _bamf_dbus_item_object_skeleton_set_view (BAMF_DBUS_ITEM_OBJECT_SKELETON (self),
1016                                             self->priv->dbus_iface);
1017 }
1018 
1019 static void
bamf_view_dbus_view_iface_init(BamfDBusItemViewIface * iface)1020 bamf_view_dbus_view_iface_init (BamfDBusItemViewIface *iface)
1021 {
1022 }
1023 
1024 static void
bamf_view_class_init(BamfViewClass * klass)1025 bamf_view_class_init (BamfViewClass * klass)
1026 {
1027   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1028 
1029   object_class->dispose = bamf_view_dispose;
1030   object_class->finalize = bamf_view_finalize;
1031   object_class->get_property = bamf_view_get_property;
1032   object_class->set_property = bamf_view_set_property;
1033 
1034   g_type_class_add_private (klass, sizeof (BamfViewPrivate));
1035 
1036   /* Overriding the properties defined in the interface, this is needed
1037    * but we actually don't use these properties, as we act like a proxy       */
1038   g_object_class_override_property (object_class, PROP_NAME, "name");
1039   g_object_class_override_property (object_class, PROP_ICON, "icon");
1040   g_object_class_override_property (object_class, PROP_ACTIVE, "active");
1041   g_object_class_override_property (object_class, PROP_URGENT, "urgent");
1042   g_object_class_override_property (object_class, PROP_STARTING, "starting");
1043   g_object_class_override_property (object_class, PROP_RUNNING, "running");
1044   g_object_class_override_property (object_class, PROP_USER_VISIBLE, "user-visible");
1045 
1046   view_signals [CLOSED_INTERNAL] =
1047     g_signal_new ("closed-internal",
1048                   G_OBJECT_CLASS_TYPE (klass),
1049                   G_SIGNAL_RUN_LAST,
1050                   G_STRUCT_OFFSET (BamfViewClass, closed_internal),
1051                   NULL, NULL, NULL,
1052                   G_TYPE_NONE, 0);
1053 
1054   view_signals [EXPORTED] =
1055     g_signal_new ("exported",
1056                   G_OBJECT_CLASS_TYPE (klass),
1057                   G_SIGNAL_RUN_FIRST,
1058                   G_STRUCT_OFFSET (BamfViewClass, exported),
1059                   NULL, NULL, NULL,
1060                   G_TYPE_NONE, 0);
1061 }
1062