nautilus-property-browser.c 76.5 KB
Newer Older
1 2
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

3 4 5
/*
 * Nautilus
 *
6 7
 * Copyright (C) 2000 Eazel, Inc.
 *
8
 * Nautilus is free software; you can redistribute it and/or modify
9 10 11 12
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
13
 * Nautilus is distributed in the hope that it will be useful,
14 15 16 17 18 19 20 21 22
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author: Andy Hertzfeld <andy@eazel.com>
23 24 25 26 27 28
 */

/* This is the implementation of the property browser window, which
 * gives the user access to an extensible palette of properties which
 * can be dropped on various elements of the user interface to
 * customize them 
29 30 31
 */

#include <config.h>
32
#include <math.h>
33 34
#include "nautilus-property-browser.h"

Ramiro Estrugo's avatar
Ramiro Estrugo committed
35 36 37 38 39 40 41 42 43 44 45
#include <eel/eel-gdk-extensions.h>
#include <eel/eel-gdk-pixbuf-extensions.h>
#include <eel/eel-glib-extensions.h>
#include <eel/eel-gnome-extensions.h>
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-gtk-macros.h>
#include <eel/eel-image-table.h>
#include <eel/eel-labeled-image.h>
#include <eel/eel-stock-dialogs.h>
#include <eel/eel-string.h>
#include <eel/eel-xml-extensions.h>
Alexander Larsson's avatar
Alexander Larsson committed
46
#include <librsvg/rsvg.h>
47
#include <libxml/parser.h>
48
#include <gtk/gtkalignment.h>
49
#include <gtk/gtkcolorseldialog.h>
50
#include <gtk/gtkmessagedialog.h>
51 52 53 54 55 56
#include <gtk/gtkdnd.h>
#include <gtk/gtkentry.h>
#include <gtk/gtkeventbox.h>
#include <gtk/gtkfilesel.h>
#include <gtk/gtkframe.h>
#include <gtk/gtkhbox.h>
57
#include <gtk/gtkimage.h>
58 59 60
#include <gtk/gtklabel.h>
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtkselection.h>
61
#include <gtk/gtksignal.h>
62
#include <gtk/gtkstock.h>
63 64
#include <gtk/gtktable.h>
#include <gtk/gtktogglebutton.h>
65
#include <gtk/gtkradiobutton.h>
66 67
#include <gtk/gtkvbox.h>
#include <gtk/gtkviewport.h>
68
#include <glib/gi18n.h>
Alexander Larsson's avatar
Alexander Larsson committed
69
#include <glib/gstdio.h>
70
#include <gio/gio.h>
71
#include <libgnome/gnome-util.h>
72
#include <libgnome/gnome-help.h>
73
#include <libgnomeui/gnome-color-picker.h>
74
#include <libgnomeui/gnome-icon-entry.h>
75
#include <libgnomeui/gnome-help.h>
76
#include <libgnomeui/gnome-stock-icons.h>
77
#include <libgnomeui/gnome-uidefs.h>
78 79
#include <libnautilus-private/nautilus-customization-data.h>
#include <libnautilus-private/nautilus-directory.h>
80
#include <libnautilus-private/nautilus-emblem-utils.h>
81 82 83 84
#include <libnautilus-private/nautilus-file-utilities.h>
#include <libnautilus-private/nautilus-file.h>
#include <libnautilus-private/nautilus-global-preferences.h>
#include <libnautilus-private/nautilus-metadata.h>
Alexander Larsson's avatar
Alexander Larsson committed
85
#include <libnautilus-private/nautilus-signaller.h>
86
#include <atk/atkrelationset.h>
87

88 89 90 91
/* property types */

typedef enum {
	NAUTILUS_PROPERTY_NONE,
92
	NAUTILUS_PROPERTY_PATTERN,
93 94 95 96
	NAUTILUS_PROPERTY_COLOR,
	NAUTILUS_PROPERTY_EMBLEM
} NautilusPropertyType;

97
struct NautilusPropertyBrowserDetails {
98
	GtkHBox *container;
99
	
100
	GtkWidget *content_container;
101 102 103
	GtkWidget *content_frame;
	GtkWidget *content_table;
	
104
	GtkWidget *category_container;
105
	GtkWidget *category_box;
106
	
107 108
	GtkWidget *title_box;
	GtkWidget *title_label;
109 110 111
	GtkWidget *help_label;
	
	GtkWidget *bottom_box;
112
	
113
	GtkWidget *add_button;
114 115
	GtkWidget *add_button_label;
	GtkWidget *add_button_image;
116 117
	GtkWidget *remove_button;
	GtkWidget *remove_button_label;
118
	GtkWidget *remove_button_image;
119
	
120 121 122
	GtkWidget *patterns_dialog;
	GtkWidget *colors_dialog;
	GtkWidget *emblems_dialog;
123
	
124 125 126 127
	GtkWidget *keyword;
	GtkWidget *emblem_image;
	GtkWidget *file_entry;
	
128 129 130
	GtkWidget *color_picker;
	GtkWidget *color_name;
	
131 132
	GList *keywords;
	
133 134 135
	char *path;
	char *category;
	char *dragged_file;
136
	char *drag_type;
137 138
	char *image_path;
	
139 140
	NautilusPropertyType category_type;
	
141
	int category_position;
142 143 144

