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-application.h"
22 #include "bamf-window.h"
23 #include "bamf-matcher.h"
24 #include "bamf-legacy-window.h"
25 #include "bamf-legacy-screen.h"
26 #include "bamf-tab.h"
27 #include <string.h>
28 #include <gio/gdesktopappinfo.h>
29
30 #define BAMF_APPLICATION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE(obj, \
31 BAMF_TYPE_APPLICATION, BamfApplicationPrivate))
32
33 static void bamf_application_dbus_application_iface_init (BamfDBusItemApplicationIface *iface);
34 G_DEFINE_TYPE_WITH_CODE (BamfApplication, bamf_application, BAMF_TYPE_VIEW,
35 G_IMPLEMENT_INTERFACE (BAMF_DBUS_ITEM_TYPE_APPLICATION,
36 bamf_application_dbus_application_iface_init));
37
38 struct _BamfApplicationPrivate
39 {
40 BamfDBusItemApplication *dbus_iface;
41 char * desktop_file;
42 GList * desktop_file_list;
43 char * app_type;
44 char * icon;
45 char * wmclass;
46 char ** mimes;
47 gboolean is_tab_container;
48 gboolean show_stubs;
49 };
50
51 enum {
52 SUPPORTED_MIMES_CHANGED,
53 LAST_SIGNAL
54 };
55
56 static guint application_signals[LAST_SIGNAL] = { 0 };
57
58 #define STUB_KEY "X-Ayatana-Appmenu-Show-Stubs"
59
60 static const char *
61 bamf_application_get_icon (BamfView *view)
62 {
63 g_return_val_if_fail (BAMF_IS_APPLICATION (view), NULL);
64
65 return BAMF_APPLICATION (view)->priv->icon;
66 }
67
68 void
69 bamf_application_supported_mime_types_changed (BamfApplication *application,
70 const gchar **new_mimes)
71 {
72 gchar **mimes = (gchar **) new_mimes;
73
74 if (!new_mimes)
75 {
76 gchar *empty[] = {NULL};
77 mimes = g_strdupv (empty);
78 }
79
80 g_signal_emit_by_name (application->priv->dbus_iface, "supported-mime-types-changed", mimes);
81
82 if (!new_mimes)
83 {
84 g_strfreev (mimes);
85 mimes = NULL;
86 }
87
88 if (application->priv->mimes)
89 g_strfreev (application->priv->mimes);
90
91 application->priv->mimes = mimes;
92 }
93
94 static gboolean
95 bamf_application_default_get_close_when_empty (BamfApplication *application)
96 {
97 return TRUE;
98 }
99
100 static gchar **
101 bamf_application_default_get_supported_mime_types (BamfApplication *application)
102 {
103 const char *desktop_file = bamf_application_get_desktop_file (application);
104
{CovLStrv2{{t{Condition {0}, taking false branch}{"!desktop_file"}}}}
105 if (!desktop_file)
106 return NULL;
107
108 GKeyFile* key_file = g_key_file_new ();
109 GError *error = NULL;
110
CID 12652 - CHECKED_RETURN
{CovLStrv2{{t{Calling function {0} without checking return value (as is done elsewhere {1} out of {2} times).}{"g_key_file_load_from_file(GKeyFile *, gchar const *, GKeyFileFlags, GError **)"}{4}{5}}}}
{CovLStrv2{{t{No check of the return value of {0}.}{"g_key_file_load_from_file(key_file, desktop_file, G_KEY_FILE_NONE, &error)"}}}}
111 g_key_file_load_from_file (key_file, desktop_file, (GKeyFileFlags) 0, &error);
112
113 if (error)
114 {
115 g_key_file_free (key_file);
116 g_error_free (error);
117 return NULL;
118 }
119
120 char** mimes = g_key_file_get_string_list (key_file, "Desktop Entry", "MimeType", NULL, NULL);
121 g_signal_emit (application, application_signals[SUPPORTED_MIMES_CHANGED], 0, mimes);
122
123 g_key_file_free (key_file);
124
125 return mimes;
126 }
127
128 char **
129 bamf_application_get_supported_mime_types (BamfApplication *application)
130 {
131 g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
132
133 if (application->priv->mimes)
134 return g_strdupv (application->priv->mimes);
135
136 gchar **mimes = BAMF_APPLICATION_GET_CLASS (application)->get_supported_mime_types (application);
137 application->priv->mimes = mimes;
138
139 return g_strdupv (mimes);
140 }
141
142 char *
143 bamf_application_get_application_type (BamfApplication *application)
144 {
145 g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
146
147 return g_strdup (application->priv->app_type);
148 }
149
150 void
151 bamf_application_set_application_type (BamfApplication *application, const gchar *type)
152 {
153 g_return_if_fail (BAMF_IS_APPLICATION (application));
154
155 if (application->priv->app_type)
156 g_free (application->priv->app_type);
157
158 application->priv->app_type = g_strdup (type);
159 }
160
161 const char *
162 bamf_application_get_desktop_file (BamfApplication *application)
163 {
164 BamfApplicationPrivate *priv;
165
166 g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
167 priv = application->priv;
168
169 return priv->desktop_file;
170 }
171
172 const char *
173 bamf_application_get_wmclass (BamfApplication *application)
174 {
175 BamfApplicationPrivate *priv;
176
177 g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
178 priv = application->priv;
179
180 return priv->wmclass;
181 }
182
183 static gboolean
184 icon_name_is_valid (char *name)
185 {
186 GtkIconTheme *icon_theme;
187
188 if (name == NULL)
189 return FALSE;
190
191 icon_theme = gtk_icon_theme_get_default ();
192 return gtk_icon_theme_has_icon (icon_theme, name);
193 }
194
195 static void
196 bamf_application_setup_icon_and_name (BamfApplication *self)
197 {
198 BamfView *view;
199 BamfWindow *window = NULL;
200 GDesktopAppInfo *desktop;
201 GKeyFile * keyfile;
202 GIcon *gicon;
203 GList *children, *l;
204 const char *class;
205 char *icon = NULL, *name = NULL;
206 GError *error;
207
208 g_return_if_fail (BAMF_IS_APPLICATION (self));
209
210 if (self->priv->icon && bamf_view_get_name (BAMF_VIEW (self)))
211 return;
212
213 if (self->priv->desktop_file)
214 {
215 keyfile = g_key_file_new();
216
CID 12652 - CHECKED_RETURN
{CovLStrv2{{t{Example3: }}{t{{0} has its value checked in {1}.}{"g_key_file_load_from_file(keyfile, self->priv->desktop_file, G_KEY_FILE_NONE, NULL)"}{"g_key_file_load_from_file(keyfile, self->priv->desktop_file, G_KEY_FILE_NONE, NULL)"}}}}
217 if (!g_key_file_load_from_file(keyfile, self->priv->desktop_file, G_KEY_FILE_NONE, NULL))
218 {
219 g_key_file_free(keyfile);
220 return;
221 }
222
223 desktop = g_desktop_app_info_new_from_keyfile (keyfile);
224
225 if (!G_IS_APP_INFO (desktop))
226 {
227 g_key_file_free(keyfile);
228 return;
229 }
230
231 gicon = g_app_info_get_icon (G_APP_INFO (desktop));
232
233 name = g_strdup (g_app_info_get_display_name (G_APP_INFO (desktop)));
234
235 if (gicon)
236 {
237 icon = g_icon_to_string (gicon);
238 }
239 else
240 {
241 icon = g_strdup ("application-default-icon");
242 }
243
244 if (g_key_file_has_key(keyfile, G_KEY_FILE_DESKTOP_GROUP, STUB_KEY, NULL))
245 {
246 /* This will error to return false, which is okay as it seems
247 unlikely anyone will want to set this flag except to turn
248 off the stub menus. */
249 self->priv->show_stubs = g_key_file_get_boolean (keyfile,
250 G_KEY_FILE_DESKTOP_GROUP,
251 STUB_KEY, NULL);
252 }
253
254 if (g_key_file_has_key (keyfile, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-FullName", NULL))
255 {
256 /* Grab the better name if its available */
257 gchar *fullname = NULL;
258 error = NULL;
259 fullname = g_key_file_get_locale_string (keyfile,
260 G_KEY_FILE_DESKTOP_GROUP,
261 "X-GNOME-FullName", NULL,
262 &error);
263 if (error != NULL)
264 {
265 g_error_free (error);
266 if (fullname)
267 g_free (fullname);
268 }
269 else
270 {
271 g_free (name);
272 name = fullname;
273 }
274 }
275
276 g_object_unref (desktop);
277 g_key_file_free(keyfile);
278 }
279 else if ((children = bamf_view_get_children (BAMF_VIEW (self))) != NULL)
280 {
281 for (l = children; l && !icon; l = l->next)
282 {
283 view = l->data;
284 if (!BAMF_IS_WINDOW (view))
285 continue;
286
287 window = BAMF_WINDOW (view);
288
289 do
290 {
291 class = bamf_legacy_window_get_class_name (bamf_window_get_window (window));
292
293 if (class == NULL)
294 break;
295
296 icon = g_utf8_strdown (class, -1);
297
298 if (icon_name_is_valid (icon))
299 break;
300
301 g_free (icon);
302 icon = bamf_legacy_window_get_exec_string (bamf_window_get_window (window));
303
304 if (icon_name_is_valid (icon))
305 break;
306
307 g_free (icon);
308 icon = NULL;
309 }
310 while (FALSE);
311
312 name = g_strdup (bamf_legacy_window_get_name (bamf_window_get_window (window)));
313 }
314
315 if (!icon)
316 {
317 if (window)
318 {
319 icon = g_strdup (bamf_legacy_window_save_mini_icon (bamf_window_get_window (window)));
320 }
321
322 if (!icon)
323 {
324 icon = g_strdup ("application-default-icon");
325 }
326 }
327 }
328 else
329 {
330 /* we do nothing as we have nothing to go on */
331 }
332
333 if (icon)
334 {
335 if (self->priv->icon)
336 g_free (self->priv->icon);
337
338 self->priv->icon = icon;
339 }
340
341 if (name)
342 bamf_view_set_name (BAMF_VIEW (self), name);
343
344 g_free (name);
345 }
346
347 void
348 bamf_application_set_desktop_file (BamfApplication *application,
349 const char * desktop_file)
350 {
351 g_return_if_fail (BAMF_IS_APPLICATION (application));
352
353 if (application->priv->desktop_file)
354 g_free (application->priv->desktop_file);
355
356 if (desktop_file && desktop_file[0] != '\0')
357 application->priv->desktop_file = g_strdup (desktop_file);
358 else
359 application->priv->desktop_file = NULL;
360
361 bamf_application_setup_icon_and_name (application);
362 }
363
364 gboolean
365 bamf_application_set_desktop_file_from_id (BamfApplication *application,
366 const char *desktop_id)
367 {
368 GDesktopAppInfo *info;
369 const char *filename;
370
371 info = g_desktop_app_info_new (desktop_id);
372
373 if (info == NULL)
374 {
375 g_warning ("Failed to load desktop file from desktop ID: %s", desktop_id);
376 return FALSE;
377 }
378 filename = g_desktop_app_info_get_filename (info);
379 bamf_application_set_desktop_file (application, filename);
380
381 g_object_unref (G_OBJECT (info));
382
383 return TRUE;
384 }
385
386 void
387 bamf_application_set_wmclass (BamfApplication *application,
388 const char *wmclass)
389 {
390 g_return_if_fail (BAMF_IS_APPLICATION (application));
391
392 if (application->priv->wmclass)
393 g_free (application->priv->wmclass);
394
395 if (wmclass && wmclass[0] != '\0')
396 application->priv->wmclass = g_strdup (wmclass);
397 else
398 application->priv->wmclass = NULL;
399 }
400
401 GVariant *
402 bamf_application_get_xids (BamfApplication *application)
403 {
404 GList *l;
405 GVariantBuilder b;
406 BamfView *view;
407 guint32 xid;
408
409 g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
410
411 g_variant_builder_init (&b, G_VARIANT_TYPE ("(au)"));
412 g_variant_builder_open (&b, G_VARIANT_TYPE ("au"));
413
414 for (l = bamf_view_get_children (BAMF_VIEW (application)); l; l = l->next)
415 {
416 view = l->data;
417
418 if (BAMF_IS_WINDOW (view))
419 xid = bamf_window_get_xid (BAMF_WINDOW (view));
420 else if (BAMF_IS_TAB (view))
421 xid = bamf_tab_get_xid (BAMF_TAB (view));
422 else
423 continue;
424 g_variant_builder_add (&b, "u", xid);
425 }
426
427 g_variant_builder_close (&b);
428
429 return g_variant_builder_end (&b);
430 }
431
432 gboolean
433 bamf_application_contains_similar_to_window (BamfApplication *self,
434 BamfWindow *bamf_window)
435 {
436 GList *children, *l;
437 BamfView *child;
438
439 g_return_val_if_fail (BAMF_IS_APPLICATION (self), FALSE);
440 g_return_val_if_fail (BAMF_IS_WINDOW (bamf_window), FALSE);
441
442 BamfLegacyWindow *window = bamf_window_get_window (bamf_window);
443 const char *window_class = bamf_legacy_window_get_class_name (window);
444 const char *instance_name = bamf_legacy_window_get_class_instance_name (window);
445
446 if (!window_class && !instance_name)
447 return FALSE;
448
449 children = bamf_view_get_children (BAMF_VIEW (self));
450 for (l = children; l; l = l->next)
451 {
452 child = l->data;
453
454 if (!BAMF_IS_WINDOW (child))
455 continue;
456
457 window = bamf_window_get_window (BAMF_WINDOW (child));
458 const char *owned_win_class = bamf_legacy_window_get_class_name (window);
459 const char *owned_instance = bamf_legacy_window_get_class_instance_name (window);
460
461 if (g_strcmp0 (window_class, owned_win_class) == 0 &&
462 g_strcmp0 (instance_name, owned_instance) == 0)
463 {
464 return TRUE;
465 }
466 }
467
468 return FALSE;
469 }
470
471 gboolean
472 bamf_application_manages_xid (BamfApplication *application,
473 guint32 xid)
474 {
475 GList *l;
476 gboolean result = FALSE;
477
478 g_return_val_if_fail (BAMF_IS_APPLICATION (application), FALSE);
479
480 for (l = bamf_view_get_children (BAMF_VIEW (application)); l; l = l->next)
481 {
482 BamfView *view = l->data;
483
484 if (!BAMF_IS_WINDOW (view))
485 continue;
486
487 if (bamf_window_get_xid (BAMF_WINDOW (view)) == xid)
488 {
489 result = TRUE;
490 break;
491 }
492 }
493
494 return result;
495 }
496
497 static const char *
498 bamf_application_get_view_type (BamfView *view)
499 {
500 return "application";
501 }
502
503 static char *
504 bamf_application_get_stable_bus_name (BamfView *view)
505 {
506 BamfApplication *self;
507 GList *children, *l;
508 BamfView *child;
509
510 g_return_val_if_fail (BAMF_IS_APPLICATION (view), NULL);
511 self = BAMF_APPLICATION (view);
512
513 if (self->priv->desktop_file)
514 return g_strdup_printf ("application%i", abs (g_str_hash (self->priv->desktop_file)));
515
516 children = bamf_view_get_children (BAMF_VIEW (self));
517 for (l = children; l; l = l->next)
518 {
519 child = l->data;
520
521 if (!BAMF_IS_WINDOW (child))
522 continue;
523
524 return g_strdup_printf ("application%s",
525 bamf_legacy_window_get_class_name (bamf_window_get_window (BAMF_WINDOW (child))));
526 }
527
528 return g_strdup_printf ("application%p", view);
529 }
530
531 static void
532 bamf_application_ensure_flags (BamfApplication *self)
533 {
534 gboolean urgent = FALSE, visible = FALSE, running = FALSE, active = FALSE, close_when_empty;
535 GList *l;
536 BamfView *view;
537
538 for (l = bamf_view_get_children (BAMF_VIEW (self)); l; l = l->next)
539 {
540 view = l->data;
541
542 if (!BAMF_IS_VIEW (view))
543 continue;
544
545 running = TRUE;
546
547 if (!BAMF_IS_WINDOW (view) && !BAMF_IS_TAB (view))
548 continue;
549
550 if (bamf_view_is_urgent (view))
551 urgent = TRUE;
552 if (bamf_view_user_visible (view))
553 visible = TRUE;
554 if (bamf_view_is_active (view))
555 active = TRUE;
556
557 if (urgent && visible && active)
558 break;
559 }
560
561 close_when_empty = bamf_application_get_close_when_empty (self);
562 bamf_view_set_urgent (BAMF_VIEW (self), urgent);
563 bamf_view_set_user_visible (BAMF_VIEW (self), (visible || !close_when_empty));
564 bamf_view_set_running (BAMF_VIEW (self), (running || !close_when_empty));
565 bamf_view_set_active (BAMF_VIEW (self), active);
566 }
567
568 static void
569 view_active_changed (BamfView *view, gboolean active, BamfApplication *self)
570 {
571 bamf_application_ensure_flags (self);
572 }
573
574 static void
575 view_urgent_changed (BamfView *view, gboolean urgent, BamfApplication *self)
576 {
577 bamf_application_ensure_flags (self);
578 }
579
580 static void
581 view_visible_changed (BamfView *view, gboolean visible, BamfApplication *self)
582 {
583 bamf_application_ensure_flags (self);
584 }
585
586 static void
587 view_xid_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
588 {
589 BamfApplication *self;
590
591 self = (BamfApplication *)user_data;
592 bamf_application_ensure_flags (self);
593 }
594
595 static void
596 view_exported (BamfView *view, BamfApplication *self)
597 {
598 g_signal_emit_by_name (self, "window-added", bamf_view_get_path (view));
599 }
600
601 static void
602 bamf_application_child_added (BamfView *view, BamfView *child)
603 {
604 BamfApplication *application;
605
606 application = BAMF_APPLICATION (view);
607
608 if (BAMF_IS_WINDOW (child))
609 {
610 if (bamf_view_is_on_bus (child))
611 g_signal_emit_by_name (BAMF_APPLICATION (view), "window-added", bamf_view_get_path (child));
612 else
613 g_signal_connect (G_OBJECT (child), "exported",
614 (GCallback) view_exported, view);
615 }
616
617 g_signal_connect (G_OBJECT (child), "active-changed",
618 (GCallback) view_active_changed, view);
619 g_signal_connect (G_OBJECT (child), "urgent-changed",
620 (GCallback) view_urgent_changed, view);
621 g_signal_connect (G_OBJECT (child), "user-visible-changed",
622 (GCallback) view_visible_changed, view);
623
624 if (BAMF_IS_TAB (child))
625 {
626 g_signal_connect (G_OBJECT (child), "notify::xid",
627 (GCallback) view_xid_changed, view);
628 }
629
630 bamf_application_ensure_flags (BAMF_APPLICATION (view));
631
632 bamf_application_setup_icon_and_name (application);
633 }
634
635 static char *
636 bamf_application_favorite_from_list (BamfApplication *self, GList *desktop_list)
637 {
638 BamfMatcher *matcher;
639 GList *favs, *l;
640 char *result = NULL;
641 const char *desktop_class;
642
643 g_return_val_if_fail (BAMF_IS_APPLICATION (self), NULL);
644
645 matcher = bamf_matcher_get_default ();
646 favs = bamf_matcher_get_favorites (matcher);
647
648 if (favs)
649 {
650 for (l = favs; l; l = l->next)
651 {
652 if (g_list_find_custom (desktop_list, l->data, (GCompareFunc) g_strcmp0))
653 {
654 desktop_class = bamf_matcher_get_desktop_file_class (matcher, l->data);
655
656 if (!desktop_class || g_strcmp0 (self->priv->wmclass, desktop_class) == 0)
657 {
658 result = l->data;
659 break;
660 }
661 }
662 }
663 }
664
665 return result;
666 }
667
668 static void
669 bamf_application_set_desktop_file_from_list (BamfApplication *self, GList *list)
670 {
671 BamfApplicationPrivate *priv;
672 GList *l;
673 char *desktop_file;
674
675 g_return_if_fail (BAMF_IS_APPLICATION (self));
676 g_return_if_fail (list);
677
678 priv = self->priv;
679
680 if (priv->desktop_file_list)
681 {
682 g_list_free_full (priv->desktop_file_list, g_free);
683 priv->desktop_file_list = NULL;
684 }
685
686 for (l = list; l; l = l->next)
687 priv->desktop_file_list = g_list_prepend (priv->desktop_file_list, g_strdup (l->data));
688
689 priv->desktop_file_list = g_list_reverse (priv->desktop_file_list);
690
691 desktop_file = bamf_application_favorite_from_list (self, priv->desktop_file_list);
692
693 /* items, after reversing them, are in priority order */
694 if (!desktop_file)
695 desktop_file = list->data;
696
697 bamf_application_set_desktop_file (self, desktop_file);
698 }
699
700 static void
701 bamf_application_child_removed (BamfView *view, BamfView *child)
702 {
703 BamfApplication *self = BAMF_APPLICATION (view);
704 if (BAMF_IS_WINDOW (child))
705 {
706 if (bamf_view_is_on_bus (child))
707 g_signal_emit_by_name (BAMF_APPLICATION (view), "window-removed",
708 bamf_view_get_path (child));
709 }
710
711 g_signal_handlers_disconnect_by_func (G_OBJECT (child), view_active_changed, view);
712 g_signal_handlers_disconnect_by_func (G_OBJECT (child), view_urgent_changed, view);
713 g_signal_handlers_disconnect_by_func (G_OBJECT (child), view_visible_changed, view);
714
715 bamf_application_ensure_flags (self);
716
717 if (!bamf_view_get_children (view) && bamf_application_get_close_when_empty (self))
718 {
719 bamf_view_close (view);
720 }
721 }
722
723 static void
724 matcher_favorites_changed (BamfMatcher *matcher, BamfApplication *self)
725 {
726 char *new_desktop_file = NULL;
727
728 g_return_if_fail (BAMF_IS_APPLICATION (self));
729 g_return_if_fail (BAMF_IS_MATCHER (matcher));
730
731 new_desktop_file = bamf_application_favorite_from_list (self, self->priv->desktop_file_list);
732
733 if (new_desktop_file)
734 {
735 bamf_application_set_desktop_file (self, new_desktop_file);
736 }
737 }
738
739 static void
740 on_window_added (BamfApplication *self, const gchar *win_path, gpointer _not_used)
741 {
742 g_return_if_fail (BAMF_IS_APPLICATION (self));
743 g_signal_emit_by_name (self->priv->dbus_iface, "window-added", win_path);
744 }
745
746 static void
747 on_window_removed (BamfApplication *self, const gchar *win_path, gpointer _not_used)
748 {
749 g_return_if_fail (BAMF_IS_APPLICATION (self));
750 g_signal_emit_by_name (self->priv->dbus_iface, "window-removed", win_path);
751 }
752
753 static gboolean
754 on_dbus_handle_show_stubs (BamfDBusItemApplication *interface,
755 GDBusMethodInvocation *invocation,
756 BamfApplication *self)
757 {
758 gboolean show_stubs = bamf_application_get_show_stubs (self);
759 g_dbus_method_invocation_return_value (invocation,
760 g_variant_new ("(b)", show_stubs));
761
762 return TRUE;
763 }
764
765 static gboolean
766 on_dbus_handle_xids (BamfDBusItemApplication *interface,
767 GDBusMethodInvocation *invocation,
768 BamfApplication *self)
769 {
770 GVariant *xids = bamf_application_get_xids (self);
771 g_dbus_method_invocation_return_value (invocation, xids);
772
773 return TRUE;
774 }
775
776 static gboolean
777 on_dbus_handle_focusable_child (BamfDBusItemApplication *interface,
778 GDBusMethodInvocation *invocation,
779 BamfApplication *self)
780 {
781 GVariant *out_variant;
782 BamfView *focusable_child;
783
784 out_variant = NULL;
785
786 focusable_child = bamf_application_get_focusable_child (self);
787
788 if (focusable_child == NULL)
789 {
790 out_variant = g_variant_new("(s)", "");
791 }
792 else
793 {
794 const gchar *path;
795
796 path = bamf_view_get_path (BAMF_VIEW (focusable_child));
797
798 out_variant = g_variant_new("(s)", path);
799 }
800
801 g_dbus_method_invocation_return_value (invocation, out_variant);
802
803 return TRUE;
804 }
805
806 static gboolean
807 on_dbus_handle_desktop_file (BamfDBusItemApplication *interface,
808 GDBusMethodInvocation *invocation,
809 BamfApplication *self)
810 {
811 const char *desktop_file = self->priv->desktop_file ? self->priv->desktop_file : "";
812 g_dbus_method_invocation_return_value (invocation,
813 g_variant_new ("(s)", desktop_file));
814
815 return TRUE;
816 }
817
818 static gboolean
819 on_dbus_handle_supported_mime_types (BamfDBusItemApplication *interface,
820 GDBusMethodInvocation *invocation,
821 BamfApplication *self)
822 {
823 GVariant *list;
824 GVariant *value;
825
826 gchar **mimes = bamf_application_get_supported_mime_types (self);
827
828 if (mimes)
829 {
830 list = g_variant_new_strv ((const gchar**) mimes, -1);
831 g_strfreev (mimes);
832 }
833 else
834 {
835 list = g_variant_new_strv (NULL, 0);
836 }
837
838 value = g_variant_new ("(@as)", list);
839 g_dbus_method_invocation_return_value (invocation, value);
840
841 return TRUE;
842 }
843
844 static gboolean
845 on_dbus_handle_application_menu (BamfDBusItemApplication *interface,
846 GDBusMethodInvocation *invocation,
847 BamfApplication *self)
848 {
849 gchar *name, *path;
850
851 bamf_application_get_application_menu (self, &name, &path);
852
853 name = name ? name : "";
854 path = path ? path : "";
855
856 g_dbus_method_invocation_return_value (invocation,
857 g_variant_new ("(ss)", name, path));
858
859 return TRUE;
860 }
861
862 static gboolean
863 on_dbus_handle_application_type (BamfDBusItemApplication *interface,
864 GDBusMethodInvocation *invocation,
865 BamfApplication *self)
866 {
867 const char *type = self->priv->app_type ? self->priv->app_type : "";
868 g_dbus_method_invocation_return_value (invocation,
869 g_variant_new ("(s)", type));
870
871 return TRUE;
872 }
873
874 static void
875 bamf_application_dispose (GObject *object)
876 {
877 BamfApplication *app;
878 BamfApplicationPrivate *priv;
879
880 app = BAMF_APPLICATION (object);
881 priv = app->priv;
882
883 if (priv->desktop_file)
884 {
885 g_free (priv->desktop_file);
886 priv->desktop_file = NULL;
887 }
888
889 if (priv->desktop_file_list)
890 {
891 g_list_free_full (priv->desktop_file_list, g_free);
892 priv->desktop_file_list = NULL;
893 }
894
895 if (priv->app_type)
896 {
897 g_free (priv->app_type);
898 priv->app_type = NULL;
899 }
900
901 if (priv->icon)
902 {
903 g_free (priv->icon);
904 priv->icon = NULL;
905 }
906
907 if (priv->wmclass)
908 {
909 g_free (priv->wmclass);
910 priv->wmclass = NULL;
911 }
912
913 g_strfreev (priv->mimes);
914 priv->mimes = NULL;
915
916 g_signal_handlers_disconnect_by_func (G_OBJECT (bamf_matcher_get_default ()),
917 matcher_favorites_changed, object);
918
919 G_OBJECT_CLASS (bamf_application_parent_class)->dispose (object);
920 }
921
922 static void
923 bamf_application_finalize (GObject *object)
924 {
925 BamfApplication *self;
926 self = BAMF_APPLICATION (object);
927
928 g_object_unref (self->priv->dbus_iface);
929
930 G_OBJECT_CLASS (bamf_application_parent_class)->finalize (object);
931 }
932
933 static void
934 bamf_application_init (BamfApplication * self)
935 {
936 BamfApplicationPrivate *priv;
937 priv = self->priv = BAMF_APPLICATION_GET_PRIVATE (self);
938
939 priv->is_tab_container = FALSE;
940 priv->app_type = g_strdup ("system");
941 priv->show_stubs = TRUE;
942 priv->wmclass = NULL;
943
944 /* Initializing the dbus interface */
945 priv->dbus_iface = bamf_dbus_item_application_skeleton_new ();
946
947 /* We need to connect to the object own signals to redirect them to the dbus
948 * interface */
949 g_signal_connect (self, "window-added", G_CALLBACK (on_window_added), NULL);
950 g_signal_connect (self, "window-removed", G_CALLBACK (on_window_removed), NULL);
951
952 /* Registering signal callbacks to reply to dbus method calls */
953 g_signal_connect (priv->dbus_iface, "handle-show-stubs",
954 G_CALLBACK (on_dbus_handle_show_stubs), self);
955
956 g_signal_connect (priv->dbus_iface, "handle-xids",
957 G_CALLBACK (on_dbus_handle_xids), self);
958
959 g_signal_connect (priv->dbus_iface, "handle-focusable-child",
960 G_CALLBACK (on_dbus_handle_focusable_child), self);
961
962 g_signal_connect (priv->dbus_iface, "handle-desktop-file",
963 G_CALLBACK (on_dbus_handle_desktop_file), self);
964
965 g_signal_connect (priv->dbus_iface, "handle-supported-mime-types",
966 G_CALLBACK (on_dbus_handle_supported_mime_types), self);
967
968 g_signal_connect (priv->dbus_iface, "handle-application-menu",
969 G_CALLBACK (on_dbus_handle_application_menu), self);
970
971 g_signal_connect (priv->dbus_iface, "handle-application-type",
972 G_CALLBACK (on_dbus_handle_application_type), self);
973
974 /* Setting the interface for the dbus object */
975 bamf_dbus_item_object_skeleton_set_application (BAMF_DBUS_ITEM_OBJECT_SKELETON (self),
976 priv->dbus_iface);
977
978 g_signal_connect (G_OBJECT (bamf_matcher_get_default ()), "favorites-changed",
979 (GCallback) matcher_favorites_changed, self);
980 }
981
982 static void
983 bamf_application_dbus_application_iface_init (BamfDBusItemApplicationIface *iface)
984 {
985 }
986
987 static void
988 bamf_application_class_init (BamfApplicationClass * klass)
989 {
990 GObjectClass *object_class = G_OBJECT_CLASS (klass);
991 BamfViewClass *view_class = BAMF_VIEW_CLASS (klass);
992
993 object_class->dispose = bamf_application_dispose;
994 object_class->finalize = bamf_application_finalize;
995
996 view_class->view_type = bamf_application_get_view_type;
997 view_class->child_added = bamf_application_child_added;
998 view_class->child_removed = bamf_application_child_removed;
999 view_class->get_icon = bamf_application_get_icon;
1000 view_class->stable_bus_name = bamf_application_get_stable_bus_name;
1001
1002 klass->get_supported_mime_types = bamf_application_default_get_supported_mime_types;
1003 klass->get_close_when_empty = bamf_application_default_get_close_when_empty;
1004 klass->supported_mimes_changed = bamf_application_supported_mime_types_changed;
1005
1006 g_type_class_add_private (klass, sizeof (BamfApplicationPrivate));
1007
1008 application_signals[SUPPORTED_MIMES_CHANGED] =
1009 g_signal_new ("supported-mimes-changed",
1010 G_OBJECT_CLASS_TYPE (klass),
1011 G_SIGNAL_RUN_FIRST,
1012 G_STRUCT_OFFSET (BamfApplicationClass, supported_mimes_changed),
1013 NULL, NULL,
1014 g_cclosure_marshal_generic,
1015 G_TYPE_NONE, 1,
1016 G_TYPE_STRV);
1017 }
1018
1019 BamfApplication *
1020 bamf_application_new (void)
1021 {
1022 BamfApplication *application;
1023 application = (BamfApplication *) g_object_new (BAMF_TYPE_APPLICATION, NULL);
1024
1025 return application;
1026 }
1027
1028 BamfApplication *
1029 bamf_application_new_from_desktop_file (const char * desktop_file)
1030 {
1031 BamfApplication *application;
1032 application = (BamfApplication *) g_object_new (BAMF_TYPE_APPLICATION, NULL);
1033
1034 bamf_application_set_desktop_file (application, desktop_file);
1035
1036 return application;
1037 }
1038
1039 BamfApplication *
1040 bamf_application_new_from_desktop_files (GList *desktop_files)
1041 {
1042 BamfApplication *application;
1043 application = (BamfApplication *) g_object_new (BAMF_TYPE_APPLICATION, NULL);
1044
1045 bamf_application_set_desktop_file_from_list (application, desktop_files);
1046
1047 return application;
1048 }
1049
1050 BamfApplication *
1051 bamf_application_new_with_wmclass (const char *wmclass)
1052 {
1053 BamfApplication *application;
1054 application = (BamfApplication *) g_object_new (BAMF_TYPE_APPLICATION, NULL);
1055
1056 bamf_application_set_wmclass (application, wmclass);
1057
1058 return application;
1059 }
1060
1061 /**
1062 bamf_application_get_show_stubs:
1063 @application: Application to check for menu stubs
1064
1065 Checks to see if the application should show menu stubs or not.
1066 This is specified with the "X-Ayatana-Appmenu-Show-Stubs" desktop
1067 file key.
1068
1069 Return Value: Defaults to TRUE, else FALSE if specified in
1070 .desktop file.
1071 */
1072 gboolean
1073 bamf_application_get_show_stubs (BamfApplication *application)
1074 {
1075 g_return_val_if_fail(BAMF_IS_APPLICATION(application), TRUE);
1076 return application->priv->show_stubs;
1077 }
1078
1079
1080 gboolean
1081 bamf_application_get_close_when_empty (BamfApplication *application)
1082 {
1083 g_return_val_if_fail (BAMF_IS_APPLICATION(application), FALSE);
1084
1085 if (BAMF_APPLICATION_GET_CLASS (application)->get_close_when_empty)
1086 {
1087 return BAMF_APPLICATION_GET_CLASS (application)->get_close_when_empty(application);
1088 }
1089 return TRUE;
1090 }
1091
1092 void
1093 bamf_application_get_application_menu (BamfApplication *application, gchar **name, gchar **object_path)
1094 {
1095 g_return_if_fail (BAMF_IS_APPLICATION (application));
1096
1097 if (BAMF_APPLICATION_GET_CLASS (application)->get_application_menu)
1098 {
1099 BAMF_APPLICATION_GET_CLASS (application)->get_application_menu (application, name, object_path);
1100 }
1101 else
1102 {
1103 *name = NULL;
1104 *object_path = NULL;
1105 }
1106 }
1107
1108 BamfView *
1109 bamf_application_get_focusable_child (BamfApplication *application)
1110 {
1111 g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
1112
1113 if (BAMF_APPLICATION_GET_CLASS (application)->get_focusable_child)
1114 {
1115 return BAMF_APPLICATION_GET_CLASS (application)->get_focusable_child (application);
1116 }
1117
1118 return NULL;
1119 }