menu.c 128 KB
Newer Older
1 2
/*
 * GNOME panel menu module.
3
 * (C) 1997, 1998, 1999, 2000 The Free Software Foundation
4 5
 * Copyright 2000 Helix Code, Inc.
 * Copyright 2000 Eazel, Inc.
6 7 8 9 10 11
 *
 * Authors: Miguel de Icaza
 *          Federico Mena
 */

#include <config.h>
12
#include <ctype.h>
13
#include <stdio.h>
14
#include <sys/types.h>
15
#include <sys/stat.h>
16
#include <sys/wait.h>
17
#include <fcntl.h>
18
#include <dirent.h>
19
#include <unistd.h>
20
#include <string.h>
21
#include <limits.h>
22
#include <errno.h>
23
#include <math.h>
Mark McLoughlin's avatar
Mark McLoughlin committed
24

25
#include <gdk/gdkkeysyms.h>
26 27
#include <libgnome/libgnome.h>
#include <libgnomeui/libgnomeui.h>
28 29
#include <gnome-desktop-item.h>
#include <gnome-ditem-edit.h>
Mark McLoughlin's avatar
Mark McLoughlin committed
30
#include <gconf/gconf-client.h>
31
#include <libbonobo.h>
32

33 34 35 36 37
#include <libgnomevfs/gnome-vfs-mime-utils.h>
#include <libgnomevfs/gnome-vfs-uri.h>
#include <libgnomevfs/gnome-vfs-ops.h>
#include <libgnomevfs/gnome-vfs-utils.h>

Mark McLoughlin's avatar
Mark McLoughlin committed
38 39 40 41 42 43 44 45
#include "aligned-widget.h"
#include "button-widget.h"
#include "conditional.h"
#include "distribution.h"
#include "drawer-widget.h"
#include "edge-widget.h"
#include "floating-widget.h"
#include "foobar-widget.h"
46
#include "gnome-run.h"
Mark McLoughlin's avatar
Mark McLoughlin committed
47 48
#include "launcher.h"
#include "logout.h"
49
#include "nothing.h"
Mark McLoughlin's avatar
Mark McLoughlin committed
50 51 52
#include "menu-fentry.h"
#include "menu-properties.h"
#include "menu-util.h"
53
#include "multiscreen-stuff.h"
Mark McLoughlin's avatar
Mark McLoughlin committed
54
#include "panel-util.h"
55
#include "panel-gconf.h"
Mark McLoughlin's avatar
Mark McLoughlin committed
56
#include "panel.h"
57
#include "panel-config-global.h"
Mark McLoughlin's avatar
Mark McLoughlin committed
58 59 60 61 62
#include "session.h"
#include "sliding-widget.h"
#include "status.h"
#include "swallow.h"
#include "tearoffitem.h"
63
#include "panel-applet-frame.h"
64
#include "quick-desktop-reader.h"
65
#include "xstuff.h"
66

67
#undef MENU_DEBUG
68

69
#define ICON_SIZE 20
70

71
static char *gnome_folder = NULL;
72

73
extern GSList *applets;
74

75
/*list of all toplevel panel widgets (basep) created*/
76
extern GSList *panel_list;
77 78
/*list of all PanelWidgets created*/
extern GSList *panels;
79

80 81
extern gboolean commie_mode;
extern gboolean no_run_box;
82 83
extern GlobalConfig global_config;

84 85 86
extern int panels_to_sync;
extern int need_complete_save;

87 88
extern int base_panels;

89 90 91
extern char *kde_menudir;
extern char *kde_icondir;

Jacob Berkman's avatar
Jacob Berkman committed
92 93
extern GtkTooltips *panel_tooltips;

94
enum {
95
	REVERT_BUTTON
96 97
};

98 99 100 101
typedef struct _TearoffMenu TearoffMenu;
struct _TearoffMenu {
	GtkWidget *menu;
	GSList *mfl;
102
	char *special;
103 104 105 106
	char *title;
	char *wmclass;
};

107
/* list of TearoffMenu s */
108 109
static GSList *tearoffs = NULL;

110 111 112 113 114 115 116 117 118 119
typedef struct _ShowItemMenu ShowItemMenu;
struct _ShowItemMenu {
	int type;
	const char *item_loc;
	MenuFinfo *mf;
	GtkWidget *menu;
	GtkWidget *menuitem;
	int applet;
};

120
typedef struct {
121 122 123
	GtkWidget *image_menu_item;
	char *image;
	char *fallback_image;
124
} IconToLoad;
125 126

static guint load_icons_id = 0;
127
static GHashTable *loaded_icons = NULL;
128 129 130 131
static GList *icons_to_load = NULL;

static void load_menu_image_deferred (GtkWidget *image_menu_item,
				      const char *image_filename,
132
				      const char *fallback_image_filename);
133 134 135 136

static GtkWidget * create_menu_at_fr (GtkWidget *menu,
				      FileRec *fr,
				      gboolean applets,
137
				      gboolean launcher_add,
138 139 140
				      const char *dir_name,
				      const char *pixmap_name,
				      gboolean fake_submenus,
141
				      gboolean force);
142 143 144 145 146 147 148 149
static GtkWidget * create_panel_submenu (GtkWidget *m,
					 gboolean fake_sub,
					 gboolean tearoff,
					 gboolean is_base);
static GtkWidget * create_desktop_menu (GtkWidget *m,
					gboolean fake_sub,
					gboolean tearoff);

150 151
static void add_kde_submenu (GtkWidget *root_menu,
			     gboolean fake_submenus,
152
			     gboolean launcher_add);
153 154
static void add_distribution_submenu (GtkWidget *root_menu,
				      gboolean fake_submenus,
155
				      gboolean launcher_add);
156 157 158