	GdkPixbuf *property_chit;
		
Darin Adler's avatar
Darin Adler committed
145 146 147
	gboolean remove_mode;
	gboolean keep_around;
	gboolean has_local;
148 149
};

150 151
static void     nautilus_property_browser_class_init      (GtkObjectClass                *object_klass);
static void     nautilus_property_browser_init            (GtkObject                     *object);
152 153 154 155 156 157 158 159 160 161 162 163 164
static void     nautilus_property_browser_destroy               (GtkObject                     *object);
static void     nautilus_property_browser_update_contents       (NautilusPropertyBrowser       *property_browser);
static void     nautilus_property_browser_set_category          (NautilusPropertyBrowser       *property_browser,
								 const char                    *new_category);
static void     nautilus_property_browser_set_dragged_file      (NautilusPropertyBrowser       *property_browser,
								 const char                    *dragged_file_name);
static void     nautilus_property_browser_set_drag_type         (NautilusPropertyBrowser       *property_browser,
								 const char                    *new_drag_type);
static void     add_new_button_callback                         (GtkWidget                     *widget,
								 NautilusPropertyBrowser       *property_browser);
static void     cancel_remove_mode                              (NautilusPropertyBrowser       *property_browser);
static void     done_button_callback                            (GtkWidget                     *widget,
								 GtkWidget                     *property_browser);
165 166
static void     help_button_callback                            (GtkWidget                     *widget,
								 GtkWidget                     *property_browser);
167 168 169 170 171 172 173 174 175
static void     remove_button_callback                          (GtkWidget                     *widget,
								 NautilusPropertyBrowser       *property_browser);
static gboolean nautilus_property_browser_delete_event_callback (GtkWidget                     *widget,
								 GdkEvent                      *event,
								 gpointer                       user_data);
static void     nautilus_property_browser_hide_callback         (GtkWidget                     *widget,
								 gpointer                       user_data);
static void     nautilus_property_browser_drag_end              (GtkWidget                     *widget,
								 GdkDragContext                *context);
176 177
static void     nautilus_property_browser_drag_begin            (GtkWidget                     *widget,
								 GdkDragContext                *context);
178 179 180 181 182 183
static void     nautilus_property_browser_drag_data_get         (GtkWidget                     *widget,
								 GdkDragContext                *context,
								 GtkSelectionData              *selection_data,
								 guint                          info,
								 guint32                        time);
static void     nautilus_property_browser_theme_changed         (gpointer                       user_data);
184
static void     emit_emblems_changed_signal                     (void);
Alexander Larsson's avatar
Alexander Larsson committed
185 186
static void     emblems_changed_callback                        (GObject                       *signaller,
								 NautilusPropertyBrowser       *property_browser);
187 188

/* misc utilities */
189 190 191
static char *   strip_extension                                 (const char                    *string_to_strip);
static void     element_clicked_callback                        (GtkWidget                     *image_table,
								 GtkWidget                     *child,
Ramiro Estrugo's avatar
Ramiro Estrugo committed
192
								 const EelImageTableEvent *event,
193
								 gpointer                       callback_data);
194

195 196 197 198 199 200
static GdkPixbuf * make_drag_image                              (NautilusPropertyBrowser       *property_browser,
								 const char                    *file_name);
static GdkPixbuf * make_color_drag_image                        (NautilusPropertyBrowser       *property_browser,
								 const char                    *color_spec,
								 gboolean                       trim_edges);

201

202
#define BROWSER_CATEGORIES_FILE_NAME "browser.xml"
203

204 205
#define PROPERTY_BROWSER_WIDTH 540
#define PROPERTY_BROWSER_HEIGHT 340
206 207
#define MAX_EMBLEM_HEIGHT 52
#define STANDARD_BUTTON_IMAGE_HEIGHT 42
208

209 210
#define MAX_ICON_WIDTH 63
#define MAX_ICON_HEIGHT 63
211
#define COLOR_SQUARE_SIZE 48
212

213 214 215
#define LABELED_IMAGE_SPACING 2
#define IMAGE_TABLE_X_SPACING 6
#define IMAGE_TABLE_Y_SPACING 4
216

217 218
#define ERASE_OBJECT_NAME "erase.png"

219
enum {
220
	PROPERTY_TYPE
221 222 223 224 225 226
};

static GtkTargetEntry drag_types[] = {
	{ "text/uri-list",  0, PROPERTY_TYPE }
};

227

