Logo Search packages:      
Sourcecode: control-center version File versions  Download package

directory-tile.c

/*
 * This file is part of libtile.
 *
 * Copyright (c) 2006 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 "directory-tile.h"
#include "config.h"

#include <glib/gi18n-lib.h>
#include <string.h>
#include <gio/gio.h>
#include <gdk/gdk.h>
#include <unistd.h>

#include "slab-gnome-util.h"
#include "gnome-utils.h"
#include "libslab-utils.h"

#define GCONF_SEND_TO_CMD_KEY       "/desktop/gnome/applications/main-menu/file-area/file_send_to_cmd"
#define GCONF_ENABLE_DELETE_KEY_DIR "/apps/nautilus/preferences"
#define GCONF_ENABLE_DELETE_KEY     GCONF_ENABLE_DELETE_KEY_DIR "/enable_delete"
#define GCONF_CONFIRM_DELETE_KEY    GCONF_ENABLE_DELETE_KEY_DIR "/confirm_trash"

G_DEFINE_TYPE (DirectoryTile, directory_tile, NAMEPLATE_TILE_TYPE)

static void directory_tile_finalize (GObject *);
static void directory_tile_style_set (GtkWidget *, GtkStyle *);

static void directory_tile_private_setup (DirectoryTile *);
static void load_image (DirectoryTile *);

static GtkWidget *create_header (const gchar *);

static void header_size_allocate_cb (GtkWidget *, GtkAllocation *, gpointer);

static void open_with_default_trigger (Tile *, TileEvent *, TileAction *);
static void rename_trigger (Tile *, TileEvent *, TileAction *);
static void move_to_trash_trigger (Tile *, TileEvent *, TileAction *);
static void delete_trigger (Tile *, TileEvent *, TileAction *);
static void send_to_trigger (Tile *, TileEvent *, TileAction *);

static void rename_entry_activate_cb (GtkEntry *, gpointer);
static gboolean rename_entry_key_release_cb (GtkWidget *, GdkEventKey *, gpointer);
static void gconf_enable_delete_cb (GConfClient *, guint, GConfEntry *, gpointer);

static void disown_spawned_child (gpointer);

00063 typedef struct
{
      gchar *basename;
      gchar *mime_type;
      gchar *icon_name;
      
      GtkBin *header_bin;
      GAppInfo *default_app;
      
      gboolean image_is_broken;
      
      gboolean delete_enabled;
      guint gconf_conn_id;
} DirectoryTilePrivate;

#define DIRECTORY_TILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DIRECTORY_TILE_TYPE, DirectoryTilePrivate))

static void directory_tile_class_init (DirectoryTileClass *this_class)
{
      GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class);
      GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (this_class);

      g_obj_class->finalize = directory_tile_finalize;

      widget_class->style_set = directory_tile_style_set;

      g_type_class_add_private (this_class, sizeof (DirectoryTilePrivate));
}

GtkWidget *
directory_tile_new (const gchar *in_uri, const gchar *title, const gchar *icon_name, const gchar *mime_type)
{
      DirectoryTile *this;
      DirectoryTilePrivate *priv;

      gchar *uri;
      GtkWidget *image;
      GtkWidget *header;
      GtkMenu *context_menu;

      GtkContainer *menu_ctnr;
      GtkWidget *menu_item;

      TileAction *action;

      gchar *basename;

      gchar *markup;

      AtkObject *accessible;
      
      gchar *filename;
      gchar *tooltip_text;

  
      uri = g_strdup (in_uri);

      image = gtk_image_new ();

      if (! title) {
            markup = g_path_get_basename (uri);
            basename = g_uri_unescape_string (markup, NULL);
            g_free (markup);
      }
      else
            basename = g_strdup (title);

      header = create_header (basename);

      filename = g_filename_from_uri (uri, NULL, NULL);

      if (filename)
            tooltip_text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
      else
            tooltip_text = NULL;

      g_free (filename);
      
      context_menu = GTK_MENU (gtk_menu_new ());

      this = g_object_new (
            DIRECTORY_TILE_TYPE,
            "tile-uri",          uri,
            "nameplate-image",   image,
            "nameplate-header",  header,
            "context-menu",      context_menu,
            NULL);
      gtk_widget_set_tooltip_text (GTK_WIDGET (this), tooltip_text);

      g_free (uri);
      if (tooltip_text)
            g_free (tooltip_text);

      priv = DIRECTORY_TILE_GET_PRIVATE (this);
      priv->basename    = g_strdup (basename);
      priv->header_bin  = GTK_BIN (header);
      priv->icon_name   = g_strdup (icon_name);
      priv->mime_type   = g_strdup (mime_type);

      directory_tile_private_setup (this);

      TILE (this)->actions = g_new0 (TileAction *, 6);
      TILE (this)->n_actions = 6;

      menu_ctnr = GTK_CONTAINER (TILE (this)->context_menu);

      /* make open with default action */

      markup = g_markup_printf_escaped (_("<b>Open</b>"));
      action = tile_action_new (TILE (this), open_with_default_trigger, markup, TILE_ACTION_OPENS_NEW_WINDOW);
      g_free (markup);

      TILE (this)->default_action = action;

      menu_item = GTK_WIDGET (GTK_WIDGET (tile_action_get_menu_item (action)));

      TILE (this)->actions [DIRECTORY_TILE_ACTION_OPEN] = action;

      gtk_container_add (menu_ctnr, menu_item);

      /* insert separator */

      menu_item = gtk_separator_menu_item_new ();
      gtk_container_add (menu_ctnr, menu_item);

      /* make rename action */

      action = tile_action_new (TILE (this), rename_trigger, _("Rename..."), 0);
      TILE (this)->actions[DIRECTORY_TILE_ACTION_RENAME] = action;

      menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
      gtk_container_add (menu_ctnr, menu_item);

      /* make send to action */

      /* Only allow Send To for local files, ideally this would use something
       * equivalent to gnome_vfs_uri_is_local, but that method will stat the file and
       * that can hang in some conditions. */

      if (!strncmp (TILE (this)->uri, "file://", 7))
      {
            action = tile_action_new (TILE (this), send_to_trigger, _("Send To..."),
                  TILE_ACTION_OPENS_NEW_WINDOW);

            menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
      }
      else
      {
            action = NULL;

            menu_item = gtk_menu_item_new_with_label (_("Send To..."));
            gtk_widget_set_sensitive (menu_item, FALSE);
      }

      TILE (this)->actions[DIRECTORY_TILE_ACTION_SEND_TO] = action;

      gtk_container_add (menu_ctnr, menu_item);

      /* insert separator */

      menu_item = gtk_separator_menu_item_new ();
      gtk_container_add (menu_ctnr, menu_item);

      /* make move to trash action */

      action = tile_action_new (TILE (this), move_to_trash_trigger, _("Move to Trash"), 0);
      TILE (this)->actions[DIRECTORY_TILE_ACTION_MOVE_TO_TRASH] = action;

      menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
      gtk_container_add (menu_ctnr, menu_item);

      /* make delete action */

      if (priv->delete_enabled)
      {
            action = tile_action_new (TILE (this), delete_trigger, _("Delete"), 0);
            TILE (this)->actions[DIRECTORY_TILE_ACTION_DELETE] = action;

            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));

      load_image (this);

      accessible = gtk_widget_get_accessible (GTK_WIDGET (this));
      if (basename)
        atk_object_set_name (accessible, basename);

      g_free (basename);

      return GTK_WIDGET (this);
}