static GtkWidget * create_add_launcher_menu (GtkWidget *menu,
					     gboolean fake_submenus);
159 160 161

static void setup_menuitem_try_pixmap (GtkWidget *menuitem,
				       const char *try_file,
162
				       const char *title);
163

164 165
static gboolean panel_menus_have_icons   = TRUE;
static gboolean panel_menus_have_tearoff = TRUE;
166

167 168 169 170 171 172
static void
panel_menu_have_icons_notify (GConfClient *client,
			      guint        cnxn_id,
			      GConfEntry  *entry,
			      gpointer     user_data)
{
173 174 175 176 177 178
	GConfValue *value = gconf_entry_get_value (entry);
	if (value->type == GCONF_VALUE_BOOL)
		panel_menus_have_icons = gconf_value_get_bool (value);
	else
		/* default to true */
		panel_menus_have_icons = TRUE;
179
}
180

181 182 183 184 185
gboolean
panel_menu_have_icons (void)
{
	static guint notify_id = 0;

186 187
	if (notify_id == 0) {
		GConfValue  *value;
188
		GConfClient *client = panel_gconf_get_client ();
189
		gchar       *key    = PANEL_MENU_HAVE_ICONS_KEY;
190 191 192 193 194 195 196 197 198 199
		GError      *error  = NULL;

		notify_id = gconf_client_notify_add (client, key, 
						     panel_menu_have_icons_notify,
						     NULL, NULL, &error);
		if (error) {
			g_warning (G_STRLOC ": failed to add notification for '%s' : '%s'",
				   key, error->message);
			g_error_free (error);
		}
200 201 202 203 204 205 206 207 208 209 210

		value = gconf_client_get (client, key, NULL);
		if (value != NULL &&
		    value->type == GCONF_VALUE_BOOL)
			panel_menus_have_icons = gconf_value_get_bool (value);
		else
			/* default to true */
			panel_menus_have_icons = TRUE;

		if (value != NULL)
			gconf_value_free (value);
211
	}
212

213
	return panel_menus_have_icons;
214 215 216 217 218 219 220
}

static void
panel_menu_have_tearoff_notify (GConfClient *client,
				guint        cnxn_id,
				GConfEntry  *entry,
				gpointer     user_data)
221
{
222 223 224 225 226 227
	GConfValue *value = gconf_entry_get_value (entry);
	if (value->type == GCONF_VALUE_BOOL)
		panel_menus_have_tearoff = gconf_value_get_bool (value);
	else
		/* default to true */
		panel_menus_have_tearoff = TRUE;
228 229
}

230 231 232 233 234
gboolean
panel_menu_have_tearoff (void)
{
	static guint notify_id = 0;

235 236
	if (notify_id == 0) {
		GConfValue  *value;
237
		GConfClient *client = panel_gconf_get_client ();
238
		gchar       *key    = PANEL_MENU_HAVE_TEAROFF_KEY;
239 240 241 242 243 244 245 246 247 248 249 250
		GError      *error  = NULL;

		notify_id = gconf_client_notify_add (client, key, 
						     panel_menu_have_tearoff_notify,
						     NULL, NULL, &error);
		if (error) {
			g_warning (G_STRLOC ": failed to add notification for '%s' : '%s'",
				   key, error->message);
			g_error_free (error);
		}
		
		panel_menus_have_tearoff = gconf_client_get_bool (client, key, NULL);
251 252 253 254 255 256 257 258 259 260 261

		value = gconf_client_get (client, key, NULL);
		if (value != NULL &&
		    value->type == GCONF_VALUE_BOOL)
			panel_menus_have_tearoff = gconf_value_get_bool (value);
		else
			/* default to true */
			panel_menus_have_tearoff = TRUE;

		if (value != NULL)
			gconf_value_free (value);
262 263 264 265
	}

	return panel_menus_have_tearoff;
}
266 267 268 269
/*to be called on startup to load in some of the directories,
  this makes the startup a little bit slower, and take up slightly
  more ram, but it also speeds up later operation*/
void
270
init_menus (void)
271
{
272
	const DistributionInfo *distribution_info = get_distribution_info ();
273
	char *menu;
274

275 276 277
	/*just load the menus from disk, don't make the widgets
	  this just reads the .desktops of the top most directory
	  and a level down*/
278
	fr_read_dir (NULL, "applications:/", 0, 2);
279

280 281
	menu = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_DATADIR, 
					  "applets", TRUE, NULL);
282 283 284 285 286
	if (menu != NULL) {
		char *uri = gnome_vfs_get_uri_from_local_path (menu);
		fr_read_dir (NULL, uri, 0, 2);
		g_free (uri);
	}
287 288 289 290
	g_free (menu);

	if (distribution_info != NULL &&
	    distribution_info->menu_init_func != NULL)
291
		distribution_info->menu_init_func ();