228
EEL_CLASS_BOILERPLATE (NautilusPropertyBrowser,
229 230
				   nautilus_property_browser,
				   GTK_TYPE_WINDOW)
231 232 233

/* initializing the class object by installing the operations we override */
static void
234
nautilus_property_browser_class_init (GtkObjectClass *object_klass)
235 236 237 238 239 240 241
{
	NautilusPropertyBrowserClass *klass;
	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (object_klass);

	klass = NAUTILUS_PROPERTY_BROWSER_CLASS (object_klass);

	object_klass->destroy = nautilus_property_browser_destroy;
242
	widget_class->drag_begin = nautilus_property_browser_drag_begin;
243 244 245 246 247 248 249
	widget_class->drag_data_get  = nautilus_property_browser_drag_data_get;
	widget_class->drag_end  = nautilus_property_browser_drag_end;
}

/* initialize the instance's fields, create the necessary subviews, etc. */

static void
250
nautilus_property_browser_init (GtkObject *object)
251
{
252
 	NautilusPropertyBrowser *property_browser;
253
 	GtkWidget *widget, *temp_box, *temp_hbox, *temp_frame, *vbox;
254
	GtkWidget *temp_button, *align;
255
	GtkWidget *viewport;
256
	char *temp_str;
257 258 259 260 261 262
	
	property_browser = NAUTILUS_PROPERTY_BROWSER (object);
	widget = GTK_WIDGET (object);

	property_browser->details = g_new0 (NautilusPropertyBrowserDetails, 1);

263 264
	property_browser->details->category = g_strdup ("patterns");
	property_browser->details->category_type = NAUTILUS_PROPERTY_PATTERN;
265
		
266 267
	/* load the chit frame */
	temp_str = nautilus_pixmap_file ("chit_frame.png");
268 269 270
	if (temp_str != NULL) {
		property_browser->details->property_chit = gdk_pixbuf_new_from_file (temp_str, NULL);
	}
271
	g_free (temp_str);
272 273
	
	/* set the initial size of the property browser */
274 275 276
	gtk_window_set_default_size (GTK_WINDOW (property_browser),
				     PROPERTY_BROWSER_WIDTH,
				     PROPERTY_BROWSER_HEIGHT);
277

278
	/* set the title and standard close accelerator */
279 280
	gtk_window_set_title (GTK_WINDOW (widget), _("Backgrounds and Emblems"));
	gtk_window_set_wmclass (GTK_WINDOW (widget), "property_browser", "Nautilus");
Ramiro Estrugo's avatar
Ramiro Estrugo committed
281
	eel_gtk_window_set_up_close_accelerator (GTK_WINDOW (widget));
282 283

	/* create the main vbox. */
284 285
  	vbox = gtk_vbox_new (FALSE, 12);
	gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
286 287 288
	gtk_widget_show (vbox);
	gtk_container_add (GTK_CONTAINER (property_browser), vbox);

289
	/* create the container box */  
290
  	property_browser->details->container = GTK_HBOX (gtk_hbox_new (FALSE, 6));
291
	gtk_widget_show (GTK_WIDGET (property_browser->details->container));
292 293 294
	gtk_box_pack_start (GTK_BOX (vbox),
			    GTK_WIDGET (property_browser->details->container),
			    TRUE, TRUE, 0);	
295

296 297
	/* make the category container */
	property_browser->details->category_container = gtk_scrolled_window_new (NULL, NULL);
298
 	property_browser->details->category_position = -1;	
299
 	
Darin Adler's avatar
Darin Adler committed
300
 	viewport = gtk_viewport_new (NULL, NULL);	
301
	gtk_widget_show (viewport);
302
	gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
303

304 305
	gtk_box_pack_start (GTK_BOX (property_browser->details->container),
			    property_browser->details->category_container, FALSE, FALSE, 0);
306
	gtk_widget_show (property_browser->details->category_container);
307 308
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (property_browser->details->category_container),
					GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
309 310

	/* allocate a table to hold the category selector */
311
  	property_browser->details->category_box = gtk_vbox_new (FALSE, 6);
312
	gtk_container_add(GTK_CONTAINER(viewport), property_browser->details->category_box); 
313
	gtk_container_add (GTK_CONTAINER (property_browser->details->category_container), viewport);
314
	gtk_widget_show (GTK_WIDGET (property_browser->details->category_box));
315 316

	/* make the content container vbox */
317
  	property_browser->details->content_container = gtk_vbox_new (FALSE, 6);
318
	gtk_widget_show (property_browser->details->content_container);
319 320 321
	gtk_box_pack_start (GTK_BOX (property_browser->details->container),
			    property_browser->details->content_container,
			    TRUE, TRUE, 0);
322
	
323 324
  	/* create the title box */
  	property_browser->details->title_box = gtk_event_box_new();
325
 	
326
  	gtk_widget_show(property_browser->details->title_box);
327 328 329
	gtk_box_pack_start (GTK_BOX(property_browser->details->content_container),
			    property_browser->details->title_box,
			    FALSE, FALSE, 0);
330 331
  	
  	temp_frame = gtk_frame_new(NULL);
332
  	gtk_frame_set_shadow_type(GTK_FRAME(temp_frame), GTK_SHADOW_NONE);
333 334 335 336 337
  	gtk_widget_show(temp_frame);
  	gtk_container_add(GTK_CONTAINER(property_browser->details->title_box), temp_frame);
  	
  	temp_hbox = gtk_hbox_new(FALSE, 0);
  	gtk_widget_show(temp_hbox);
338
 
339
  	gtk_container_add(GTK_CONTAINER(temp_frame), temp_hbox);
340
 	
341
	/* add the title label */
Darin Adler's avatar
Darin Adler committed
342
	property_browser->details->title_label = gtk_label_new ("");
343
	eel_gtk_label_set_scale (GTK_LABEL (property_browser->details->title_label), PANGO_SCALE_X_LARGE);
344
	eel_gtk_label_make_bold (GTK_LABEL (property_browser->details->title_label));
345 346
 	
	gtk_widget_show(property_browser->details->title_label);
347
	gtk_box_pack_start (GTK_BOX(temp_hbox), property_browser->details->title_label, FALSE, FALSE, 0);
348 349
 
 	/* add the help label */
Darin Adler's avatar
Darin Adler committed
350
	property_browser->details->help_label = gtk_label_new  ("");
351
	gtk_widget_show(property_browser->details->help_label);
352
	gtk_box_pack_end (GTK_BOX (temp_hbox), property_browser->details->help_label, FALSE, FALSE, 0);
353 354 355 356 357 358
 	 	
  	/* add the bottom box to hold the command buttons */
  	temp_box = gtk_event_box_new();
  	gtk_widget_show(temp_box);

  	temp_frame = gtk_frame_new(NULL);
359
  	gtk_frame_set_shadow_type(GTK_FRAME(temp_frame), GTK_SHADOW_NONE);
360 361 362
  	gtk_widget_show(temp_frame);
  	gtk_container_add(GTK_CONTAINER(temp_box), temp_frame);

363
  	property_browser->details->bottom_box = gtk_hbox_new (FALSE, 6);
364 365
	gtk_widget_show (property_browser->details->bottom_box);
	
366
	gtk_box_pack_end (GTK_BOX (vbox), temp_box, FALSE, FALSE, 0);
367
  	gtk_container_add (GTK_CONTAINER (temp_frame), property_browser->details->bottom_box);
368 369 370 371 372
  
  	/* create the "help" button */
	temp_button = gtk_button_new_from_stock (GTK_STOCK_HELP);

	gtk_widget_show (temp_button);
373
	gtk_box_pack_start (GTK_BOX (property_browser->details->bottom_box), temp_button, FALSE, FALSE, 0);
374 375
	g_signal_connect_object (temp_button, "clicked", G_CALLBACK (help_button_callback), property_browser, 0);
	
376
  	/* create the "done" button */
Darin Adler's avatar
Darin Adler committed
377
 	temp_button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
378
	GTK_WIDGET_SET_FLAGS (temp_button, GTK_CAN_DEFAULT);
379

380
	gtk_widget_show (temp_button);
381
	gtk_box_pack_end (GTK_BOX (property_browser->details->bottom_box), temp_button, FALSE, FALSE, 0);
382 383
	gtk_widget_grab_default (temp_button);
	gtk_widget_grab_focus (temp_button);
384
 	g_signal_connect_object (temp_button, "clicked", G_CALLBACK (done_button_callback), property_browser, 0);
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400

	/* create the "remove" button */
  	property_browser->details->remove_button = gtk_button_new();
	property_browser->details->remove_button_label = gtk_label_new_with_mnemonic (_("_Remove..."));	
	gtk_label_set_mnemonic_widget (GTK_LABEL (property_browser->details->remove_button_label),
				       GTK_WIDGET (property_browser->details->remove_button));
	property_browser->details->remove_button_image = gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_BUTTON);
	temp_hbox = gtk_hbox_new (FALSE, 2);
	align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
	gtk_box_pack_start (GTK_BOX (temp_hbox), property_browser->details->remove_button_image, FALSE, FALSE, 0);
	gtk_box_pack_end (GTK_BOX (temp_hbox), property_browser->details->remove_button_label, FALSE, FALSE, 0);
	gtk_container_add (GTK_CONTAINER (property_browser->details->remove_button), align);
	gtk_container_add (GTK_CONTAINER (align), temp_hbox);
	gtk_widget_show_all (property_browser->details->remove_button);

	gtk_box_pack_end (GTK_BOX (property_browser->details->bottom_box),
401
			  property_browser->details->remove_button, FALSE, FALSE, 0);