static void
directory_tile_private_setup (DirectoryTile *tile)
{
      DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
      GConfClient *client;

      if (priv->mime_type)
            priv->default_app = g_app_info_get_default_for_type (priv->mime_type, TRUE);
      else
            priv->default_app = NULL;

      priv->delete_enabled =
            (gboolean) GPOINTER_TO_INT (get_gconf_value (GCONF_ENABLE_DELETE_KEY));

      client = gconf_client_get_default ();

      gconf_client_add_dir (client, GCONF_ENABLE_DELETE_KEY_DIR, GCONF_CLIENT_PRELOAD_NONE, NULL);
      priv->gconf_conn_id =
            connect_gconf_notify (GCONF_ENABLE_DELETE_KEY, gconf_enable_delete_cb, tile);

      g_object_unref (client);
}

static void
directory_tile_init (DirectoryTile *tile)
{
      DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);

      priv->default_app = NULL;
      priv->basename = NULL;
      priv->header_bin = NULL;
      priv->icon_name = NULL;
      priv->mime_type = NULL;
      priv->image_is_broken = TRUE;
      priv->delete_enabled = FALSE;
      priv->gconf_conn_id = 0;
}

static void
directory_tile_finalize (GObject *g_object)
{
      DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (g_object);

      GConfClient *client;

      g_free (priv->basename);
      g_free (priv->icon_name);
      g_free (priv->mime_type);

      if (priv->default_app)
            g_object_unref (priv->default_app);
    
      client = gconf_client_get_default ();

      gconf_client_notify_remove (client, priv->gconf_conn_id);
      gconf_client_remove_dir (client, GCONF_ENABLE_DELETE_KEY_DIR, NULL);

      g_object_unref (client);

      (* G_OBJECT_CLASS (directory_tile_parent_class)->finalize) (g_object);
}