292
}
293

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
static gboolean
check_for_screen (GtkWidget *w, GdkEvent *ev, gpointer data)
{
	static int times = 0;
	if (ev->type != GDK_KEY_PRESS)
		return FALSE;
	if (ev->key.keyval == GDK_f ||
	    ev->key.keyval == GDK_F) {
		times++;
		if (times == 3) {
			times = 0;
			start_screen_check ();
		}
	}
	return FALSE;
}

Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
311
/*the most important dialog in the whole application*/
312
static void
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
313 314
about_cb (GtkWidget *widget, gpointer data)
{
315
	static GtkWidget *about;
316
	GtkWidget *hbox, *l;
317
	/* FIXME: fill in all the wankers who did stuff */
George Lebl's avatar
George Lebl committed
318
	char *authors[] = {
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
319
	  "George Lebl (jirka@5z.com)",
320
	  "Jacob Berkman (jberkman@andrew.cmu.edu)",
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
321 322 323
	  "Miguel de Icaza (miguel@kernel.org)",
	  "Federico Mena (quartic@gimp.org)",
	  "Tom Tromey (tromey@cygnus.com)",
George Lebl's avatar
George Lebl committed
324
	  "Ian Main (imain@gtk.org)",
325
	  "Elliot Lee (sopwith@redhat.com)",
326
	  "Owen Taylor (otaylor@redhat.com)",
327 328 329
	N_("Many many others ..."),
	/* ... from the Monty Pythons show...  */
	N_("and finally, The Knights Who Say ... NI!"),
330
	  NULL
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
331
	  };
332 333 334 335 336
	char *documenters[] = {
		/* FIXME: */
		"Joe Documentor",
		NULL
	  };
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
337

338
	if (about) {
339
		xstuff_window_raise_on_current_wspace (about);
340 341 342
		return;
	}

343 344 345 346 347 348 349 350 351 352
#ifdef ENABLE_NLS
	{
		int i=0;
		while (authors[i] != NULL) {
		       	authors[i]=_(authors[i]);
			i++;
		}
	}
#endif
	
353
	about = gnome_about_new ( _("The GNOME Panel"), VERSION,
354
			_("(C) 1997-2000 the Free Software Foundation"),
355
			_("This program is responsible for launching "
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
356
			"other applications, embedding small applets "
357
			"within itself, world peace, and random X crashes."),
358 359 360 361
			(const char **)authors,
			(const char **)documenters,
			"foo" /* FIXME: ??? translator_credits */,
			NULL /* FIXME: logo "gnome-gegl2.png" */);
362
	gtk_window_set_wmclass (GTK_WINDOW (about), "about_dialog", "Panel");
363 364
	g_signal_connect (G_OBJECT (about), "destroy",
			    G_CALLBACK (gtk_widget_destroyed),
365
			    &about);
366 367
	g_signal_connect (G_OBJECT (about), "event",
			    G_CALLBACK (check_for_screen), NULL);
368 369 370 371 372

	hbox = gtk_hbox_new (TRUE, 0);
	l = gnome_href_new ("http://www.wfp.org/",
			    _("End world hunger"));
	gtk_box_pack_start (GTK_BOX (hbox), l, FALSE, FALSE, 0);
373
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (about)->vbox),
374 375 376
			    hbox, TRUE, FALSE, 0);
	gtk_widget_show_all (hbox);

377 378 379 380 381 382
	if (commie_mode) {
		l = gtk_label_new (_("Running in \"Lockdown\" mode.  This "
				     "means your system administrator has "
				     "prohibited any changes to the panel's "
				     "configuration to take place."));
		gtk_widget_show (l);
383
		gtk_box_pack_start (GTK_BOX (GTK_DIALOG (about)->vbox),
384 385 386
				    l, FALSE, FALSE, 0);
	}

Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
387 388 389
	gtk_widget_show (about);
}

390
static void
391 392
about_gnome_cb(GtkObject *object, char *program_path)
{
393
	if (gnome_execute_async (g_get_home_dir (), 1, &program_path)<0)
394 395
		panel_error_dialog ("cannot_exec_about_gnome",
				    _("Can't execute 'About GNOME'"));
396 397
}

398
static void
399
activate_app_def (GtkWidget *widget, const char *item_loc)
400
{
401
	GnomeDesktopItem *item = gnome_desktop_item_new_from_uri
402
		(item_loc,
403
		 GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS,
404
		 NULL /* error */);
405
	if (item != NULL) {
406 407 408
		char *curdir = g_get_current_dir ();
		chdir (g_get_home_dir ());

409 410 411 412
		gnome_desktop_item_launch (item,
					   NULL /* file_list */,
					   0 /* flags */,
					   NULL /* error */);
413
		gnome_desktop_item_unref (item);
414 415 416

		chdir (curdir);
		g_free (curdir);
417
	} else {
418 419
		panel_error_dialog ("cannot_load_entry",
				    _("Can't load entry"));
420
	}
421 422
}

423
static PanelWidget *
424
get_panel_from_menu_data(GtkWidget *menu, gboolean must_have)
425 426 427 428
{
	g_return_val_if_fail (menu != NULL, NULL);
	g_return_val_if_fail (GTK_IS_MENU(menu) || GTK_IS_MENU_ITEM(menu),
			      NULL);
429

430 431
	if(GTK_IS_MENU_ITEM(menu))
		menu = menu->parent;
432

433 434 435
	while(menu) {
		PanelWidget *panel = gtk_object_get_data(GTK_OBJECT(menu),
							 "menu_panel");
436

437
		if (panel != NULL) {
438
			if(PANEL_IS_WIDGET(panel))
439 440 441 442
				return panel;
			else
				g_warning("Menu is on crack");
		}
443
		menu = gtk_menu_get_attach_widget (GTK_MENU (menu))->parent;
444
	}
445 446 447 448 449 450 451
	if (must_have) {
		g_warning("Something went quite terribly wrong and we can't "
			  "find where this menu belongs");
		return panels->data;
	} else {
		return NULL;
	}
452 453 454
}

static void
455
setup_menu_panel (GtkWidget *menu)
456
{
457 458 459 460 461
	PanelWidget *menu_panel = gtk_object_get_data (GTK_OBJECT (menu),
						       "menu_panel");
	if (menu_panel != NULL) {
		menu_panel = get_panel_from_menu_data (menu, TRUE);
		gtk_object_set_data (GTK_OBJECT (menu), "menu_panel", menu_panel);
462 463 464 465
	}
}

static GtkWidget *
466
menu_new (void)
467 468
{
	GtkWidget *ret;
469
	ret = gtk_menu_new ();
470 471
	g_signal_connect (G_OBJECT (ret), "show",
			  G_CALLBACK (setup_menu_panel), NULL);
472 473 474 475

	return ret;
}