402 403 404 405 406
	
 	g_signal_connect_object (property_browser->details->remove_button, "clicked",
				 G_CALLBACK (remove_button_callback), property_browser, 0);

  	/* now create the "add new" button */
Darin Adler's avatar
Darin Adler committed
407
  	property_browser->details->add_button = gtk_button_new ();
408
	property_browser->details->add_button_label = gtk_label_new_with_mnemonic (_("_Add new..."));
409 410 411
	gtk_label_set_mnemonic_widget (GTK_LABEL (property_browser->details->add_button_label),
				       GTK_WIDGET (property_browser->details->add_button));

412
	property_browser->details->add_button_image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
413 414
	temp_hbox = gtk_hbox_new (FALSE, 2);
	align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
415
	gtk_box_pack_start (GTK_BOX (temp_hbox), property_browser->details->add_button_image, FALSE, FALSE, 0);
416 417 418 419 420
	gtk_box_pack_end (GTK_BOX (temp_hbox), property_browser->details->add_button_label, FALSE, FALSE, 0);
	gtk_container_add (GTK_CONTAINER (property_browser->details->add_button), align);
	gtk_container_add (GTK_CONTAINER (align), temp_hbox);
	gtk_widget_show_all (property_browser->details->add_button);

421
	gtk_box_pack_end (GTK_BOX(property_browser->details->bottom_box),
422
			  property_browser->details->add_button, FALSE, FALSE, 0);