static void
directory_tile_style_set (GtkWidget *widget, GtkStyle *prev_style)
{
      load_image (DIRECTORY_TILE (widget));
}

static void
load_image (DirectoryTile *tile)
{
      DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
      gchar *icon_name;

      if (priv->icon_name)
            icon_name = priv->icon_name;
      else
            icon_name = "gnome-fs-directory";

      priv->image_is_broken = slab_load_image (
            GTK_IMAGE (NAMEPLATE_TILE (tile)->image), GTK_ICON_SIZE_DND, icon_name);
}

static GtkWidget *
create_header (const gchar *name)
{
      GtkWidget *header_bin;
      GtkWidget *header;

      header = gtk_label_new (name);
      gtk_label_set_ellipsize (GTK_LABEL (header), PANGO_ELLIPSIZE_END);
      gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5);

      header_bin = gtk_alignment_new (0.0, 0.5, 1.0, 0.0);
      gtk_container_add (GTK_CONTAINER (header_bin), header);

      g_signal_connect (G_OBJECT (header), "size-allocate", G_CALLBACK (header_size_allocate_cb),
            NULL);

      return header_bin;
}

static void
header_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, gpointer user_data)
{
      gtk_widget_set_size_request (widget, alloc->width, -1);
}

static void
rename_entry_activate_cb (GtkEntry *entry, gpointer user_data)
{
      DirectoryTile *tile = DIRECTORY_TILE (user_data);
      DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);

      GFile *src_file;
      GFile *dst_file;

      gchar *dirname;
      gchar *dst_uri;
      gchar *src_path;

      GtkWidget *child;
      GtkWidget *header;

      gboolean res;
      GError *error = NULL;

      if (strlen (gtk_entry_get_text (entry)) < 1)
            return;

      src_file = g_file_new_for_uri (TILE (tile)->uri);

      src_path = g_filename_from_uri (TILE (tile)->uri, NULL, NULL);
      dirname = g_path_get_dirname (src_path);
      dst_uri = g_build_filename (dirname, gtk_entry_get_text (entry), NULL);
      dst_file = g_file_new_for_uri (dst_uri);

      g_free (dirname);
      g_free (src_path);

      res = g_file_move (src_file, dst_file,
                     G_FILE_COPY_NONE, NULL, NULL, NULL, &error);

      if (res) {
            g_free (priv->basename);
            priv->basename = g_strdup (gtk_entry_get_text (entry));
      }
      else {
            g_warning ("unable to move [%s] to [%s]: %s\n", TILE (tile)->uri, dst_uri,
                     error->message);
            g_error_free (error);
      }

      g_free (dst_uri);
      g_object_unref (src_file);
      g_object_unref (dst_file);

      header = gtk_label_new (priv->basename);
      gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5);

      child = gtk_bin_get_child (priv->header_bin);

      if (child)
            gtk_widget_destroy (child);

      gtk_container_add (GTK_CONTAINER (priv->header_bin), header);

      gtk_widget_show (header);
}