476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
static void
icon_to_load_free (IconToLoad *icon)
{
	if (icon != NULL) {
	        gtk_widget_unref (icon->image_menu_item);
	        icon->image_menu_item = NULL;
	        g_free (icon->image);
	        icon->image = NULL;
		g_free (icon->fallback_image);
		icon->fallback_image = NULL;
		g_free (icon);
	}
}

static IconToLoad *
icon_to_load_copy (IconToLoad *icon)
{
	IconToLoad *new_icon = NULL;
	if (icon != NULL) {
		new_icon = g_new0 (IconToLoad, 1);
	        new_icon->image_menu_item = gtk_widget_ref (icon->image_menu_item);
	        new_icon->image = g_strdup (icon->image);
		new_icon->fallback_image = g_strdup (icon->fallback_image);
	}
	return new_icon;
}

503 504 505 506 507 508 509
static void
remove_pixmap_from_loaded (GtkWidget *pixmap, gpointer data)
{
	if (loaded_icons != NULL) 
		g_hash_table_remove (loaded_icons, data);
}

510
static gboolean
511
load_icons_handler (gpointer data)
512
{
Jacob Berkman's avatar
Jacob Berkman committed
513 514
	GtkWidget *parent;
	GtkWidget *pixmap = NULL;
515
	GdkPixbuf *pb;
516
	IconToLoad *icon;
517
	char *file;
518 519 520
	gboolean loaded;

load_icons_handler_again:
521

522
	if (icons_to_load == NULL) {
Jacob Berkman's avatar
Jacob Berkman committed
523 524 525
		load_icons_id = 0;
		return FALSE;
	}
526

527 528
	icon = icons_to_load->data;
	icons_to_load = g_list_remove (icons_to_load, icon);
529

530
	parent = icon->image_menu_item;
531

532 533 534
	/* if not visible anymore, just ignore */
	if ( ! GTK_WIDGET_VISIBLE (parent)) {
		icon_to_load_free (icon);
535 536 537
		/* we didn't do anything long/hard, so just do this again,
		 * this is fun, don't go back to main loop */
		goto load_icons_handler_again;
538 539
	}

540
	file = quick_desktop_item_find_icon (icon->image);
541
	if (file == NULL)
542
		file = quick_desktop_item_find_icon (icon->fallback_image);
543 544

	if (file == NULL) {
545
		icon_to_load_free (icon);
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
		/* we didn't do anything long/hard, so just do this again,
		 * this is fun, don't go back to main loop */
		goto load_icons_handler_again;
	}

	pb = NULL;

	loaded = FALSE;
	pixmap = NULL;

	if (loaded_icons != NULL &&
	    (pixmap = g_hash_table_lookup (loaded_icons, file)) != NULL) {
		pb = gtk_image_get_pixbuf (GTK_IMAGE (pixmap));
		if (pb != NULL)
			gdk_pixbuf_ref (pb);
561 562
	}

563
	if (pb == NULL) {
564 565 566 567 568
		pb = gdk_pixbuf_new_from_file (file, NULL);
		loaded = TRUE;
	}
	if (pb == NULL) {
		g_free (file);
569
		icon_to_load_free (icon);
570 571
		/* this may have been a long operation so jump back to
		 * the main loop for a while */
Jacob Berkman's avatar
Jacob Berkman committed
572 573
		return TRUE;
	}
574

575 576 577 578 579 580 581 582
	if (gdk_pixbuf_get_width (pb) != ICON_SIZE ||
	    gdk_pixbuf_get_height (pb) != ICON_SIZE) {
		GdkPixbuf *pb2;
		pb2 = gdk_pixbuf_scale_simple (pb, ICON_SIZE, ICON_SIZE,
					       GDK_INTERP_BILINEAR);
		gdk_pixbuf_unref (pb);
		pb = pb2;
	}
583

584 585
	pixmap = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (parent));
	gtk_image_set_from_pixbuf (GTK_IMAGE (pixmap), pb);
586
	gdk_pixbuf_unref (pb);
587

588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
	if (loaded) {
		if (loaded_icons == NULL)
			loaded_icons = g_hash_table_new_full
				(g_str_hash, g_str_equal,
				 (GDestroyNotify) g_free,
				 (GDestroyNotify) gtk_widget_unref);
		g_hash_table_replace (loaded_icons,
				      g_strdup (file),
				      gtk_widget_ref (pixmap));
		g_signal_connect_data (G_OBJECT (pixmap), "destroy",
				       G_CALLBACK (remove_pixmap_from_loaded),
				       g_strdup (file),
				       (GClosureNotify) g_free,
				       0 /* connect_flags */);
	} else {
		g_free (file);
		/* we didn't load from disk, so just do this again,
		 * this is fun, don't go back to main loop */
		goto load_icons_handler_again;
	}
608

609
	g_free (file);
610

Jacob Berkman's avatar
Jacob Berkman committed
611 612
	/* if still more we'll come back */
	return TRUE;
613 614
}

615 616
/* replaces '/' with returns _'s, originally from gmenu */
static void
617
validate_for_filename(char *file)
618 619 620
{
	char *ptr;

621
	g_return_if_fail (file != NULL);
622
	
623
	ptr = file;
624
	while (*ptr != '\0') {
625 626
		if (*ptr == '/')
			*ptr = '_';
627 628 629 630 631
		ptr++;
	}
}