423
 	  
424 425
 	g_signal_connect_object (property_browser->details->add_button, "clicked",
				 G_CALLBACK (add_new_button_callback), property_browser, 0);
426 427
	

428 429
	/* now create the actual content, with the category pane and the content frame */	
	
430
	/* the actual contents are created when necessary */	
431 432
  	property_browser->details->content_frame = NULL;

433
	/* add a callback for when the theme changes */
434 435 436
	eel_preferences_add_callback (NAUTILUS_PREFERENCES_THEME, 
				      nautilus_property_browser_theme_changed,
				      property_browser);	
437
	
438
	g_signal_connect (property_browser, "delete_event",
439
			  G_CALLBACK (nautilus_property_browser_delete_event_callback), NULL);
440
	g_signal_connect (property_browser, "hide",
441
			  G_CALLBACK (nautilus_property_browser_hide_callback), NULL);
442

Alexander Larsson's avatar
Alexander Larsson committed
443 444 445 446
	g_signal_connect_object (nautilus_signaller_get_current (),
				 "emblems_changed",
				 G_CALLBACK (emblems_changed_callback), property_browser, 0);

447 448 449 450
	/* initially, display the top level */
	nautilus_property_browser_set_path(property_browser, BROWSER_CATEGORIES_FILE_NAME);
}

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
/* Destroy the three dialogs for adding patterns/colors/emblems if any of them
   exist. */
static void
nautilus_property_browser_destroy_dialogs (NautilusPropertyBrowser *property_browser)
{
	if (property_browser->details->patterns_dialog) {
		gtk_widget_destroy (property_browser->details->patterns_dialog);
		property_browser->details->patterns_dialog = NULL;
	}
	if (property_browser->details->colors_dialog) {
		gtk_widget_destroy (property_browser->details->colors_dialog);
		property_browser->details->colors_dialog = NULL;
	}
	if (property_browser->details->emblems_dialog) {
		gtk_widget_destroy (property_browser->details->emblems_dialog);
		property_browser->details->emblems_dialog = NULL;
	}
}

470 471 472 473 474
static void
nautilus_property_browser_destroy (GtkObject *object)
{
	NautilusPropertyBrowser *property_browser;

475
	
476
	property_browser = NAUTILUS_PROPERTY_BROWSER (object);
477

478 479
	nautilus_property_browser_destroy_dialogs (property_browser);

Darin Adler's avatar
Darin Adler committed
480 481 482 483
	g_free (property_browser->details->path);
	g_free (property_browser->details->category);
	g_free (property_browser->details->dragged_file);
	g_free (property_browser->details->drag_type);
484

Ramiro Estrugo's avatar
Ramiro Estrugo committed
485
	eel_g_list_free_deep (property_browser->details->keywords);
486
		
487
	if (property_browser->details->property_chit) {
488
		g_object_unref (property_browser->details->property_chit);
489 490
	}
	
491
	g_free (property_browser->details);
492
	
493 494 495
	eel_preferences_remove_callback (NAUTILUS_PREFERENCES_THEME,
					 nautilus_property_browser_theme_changed,
					 property_browser);
496
		
Ramiro Estrugo's avatar
Ramiro Estrugo committed
497
	EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
498 499 500 501
}