static gboolean
rename_entry_key_release_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
      return TRUE;
}

static void
gconf_enable_delete_cb (GConfClient *client, guint conn_id, GConfEntry *entry, gpointer user_data)
{
      Tile *tile = TILE (user_data);
      DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (user_data);

      GtkMenuShell *menu;
      gboolean delete_enabled;

      TileAction *action;
      GtkWidget *menu_item;

      menu = GTK_MENU_SHELL (tile->context_menu);

      delete_enabled = gconf_value_get_bool (entry->value);

      if (delete_enabled == priv->delete_enabled)
            return;

      priv->delete_enabled = delete_enabled;

      if (priv->delete_enabled)
      {
            action = tile_action_new (tile, delete_trigger, _("Delete"), 0);
            tile->actions[DIRECTORY_TILE_ACTION_DELETE] = action;

            menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
            gtk_menu_shell_insert (menu, menu_item, 7);

            gtk_widget_show_all (menu_item);
      }
      else
      {
            g_object_unref (tile->actions[DIRECTORY_TILE_ACTION_DELETE]);

            tile->actions[DIRECTORY_TILE_ACTION_DELETE] = NULL;
      }
}

static void
rename_trigger (Tile *tile, TileEvent *event, TileAction *action)
{
      DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);

      GtkWidget *child;
      GtkWidget *entry;


      entry = gtk_entry_new ();
      gtk_entry_set_text (GTK_ENTRY (entry), priv->basename);
      gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);

      child = gtk_bin_get_child (priv->header_bin);

      if (child)
            gtk_widget_destroy (child);

      gtk_container_add (GTK_CONTAINER (priv->header_bin), entry);

      g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (rename_entry_activate_cb), tile);

      g_signal_connect (G_OBJECT (entry), "key_release_event",
            G_CALLBACK (rename_entry_key_release_cb), NULL);

      gtk_widget_show (entry);
      gtk_widget_grab_focus (entry);
}

static void
move_to_trash_trigger (Tile *tile, TileEvent *event, TileAction *action)
{
      GFile *src_file;
      gboolean res;
      GError *error = NULL;

      src_file = g_file_new_for_uri (TILE (tile)->uri);

      res = g_file_trash (src_file, NULL, &error);
      if (!res) {
            g_warning ("unable to move [%s] to the trash: %s\n", TILE (tile)->uri,
                     error->message);
            g_error_free (error);
      }

      g_object_unref (src_file);
}

static void
delete_trigger (Tile *tile, TileEvent *event, TileAction *action)
{
      GtkDialog *confirm_dialog;
      gint       result;

      GFile *src_file;
      gboolean res;
      GError *error = NULL;

      if (GPOINTER_TO_INT (libslab_get_gconf_value (GCONF_CONFIRM_DELETE_KEY))) {
            confirm_dialog = GTK_DIALOG(gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING, 
                        GTK_BUTTONS_NONE, _("Are you sure you want to permanently delete \"%s\"?"), DIRECTORY_TILE_GET_PRIVATE (tile)->basename));
            gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(confirm_dialog), _("If you delete an item, it is permanently lost."));
                                          
            gtk_dialog_add_button (confirm_dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
            gtk_dialog_add_button (confirm_dialog, GTK_STOCK_DELETE, GTK_RESPONSE_YES);
            gtk_dialog_set_default_response (GTK_DIALOG (confirm_dialog), GTK_RESPONSE_YES);

            result = gtk_dialog_run (confirm_dialog);

            gtk_widget_destroy (GTK_WIDGET (confirm_dialog));

            if (result != GTK_RESPONSE_YES)
                  return;
      }

      src_file = g_file_new_for_uri (TILE (tile)->uri);

      res = g_file_delete (src_file, NULL, &error);

      if (!res) {
            g_warning ("unable to delete [%s]: %s\n", TILE (tile)->uri,
                     error->message);
            g_error_free (error);
      }

      g_object_unref (src_file);
}