static void
632
really_add_new_menu_item (GtkWidget *d, int response, gpointer data)
633
{
634
	GnomeDItemEdit *dedit = GNOME_DITEM_EDIT(data);
635
	char *dir = gtk_object_get_data(GTK_OBJECT(d), "dir");
636
	GnomeDesktopItem *ditem;
637
	GError *error = NULL;
638 639
	int i;
	char *name, *loc;
640

641
	if (response != GTK_RESPONSE_OK) {
642
		gtk_widget_destroy (d);
643 644
		return;
	}
645

646 647
	g_return_if_fail (dir != NULL);

648 649
	panel_push_window_busy (d);

650
	ditem = gnome_ditem_edit_get_ditem (dedit);
651

652 653 654 655 656 657 658 659
	if ((gnome_desktop_item_get_entry_type (ditem) == GNOME_DESKTOP_ITEM_TYPE_APPLICATION &&
	     string_empty (gnome_desktop_item_get_string (ditem, GNOME_DESKTOP_ITEM_EXEC))) ||
	    (gnome_desktop_item_get_entry_type (ditem) == GNOME_DESKTOP_ITEM_TYPE_LINK &&
	     string_empty (gnome_desktop_item_get_string (ditem, GNOME_DESKTOP_ITEM_URL)))) {
		gnome_desktop_item_unref (ditem);
		panel_error_dialog ("cannot_create_launcher",
				    _("Cannot create the launcher.\n\n"
				      "No command or url specified."));
660 661 662 663
		return;
	}

	/* assume we are making a new file */
664
	name = g_strdup (gnome_desktop_item_get_string (ditem, GNOME_DESKTOP_ITEM_NAME));
665

666
	validate_for_filename (name);
667

668
	loc = g_strdup_printf ("%s/%s.desktop", dir, name);
669

670 671 672 673 674 675
	i = 2;
	while (g_file_test (loc, G_FILE_TEST_EXISTS)) {
		g_free (loc);
		loc = g_strdup_printf ("%s/%s%d.desktop",
				       dir, name,
				       i ++);
676
	}
677
	gnome_desktop_item_set_location_file (ditem, loc);
678
	g_free (name);
679

680
	error = NULL;
681 682 683
	gnome_desktop_item_save (ditem,
				 NULL /* under */,
				 TRUE /* force */,
684 685 686 687 688 689 690 691 692 693
				 &error);
	if (error != NULL) {
		panel_error_dialog ("cannot_save_menu_item" /* class */,
				    _("Cannot save menu item to disk, "
				      "the following error occured:\n\n"
				      "%s"),
				    error->message);
		g_clear_error (&error);
	}

694
	gnome_desktop_item_unref (ditem);
695

696 697
	panel_pop_window_busy (d);

698 699
	gtk_widget_destroy (d);
	g_free (loc);
700 701 702
}

static void
703
add_new_app_to_menu (GtkWidget *widget, const char *item_loc)
704
{
705 706
	GtkWidget *dialog;
	GtkWidget *dee;
707

708 709 710 711 712
	dialog = gtk_dialog_new_with_buttons (_("Create menu item"),
					      NULL /* parent */,
					      0 /* flags */,
					      GTK_STOCK_CANCEL,
					      GTK_RESPONSE_CANCEL,
713 714
					      GTK_STOCK_OK,
					      GTK_RESPONSE_OK,
715 716
					      NULL);

717 718 719
	gtk_window_set_wmclass (GTK_WINDOW (dialog),
			       "create_menu_item", "Panel");
	gtk_window_set_policy (GTK_WINDOW (dialog), FALSE, FALSE, TRUE);
720
	
721 722
	dee = gnome_ditem_edit_new ();
	gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), dee,
723 724
			    TRUE, TRUE, GNOME_PAD_SMALL);

725 726
	gnome_ditem_edit_set_entry_type (GNOME_DITEM_EDIT (dee), 
					 "Application");
727

728 729 730
	gtk_object_set_data_full (GTK_OBJECT (dialog), "dir",
				  g_strdup (item_loc),
				  (GtkDestroyNotify)g_free);
731
	
732 733
	g_signal_connect (G_OBJECT (dialog), "response",
			    G_CALLBACK (really_add_new_menu_item),
734 735
			    dee);

736 737
	gtk_dialog_set_default_response (GTK_DIALOG(dialog),
					 GTK_RESPONSE_OK);
738

739
	gtk_widget_show_all (dialog);
740

741
	gnome_ditem_edit_grab_focus (GNOME_DITEM_EDIT (dee));
742 743
}

744
static void
745
remove_menuitem (GtkWidget *widget, ShowItemMenu *sim)
746
{
747
	char *file;
748 749
	char *dir, *directory_file;
	GnomeDesktopItem *ditem;
750

751 752 753 754
	g_return_if_fail (sim->item_loc != NULL);
	g_return_if_fail (sim->menuitem != NULL);

	gtk_widget_hide (sim->menuitem);
755

756
	if (unlink (sim->item_loc) < 0) {
757 758
		panel_error_dialog("cant_remove_menu_item",
				   _("Could not remove the menu item %s: %s\n"), 
759
				    sim->item_loc, g_strerror (errno));
760 761 762
		return;
	}

763
	file = g_path_get_basename (sim->item_loc);
764 765
	if (file == NULL) {
		g_warning (_("Could not get file name from path: %s"),
766
			  sim->item_loc);
767 768 769
		return;
	}

770
	dir = g_path_get_dirname (sim->item_loc);
771 772
	if (dir == NULL) {
		g_warning (_("Could not get directory name from path: %s"),
773
			  sim->item_loc);
774
		g_free (file);
775 776
		return;
	}
777

778 779 780 781 782
	directory_file = g_build_path ("/", dir, ".directory", NULL);
	ditem = gnome_desktop_item_new_from_uri (directory_file,
						 0 /* flags */,
						 NULL /* error */);
	g_free (directory_file);
783

784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
	if (ditem != NULL) {
		char **sort_order = gnome_desktop_item_get_strings (ditem,
								    GNOME_DESKTOP_ITEM_SORT_ORDER);
		if (sort_order != NULL) {
			int i, j;
			j = 0;
			for (i = 0; sort_order[i] != NULL; i++) {
				if (strcmp (file, sort_order[i]) != 0) {
					sort_order[j++] = sort_order[i];
				} else {
					g_free (sort_order[i]);
					sort_order[i] = NULL;
				}
			}
			sort_order[j++] = NULL;
			gnome_desktop_item_set_strings (ditem,
							GNOME_DESKTOP_ITEM_SORT_ORDER,
							sort_order);
			g_strfreev (sort_order);

			gnome_desktop_item_save (ditem,
						 NULL /* under */,
						 TRUE /* force */,
						 NULL /* error */);
808

809 810
			/* ignore errors, it's not at all important if we failed,
			 * no point bothering the user with it. */
811

812 813
		}
		gnome_desktop_item_unref (ditem);
814
	}
815 816
}