/* create a new instance */
NautilusPropertyBrowser *
502
nautilus_property_browser_new (GdkScreen *screen)
503
{
504 505 506 507
	NautilusPropertyBrowser *browser;

	browser = NAUTILUS_PROPERTY_BROWSER
		(gtk_widget_new (nautilus_property_browser_get_type (), NULL));
508

509
	gtk_window_set_screen (GTK_WINDOW (browser), screen);
510 511 512 513 514
  	gtk_widget_show (GTK_WIDGET(browser));
	
	return browser;
}

515 516 517
/* show the main property browser */

void
518
nautilus_property_browser_show (GdkScreen *screen)
519
{
520 521 522 523 524 525
	static GtkWindow *browser = NULL;

	if (browser == NULL) {
		browser = GTK_WINDOW (nautilus_property_browser_new (screen));
		g_object_add_weak_pointer (G_OBJECT (browser),
					   (gpointer *) &browser);
526
	} else {
527 528
		gtk_window_set_screen (browser, screen);
		gtk_window_present (browser);
529 530 531 532 533 534 535 536 537 538 539 540 541
	}
}

static gboolean
nautilus_property_browser_delete_event_callback (GtkWidget *widget,
					   GdkEvent  *event,
					   gpointer   user_data)
{
	/* Hide but don't destroy */
	gtk_widget_hide(widget);
	return TRUE;
}

542 543 544 545
static void
nautilus_property_browser_hide_callback (GtkWidget *widget,
					 gpointer   user_data)
{
546 547 548 549 550 551 552 553
	NautilusPropertyBrowser *property_browser;

	property_browser = NAUTILUS_PROPERTY_BROWSER (widget);

	cancel_remove_mode (property_browser);

	/* Destroy the 3 dialogs to add new patterns/colors/emblems. */
	nautilus_property_browser_destroy_dialogs (property_browser);
554 555
}

556 557
/* remember the name of the dragged file */
static void
Darin Adler's avatar
Darin Adler committed
558 559
nautilus_property_browser_set_dragged_file (NautilusPropertyBrowser *property_browser,
					    const char *dragged_file_name)
560
{       
Darin Adler's avatar
Darin Adler committed
561
	g_free (property_browser->details->dragged_file);
562 563 564 565 566
	property_browser->details->dragged_file = g_strdup (dragged_file_name);
}

/* remember the drag type */
static void
Darin Adler's avatar
Darin Adler committed
567 568
nautilus_property_browser_set_drag_type (NautilusPropertyBrowser *property_browser,
					 const char *new_drag_type)
569
{       
Darin Adler's avatar
Darin Adler committed
570
	g_free (property_browser->details->drag_type);
571 572 573
	property_browser->details->drag_type = g_strdup (new_drag_type);
}

574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
static void
nautilus_property_browser_drag_begin (GtkWidget *widget,
				      GdkDragContext *context)
{
	NautilusPropertyBrowser *property_browser;
	GtkWidget *child;
	GdkPixbuf *pixbuf;
	int x_delta, y_delta;
	char *element_name;

	property_browser = NAUTILUS_PROPERTY_BROWSER (widget);

	child = g_object_steal_data (G_OBJECT (property_browser), "dragged-image");
	g_return_if_fail (child != NULL);

	element_name = g_object_get_data (G_OBJECT (child), "property-name");
	g_return_if_fail (child != NULL);

	/* compute the offsets for dragging */
	if (strcmp (drag_types[0].target, "application/x-color") != 0) {
		/* it's not a color, so, for now, it must be an image */
		/* fiddle with the category to handle the "reset" case properly */
		char * save_category = property_browser->details->category;
		if (eel_strcmp (property_browser->details->category, "colors") == 0) {
			property_browser->details->category = "patterns";
		}
		pixbuf = make_drag_image (property_browser, element_name);
		property_browser->details->category = save_category;
	} else {
		pixbuf = make_color_drag_image (property_browser, element_name, TRUE);
	}

        /* set the pixmap and mask for dragging */       
	if (pixbuf != NULL) {
		x_delta = gdk_pixbuf_get_width (pixbuf) / 2;
		y_delta = gdk_pixbuf_get_height (pixbuf) / 2;

		gtk_drag_set_icon_pixbuf
			(context,
			 pixbuf,
			 x_delta, y_delta);
		g_object_unref (pixbuf);
	}

}


621 622 623 624
/* drag and drop data get handler */

static void
nautilus_property_browser_drag_data_get (GtkWidget *widget,
Darin Adler's avatar
Darin Adler committed
625 626 627 628
					 GdkDragContext *context,
					 GtkSelectionData *selection_data,
					 guint info,
					 guint32 time)