static void
send_to_trigger (Tile *tile, TileEvent *event, TileAction *action)
{
      gchar  *cmd;
      gint    argc;
      gchar **argv_parsed = NULL;
      gchar **argv        = NULL;

      gchar *path;
      gchar *dirname;
      gchar *basename;

      GError *error = NULL;

      gint i;


      cmd = (gchar *) get_gconf_value (GCONF_SEND_TO_CMD_KEY);

      if (! g_shell_parse_argv (cmd, & argc, & argv_parsed, NULL))
            goto exit;

      argv = g_new0 (gchar *, argc + 1);

      path     = g_filename_from_uri (tile->uri, NULL, NULL);
      dirname  = g_path_get_dirname  (path);
      basename = g_path_get_basename (path);

      for (i = 0; i < argc; ++i) {
            if (strstr (argv_parsed [i], "DIRNAME"))
                  argv [i] = string_replace_once (argv_parsed [i], "DIRNAME", dirname);
            else if (strstr (argv_parsed [i], "BASENAME"))
                  argv [i] = string_replace_once (argv_parsed [i], "BASENAME", basename);
            else
                  argv [i] = g_strdup (argv_parsed [i]);
      }

      argv [argc] = NULL;

      g_free (path);
      g_free (dirname);
      g_free (basename);

      g_spawn_async (
            NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
            disown_spawned_child, NULL, NULL, & error);

      if (error) {
            cmd = g_strjoinv (" ", argv);
            libslab_handle_g_error (
                  & error, "%s: can't execute search [%s]\n", G_STRFUNC, cmd);
            g_free (cmd);
      }

      g_strfreev (argv);

exit:

      g_free (cmd);
      g_strfreev (argv_parsed);
}

static void
disown_spawned_child (gpointer user_data)
{
      setsid  ();
      setpgrp ();
}

static void
open_with_default_trigger (Tile *tile, TileEvent *event, TileAction *action)
{
      DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
      GList *uris = NULL;
      gboolean res;
      GdkAppLaunchContext *launch_context;
      GError *error = NULL;

      if (priv->default_app)
      {
            uris = g_list_append (uris, TILE (tile)->uri);
            
            launch_context = gdk_app_launch_context_new ();
            gdk_app_launch_context_set_screen (launch_context,
                                       gtk_widget_get_screen (GTK_WIDGET (tile)));
            gdk_app_launch_context_set_timestamp (launch_context,
                                          event->time);

            res = g_app_info_launch_uris (priv->default_app, uris,
                                    G_APP_LAUNCH_CONTEXT (launch_context),
                                    &error);

            if (!res) {
                  g_warning
                        ("error: could not launch application with [%s]: %s\n",
                         TILE (tile)->uri, error->message);
                  g_error_free (error);
            }

            g_list_free (uris);
            g_object_unref (launch_context);
      } else {
            gchar *cmd;
            cmd = string_replace_once (
                  get_slab_gconf_string (SLAB_FILE_MANAGER_OPEN_CMD), "FILE_URI", tile->uri);
            spawn_process (cmd);
            g_free (cmd);
      }
}

Generated by  Doxygen 1.6.0   Back to index