817
static void
818 819
add_to_run_dialog (GtkWidget *widget, const char *item_loc)
{
820
	GnomeDesktopItem *item =
821 822 823
		gnome_desktop_item_new_from_uri (item_loc,
						 GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS,
						 NULL /* error */);
824
	if (item != NULL) {
825 826 827 828 829 830 831 832 833 834
		const char *exec;

		exec = gnome_desktop_item_get_string
			(item, GNOME_DESKTOP_ITEM_EXEC);
		if (exec == NULL)
			exec = gnome_desktop_item_get_string
				(item, GNOME_DESKTOP_ITEM_URL);

		if (exec != NULL) {
			show_run_dialog_with_text (exec);
835
		} else {
836 837
			panel_error_dialog ("no_exec_or_url_field",
					    _("No 'Exec' or 'URL' field in entry"));
838
		}
839
		gnome_desktop_item_unref (item);
840
	} else {
841 842
		panel_error_dialog ("cant_load_entry",
				    _("Can't load entry"));
843 844 845
	}
}

846 847 848
static void
show_help_on (GtkWidget *widget, const char *item_loc)
{
849
	GnomeDesktopItem *item =
850 851 852
		gnome_desktop_item_new_from_uri (item_loc,
						 GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS,
						 NULL /* error */);
853
	if (item != NULL) {
854 855 856
		const char *docpath = gnome_desktop_item_get_string
			(item, "DocPath");
		char *path = panel_gnome_kde_help_path (docpath);
857
		if (path != NULL) {
858 859 860 861 862 863 864 865 866 867 868 869
			GError *error = NULL;
			gnome_url_show (path, &error);
			if (error != NULL) {
				const char *name = gnome_desktop_item_get_localestring
					(item, GNOME_DESKTOP_ITEM_NAME);
				panel_error_dialog ("cant_load_help_on",
						    _("Cannot load help on %s.\n\n%s"),
						    name, error->message);
				g_clear_error (&error);
			}

			/* FIXME: this should prolly use gnome_help */
870 871
			g_free (path);
		}
872
		gnome_desktop_item_unref (item);
873
	} else {
874 875
		panel_error_dialog ("cant_load_entry",
				    _("Can't load entry"));
876 877 878
	}
}

879
static void
880
add_app_to_panel (GtkWidget *widget, const char *item_loc)
881
{
882
	PanelWidget *panel = get_panel_from_menu_data (widget, TRUE);
883 884
	Launcher *launcher;

885
	launcher = load_launcher_applet (item_loc, panel, 0, FALSE);
886 887 888

	if (launcher != NULL)
		launcher_hoard (launcher);
889 890 891 892
}


static void
893 894
add_drawers_from_dir (const char *dirname, const char *name,
		      int pos, PanelWidget *panel)
895 896 897
{
	Drawer *drawer;
	PanelWidget *newpanel;
898
	QuickDesktopItem *item_info;
899
	char *dentry_name;
900
	const char *subdir_name;
901
	char *pixmap_name;
902
	char *filename = NULL;
903
	GSList *list, *li;
904

905 906 907 908 909 910 911
	dentry_name = g_build_path ("/",
				    dirname,
				    ".directory",
				    NULL);
	item_info = quick_desktop_item_load_uri (dentry_name,
						 NULL /* expected_type */,
						 FALSE /* run_tryexec */);
912 913
	g_free (dentry_name);

914 915
	if (name == NULL)
		subdir_name = item_info != NULL ? item_info->name : NULL;
916 917
	else
		subdir_name = name;
918
	pixmap_name = item_info != NULL ? item_info->icon : NULL;
919

920 921
	drawer = load_drawer_applet (-1, pixmap_name, subdir_name, panel, pos, FALSE);
	if (!drawer) {
922 923 924
		g_warning("Can't load a drawer");
		return;
	}
925

926
	newpanel = PANEL_WIDGET(BASEP_WIDGET(drawer->drawer)->panel);
927

928
	list = get_mfiles_from_menudir (dirname);
929
	for(li = list; li!= NULL; li = li->next) {
930
		MFile *mfile = li->data;
931
		GnomeDesktopItem *dentry;
932 933

		g_free (filename);
934
		filename = g_build_filename (dirname, mfile->name, NULL);
935

936
		if ( ! mfile->verified) {
937
			continue;
938
		}
939

940
		if (mfile->is_dir) {
941 942
			add_drawers_from_dir (filename, NULL, G_MAXINT/2,
					      newpanel);
943 944 945
			continue;
		}
			
946
		if (is_ext2 (mfile->name, ".desktop", ".kdelnk")) {
947 948
			/*we load the applet at the right
			  side, that is end of the drawer*/
949 950
			dentry = gnome_desktop_item_new_from_uri (filename,
								  GNOME_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS, NULL);
951 952 953 954 955 956 957 958 959 960 961 962 963
			if (dentry) {
				Launcher *launcher;

				launcher =
					load_launcher_applet_full (filename,
								   dentry,
								   newpanel,
								   G_MAXINT/2,
								   FALSE);

				if (launcher != NULL)
					launcher_hoard (launcher);
			}
964 965
		}
	}
966 967
	g_free (filename);

968
	free_mfile_list (list);
969 970 971 972 973 974 975
}