629
{
630
	char  *image_file_name, *image_file_uri;
631
	gboolean is_reset;
632 633 634 635 636 637 638
	NautilusPropertyBrowser *property_browser = NAUTILUS_PROPERTY_BROWSER(widget);
	
	g_return_if_fail (widget != NULL);
	g_return_if_fail (context != NULL);

	switch (info) {
	case PROPERTY_TYPE:
639 640 641 642
		/* formulate the drag data based on the drag type.  Eventually, we will
		   probably select the behavior from properties in the category xml definition,
		   but for now we hardwire it to the drag_type */
		
643
		is_reset = FALSE;
Michael Meeks's avatar
Michael Meeks committed
644 645
		if (strcmp (property_browser->details->drag_type,
			    "property/keyword") == 0) {
646 647 648 649 650
			char* keyword_str = strip_extension(property_browser->details->dragged_file);
		        gtk_selection_data_set(selection_data, selection_data->target, 8, keyword_str, strlen(keyword_str));
			g_free(keyword_str);
			return;	
		}
Michael Meeks's avatar
Michael Meeks committed
651 652
		else if (strcmp (property_browser->details->drag_type,
				 "application/x-color") == 0) {
653 654 655
		        GdkColor color;
			guint16 colorArray[4];
			
656
			/* handle the "reset" case as an image */
Ramiro Estrugo's avatar
Ramiro Estrugo committed
657
			if (eel_strcmp (property_browser->details->dragged_file, RESET_IMAGE_NAME) != 0) {
David Emory Watson's avatar
David Emory Watson committed
658 659
				eel_gdk_color_parse (property_browser->details->dragged_file, &color);

660 661 662 663
				colorArray[0] = color.red;
				colorArray[1] = color.green;
				colorArray[2] = color.blue;
				colorArray[3] = 0xffff;
David Emory Watson's avatar
David Emory Watson committed
664

665 666 667
				gtk_selection_data_set(selection_data,
				selection_data->target, 16, (const char *) &colorArray[0], 8);
				return;	
668 669
			} else {
				is_reset = TRUE;
670
			}
671

672
		}
673
		
Darin Adler's avatar
Darin Adler committed
674 675
		image_file_name = g_strdup_printf ("%s/%s/%s",
						   NAUTILUS_DATADIR,
676
						   is_reset ? "patterns" : property_browser->details->category,
677
						   property_browser->details->dragged_file);
678
		
679
		if (!g_file_test (image_file_name, G_FILE_TEST_EXISTS)) {
680
			char *user_directory;
681
			g_free (image_file_name);
682 683

			user_directory = nautilus_get_user_directory ();
684
			image_file_name = g_strdup_printf ("%s/%s/%s",
685
							   user_directory,
686 687
							   property_browser->details->category, 
							   property_browser->details->dragged_file);	
688 689

			g_free (user_directory);
690 691
		}

Alexander Larsson's avatar
Alexander Larsson committed
692
		image_file_uri = g_filename_to_uri (image_file_name, NULL, NULL);
693
		gtk_selection_data_set (selection_data, selection_data->target, 8, image_file_uri, strlen (image_file_uri));
694 695
		g_free (image_file_name);
		g_free (image_file_uri);
696 697 698 699 700 701 702 703 704 705
		
		break;
	default:
		g_assert_not_reached ();
	}
}

/* drag and drop end handler, where we destroy ourselves, since the transaction is complete */

static void
Darin Adler's avatar
Darin Adler committed
706
nautilus_property_browser_drag_end (GtkWidget *widget, GdkDragContext *context)
707
{
708
	NautilusPropertyBrowser *property_browser = NAUTILUS_PROPERTY_BROWSER(widget);
709
	if (!property_browser->details->keep_around) {
710
		gtk_widget_hide (GTK_WIDGET (widget));
711
	}
712 713
}

714 715
/* utility routine to check if the passed-in uri is an image file */
static gboolean
Alexander Larsson's avatar
Alexander Larsson committed
716 717 718 719 720
ensure_file_is_image (GFile *file)
{
	GFileInfo *info;
	const char *mime_type;

721
	info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0, NULL, NULL);
Alexander Larsson's avatar
Alexander Larsson committed
722 723 724 725 726 727 728 729 730 731 732 733 734 735
	if (info == NULL) {
		return FALSE;
	}

	mime_type = g_file_info_get_content_type (info);
	if (mime_type == NULL) {
		return FALSE;
	}

	g_object_unref (info);

	return  g_content_type_is_a (mime_type, "image/*") &&
		!g_content_type_equals (mime_type, "image/svg") &&
		!g_content_type_equals (mime_type, "image/svg+xml");
736
}
Darin Adler's avatar
Darin Adler committed
737

738
/* create the appropriate pixbuf for the passed in file */
739

