2
* This file is part of libtile.
4
* Copyright (c) 2006 Novell, Inc.
6
* Libtile is free software; you can redistribute it and/or modify it under the
7
* terms of the GNU Lesser General Public License as published by the Free
8
* Software Foundation; either version 2 of the License, or (at your option)
11
* Libtile is distributed in the hope that it will be useful, but WITHOUT ANY
12
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
16
* You should have received a copy of the GNU Lesser General Public License
17
* along with libslab; if not, write to the Free Software Foundation, Inc., 51
18
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
#include "directory-tile.h"
24
#include <glib/gi18n-lib.h>
30
#include "slab-gnome-util.h"
31
#include "gnome-utils.h"
32
#include "libslab-utils.h"
34
#define GCONF_SEND_TO_CMD_KEY "/desktop/gnome/applications/main-menu/file-area/file_send_to_cmd"
35
#define GCONF_ENABLE_DELETE_KEY_DIR "/apps/nautilus/preferences"
36
#define GCONF_ENABLE_DELETE_KEY GCONF_ENABLE_DELETE_KEY_DIR "/enable_delete"
37
#define GCONF_CONFIRM_DELETE_KEY GCONF_ENABLE_DELETE_KEY_DIR "/confirm_trash"
39
G_DEFINE_TYPE (DirectoryTile, directory_tile, NAMEPLATE_TILE_TYPE)
41
static void directory_tile_finalize (GObject *);
42
static void directory_tile_style_set (GtkWidget *, GtkStyle *);
44
static void directory_tile_private_setup (DirectoryTile *);
45
static void load_image (DirectoryTile *);
47
static GtkWidget *create_header (const gchar *);
49
static void header_size_allocate_cb (GtkWidget *, GtkAllocation *, gpointer);
51
static void open_with_default_trigger (Tile *, TileEvent *, TileAction *);
52
static void rename_trigger (Tile *, TileEvent *, TileAction *);
53
static void move_to_trash_trigger (Tile *, TileEvent *, TileAction *);
54
static void delete_trigger (Tile *, TileEvent *, TileAction *);
55
static void send_to_trigger (Tile *, TileEvent *, TileAction *);
57
static void rename_entry_activate_cb (GtkEntry *, gpointer);
58
static gboolean rename_entry_key_release_cb (GtkWidget *, GdkEventKey *, gpointer);
59
static void gconf_enable_delete_cb (GConfClient *, guint, GConfEntry *, gpointer);
61
static void disown_spawned_child (gpointer);
70
GAppInfo *default_app;
72
gboolean image_is_broken;
74
gboolean delete_enabled;
76
} DirectoryTilePrivate;
78
#define DIRECTORY_TILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DIRECTORY_TILE_TYPE, DirectoryTilePrivate))
80
static void directory_tile_class_init (DirectoryTileClass *this_class)
82
GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class);
83
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (this_class);
85
g_obj_class->finalize = directory_tile_finalize;
87
widget_class->style_set = directory_tile_style_set;
89
g_type_class_add_private (this_class, sizeof (DirectoryTilePrivate));
93
directory_tile_new (const gchar *in_uri, const gchar *title, const gchar *icon_name, const gchar *mime_type)
96
DirectoryTilePrivate *priv;
101
GtkMenu *context_menu;
103
GtkContainer *menu_ctnr;
104
GtkWidget *menu_item;
112
AtkObject *accessible;
118
uri = g_strdup (in_uri);
120
image = gtk_image_new ();
123
markup = g_path_get_basename (uri);
124
basename = g_uri_unescape_string (markup, NULL);
128
basename = g_strdup (title);
130
header = create_header (basename);
132
filename = g_filename_from_uri (uri, NULL, NULL);
135
tooltip_text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
141
context_menu = GTK_MENU (gtk_menu_new ());
143
this = g_object_new (
146
"nameplate-image", image,
147
"nameplate-header", header,
148
"context-menu", context_menu,
150
gtk_widget_set_tooltip_text (GTK_WIDGET (this), tooltip_text);
154
g_free (tooltip_text);
156
priv = DIRECTORY_TILE_GET_PRIVATE (this);
157
priv->basename = g_strdup (basename);
158
priv->header_bin = GTK_BIN (header);
159
priv->icon_name = g_strdup (icon_name);
160
priv->mime_type = g_strdup (mime_type);
162
directory_tile_private_setup (this);
164
TILE (this)->actions = g_new0 (TileAction *, 6);
165
TILE (this)->n_actions = 6;
167
menu_ctnr = GTK_CONTAINER (TILE (this)->context_menu);
169
/* make open with default action */
171
markup = g_markup_printf_escaped (_("<b>Open</b>"));
172
action = tile_action_new (TILE (this), open_with_default_trigger, markup, TILE_ACTION_OPENS_NEW_WINDOW);
175
TILE (this)->default_action = action;
177
menu_item = GTK_WIDGET (GTK_WIDGET (tile_action_get_menu_item (action)));
179
TILE (this)->actions [DIRECTORY_TILE_ACTION_OPEN] = action;
181
gtk_container_add (menu_ctnr, menu_item);
183
/* insert separator */
185
menu_item = gtk_separator_menu_item_new ();
186
gtk_container_add (menu_ctnr, menu_item);
188
/* make rename action */
190
action = tile_action_new (TILE (this), rename_trigger, _("Rename..."), 0);
191
TILE (this)->actions[DIRECTORY_TILE_ACTION_RENAME] = action;
193
menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
194
gtk_container_add (menu_ctnr, menu_item);
196
/* make send to action */
198
/* Only allow Send To for local files, ideally this would use something
199
* equivalent to gnome_vfs_uri_is_local, but that method will stat the file and
200
* that can hang in some conditions. */
202
if (!strncmp (TILE (this)->uri, "file://", 7))
204
action = tile_action_new (TILE (this), send_to_trigger, _("Send To..."),
205
TILE_ACTION_OPENS_NEW_WINDOW);
207
menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
213
menu_item = gtk_menu_item_new_with_label (_("Send To..."));
214
gtk_widget_set_sensitive (menu_item, FALSE);
217
TILE (this)->actions[DIRECTORY_TILE_ACTION_SEND_TO] = action;
219
gtk_container_add (menu_ctnr, menu_item);
221
/* insert separator */
223
menu_item = gtk_separator_menu_item_new ();
224
gtk_container_add (menu_ctnr, menu_item);
226
/* make move to trash action */
228
action = tile_action_new (TILE (this), move_to_trash_trigger, _("Move to Trash"), 0);
229
TILE (this)->actions[DIRECTORY_TILE_ACTION_MOVE_TO_TRASH] = action;
231
menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
232
gtk_container_add (menu_ctnr, menu_item);
234
/* make delete action */
236
if (priv->delete_enabled)
238
action = tile_action_new (TILE (this), delete_trigger, _("Delete"), 0);
239
TILE (this)->actions[DIRECTORY_TILE_ACTION_DELETE] = action;
241
menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
242
gtk_container_add (menu_ctnr, menu_item);
245
gtk_widget_show_all (GTK_WIDGET (TILE (this)->context_menu));
249
accessible = gtk_widget_get_accessible (GTK_WIDGET (this));
251
atk_object_set_name (accessible, basename);
255
return GTK_WIDGET (this);
259
directory_tile_private_setup (DirectoryTile *tile)
261
DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
265
priv->default_app = g_app_info_get_default_for_type (priv->mime_type, TRUE);
267
priv->default_app = NULL;
269
priv->delete_enabled =
270
(gboolean) GPOINTER_TO_INT (get_gconf_value (GCONF_ENABLE_DELETE_KEY));
272
client = gconf_client_get_default ();
274
gconf_client_add_dir (client, GCONF_ENABLE_DELETE_KEY_DIR, GCONF_CLIENT_PRELOAD_NONE, NULL);
275
priv->gconf_conn_id =
276
connect_gconf_notify (GCONF_ENABLE_DELETE_KEY, gconf_enable_delete_cb, tile);
278
g_object_unref (client);
282
directory_tile_init (DirectoryTile *tile)
284
DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
286
priv->default_app = NULL;
287
priv->basename = NULL;
288
priv->header_bin = NULL;
289
priv->icon_name = NULL;
290
priv->mime_type = NULL;
291
priv->image_is_broken = TRUE;
292
priv->delete_enabled = FALSE;
293
priv->gconf_conn_id = 0;
297
directory_tile_finalize (GObject *g_object)
299
DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (g_object);
303
g_free (priv->basename);
304
g_free (priv->icon_name);
305
g_free (priv->mime_type);
307
if (priv->default_app)
308
g_object_unref (priv->default_app);
310
client = gconf_client_get_default ();
312
gconf_client_notify_remove (client, priv->gconf_conn_id);
313
gconf_client_remove_dir (client, GCONF_ENABLE_DELETE_KEY_DIR, NULL);
315
g_object_unref (client);
317
(* G_OBJECT_CLASS (directory_tile_parent_class)->finalize) (g_object);
321
directory_tile_style_set (GtkWidget *widget, GtkStyle *prev_style)
323
load_image (DIRECTORY_TILE (widget));
327
load_image (DirectoryTile *tile)
329
DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
333
icon_name = priv->icon_name;
335
icon_name = "folder";
337
priv->image_is_broken = slab_load_image (
338
GTK_IMAGE (NAMEPLATE_TILE (tile)->image), GTK_ICON_SIZE_DND, icon_name);
342
create_header (const gchar *name)
344
GtkWidget *header_bin;
347
header = gtk_label_new (name);
348
gtk_label_set_ellipsize (GTK_LABEL (header), PANGO_ELLIPSIZE_END);
349
gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5);
351
header_bin = gtk_alignment_new (0.0, 0.5, 1.0, 0.0);
352
gtk_container_add (GTK_CONTAINER (header_bin), header);
354
g_signal_connect (G_OBJECT (header), "size-allocate", G_CALLBACK (header_size_allocate_cb),
361
header_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, gpointer user_data)
363
gtk_widget_set_size_request (widget, alloc->width, -1);
367
rename_entry_activate_cb (GtkEntry *entry, gpointer user_data)
369
DirectoryTile *tile = DIRECTORY_TILE (user_data);
370
DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
383
GError *error = NULL;
385
if (strlen (gtk_entry_get_text (entry)) < 1)
388
src_file = g_file_new_for_uri (TILE (tile)->uri);
390
src_path = g_filename_from_uri (TILE (tile)->uri, NULL, NULL);
391
dirname = g_path_get_dirname (src_path);
392
dst_uri = g_build_filename (dirname, gtk_entry_get_text (entry), NULL);
393
dst_file = g_file_new_for_uri (dst_uri);
398
res = g_file_move (src_file, dst_file,
399
G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
402
g_free (priv->basename);
403
priv->basename = g_strdup (gtk_entry_get_text (entry));
406
g_warning ("unable to move [%s] to [%s]: %s\n", TILE (tile)->uri, dst_uri,
408
g_error_free (error);
412
g_object_unref (src_file);
413
g_object_unref (dst_file);
415
header = gtk_label_new (priv->basename);
416
gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5);
418
child = gtk_bin_get_child (priv->header_bin);
421
gtk_widget_destroy (child);
423
gtk_container_add (GTK_CONTAINER (priv->header_bin), header);
425
gtk_widget_show (header);
429
rename_entry_key_release_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
435
gconf_enable_delete_cb (GConfClient *client, guint conn_id, GConfEntry *entry, gpointer user_data)
437
Tile *tile = TILE (user_data);
438
DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (user_data);
441
gboolean delete_enabled;
444
GtkWidget *menu_item;
446
menu = GTK_MENU_SHELL (tile->context_menu);
448
delete_enabled = gconf_value_get_bool (entry->value);
450
if (delete_enabled == priv->delete_enabled)
453
priv->delete_enabled = delete_enabled;
455
if (priv->delete_enabled)
457
action = tile_action_new (tile, delete_trigger, _("Delete"), 0);
458
tile->actions[DIRECTORY_TILE_ACTION_DELETE] = action;
460
menu_item = GTK_WIDGET (tile_action_get_menu_item (action));
461
gtk_menu_shell_insert (menu, menu_item, 7);
463
gtk_widget_show_all (menu_item);
467
g_object_unref (tile->actions[DIRECTORY_TILE_ACTION_DELETE]);
469
tile->actions[DIRECTORY_TILE_ACTION_DELETE] = NULL;
474
rename_trigger (Tile *tile, TileEvent *event, TileAction *action)
476
DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
482
entry = gtk_entry_new ();
483
gtk_entry_set_text (GTK_ENTRY (entry), priv->basename);
484
gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
486
child = gtk_bin_get_child (priv->header_bin);
489
gtk_widget_destroy (child);
491
gtk_container_add (GTK_CONTAINER (priv->header_bin), entry);
493
g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (rename_entry_activate_cb), tile);
495
g_signal_connect (G_OBJECT (entry), "key_release_event",
496
G_CALLBACK (rename_entry_key_release_cb), NULL);
498
gtk_widget_show (entry);
499
gtk_widget_grab_focus (entry);
503
move_to_trash_trigger (Tile *tile, TileEvent *event, TileAction *action)
507
GError *error = NULL;
509
src_file = g_file_new_for_uri (TILE (tile)->uri);
511
res = g_file_trash (src_file, NULL, &error);
513
g_warning ("unable to move [%s] to the trash: %s\n", TILE (tile)->uri,
515
g_error_free (error);
518
g_object_unref (src_file);
522
delete_trigger (Tile *tile, TileEvent *event, TileAction *action)
524
GtkDialog *confirm_dialog;
529
GError *error = NULL;
531
if (GPOINTER_TO_INT (libslab_get_gconf_value (GCONF_CONFIRM_DELETE_KEY))) {
532
confirm_dialog = GTK_DIALOG(gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING,
533
GTK_BUTTONS_NONE, _("Are you sure you want to permanently delete \"%s\"?"), DIRECTORY_TILE_GET_PRIVATE (tile)->basename));
534
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(confirm_dialog), _("If you delete an item, it is permanently lost."));
536
gtk_dialog_add_button (confirm_dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
537
gtk_dialog_add_button (confirm_dialog, GTK_STOCK_DELETE, GTK_RESPONSE_YES);
538
gtk_dialog_set_default_response (GTK_DIALOG (confirm_dialog), GTK_RESPONSE_YES);
540
result = gtk_dialog_run (confirm_dialog);
542
gtk_widget_destroy (GTK_WIDGET (confirm_dialog));
544
if (result != GTK_RESPONSE_YES)
548
src_file = g_file_new_for_uri (TILE (tile)->uri);
550
res = g_file_delete (src_file, NULL, &error);
553
g_warning ("unable to delete [%s]: %s\n", TILE (tile)->uri,
555
g_error_free (error);
558
g_object_unref (src_file);
562
send_to_trigger (Tile *tile, TileEvent *event, TileAction *action)
566
gchar **argv_parsed = NULL;
573
GError *error = NULL;
578
cmd = (gchar *) get_gconf_value (GCONF_SEND_TO_CMD_KEY);
580
if (! g_shell_parse_argv (cmd, & argc, & argv_parsed, NULL))
583
argv = g_new0 (gchar *, argc + 1);
585
path = g_filename_from_uri (tile->uri, NULL, NULL);
586
dirname = g_path_get_dirname (path);
587
basename = g_path_get_basename (path);
589
for (i = 0; i < argc; ++i) {
590
if (strstr (argv_parsed [i], "DIRNAME"))
591
argv [i] = string_replace_once (argv_parsed [i], "DIRNAME", dirname);
592
else if (strstr (argv_parsed [i], "BASENAME"))
593
argv [i] = string_replace_once (argv_parsed [i], "BASENAME", basename);
595
argv [i] = g_strdup (argv_parsed [i]);
605
NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
606
disown_spawned_child, NULL, NULL, & error);
609
cmd = g_strjoinv (" ", argv);
610
libslab_handle_g_error (
611
& error, "%s: can't execute search [%s]\n", G_STRFUNC, cmd);
620
g_strfreev (argv_parsed);
624
disown_spawned_child (gpointer user_data)
631
open_with_default_trigger (Tile *tile, TileEvent *event, TileAction *action)
633
DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile);
636
GdkAppLaunchContext *launch_context;
637
GError *error = NULL;
639
if (priv->default_app)
641
uris = g_list_append (uris, TILE (tile)->uri);
643
launch_context = gdk_app_launch_context_new ();
644
gdk_app_launch_context_set_screen (launch_context,
645
gtk_widget_get_screen (GTK_WIDGET (tile)));
646
gdk_app_launch_context_set_timestamp (launch_context,
649
res = g_app_info_launch_uris (priv->default_app, uris,
650
G_APP_LAUNCH_CONTEXT (launch_context),
655
("error: could not launch application with [%s]: %s\n",
656
TILE (tile)->uri, error->message);
657
g_error_free (error);
661
g_object_unref (launch_context);
664
cmd = string_replace_once (
665
get_slab_gconf_string (SLAB_FILE_MANAGER_OPEN_CMD), "FILE_URI", tile->uri);