/*add a drawer with the contents of a menu to the panel*/
static void
add_menudrawer_to_panel(GtkWidget *w, gpointer data)
{
	MenuFinfo *mf = data;
976
	PanelWidget *panel = get_panel_from_menu_data (w, TRUE);
977 978
	g_return_if_fail(mf);
	
979
	add_drawers_from_dir (mf->menudir, mf->dir_name, 0, panel);
980 981 982
}

static void
983
add_menu_to_panel (GtkWidget *widget, gpointer data)
984
{
985 986
	const char *menudir = data;
	gboolean main_menu;
987
	PanelWidget *panel;
988
	DistributionType distribution = get_distribution_type ();
989 990
	int flags = MAIN_MENU_SYSTEM_SUB | MAIN_MENU_APPLETS_SUB |
		MAIN_MENU_PANEL_SUB | MAIN_MENU_DESKTOP_SUB;
991
	
992 993 994
	/*guess distribution menus*/
	if (distribution != DISTRIBUTION_UNKNOWN)
		flags |= MAIN_MENU_DISTRIBUTION_SUB;
995

996
	/*guess KDE menus*/
997
	if (g_file_test (kde_menudir, G_FILE_TEST_IS_DIR))
998
		flags |= MAIN_MENU_KDE_SUB;
999

1000
	panel = get_panel_from_menu_data (widget, TRUE);
1001

1002 1003
	if (menudir == NULL) {
		main_menu = TRUE;
1004
		menudir = "applications:/";
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
	} else {
		main_menu = FALSE;
	}

	load_menu_applet (menudir, main_menu, flags,
			  TRUE /* global_main */,
			  FALSE /* custom_icon */,
			  NULL /* custom_icon_file */,
			  panel,
			  0 /* pos */,
			  FALSE /* exactpos */);
1016 1017
}

1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
/*most of this function stolen from the real gtk_menu_popup*/
static void
restore_grabs(GtkWidget *w, gpointer data)
{
	GtkWidget *menu_item = data;
	GtkMenu *menu = GTK_MENU(menu_item->parent); 
	GtkWidget *xgrab_shell;
	GtkWidget *parent;
	/* Find the last viewable ancestor, and make an X grab on it
	 */
	parent = GTK_WIDGET (menu);
	xgrab_shell = NULL;
	while (parent) {
		gboolean viewable = TRUE;
		GtkWidget *tmp = parent;

		while (tmp) {
			if (!GTK_WIDGET_MAPPED (tmp)) {
				viewable = FALSE;
				break;
			}
			tmp = tmp->parent;
		}

		if (viewable)
			xgrab_shell = parent;

		parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
	}

	/*only grab if this HAD a grab before*/
	if (xgrab_shell && (GTK_MENU_SHELL (xgrab_shell)->have_xgrab)) {
		GdkCursor *cursor = gdk_cursor_new (GDK_ARROW);

		GTK_MENU_SHELL (xgrab_shell)->have_xgrab = 
			(gdk_pointer_grab (xgrab_shell->window, TRUE,
1054 1055 1056 1057
					   GDK_BUTTON_PRESS_MASK |
					   GDK_BUTTON_RELEASE_MASK |
					   GDK_ENTER_NOTIFY_MASK |
					   GDK_LEAVE_NOTIFY_MASK,
1058 1059 1060
					   NULL, cursor, 0) == 0);
		gdk_cursor_destroy (cursor);
	}
1061
	
1062 1063
	gtk_grab_add (GTK_WIDGET (menu));
}
1064 1065

static void
1066
ditem_properties_clicked (GtkWidget *w, int response, gpointer data)
1067
{
1068 1069
	GnomeDItemEdit *dee = gtk_object_get_user_data (GTK_OBJECT (w));
	GnomeDesktopItem *ditem = data;
1070

1071
	if (response == GTK_RESPONSE_HELP) {
1072
		panel_show_help ("launchers", NULL);
1073
	} else if (response == REVERT_BUTTON) {
1074 1075
		if (ditem != NULL)
			gnome_ditem_edit_set_ditem (dee, ditem);
1076
		else
1077
			gnome_ditem_edit_clear (dee);
1078
	} else {
1079
		gtk_widget_destroy (w);
1080 1081 1082 1083 1084 1085
	}
}

static gboolean
ditem_properties_apply_timeout (gpointer data)
{
1086 1087 1088
	GtkWidget *dedit = data;
	GnomeDesktopItem *ditem;
	const char *loc;
1089

1090 1091 1092 1093 1094 1095 1096 1097
	gtk_object_remove_data (GTK_OBJECT (dedit), "apply_timeout");

	ditem = gnome_ditem_edit_get_ditem (GNOME_DITEM_EDIT (dedit));
	loc = gtk_object_get_data (GTK_OBJECT (dedit), "location");
	gnome_desktop_item_save (ditem,
				 loc /* under */,
				 TRUE /* force */,
				 NULL /* error */);
1098 1099 1100
	/* FIXME: we don't want to really handle errors here though,
	 * only on OK, but make sure that we know by the time we
	 * hit OK that something went wrong here */
1101
	gnome_desktop_item_unref (ditem);
1102

1103
	return FALSE;
1104 1105
}