740
static GdkPixbuf *
741
make_drag_image (NautilusPropertyBrowser *property_browser, const char* file_name)
742
{
743 744
	GdkPixbuf *pixbuf, *orig_pixbuf;
	char *image_file_name;
745
	char *icon_name;
746
	gboolean is_reset;
Alexander Larsson's avatar
Alexander Larsson committed
747
	NautilusIconInfo *info;
748 749

	if (property_browser->details->category_type == NAUTILUS_PROPERTY_EMBLEM) {
750
		if (strcmp (file_name, "erase") == 0) {
751 752
			pixbuf = NULL;

753
			image_file_name = nautilus_pixmap_file (ERASE_OBJECT_NAME);
754 755 756
			if (image_file_name != NULL) {
				pixbuf = gdk_pixbuf_new_from_file (image_file_name, NULL);
			}
757 758 759
			g_free (image_file_name);
		} else {
			icon_name = nautilus_emblem_get_icon_name_from_keyword (file_name);
Alexander Larsson's avatar
Alexander Larsson committed
760 761 762
			info = nautilus_icon_info_lookup_from_name (file_name, NAUTILUS_ICON_SIZE_STANDARD);
			pixbuf = nautilus_icon_info_get_pixbuf_at_size (info, NAUTILUS_ICON_SIZE_STANDARD);
			g_object_unref (info);
763 764
			g_free (icon_name);
		}
765 766 767
		return pixbuf;
	}

Darin Adler's avatar
Darin Adler committed
768 769
	image_file_name = g_strdup_printf ("%s/%s/%s",
					   NAUTILUS_DATADIR,
770 771
					   property_browser->details->category,
					   file_name);
772
	
773
	if (!g_file_test (image_file_name, G_FILE_TEST_EXISTS)) {
774
		char *user_directory;
Darin Adler's avatar
Darin Adler committed
775
		g_free (image_file_name);
776 777 778

		user_directory = nautilus_get_user_directory ();

Darin Adler's avatar
Darin Adler committed
779
		image_file_name = g_strdup_printf ("%s/%s/%s",
780
						   user_directory,
Darin Adler's avatar
Darin Adler committed
781 782
						   property_browser->details->category,
						   file_name);	
783 784

		g_free (user_directory);	
785 786
	}
	
787
	orig_pixbuf = gdk_pixbuf_new_from_file (image_file_name, NULL);
788
	
Alexander Larsson's avatar
Alexander Larsson committed
789 790 791 792 793 794 795 796 797 798 799 800
	if (orig_pixbuf == NULL) {
		orig_pixbuf = rsvg_pixbuf_from_file_at_max_size (image_file_name,
								 MAX_ICON_WIDTH, MAX_ICON_HEIGHT,
								 NULL);
	}
	
	g_free (image_file_name);

	if (orig_pixbuf == NULL) {
		return NULL;
	}
	
801 802
	is_reset = eel_strcmp (file_name, RESET_IMAGE_NAME) == 0;
	
803 804
	if (strcmp (property_browser->details->category, "patterns") == 0 &&
	    property_browser->details->property_chit != NULL) {
805
		pixbuf = nautilus_customization_make_pattern_chit (orig_pixbuf, property_browser->details->property_chit, TRUE, is_reset);
806
	} else {
Ramiro Estrugo's avatar
Ramiro Estrugo committed
807
		pixbuf = eel_gdk_pixbuf_scale_down_to_fit (orig_pixbuf, MAX_ICON_WIDTH, MAX_ICON_HEIGHT);
808
	}
809

810 811
	gdk_pixbuf_unref (orig_pixbuf);

812 813 814
	return pixbuf;
}

815

816 817 818
/* create a pixbuf and fill it with a color */

static GdkPixbuf*
819
make_color_drag_image (NautilusPropertyBrowser *property_browser, const char *color_spec, gboolean trim_edges)
820
{
821
	GdkPixbuf *color_square;
822
	GdkPixbuf *ret;
823 824 825
	int row, col, stride;
	char *pixels, *row_pixels;
	GdkColor color;
826 827

	color_square = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, COLOR_SQUARE_SIZE, COLOR_SQUARE_SIZE);
828
	
David Emory Watson's avatar
David Emory Watson committed
829
	eel_gdk_color_parse (color_spec, &color);
830 831 832
	color.red >>= 8;
	color.green >>= 8;
	color.blue >>= 8;
833 834 835 836 837
	
	pixels = gdk_pixbuf_get_pixels (color_square);
	stride = gdk_pixbuf_get_rowstride (color_square);
	
	/* loop through and set each pixel */
838
	for (row = 0; row < COLOR_SQUARE_SIZE; row++) {
839
		row_pixels =  (pixels + (row * stride));
840
		for (col = 0; col < COLOR_SQUARE_SIZE; col++) {		
841 842 843 844 845 846
			*row_pixels++ = color.red;
			*row_pixels++ = color.green;
			*row_pixels++ = color.blue;
			*row_pixels++ = 255;
		}
	}
847 848 849 850 851 852 853 854 855 856 857 858 859

	g_assert (color_square != NULL);

	if (property_browser->details->property_chit != NULL) {
		ret = nautilus_customization_make_pattern_chit (color_square, 
								property_browser->details->property_chit,
								trim_edges, FALSE);
		gdk_pixbuf_unref (color_square);
	} else {
		ret = color_square;
	}

	return ret;
860 861
}