diff options
Diffstat (limited to 'shell/application-tile.c')
-rw-r--r-- | shell/application-tile.c | 764 |
1 files changed, 764 insertions, 0 deletions
diff --git a/shell/application-tile.c b/shell/application-tile.c new file mode 100644 index 00000000..ab1b4cf5 --- /dev/null +++ b/shell/application-tile.c @@ -0,0 +1,764 @@ +/* + * This file is part of libtile. + * + * Copyright (c) 2006, 2007 Novell, Inc. + * + * Libtile is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Libtile 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 Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libslab; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "application-tile.h" +#include "config.h" + +#include <string.h> +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <glib/gstdio.h> +#include <unistd.h> + +#include "slab-mate-util.h" +#include "libslab-utils.h" +#include "bookmark-agent.h" +#include "themed-icon.h" + +typedef enum { + APP_IN_USER_STARTUP_DIR, + APP_NOT_IN_STARTUP_DIR, + APP_NOT_ELIGIBLE +} StartupStatus; + +static void application_tile_get_property (GObject *, guint, GValue *, GParamSpec *); +static void application_tile_set_property (GObject *, guint, const GValue *, GParamSpec *); +static void application_tile_finalize (GObject *); + +static void application_tile_setup (ApplicationTile *); + +static GtkWidget *create_header (const gchar *); +static GtkWidget *create_subheader (const gchar *); + +static void header_size_allocate_cb (GtkWidget *, GtkAllocation *, gpointer); + +static void start_trigger (Tile *, TileEvent *, TileAction *); +static void help_trigger (Tile *, TileEvent *, TileAction *); +static void user_apps_trigger (Tile *, TileEvent *, TileAction *); +static void startup_trigger (Tile *, TileEvent *, TileAction *); + +static void add_to_user_list (ApplicationTile *); +static void remove_from_user_list (ApplicationTile *); +static void add_to_startup_list (ApplicationTile *); +static void remove_from_startup_list (ApplicationTile *); + +static void update_user_list_menu_item (ApplicationTile *); +static void agent_notify_cb (GObject *, GParamSpec *, gpointer); + +static StartupStatus get_desktop_item_startup_status (MateDesktopItem *); +static void update_startup_menu_item (ApplicationTile *); + +typedef struct { + MateDesktopItem *desktop_item; + + gchar *image_id; + gboolean image_is_broken; + GtkIconSize image_size; + + gboolean show_generic_name; + StartupStatus startup_status; + + BookmarkAgent *agent; + BookmarkStoreStatus agent_status; + gboolean is_bookmarked; + gulong notify_signal_id; +} ApplicationTilePrivate; + +enum { + PROP_0, + PROP_APPLICATION_NAME, + PROP_APPLICATION_DESCRIPTION +}; + +G_DEFINE_TYPE_WITH_PRIVATE (ApplicationTile, application_tile, NAMEPLATE_TILE_TYPE) + +static void +application_tile_class_init (ApplicationTileClass *app_tile_class) +{ + GObjectClass *g_obj_class = G_OBJECT_CLASS (app_tile_class); + + g_obj_class->get_property = application_tile_get_property; + g_obj_class->set_property = application_tile_set_property; + g_obj_class->finalize = application_tile_finalize; + + g_object_class_install_property ( + g_obj_class, PROP_APPLICATION_NAME, + g_param_spec_string ( + "application-name", "application-name", + "the name of the application", NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + g_obj_class, PROP_APPLICATION_DESCRIPTION, + g_param_spec_string ( + "application-description", "application-description", + "the name of the application", NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); +} + +GtkWidget * +application_tile_new (const gchar *desktop_item_id) +{ + return application_tile_new_full (desktop_item_id, GTK_ICON_SIZE_DND, TRUE); +} + +GtkWidget * +application_tile_new_full (const gchar *desktop_item_id, + GtkIconSize image_size, gboolean show_generic_name) +{ + ApplicationTile *this; + ApplicationTilePrivate *priv; + + const gchar *uri = NULL; + + MateDesktopItem *desktop_item; + + desktop_item = load_desktop_item_from_unknown (desktop_item_id); + + if ( + desktop_item && + mate_desktop_item_get_entry_type (desktop_item) == MATE_DESKTOP_ITEM_TYPE_APPLICATION + ) + uri = mate_desktop_item_get_location (desktop_item); + + if (! uri) { + if (desktop_item) + mate_desktop_item_unref (desktop_item); + + return NULL; + } + + this = g_object_new (APPLICATION_TILE_TYPE, "tile-uri", uri, NULL); + priv = application_tile_get_instance_private (this); + + priv->image_size = image_size; + priv->desktop_item = desktop_item; + priv->show_generic_name = show_generic_name; + + application_tile_setup (this); + + return GTK_WIDGET (this); +} + +static void +application_tile_init (ApplicationTile *tile) +{ + ApplicationTilePrivate *priv = application_tile_get_instance_private (tile); + + priv->desktop_item = NULL; + priv->image_id = NULL; + priv->image_is_broken = TRUE; + + priv->agent = NULL; + priv->agent_status = BOOKMARK_STORE_ABSENT; + priv->is_bookmarked = FALSE; + priv->notify_signal_id = 0; + + tile->name = tile->description = NULL; +} + +static void +application_tile_finalize (GObject *g_object) +{ + ApplicationTile *tile = APPLICATION_TILE (g_object); + ApplicationTilePrivate *priv = application_tile_get_instance_private (tile); + + if (tile->name) { + g_free (tile->name); + tile->name = NULL; + } + if (tile->description) { + g_free (tile->description); + tile->description = NULL; + } + + if (priv->desktop_item) { + mate_desktop_item_unref (priv->desktop_item); + priv->desktop_item = NULL; + } + if (priv->image_id) { + g_free (priv->image_id); + priv->image_id = NULL; + } + + if (priv->notify_signal_id) + g_signal_handler_disconnect (priv->agent, priv->notify_signal_id); + + g_object_unref (G_OBJECT (priv->agent)); + + G_OBJECT_CLASS (application_tile_parent_class)->finalize (g_object); +} + +static void +application_tile_get_property (GObject *g_obj, guint prop_id, GValue *value, GParamSpec *param_spec) +{ + ApplicationTile *tile = APPLICATION_TILE (g_obj); + + switch (prop_id) { + case PROP_APPLICATION_NAME: + g_value_set_string (value, tile->name); + break; + + case PROP_APPLICATION_DESCRIPTION: + g_value_set_string (value, tile->description); + break; + + default: + break; + } +} + +static void +application_tile_set_property (GObject *g_obj, guint prop_id, const GValue *value, GParamSpec *param_spec) +{ + ApplicationTile *tile = APPLICATION_TILE (g_obj); + + switch (prop_id) { + case PROP_APPLICATION_NAME: + if (tile->name) + g_free (tile->name); + tile->name = g_strdup (g_value_get_string (value)); + break; + + case PROP_APPLICATION_DESCRIPTION: + if (tile->description) + g_free (tile->description); + tile->description = g_strdup (g_value_get_string (value)); + break; + + default: + break; + } +} + +static void +application_tile_setup (ApplicationTile *this) +{ + ApplicationTilePrivate *priv = application_tile_get_instance_private (this); + + GtkWidget *image; + GtkWidget *header; + GtkWidget *subheader; + GtkMenu *context_menu; + AtkObject *accessible; + + TileAction **actions; + TileAction *action; + GtkWidget *menu_item; + GtkContainer *menu_ctnr; + + gchar *name; + gchar *desc; + + gchar *comment; + + gchar *markup; + gchar *str; + + if (! priv->desktop_item) { + priv->desktop_item = load_desktop_item_from_unknown (TILE (this)->uri); + + if (! priv->desktop_item) + return; + } + + priv->image_id = g_strdup (mate_desktop_item_get_localestring (priv->desktop_item, "Icon")); + image = themed_icon_new (priv->image_id, priv->image_size); + + gchar *filename = g_filename_from_uri (mate_desktop_item_get_location (priv->desktop_item), NULL, NULL); + GKeyFile *keyfile = g_key_file_new (); + g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL); + + name = g_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, "Name", NULL, NULL); + desc = g_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, "GenericName", NULL, NULL); + comment = g_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, "Comment", NULL, NULL); + + accessible = gtk_widget_get_accessible (GTK_WIDGET (this)); + if (name) + atk_object_set_name (accessible, name); + if (desc) + atk_object_set_description (accessible, desc); + + header = create_header (name); + + /*if no GenericName then just show and center the Name */ + if (desc && priv->show_generic_name + && (!name || strcmp(name, desc) != 0)) + subheader = create_subheader (desc); + else + subheader = NULL; + + context_menu = GTK_MENU (gtk_menu_new ()); + + g_object_set ( + G_OBJECT (this), + "nameplate-image", image, + "nameplate-header", header, + "nameplate-subheader", subheader, + "context-menu", context_menu, + "application-name", name, + "application-description", desc, + NULL); + gtk_widget_set_tooltip_text (GTK_WIDGET (this), comment); + + priv->agent = bookmark_agent_get_instance (BOOKMARK_STORE_USER_APPS); + g_object_get (G_OBJECT (priv->agent), BOOKMARK_AGENT_STORE_STATUS_PROP, & priv->agent_status, NULL); + + priv->notify_signal_id = g_signal_connect ( + G_OBJECT (priv->agent), "notify", G_CALLBACK (agent_notify_cb), this); + + priv->startup_status = get_desktop_item_startup_status (priv->desktop_item); + + actions = g_new0 (TileAction *, 6); + + TILE (this)->actions = actions; + TILE (this)->n_actions = 6; + + menu_ctnr = GTK_CONTAINER (TILE (this)->context_menu); + +/* make start action */ + + str = g_strdup_printf (_("Start %s"), this->name); + markup = g_markup_printf_escaped ("<b>%s</b>", str); + action = tile_action_new (TILE (this), start_trigger, markup, TILE_ACTION_OPENS_NEW_WINDOW); + actions [APPLICATION_TILE_ACTION_START] = action; + g_free (markup); + g_free (str); + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + + gtk_container_add (menu_ctnr, menu_item); + + TILE (this)->default_action = action; + +/* insert separator */ + + gtk_container_add (menu_ctnr, gtk_separator_menu_item_new ()); + +/* make help action */ + + if (mate_desktop_item_get_string (priv->desktop_item, "DocPath")) { + action = tile_action_new ( + TILE (this), help_trigger, _("Help"), + TILE_ACTION_OPENS_NEW_WINDOW | TILE_ACTION_OPENS_HELP); + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_container_add (menu_ctnr, menu_item); + } + else { + action = NULL; + } + + actions [APPLICATION_TILE_ACTION_HELP] = action; + +/* insert separator */ + + if (action != NULL) + gtk_container_add (menu_ctnr, gtk_separator_menu_item_new ()); + +/* make "add/remove to favorites" action */ + + update_user_list_menu_item (this); + +/* make "add/remove to startup" action */ + + if (priv->startup_status != APP_NOT_ELIGIBLE) { + action = tile_action_new (TILE (this), startup_trigger, NULL, 0); + actions [APPLICATION_TILE_ACTION_UPDATE_STARTUP] = action; + + update_startup_menu_item (this); + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + + gtk_container_add (menu_ctnr, menu_item); + } + + gtk_widget_show_all (GTK_WIDGET (TILE (this)->context_menu)); + + g_free (name); + g_free (desc); + g_free (comment); + g_free (filename); + g_key_file_unref (keyfile); +} + +static GtkWidget * +create_header (const gchar *name) +{ + GtkWidget *header; + + header = gtk_label_new (name); + gtk_label_set_line_wrap (GTK_LABEL (header), TRUE); + gtk_label_set_xalign (GTK_LABEL (header), 0.0); + + g_signal_connect ( + G_OBJECT (header), + "size-allocate", + G_CALLBACK (header_size_allocate_cb), + NULL); + + return header; +} + +static void +set_background_color (GtkWidget *widget, + GdkRGBA *rgba) +{ + gchar *css; + GtkCssProvider *provider; + + provider = gtk_css_provider_new (); + + css = g_strdup_printf ("* { background-color: %s;}", + gdk_rgba_to_string (rgba)); + gtk_css_provider_load_from_data (provider, css, -1, NULL); + g_free (css); + + gtk_style_context_add_provider (gtk_widget_get_style_context (widget), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + g_object_unref (provider); +} + +static GtkWidget * +create_subheader (const gchar *desc) +{ + GtkWidget *subheader; + GtkStyleContext *context; + GdkRGBA *rgba = NULL; + + subheader = gtk_label_new (desc); + gtk_label_set_ellipsize (GTK_LABEL (subheader), PANGO_ELLIPSIZE_END); + gtk_label_set_xalign (GTK_LABEL (subheader), 0.0); + context = gtk_widget_get_style_context (subheader); + gtk_style_context_get (context, + GTK_STATE_FLAG_INSENSITIVE, + "background-color", &rgba, + NULL); + + set_background_color (subheader, rgba); + + return subheader; +} + +static void +start_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + ApplicationTile *this = APPLICATION_TILE (tile); + ApplicationTilePrivate *priv = application_tile_get_instance_private (this); + open_desktop_item_exec (priv->desktop_item); +} + +static void +help_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + ApplicationTile *this = APPLICATION_TILE (tile); + ApplicationTilePrivate *priv = application_tile_get_instance_private (this); + open_desktop_item_help (priv->desktop_item); +} + +static void +user_apps_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + ApplicationTile *this = APPLICATION_TILE (tile); + ApplicationTilePrivate *priv = application_tile_get_instance_private (this); + + if (priv->is_bookmarked) + remove_from_user_list (this); + else + add_to_user_list (this); + + update_user_list_menu_item (this); +} + +static void +add_to_user_list (ApplicationTile *this) +{ + ApplicationTilePrivate *priv = application_tile_get_instance_private (this); + + BookmarkItem *item; + + item = g_new0 (BookmarkItem, 1); + item->uri = TILE (this)->uri; + item->mime_type = "application/x-desktop"; + + bookmark_agent_add_item (priv->agent, item); + g_free (item); + + priv->is_bookmarked = TRUE; +} + +static void +remove_from_user_list (ApplicationTile *this) +{ + ApplicationTilePrivate *priv = application_tile_get_instance_private (this); + + bookmark_agent_remove_item (priv->agent, TILE (this)->uri); + + priv->is_bookmarked = FALSE; +} + +static void +startup_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + ApplicationTile *this = APPLICATION_TILE (tile); + ApplicationTilePrivate *priv = application_tile_get_instance_private (this); + + switch (priv->startup_status) { + case APP_IN_USER_STARTUP_DIR: + remove_from_startup_list (this); + break; + + case APP_NOT_IN_STARTUP_DIR: + add_to_startup_list (this); + break; + + default: + break; + } + + update_startup_menu_item (this); +} + +static void +add_to_startup_list (ApplicationTile *this) +{ + ApplicationTilePrivate *priv = application_tile_get_instance_private (this); + + gchar *desktop_item_filename; + gchar *desktop_item_basename; + + gchar *startup_dir; + gchar *dst_filename; + + const gchar *src_uri; + gchar *dst_uri; + + desktop_item_filename = + g_filename_from_uri (mate_desktop_item_get_location (priv->desktop_item), NULL, + NULL); + + g_return_if_fail (desktop_item_filename != NULL); + + desktop_item_basename = g_path_get_basename (desktop_item_filename); + + startup_dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL); + + if (! g_file_test (startup_dir, G_FILE_TEST_EXISTS)) + g_mkdir_with_parents (startup_dir, 0700); + + dst_filename = g_build_filename (startup_dir, desktop_item_basename, NULL); + + src_uri = mate_desktop_item_get_location (priv->desktop_item); + dst_uri = g_filename_to_uri (dst_filename, NULL, NULL); + + copy_file (src_uri, dst_uri); + priv->startup_status = APP_IN_USER_STARTUP_DIR; + + g_free (desktop_item_filename); + g_free (desktop_item_basename); + g_free (startup_dir); + g_free (dst_filename); + g_free (dst_uri); +} + +static void +remove_from_startup_list (ApplicationTile *this) +{ + ApplicationTilePrivate *priv = application_tile_get_instance_private (this); + + gchar *ditem_filename; + gchar *ditem_basename; + gchar *src_filename; + + ditem_filename = + g_filename_from_uri (mate_desktop_item_get_location (priv->desktop_item), NULL, + NULL); + + g_return_if_fail (ditem_filename != NULL); + + ditem_basename = g_path_get_basename (ditem_filename); + + src_filename = g_build_filename (g_get_user_config_dir (), "autostart", ditem_basename, NULL); + + priv->startup_status = APP_NOT_IN_STARTUP_DIR; + if (g_file_test (src_filename, G_FILE_TEST_EXISTS)) + { + if(g_file_test (src_filename, G_FILE_TEST_IS_DIR)) + g_assert_not_reached (); + g_unlink (src_filename); + } + + g_free (ditem_filename); + g_free (ditem_basename); + g_free (src_filename); +} + +MateDesktopItem * +application_tile_get_desktop_item (ApplicationTile *tile) +{ + ApplicationTilePrivate *priv; + + priv = application_tile_get_instance_private (tile); + return priv->desktop_item; +} + +static void +update_user_list_menu_item (ApplicationTile *this) +{ + ApplicationTilePrivate *priv = application_tile_get_instance_private (this); + + TileAction *action; + GtkWidget *item; + + if (priv->agent_status == BOOKMARK_STORE_ABSENT) { + if (TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU]) + g_object_unref (TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU]); + + TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU] = NULL; + } + else if (! TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU]) { + TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU] = + tile_action_new (TILE (this), user_apps_trigger, NULL, 0); + + tile_action_set_menu_item_label ( + TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU], "blah"); + + item = GTK_WIDGET (tile_action_get_menu_item ( + TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU])); + gtk_menu_shell_insert (GTK_MENU_SHELL (TILE (this)->context_menu), item, 4); + + gtk_widget_show_all (item); + } + else { + /* do nothing */ ; + } + + action = TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU]; + + if (! action) + return; + + priv->is_bookmarked = bookmark_agent_has_item (priv->agent, TILE (this)->uri); + + if (priv->is_bookmarked) + tile_action_set_menu_item_label (action, _("Remove from Favorites")); + else + tile_action_set_menu_item_label (action, _("Add to Favorites")); + + item = GTK_WIDGET (tile_action_get_menu_item (action)); + + if (! GTK_IS_MENU_ITEM (item)) + return; + + g_object_get (G_OBJECT (priv->agent), BOOKMARK_AGENT_STORE_STATUS_PROP, & priv->agent_status, NULL); + + gtk_widget_set_sensitive (item, (priv->agent_status != BOOKMARK_STORE_DEFAULT_ONLY)); +} + +static StartupStatus +get_desktop_item_startup_status (MateDesktopItem *desktop_item) +{ + gchar *filename; + gchar *basename; + + const gchar * const * global_dirs; + gchar *global_target; + gchar *user_target; + + StartupStatus retval; + gint x; + + filename = g_filename_from_uri (mate_desktop_item_get_location (desktop_item), NULL, NULL); + if (!filename) + return APP_NOT_ELIGIBLE; + basename = g_path_get_basename (filename); + + retval = APP_NOT_IN_STARTUP_DIR; + global_dirs = g_get_system_config_dirs(); + for(x=0; global_dirs[x]; x++) + { + global_target = g_build_filename (global_dirs[x], "autostart", basename, NULL); + if (g_file_test (global_target, G_FILE_TEST_EXISTS)) + { + retval = APP_NOT_ELIGIBLE; + g_free (global_target); + break; + } + g_free (global_target); + } + + /* mate-session currently checks these dirs also. see startup-programs.c */ + if (retval != APP_NOT_ELIGIBLE) + { + global_dirs = g_get_system_data_dirs(); + for(x=0; global_dirs[x]; x++) + { + global_target = g_build_filename (global_dirs[x], "mate", "autostart", basename, NULL); + if (g_file_test (global_target, G_FILE_TEST_EXISTS)) + { + retval = APP_NOT_ELIGIBLE; + g_free (global_target); + break; + } + g_free (global_target); + } + } + + if (retval != APP_NOT_ELIGIBLE) + { + user_target = g_build_filename (g_get_user_config_dir (), "autostart", basename, NULL); + if (g_file_test (user_target, G_FILE_TEST_EXISTS)) + retval = APP_IN_USER_STARTUP_DIR; + g_free (user_target); + } + + g_free (basename); + g_free (filename); + + return retval; +} + +static void +update_startup_menu_item (ApplicationTile *this) +{ + TileAction *action = TILE (this)->actions [APPLICATION_TILE_ACTION_UPDATE_STARTUP]; + ApplicationTilePrivate *priv = application_tile_get_instance_private (this); + + if (!action) + return; + + if (priv->startup_status == APP_IN_USER_STARTUP_DIR) + tile_action_set_menu_item_label (action, _("Remove from Startup Programs")); + else + tile_action_set_menu_item_label (action, _("Add to Startup Programs")); +} + +static void +header_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, gpointer user_data) +{ + gtk_widget_set_size_request (widget, alloc->width, -1); +} + +static void +agent_notify_cb (GObject *g_obj, GParamSpec *pspec, gpointer user_data) +{ + update_user_list_menu_item (APPLICATION_TILE (user_data)); +} |