1106 1107 1108 1109 1110 1111
/* 
 * Will save after 5 seconds of no changes.  If something is changed, the save
 * is postponed to another 5 seconds.  This seems to be a saner behaviour,
 * then just saving every N seconds.
 */
static void
1112
ditem_properties_changed (GtkWidget *dedit, gpointer data)
1113
{
1114 1115
	gpointer timeout_data = gtk_object_get_data (GTK_OBJECT (dedit),
						     "apply_timeout");
1116 1117
	guint timeout = GPOINTER_TO_UINT (timeout_data);

1118
	gtk_object_remove_data (GTK_OBJECT (dedit), "apply_timeout");
1119 1120 1121 1122 1123 1124 1125 1126 1127

	if (timeout != 0)
		gtk_timeout_remove (timeout);

	/* Will save after 5 seconds */
	timeout = gtk_timeout_add (5 * 1000,
				   ditem_properties_apply_timeout,
				   dedit);

1128
	gtk_object_set_data (GTK_OBJECT (dedit), "apply_timeout",
1129 1130 1131 1132 1133 1134 1135
			     GUINT_TO_POINTER (timeout));
}


static void
ditem_properties_close (GtkWidget *widget, gpointer data)
{
1136 1137 1138
	GtkWidget *dedit = data;
	gpointer timeout_data = gtk_object_get_data (GTK_OBJECT (dedit),
						     "apply_timeout");
1139 1140
	guint timeout = GPOINTER_TO_UINT (timeout_data);

1141
	gtk_object_remove_data (GTK_OBJECT (dedit), "apply_timeout");
1142 1143 1144 1145 1146 1147 1148 1149

	if (timeout != 0) {
		gtk_timeout_remove (timeout);

		ditem_properties_apply_timeout (dedit);
	}
}

1150 1151 1152 1153 1154 1155 1156
static gboolean
is_item_writable (const ShowItemMenu *sim)
{
	errno = 0;
	if (sim->item_loc != NULL &&
	    /*A HACK: but it works, don't have it edittable if it's redhat
	      menus as they are auto generated!*/
1157
	    strstr (sim->item_loc,"/" GNOME_DOT_GNOME "/apps-redhat/") == NULL &&
1158 1159 1160
	    /*if it's a kdelnk file, don't let it be editted*/
	    ! is_ext (sim->item_loc, ".kdelnk") &&
	    access (sim->item_loc, W_OK) == 0) {
1161
#ifdef MENU_DEBUG
1162 1163 1164 1165
		puts (sim->item_loc);
#endif
		/*file exists and is writable, we're in bussines*/
		return TRUE;
1166 1167 1168
	} else if ((sim->item_loc == NULL ||
		    errno == ENOENT) &&
		   sim->mf != NULL) {
1169 1170 1171 1172 1173
		/*the dentry isn't there, check if we can write the
		  directory*/
		if (access (sim->mf->menudir, W_OK) == 0 &&
		   /*A HACK: but it works, don't have it edittable if it's redhat
		     menus as they are auto generated!*/
1174
		   strstr (sim->mf->menudir, GNOME_DOT_GNOME "apps-redhat/") == NULL)
1175 1176 1177 1178 1179 1180
			return TRUE;
	}
	return FALSE;
}

static void
1181
set_ditem_sensitive (GtkDialog *dialog,
1182
		     GnomeDItemEdit *dedit,
1183
		     ShowItemMenu *sim)
1184 1185 1186 1187 1188
{
	gboolean sensitive;

	sensitive = is_item_writable (sim);

1189
	gnome_ditem_edit_set_editable (dedit, sensitive);
1190

1191
	gtk_dialog_set_response_sensitive (dialog, REVERT_BUTTON, sensitive);
1192
}
1193

1194
static void
1195
edit_dentry (GtkWidget *widget, ShowItemMenu *sim)
1196
{
1197 1198 1199
	GtkWidget *dialog;
	GtkWidget *dedit;
	GnomeDesktopItem *ditem;
1200
	
1201 1202
	g_return_if_fail (sim != NULL);
	g_return_if_fail (sim->item_loc != NULL);
1203

1204 1205 1206
	ditem = gnome_desktop_item_new_from_uri (sim->item_loc,
						 0 /* flags */,
						 NULL /* error */);
1207

1208
	/* watch the enum at the top of the file */
1209 1210 1211 1212
	dialog = gtk_dialog_new_with_buttons (_("Desktop entry properties"),
					      NULL /* parent */,
					      0 /* flags */,
					      GTK_STOCK_HELP,
1213
					      GTK_RESPONSE_HELP,
1214 1215 1216
					      GTK_STOCK_REVERT_TO_SAVED,
					      REVERT_BUTTON,
					      GTK_STOCK_CLOSE,
1217
					      GTK_RESPONSE_CLOSE,
1218
					      NULL);
1219

1220 1221
	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);

1222 1223 1224
	dedit = gnome_ditem_edit_new ();

	gtk_widget_show (dedit);
1225
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
1226
			    dedit, TRUE, TRUE, 0);
1227

1228 1229
	gtk_window_set_wmclass(GTK_WINDOW(dialog),
			       "desktop_entry_properties","Panel");
1230
	gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, TRUE);
1231
	
1232
	gtk_object_set_data_full (GTK_OBJECT (dedit), "location",
1233
				  g_strdup (sim->item_loc),
1234 1235
				  (GtkDestroyNotify)g_free);

1236 1237
	if (ditem != NULL)
		gnome_ditem_edit_set_ditem (GNOME_DITEM_EDIT (dedit), ditem);
1238

1239
	set_ditem_sensitive (GTK_DIALOG (dialog),
1240
			     GNOME_DITEM_EDIT (dedit), sim);
1241