/*
* Copyright (C) 2010-2011 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Authored by: Jason Smith
* Marco Trevisan (TreviƱo) <3v1n0@ubuntu.com>
*
*/
#include "bamf-application.h"
#include "bamf-window.h"
#include "bamf-matcher.h"
#include "bamf-legacy-window.h"
#include "bamf-legacy-screen.h"
#include "bamf-tab.h"
#include
#include
#define BAMF_APPLICATION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE(obj, \
BAMF_TYPE_APPLICATION, BamfApplicationPrivate))
static void bamf_application_dbus_application_iface_init (BamfDBusItemApplicationIface *iface);
G_DEFINE_TYPE_WITH_CODE (BamfApplication, bamf_application, BAMF_TYPE_VIEW,
G_IMPLEMENT_INTERFACE (BAMF_DBUS_ITEM_TYPE_APPLICATION,
bamf_application_dbus_application_iface_init));
struct _BamfApplicationPrivate
{
BamfDBusItemApplication *dbus_iface;
BamfApplicationType app_type;
BamfView * main_child;
GCancellable * cancellable;
char * desktop_file;
GList * desktop_file_list;
char * wmclass;
char ** mimes;
gboolean show_stubs;
};
enum
{
SUPPORTED_MIMES_CHANGED,
LAST_SIGNAL
};
static guint application_signals[LAST_SIGNAL] = { 0 };
#define STUB_KEY "X-Ayatana-Appmenu-Show-Stubs"
static void on_main_child_name_changed (BamfView *, const gchar *, const gchar *, BamfApplication *);
void
bamf_application_supported_mime_types_changed (BamfApplication *application,
const gchar **new_mimes)
{
gchar **mimes = (gchar **) new_mimes;
if (!new_mimes)
{
gchar *empty[] = {NULL};
mimes = g_strdupv (empty);
}
g_signal_emit_by_name (application->priv->dbus_iface, "supported-mime-types-changed", mimes);
if (!new_mimes)
{
g_strfreev (mimes);
mimes = NULL;
}
if (application->priv->mimes)
g_strfreev (application->priv->mimes);
application->priv->mimes = mimes;
}
static gboolean
bamf_application_default_get_close_when_empty (BamfApplication *application)
{
return TRUE;
}
static gchar **
bamf_application_default_get_supported_mime_types (BamfApplication *application)
{
const char *desktop_file;
char** mimes;
desktop_file = bamf_application_get_desktop_file (application);
if (!desktop_file)
return NULL;
GKeyFile* key_file = g_key_file_new ();
if (!g_key_file_load_from_file (key_file, desktop_file, G_KEY_FILE_NONE, NULL))
{
g_key_file_free (key_file);
return NULL;
}
mimes = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, NULL, NULL);
g_signal_emit (application, application_signals[SUPPORTED_MIMES_CHANGED], 0, mimes);
g_key_file_free (key_file);
return mimes;
}
char **
bamf_application_get_supported_mime_types (BamfApplication *application)
{
gchar **mimes = NULL;
g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
if (application->priv->mimes)
return g_strdupv (application->priv->mimes);
if (BAMF_APPLICATION_GET_CLASS (application)->get_supported_mime_types)
mimes = BAMF_APPLICATION_GET_CLASS (application)->get_supported_mime_types (application);
application->priv->mimes = mimes;
return g_strdupv (mimes);
}
BamfApplicationType
bamf_application_get_application_type (BamfApplication *application)
{
g_return_val_if_fail (BAMF_IS_APPLICATION (application), BAMF_APPLICATION_UNKNOWN);
return application->priv->app_type;
}
void
bamf_application_set_application_type (BamfApplication *application, BamfApplicationType type)
{
g_return_if_fail (BAMF_IS_APPLICATION (application));
g_return_if_fail (type >= 0 && type < BAMF_APPLICATION_UNKNOWN);
application->priv->app_type = type;
}
const char *
bamf_application_get_desktop_file (BamfApplication *application)
{
BamfApplicationPrivate *priv;
g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
priv = application->priv;
return priv->desktop_file;
}
const char *
bamf_application_get_wmclass (BamfApplication *application)
{
BamfApplicationPrivate *priv;
g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
priv = application->priv;
return priv->wmclass;
}
static gboolean
icon_name_is_valid (const char *name)
{
GtkIconTheme *icon_theme;
if (!name || name[0] == '\0')
return FALSE;
if (g_file_test (name, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
return TRUE;
icon_theme = gtk_icon_theme_get_default ();
return gtk_icon_theme_has_icon (icon_theme, name);
}
static gboolean
icon_name_is_generic (const char *name)
{
BamfMatcher *matcher = bamf_matcher_get_default ();
return !bamf_matcher_is_valid_process_prefix (matcher, name);
}
static void
bamf_application_setup_icon_and_name (BamfApplication *self, gboolean force)
{
BamfWindow *window;
BamfLegacyWindow *legacy_window;
GDesktopAppInfo *desktop;
GKeyFile * keyfile;
GIcon *gicon;
const char *class;
char *icon = NULL, *generic_icon = NULL, *name = NULL;
GError *error;
g_return_if_fail (BAMF_IS_APPLICATION (self));
if (!force)
{
if (bamf_view_get_icon (BAMF_VIEW (self)) && bamf_view_get_name (BAMF_VIEW (self)))
return;
}
if (self->priv->desktop_file)
{
keyfile = g_key_file_new ();
if (!g_key_file_load_from_file (keyfile, self->priv->desktop_file, G_KEY_FILE_NONE, NULL))
{
g_key_file_free (keyfile);
return;
}
desktop = g_desktop_app_info_new_from_keyfile (keyfile);
if (!G_IS_APP_INFO (desktop))
{
g_key_file_free (keyfile);
return;
}
gicon = g_app_info_get_icon (G_APP_INFO (desktop));
name = g_strdup (g_app_info_get_display_name (G_APP_INFO (desktop)));
if (gicon)
{
icon = g_icon_to_string (gicon);
if (!icon_name_is_valid (icon))
{
g_free (icon);
icon = NULL;
}
}
if (!icon)
{
icon = g_strdup (BAMF_APPLICATION_DEFAULT_ICON);
}
if (g_key_file_has_key (keyfile, G_KEY_FILE_DESKTOP_GROUP, STUB_KEY, NULL))
{
/* This will error to return false, which is okay as it seems
unlikely anyone will want to set this flag except to turn
off the stub menus. */
self->priv->show_stubs = g_key_file_get_boolean (keyfile,
G_KEY_FILE_DESKTOP_GROUP,
STUB_KEY, NULL);
}
if (g_key_file_has_key (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_FULLNAME, NULL))
{
/* Grab the better name if its available */
gchar *fullname = NULL;
error = NULL;
fullname = g_key_file_get_locale_string (keyfile,
G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_FULLNAME,
NULL, &error);
if (error != NULL)
{
g_error_free (error);
g_free (fullname);
}
else
{
g_free (name);
name = fullname;
}
}
g_object_unref (desktop);
g_key_file_free (keyfile);
}
else if (BAMF_IS_WINDOW (self->priv->main_child))
{
name = g_strdup (bamf_view_get_name (self->priv->main_child));
window = BAMF_WINDOW (self->priv->main_child);
legacy_window = bamf_window_get_window (window);
class = bamf_legacy_window_get_class_name (legacy_window);
if (class)
{
icon = g_utf8_strdown (class, -1);
if (icon_name_is_valid (icon))
{
if (icon_name_is_generic (icon))
{
generic_icon = icon;
icon = NULL;
}
}
else
{
g_free (icon);
icon = NULL;
}
}
if (!icon)
{
const char *exec = bamf_legacy_window_get_exec_string (legacy_window);
icon = bamf_matcher_get_trimmed_exec (bamf_matcher_get_default (), exec);
if (icon_name_is_valid (icon))
{
if (icon_name_is_generic (icon))
{
g_free (generic_icon);
generic_icon = icon;
icon = NULL;
}
}
else
{
g_free (icon);
icon = NULL;
}
}
if (!icon)
{
icon = bamf_legacy_window_save_mini_icon (legacy_window);
if (!icon)
{
if (generic_icon)
{
icon = generic_icon;
generic_icon = NULL;
}
else
{
icon = g_strdup (BAMF_APPLICATION_DEFAULT_ICON);
}
}
}
g_free (generic_icon);
generic_icon = NULL;
}
bamf_view_set_icon (BAMF_VIEW (self), icon);
bamf_view_set_name (BAMF_VIEW (self), name);
g_free (name);
g_free (icon);
}
void
bamf_application_set_desktop_file (BamfApplication *application,
const char * desktop_file)
{
g_return_if_fail (BAMF_IS_APPLICATION (application));
if (g_strcmp0 (application->priv->desktop_file, desktop_file) == 0)
return;
g_free (application->priv->desktop_file);
application->priv->desktop_file = NULL;
if (desktop_file && desktop_file[0] != '\0')
application->priv->desktop_file = g_strdup (desktop_file);
if (application->priv->main_child)
{
g_signal_handlers_disconnect_by_func (application->priv->main_child,
on_main_child_name_changed, application);
}
g_signal_emit_by_name (application, "desktop-file-updated",
application->priv->desktop_file);
bamf_application_setup_icon_and_name (application, TRUE);
}
gboolean
bamf_application_set_desktop_file_from_id (BamfApplication *application,
const char *desktop_id)
{
GDesktopAppInfo *info;
const char *filename;
info = g_desktop_app_info_new (desktop_id);
if (info == NULL)
{
g_warning ("Failed to load desktop file from desktop ID: %s", desktop_id);
return FALSE;
}
filename = g_desktop_app_info_get_filename (info);
bamf_application_set_desktop_file (application, filename);
g_object_unref (G_OBJECT (info));
return TRUE;
}
static GFile *
try_create_subdir (GFile *parent, const gchar *child_name, GCancellable *cancellable)
{
GFile *child;
GError *error = NULL;
child = g_file_get_child (parent, child_name);
g_return_val_if_fail (G_IS_FILE (child), NULL);
g_file_make_directory_with_parents (child, cancellable, &error);
if (error)
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
{
g_error ("Impossible to create `%s` directory: %s", child_name, error->message);
g_clear_object (&child);
}
g_error_free (error);
}
return child;
}
static GFile *
try_create_child (GFile *parent, const gchar *basename, const gchar *extension, GCancellable *cancellable)
{
gchar *down, *child_name;
down = g_ascii_strdown (basename, -1);
g_strdelimit (down, "/\\&%\"'!?`*.;:^|()= <>[]{}", '_');
child_name = g_strconcat (down, extension, NULL);
GFile *child = g_file_get_child (parent, child_name);
g_return_val_if_fail (G_IS_FILE (child), NULL);
if (g_file_query_exists (child, cancellable))
g_clear_object (&child);
g_free (child_name);
g_free (down);
return child;
}
gboolean
try_create_local_desktop_data (GFile *apps_dir, GFile *icons_dir, const char *basename,
GFile **out_desktop_file, GFile **out_icon_file,
GCancellable *cancellable)
{
g_return_val_if_fail (out_desktop_file, NULL);
if (!apps_dir)
{
*out_desktop_file = NULL;
g_warn_if_reached ();
}
*out_desktop_file = try_create_child (apps_dir, basename, ".desktop", cancellable);
if (G_IS_FILE (*out_desktop_file))
{
if (G_IS_FILE (icons_dir) && out_icon_file)
*out_icon_file = try_create_child (icons_dir, basename, ".png", cancellable);
return TRUE;
}
return FALSE;
}
gboolean
bamf_application_create_local_desktop_file (BamfApplication *self)
{
BamfApplicationPrivate *priv;
BamfLegacyWindow *window;
BamfMatcher *matcher;
GKeyFile *key_file;
const gchar *name, *icon, *iclass, *nclass, *class, *exec;
GFile *data_dir, *apps_dir, *icons_dir, *desktop_file, *icon_file, *mini_icon;
GError *error = NULL;
g_return_val_if_fail (BAMF_IS_APPLICATION (self), FALSE);
priv = self->priv;
if (priv->desktop_file || !BAMF_IS_WINDOW (priv->main_child))
{
return FALSE;
}
window = bamf_window_get_window (BAMF_WINDOW (priv->main_child));
exec = bamf_legacy_window_get_exec_string (window);
if (!exec)
{
return FALSE;
}
matcher = bamf_matcher_get_default ();
data_dir = g_file_new_for_path (g_get_user_data_dir ());
name = bamf_view_get_name (BAMF_VIEW (self));
icon = bamf_view_get_icon (BAMF_VIEW (self));
nclass = bamf_legacy_window_get_class_name (window);
iclass = bamf_legacy_window_get_class_instance_name (window);
mini_icon = bamf_legacy_window_get_saved_mini_icon (window);
if (!bamf_matcher_is_valid_class_name (matcher, iclass))
iclass = NULL;
if (!bamf_matcher_is_valid_class_name (matcher, nclass))
nclass = NULL;
apps_dir = try_create_subdir (data_dir, "applications", priv->cancellable);
icons_dir = NULL;
if (!G_IS_FILE (apps_dir))
{
g_object_unref (data_dir);
return FALSE;
}
if (icon && G_IS_FILE (mini_icon))
icons_dir = try_create_subdir (data_dir, "icons", priv->cancellable);
g_clear_object (&data_dir);
desktop_file = NULL;
icon_file = NULL;
class = (nclass) ? nclass : iclass;
if (class)
{
try_create_local_desktop_data (apps_dir, icons_dir, class,
&desktop_file, &icon_file, priv->cancellable);
}
if (!G_IS_FILE (desktop_file))
{
gchar *trimmed_exec = bamf_matcher_get_trimmed_exec (matcher, exec);
try_create_local_desktop_data (apps_dir, icons_dir, trimmed_exec,
&desktop_file, &icon_file, priv->cancellable);
g_free (trimmed_exec);
}
if (!G_IS_FILE (desktop_file))
{
try_create_local_desktop_data (apps_dir, icons_dir, exec,
&desktop_file, &icon_file, priv->cancellable);
}
g_object_unref (apps_dir);
if (!G_IS_FILE (desktop_file))
{
g_critical ("Impossible to find a valid path where to save a .desktop file");
g_clear_object (&icons_dir);
g_clear_object (&icon_file);
return FALSE;
}
if (G_IS_FILE (icons_dir) && !G_IS_FILE (icon_file))
{
gchar *basename = g_file_get_basename (mini_icon);
icon_file = try_create_child (icons_dir, basename+1, ".png", priv->cancellable);
g_free (basename);
}
g_clear_object (&icons_dir);
if (G_IS_FILE (icon_file))
{
if (!g_file_copy (mini_icon, icon_file, G_FILE_COPY_NONE,
priv->cancellable, NULL, NULL, &error))
{
g_warning ("Impossible to copy icon to final destination: %s", error->message);
g_clear_error (&error);
g_clear_object (&icon_file);
}
}
key_file = g_key_file_new ();
g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_TYPE,
G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
if (name)
{
g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_NAME, name);
}
if (icon_file)
{
gchar *basename = g_file_get_basename (icon_file);
g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_ICON, basename);
bamf_view_set_icon (BAMF_VIEW (self), basename);
g_free (basename);
g_clear_object (&icon_file);
}
else if (icon)
{
g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_ICON, icon);
}
g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_EXEC, exec);
// It would be nice to be able to find if enabling this from some win property
g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, FALSE);
if (class)
{
g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS, class);
}
gsize data_length = 0;
gchar *data = g_key_file_to_data (key_file, &data_length, &error);
g_key_file_free (key_file);
if (error)
{
g_critical ("Impossible to generate local desktop file: %s", error->message);
g_clear_error (&error);
g_clear_pointer (&data, g_free);
}
if (data)
{
g_file_replace_contents (desktop_file, data, data_length, NULL, FALSE,
G_FILE_CREATE_NONE, NULL, priv->cancellable, &error);
g_free (data);
if (error)
{
g_critical ("Impossible to create local desktop file: %s", error->message);
g_clear_error (&error);
g_object_unref (desktop_file);
return FALSE;
}
}
gchar *desktop_path = g_file_get_path (desktop_file);
g_object_unref (desktop_file);
bamf_application_set_desktop_file (self, desktop_path);
g_free (desktop_path);
return TRUE;
}
void
bamf_application_set_wmclass (BamfApplication *application,
const char *wmclass)
{
g_return_if_fail (BAMF_IS_APPLICATION (application));
if (application->priv->wmclass)
g_free (application->priv->wmclass);
if (wmclass && wmclass[0] != '\0')
application->priv->wmclass = g_strdup (wmclass);
else
application->priv->wmclass = NULL;
}
GVariant *
bamf_application_get_xids (BamfApplication *application)
{
GList *l;
GVariantBuilder b;
BamfView *view;
guint32 xid;
g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
g_variant_builder_init (&b, G_VARIANT_TYPE ("(au)"));
g_variant_builder_open (&b, G_VARIANT_TYPE ("au"));
for (l = bamf_view_get_children (BAMF_VIEW (application)); l; l = l->next)
{
view = l->data;
if (BAMF_IS_WINDOW (view))
xid = bamf_window_get_xid (BAMF_WINDOW (view));
else if (BAMF_IS_TAB (view))
xid = bamf_tab_get_xid (BAMF_TAB (view));
else
continue;
g_variant_builder_add (&b, "u", xid);
}
g_variant_builder_close (&b);
return g_variant_builder_end (&b);
}
gboolean
bamf_application_contains_similar_to_window (BamfApplication *self,
BamfWindow *bamf_window)
{
GList *children, *l;
BamfView *child;
g_return_val_if_fail (BAMF_IS_APPLICATION (self), FALSE);
g_return_val_if_fail (BAMF_IS_WINDOW (bamf_window), FALSE);
BamfLegacyWindow *window = bamf_window_get_window (bamf_window);
const char *window_class = bamf_legacy_window_get_class_name (window);
const char *instance_name = bamf_legacy_window_get_class_instance_name (window);
children = bamf_view_get_children (BAMF_VIEW (self));
for (l = children; l; l = l->next)
{
child = l->data;
if (!BAMF_IS_WINDOW (child))
continue;
window = bamf_window_get_window (BAMF_WINDOW (child));
const char *owned_win_class = bamf_legacy_window_get_class_name (window);
const char *owned_instance = bamf_legacy_window_get_class_instance_name (window);
if (g_strcmp0 (window_class, owned_win_class) == 0 &&
g_strcmp0 (instance_name, owned_instance) == 0)
{
return TRUE;
}
}
return FALSE;
}
gboolean
bamf_application_manages_xid (BamfApplication *application,
guint32 xid)
{
return (bamf_application_get_window (application, xid) != NULL);
}
BamfWindow *
bamf_application_get_window (BamfApplication *application,
guint32 xid)
{
GList *l;
g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
for (l = bamf_view_get_children (BAMF_VIEW (application)); l; l = l->next)
{
BamfView *view = l->data;
if (!BAMF_IS_WINDOW (view))
continue;
BamfWindow *window = BAMF_WINDOW (view);
if (bamf_window_get_xid (window) == xid)
{
return window;
}
}
return NULL;
}
static const char *
bamf_application_get_view_type (BamfView *view)
{
return "application";
}
static char *
bamf_application_get_stable_bus_name (BamfView *view)
{
BamfApplication *self;
g_return_val_if_fail (BAMF_IS_APPLICATION (view), NULL);
self = BAMF_APPLICATION (view);
if (self->priv->desktop_file)
return g_strdup_printf ("application/%i", abs (g_str_hash (self->priv->desktop_file)));
return g_strdup_printf ("application/%p", view);
}
static void
bamf_application_ensure_flags (BamfApplication *self)
{
gboolean urgent = FALSE, visible = FALSE, running = FALSE, active = FALSE;
GList *l;
BamfView *view;
for (l = bamf_view_get_children (BAMF_VIEW (self)); l; l = l->next)
{
view = l->data;
if (!BAMF_IS_VIEW (view))
continue;
running = TRUE;
if (!BAMF_IS_WINDOW (view) && !BAMF_IS_TAB (view))
continue;
if (bamf_view_is_urgent (view))
urgent = TRUE;
if (bamf_view_is_user_visible (view))
visible = TRUE;
if (bamf_view_is_active (view))
active = TRUE;
if (urgent && visible && active)
break;
}
gboolean close_when_empty = bamf_application_get_close_when_empty (self);
bamf_view_set_urgent (BAMF_VIEW (self), urgent);
bamf_view_set_user_visible (BAMF_VIEW (self), (visible || !close_when_empty));
bamf_view_set_running (BAMF_VIEW (self), (running || !close_when_empty));
bamf_view_set_active (BAMF_VIEW (self), active);
}
static void
view_active_changed (BamfView *view, gboolean active, BamfApplication *self)
{
bamf_application_ensure_flags (self);
}
static void
view_urgent_changed (BamfView *view, gboolean urgent, BamfApplication *self)
{
bamf_application_ensure_flags (self);
}
static void
view_visible_changed (BamfView *view, gboolean visible, BamfApplication *self)
{
bamf_application_ensure_flags (self);
}
static void
view_xid_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
{
BamfApplication *self;
self = (BamfApplication *)user_data;
bamf_application_ensure_flags (self);
}
static void
on_main_child_name_changed (BamfView *child, const gchar *old_name,
const gchar *new_name, BamfApplication *self)
{
bamf_view_set_name (BAMF_VIEW (self), new_name);
}
static void
bamf_application_set_main_child (BamfApplication *self, BamfView *child)
{
if (self->priv->main_child == child)
return;
if (self->priv->main_child)
{
g_object_remove_weak_pointer (G_OBJECT (self->priv->main_child),
(gpointer*) &self->priv->main_child);
g_signal_handlers_disconnect_by_func (self->priv->main_child,
on_main_child_name_changed, self);
}
self->priv->main_child = child;
if (self->priv->main_child)
{
g_object_add_weak_pointer (G_OBJECT (self->priv->main_child),
(gpointer*) &self->priv->main_child);
if (!self->priv->desktop_file)
{
g_signal_connect (child, "name-changed",
G_CALLBACK (on_main_child_name_changed), self);
}
}
}
BamfView *
bamf_application_get_main_child (BamfApplication *self)
{
g_return_val_if_fail (BAMF_IS_APPLICATION (self), NULL);
return self->priv->main_child;
}
static void
view_exported (BamfView *view, BamfApplication *self)
{
g_signal_emit_by_name (self, "window-added", bamf_view_get_path (view));
g_signal_handlers_disconnect_by_func (view, view_exported, self);
}
static void
bamf_application_child_added (BamfView *view, BamfView *child)
{
BamfApplication *application;
BamfWindow *window = NULL;
gboolean reset_emblems = FALSE;
application = BAMF_APPLICATION (view);
if (BAMF_IS_WINDOW (child))
{
window = BAMF_WINDOW (child);
if (bamf_view_is_on_bus (child))
{
g_signal_emit_by_name (BAMF_APPLICATION (view), "window-added",
bamf_view_get_path (child));
}
else
{
g_signal_connect (G_OBJECT (child), "exported",
(GCallback) view_exported, view);
}
}
g_signal_connect (G_OBJECT (child), "active-changed",
(GCallback) view_active_changed, view);
g_signal_connect (G_OBJECT (child), "urgent-changed",
(GCallback) view_urgent_changed, view);
g_signal_connect (G_OBJECT (child), "user-visible-changed",
(GCallback) view_visible_changed, view);
if (BAMF_IS_TAB (child))
{
g_signal_connect (G_OBJECT (child), "notify::xid",
(GCallback) view_xid_changed, view);
}
if (application->priv->main_child)
{
if (window && BAMF_IS_WINDOW (application->priv->main_child))
{
BamfWindow *main_window = BAMF_WINDOW (application->priv->main_child);
if (bamf_window_get_window_type (main_window) != BAMF_WINDOW_NORMAL &&
bamf_window_get_window_type (window) == BAMF_WINDOW_NORMAL)
{
bamf_application_set_main_child (application, child);
}
}
}
else
{
bamf_application_set_main_child (application, child);
}
bamf_application_ensure_flags (BAMF_APPLICATION (view));
if (!application->priv->desktop_file && application->priv->main_child == child)
reset_emblems = TRUE;
bamf_application_setup_icon_and_name (application, reset_emblems);
}
static char *
bamf_application_favorite_from_list (BamfApplication *self, GList *desktop_list)
{
BamfMatcher *matcher;
GList *favs, *l;
char *result = NULL;
const char *desktop_class;
g_return_val_if_fail (BAMF_IS_APPLICATION (self), NULL);
matcher = bamf_matcher_get_default ();
favs = bamf_matcher_get_favorites (matcher);
if (favs)
{
for (l = favs; l; l = l->next)
{
if (g_list_find_custom (desktop_list, l->data, (GCompareFunc) g_strcmp0))
{
desktop_class = bamf_matcher_get_desktop_file_class (matcher, l->data);
if (!desktop_class || g_strcmp0 (self->priv->wmclass, desktop_class) == 0)
{
result = l->data;
break;
}
}
}
}
return result;
}
static void
bamf_application_set_desktop_file_from_list (BamfApplication *self, GList *list)
{
BamfApplicationPrivate *priv;
GList *l;
char *desktop_file;
g_return_if_fail (BAMF_IS_APPLICATION (self));
g_return_if_fail (list);
priv = self->priv;
if (priv->desktop_file_list)
{
g_list_free_full (priv->desktop_file_list, g_free);
priv->desktop_file_list = NULL;
}
for (l = list; l; l = l->next)
priv->desktop_file_list = g_list_prepend (priv->desktop_file_list, g_strdup (l->data));
priv->desktop_file_list = g_list_reverse (priv->desktop_file_list);
desktop_file = bamf_application_favorite_from_list (self, priv->desktop_file_list);
/* items, after reversing them, are in priority order */
if (!desktop_file)
desktop_file = list->data;
bamf_application_set_desktop_file (self, desktop_file);
}
static void
bamf_application_child_removed (BamfView *view, BamfView *child)
{
BamfApplication *self = BAMF_APPLICATION (view);
GList *children, *l;
if (BAMF_IS_WINDOW (child))
{
if (bamf_view_is_on_bus (child))
g_signal_emit_by_name (BAMF_APPLICATION (view), "window-removed",
bamf_view_get_path (child));
}
g_signal_handlers_disconnect_by_data (G_OBJECT (child), view);
bamf_application_ensure_flags (self);
children = bamf_view_get_children (view);
if (self->priv->main_child == child)
{
/* Giving priority to older windows, and BamfView has a reversed list */
children = g_list_last (children);
bamf_application_set_main_child (self, (children ? children->data : NULL));
if (self->priv->app_type == BAMF_APPLICATION_SYSTEM)
{
/* We check if we have a better target in next windows */
for (l = children; l; l = l->prev)
{
if (bamf_window_get_window_type (BAMF_WINDOW (l->data)) == BAMF_WINDOW_NORMAL)
{
bamf_application_set_main_child (self, l->data);
break;
}
}
}
if (self->priv->main_child)
{
gboolean reset_emblems = (!self->priv->desktop_file);
bamf_application_setup_icon_and_name (self, reset_emblems);
}
}
if (!children && bamf_application_get_close_when_empty (self))
{
bamf_view_close (view);
}
}
static void
matcher_favorites_changed (BamfMatcher *matcher, BamfApplication *self)
{
char *new_desktop_file = NULL;
g_return_if_fail (BAMF_IS_APPLICATION (self));
g_return_if_fail (BAMF_IS_MATCHER (matcher));
new_desktop_file = bamf_application_favorite_from_list (self, self->priv->desktop_file_list);
if (new_desktop_file)
{
bamf_application_set_desktop_file (self, new_desktop_file);
}
}
static void
on_window_added (BamfApplication *self, const gchar *win_path, gpointer _not_used)
{
g_return_if_fail (BAMF_IS_APPLICATION (self));
g_signal_emit_by_name (self->priv->dbus_iface, "window-added", win_path);
}
static void
on_window_removed (BamfApplication *self, const gchar *win_path, gpointer _not_used)
{
g_return_if_fail (BAMF_IS_APPLICATION (self));
g_signal_emit_by_name (self->priv->dbus_iface, "window-removed", win_path);
}
static void
on_desktop_file_updated (BamfApplication *self, const gchar *file, gpointer _not_used)
{
g_return_if_fail (BAMF_IS_APPLICATION (self));
g_signal_emit_by_name (self->priv->dbus_iface, "desktop-file-updated", file);
}
static gboolean
on_dbus_handle_show_stubs (BamfDBusItemApplication *interface,
GDBusMethodInvocation *invocation,
BamfApplication *self)
{
gboolean show_stubs = bamf_application_get_show_stubs (self);
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(b)", show_stubs));
return TRUE;
}
static gboolean
on_dbus_handle_xids (BamfDBusItemApplication *interface,
GDBusMethodInvocation *invocation,
BamfApplication *self)
{
GVariant *xids = bamf_application_get_xids (self);
g_dbus_method_invocation_return_value (invocation, xids);
return TRUE;
}
static gboolean
on_dbus_handle_focusable_child (BamfDBusItemApplication *interface,
GDBusMethodInvocation *invocation,
BamfApplication *self)
{
GVariant *out_variant;
BamfView *focusable_child;
out_variant = NULL;
focusable_child = bamf_application_get_focusable_child (self);
if (focusable_child == NULL)
{
out_variant = g_variant_new("(s)", "");
}
else
{
const gchar *path;
path = bamf_view_get_path (BAMF_VIEW (focusable_child));
out_variant = g_variant_new("(s)", path);
}
g_dbus_method_invocation_return_value (invocation, out_variant);
return TRUE;
}
static gboolean
on_dbus_handle_desktop_file (BamfDBusItemApplication *interface,
GDBusMethodInvocation *invocation,
BamfApplication *self)
{
const char *desktop_file = self->priv->desktop_file ? self->priv->desktop_file : "";
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(s)", desktop_file));
return TRUE;
}
static gboolean
on_dbus_handle_supported_mime_types (BamfDBusItemApplication *interface,
GDBusMethodInvocation *invocation,
BamfApplication *self)
{
GVariant *list;
GVariant *value;
gchar **mimes = bamf_application_get_supported_mime_types (self);
if (mimes)
{
list = g_variant_new_strv ((const gchar**) mimes, -1);
g_strfreev (mimes);
}
else
{
list = g_variant_new_strv (NULL, 0);
}
value = g_variant_new ("(@as)", list);
g_dbus_method_invocation_return_value (invocation, value);
return TRUE;
}
static gboolean
on_dbus_handle_application_menu (BamfDBusItemApplication *interface,
GDBusMethodInvocation *invocation,
BamfApplication *self)
{
gchar *name, *path;
bamf_application_get_application_menu (self, &name, &path);
name = name ? name : "";
path = path ? path : "";
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(ss)", name, path));
return TRUE;
}
static gboolean
on_dbus_handle_application_type (BamfDBusItemApplication *interface,
GDBusMethodInvocation *invocation,
BamfApplication *self)
{
const char *type = "";
switch (self->priv->app_type)
{
case BAMF_APPLICATION_SYSTEM:
type = "system";
break;
case BAMF_APPLICATION_WEB:
type = "webapp";
break;
default:
type = "unknown";
}
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", type));
return TRUE;
}
static void
bamf_application_dispose (GObject *object)
{
BamfApplication *app;
BamfApplicationPrivate *priv;
app = BAMF_APPLICATION (object);
priv = app->priv;
if (priv->desktop_file)
{
g_free (priv->desktop_file);
priv->desktop_file = NULL;
}
if (priv->desktop_file_list)
{
g_list_free_full (priv->desktop_file_list, g_free);
priv->desktop_file_list = NULL;
}
if (priv->wmclass)
{
g_free (priv->wmclass);
priv->wmclass = NULL;
}
if (priv->main_child)
{
g_object_remove_weak_pointer (G_OBJECT (priv->main_child),
(gpointer*) &priv->main_child);
g_signal_handlers_disconnect_by_data (priv->main_child, app);
priv->main_child = NULL;
}
if (priv->cancellable)
{
g_cancellable_cancel (priv->cancellable);
g_object_unref (priv->cancellable);
priv->cancellable = NULL;
}
g_strfreev (priv->mimes);
priv->mimes = NULL;
g_signal_handlers_disconnect_by_func (G_OBJECT (bamf_matcher_get_default ()),
matcher_favorites_changed, object);
G_OBJECT_CLASS (bamf_application_parent_class)->dispose (object);
}
static void
bamf_application_finalize (GObject *object)
{
BamfApplication *self;
self = BAMF_APPLICATION (object);
g_object_unref (self->priv->dbus_iface);
G_OBJECT_CLASS (bamf_application_parent_class)->finalize (object);
}
static void
bamf_application_init (BamfApplication * self)
{
BamfApplicationPrivate *priv;
priv = self->priv = BAMF_APPLICATION_GET_PRIVATE (self);
priv->app_type = BAMF_APPLICATION_SYSTEM;
priv->show_stubs = TRUE;
priv->cancellable = g_cancellable_new ();
/* Initializing the dbus interface */
priv->dbus_iface = _bamf_dbus_item_application_skeleton_new ();
/* We need to connect to the object own signals to redirect them to the dbus
* interface */
g_signal_connect (self, "window-added", G_CALLBACK (on_window_added), NULL);
g_signal_connect (self, "window-removed", G_CALLBACK (on_window_removed), NULL);
g_signal_connect (self, "desktop-file-updated", G_CALLBACK (on_desktop_file_updated), NULL);
/* Registering signal callbacks to reply to dbus method calls */
g_signal_connect (priv->dbus_iface, "handle-show-stubs",
G_CALLBACK (on_dbus_handle_show_stubs), self);
g_signal_connect (priv->dbus_iface, "handle-xids",
G_CALLBACK (on_dbus_handle_xids), self);
g_signal_connect (priv->dbus_iface, "handle-focusable-child",
G_CALLBACK (on_dbus_handle_focusable_child), self);
g_signal_connect (priv->dbus_iface, "handle-desktop-file",
G_CALLBACK (on_dbus_handle_desktop_file), self);
g_signal_connect (priv->dbus_iface, "handle-supported-mime-types",
G_CALLBACK (on_dbus_handle_supported_mime_types), self);
g_signal_connect (priv->dbus_iface, "handle-application-menu",
G_CALLBACK (on_dbus_handle_application_menu), self);
g_signal_connect (priv->dbus_iface, "handle-application-type",
G_CALLBACK (on_dbus_handle_application_type), self);
/* Setting the interface for the dbus object */
_bamf_dbus_item_object_skeleton_set_application (BAMF_DBUS_ITEM_OBJECT_SKELETON (self),
priv->dbus_iface);
g_signal_connect (G_OBJECT (bamf_matcher_get_default ()), "favorites-changed",
(GCallback) matcher_favorites_changed, self);
}
static void
bamf_application_dbus_application_iface_init (BamfDBusItemApplicationIface *iface)
{
}
static void
bamf_application_class_init (BamfApplicationClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
BamfViewClass *view_class = BAMF_VIEW_CLASS (klass);
object_class->dispose = bamf_application_dispose;
object_class->finalize = bamf_application_finalize;
view_class->view_type = bamf_application_get_view_type;
view_class->child_added = bamf_application_child_added;
view_class->child_removed = bamf_application_child_removed;
view_class->stable_bus_name = bamf_application_get_stable_bus_name;
klass->get_supported_mime_types = bamf_application_default_get_supported_mime_types;
klass->get_close_when_empty = bamf_application_default_get_close_when_empty;
klass->supported_mimes_changed = bamf_application_supported_mime_types_changed;
g_type_class_add_private (klass, sizeof (BamfApplicationPrivate));
application_signals[SUPPORTED_MIMES_CHANGED] =
g_signal_new ("supported-mimes-changed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (BamfApplicationClass, supported_mimes_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
G_TYPE_STRV);
}
BamfApplication *
bamf_application_new (void)
{
BamfApplication *application;
application = (BamfApplication *) g_object_new (BAMF_TYPE_APPLICATION, NULL);
return application;
}
BamfApplication *
bamf_application_new_from_desktop_file (const char * desktop_file)
{
BamfApplication *application;
application = (BamfApplication *) g_object_new (BAMF_TYPE_APPLICATION, NULL);
bamf_application_set_desktop_file (application, desktop_file);
return application;
}
BamfApplication *
bamf_application_new_from_desktop_files (GList *desktop_files)
{
BamfApplication *application;
application = (BamfApplication *) g_object_new (BAMF_TYPE_APPLICATION, NULL);
bamf_application_set_desktop_file_from_list (application, desktop_files);
return application;
}
BamfApplication *
bamf_application_new_with_wmclass (const char *wmclass)
{
BamfApplication *application;
application = (BamfApplication *) g_object_new (BAMF_TYPE_APPLICATION, NULL);
bamf_application_set_wmclass (application, wmclass);
return application;
}
gboolean
bamf_application_get_show_stubs (BamfApplication *application)
{
g_return_val_if_fail (BAMF_IS_APPLICATION(application), TRUE);
return application->priv->show_stubs;
}
gboolean
bamf_application_get_close_when_empty (BamfApplication *application)
{
g_return_val_if_fail (BAMF_IS_APPLICATION(application), FALSE);
if (BAMF_APPLICATION_GET_CLASS (application)->get_close_when_empty)
{
return BAMF_APPLICATION_GET_CLASS (application)->get_close_when_empty(application);
}
return TRUE;
}
void
bamf_application_get_application_menu (BamfApplication *application, gchar **name, gchar **object_path)
{
g_return_if_fail (BAMF_IS_APPLICATION (application));
if (BAMF_APPLICATION_GET_CLASS (application)->get_application_menu)
{
BAMF_APPLICATION_GET_CLASS (application)->get_application_menu (application, name, object_path);
}
else
{
*name = NULL;
*object_path = NULL;
}
}
BamfView *
bamf_application_get_focusable_child (BamfApplication *application)
{
g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
if (BAMF_APPLICATION_GET_CLASS (application)->get_focusable_child)
{
return BAMF_APPLICATION_GET_CLASS (application)->get_focusable_child (application);
}
return NULL;
}