gnome-run.c 36 KB
Newer Older
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 *   grun: Popup a command dialog. Original version by Elliot Lee, 
 *    bloatware edition by Havoc Pennington. Both versions written in 10
 *    minutes or less. :-)
 *   Copyright (C) 1998 Havoc Pennington <hp@pobox.com>
 *
 * This program is free software; you can redistribute it and/or 
 * modify 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.
 *
 * This program 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 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
 */

#include <config.h>
#include <errno.h>
26 27
#include <sys/types.h>
#include <dirent.h>
28
#include <string.h>
29

Mark McLoughlin's avatar
Mark McLoughlin committed
30 31 32 33 34
#include <libgnome/libgnome.h>
#include <libgnomeui/libgnomeui.h>

#include "gnome-run.h"

35
#include "foobar-widget.h"
36 37
#include "menu-fentry.h"
#include "menu.h"
38
#include "nothing.h"
39
#include "multiscreen-stuff.h"
40
#include "quick-desktop-reader.h"
41

Mark McLoughlin's avatar
Mark McLoughlin committed
42 43 44 45 46 47 48
#include "applet.h"
#include "button-widget.h"
#include "foobar-widget.h"
#include "menu-fentry.h"
#include "menu.h"
#include "multiscreen-stuff.h"
#include "panel-util.h"
49
#include "panel-gconf.h"
50

51 52 53 54
#define ICON_SIZE 20

enum {
	COLUMN_ICON,
55
	COLUMN_ICON_FILE,
56 57 58 59 60 61
	COLUMN_FULLNAME,
	COLUMN_COMMENT,
	COLUMN_NAME,
	NUM_COLUMNS
};

62 63 64 65 66 67
typedef enum {
	PANEL_RESPONSE_RUN,
} PanelResponseType;

#define PANEL_STOCK_RUN "panel-run"

68
extern GtkTooltips *panel_tooltips;
69
extern gboolean no_run_box;
70

71
static GtkWidget *run_dialog = NULL;
72 73 74
static GSList *add_icon_iters = NULL;
static guint add_icon_idle_id = 0;
static guint add_items_idle_id = 0;
75

76 77 78
static GList *executables = NULL;
static GCompletion *exe_completion = NULL;

79 80 81 82
static GtkWidget* create_advanced_contents (void);
static void       update_contents          (GtkWidget *dialog);
static void       unset_selected           (GtkWidget *dialog);

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
static void
fill_executables_from (const char *dirname)
{
	struct dirent *dent;
	DIR *dir;

	dir = opendir (dirname);

	if (dir == NULL)
		return;

	while ( (dent = readdir (dir)) != NULL) {
		char *file = g_strconcat (dirname, "/", dent->d_name, NULL);

		if (access (file, X_OK) == 0)
			executables = g_list_prepend (executables,
						      g_strdup (dent->d_name));
	}

	closedir (dir);
}

static void
fill_executables (void)
{
	int i;
109
	const char *path;
110 111
	char **pathv;

112
	panel_g_list_deep_free (executables);
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
	executables = NULL;

	path = g_getenv ("PATH");

	if (path == NULL ||
	    path[0] == '\0')
		return;

	pathv = g_strsplit (path, ":", 0);

	for (i = 0; pathv[i] != NULL; i++)
		fill_executables_from (pathv[i]);

	g_strfreev (pathv);
}

static void
ensure_completion (void)
{
	if (exe_completion == NULL) {
		exe_completion = g_completion_new (NULL);
		fill_executables ();

		g_completion_add_items (exe_completion, executables);
	}
}

static void
kill_completion (void)
{
	if (executables != NULL) {
144
		panel_g_list_deep_free (executables);
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
		executables = NULL;
	}

	if (exe_completion != NULL) {
		g_completion_free (exe_completion);
		exe_completion = NULL;
	}
}

static void
get_environment (int *argc, char ***argv, int *envc, char ***envv)
{
	GList *envar = NULL, *li;
	int i, moveby;

	*envv = NULL;
	*envc = 0;

	moveby = 0;
	for (i = 0; i < *argc; i++) {
		if (strchr ((*argv)[i], '=') == NULL) {
			break;
		}
		envar = g_list_append (envar, g_strdup ((*argv)[i]));
		moveby ++;
	}

	if (moveby == *argc) {
173
		panel_g_list_deep_free (envar);
174 175 176 177 178 179 180
		return;
	}

	if (envar == NULL)
		return;

	for (i = 0; i < *argc; i++) {
181 182
		g_free ((*argv)[i]);
		if (i + moveby < *argc) {
183
			(*argv)[i] = (*argv)[i+moveby];
184 185
			(*argv)[i+moveby] = NULL;
		} else {
186
			(*argv)[i] = NULL;
187
		}
188 189 190 191 192 193 194 195 196 197 198 199 200
	}
	*argc -= moveby;

	*envc = g_list_length (envar);
	*envv = g_new0 (char *, *envc + 1);
	for (i = 0, li = envar; li != NULL; li = li->next, i++) {
		(*envv)[i] = li->data;
		li->data = NULL;
	}	
	(*envv)[i] = NULL;
	g_list_free (envar);
}

201
static void 
202
run_dialog_response (GtkWidget *w, int response, gpointer data)
203
{
204
	GtkEntry *entry;
205
        GtkWidget *list;
206
	GtkToggleButton *terminal;
207
	char **argv = NULL;
208
	char **temp_argv = NULL;
209
	int argc, temp_argc;
210
	const char *s;
211 212
	char **envv = NULL;
	int envc;
213 214
        gboolean use_advanced;
        
215 216 217 218 219 220
	use_advanced = gconf_client_get_bool (
				panel_gconf_get_client (),
				panel_gconf_general_key (
					panel_gconf_get_profile (), "advanced_run_dialog"),
				NULL);

221
	if (response == GTK_RESPONSE_HELP) {
222
		panel_show_help ("specialobjects", "RUNBUTTON");
223
		/* just return as we don't want to close */
224
		return;
Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
225
	} else if (response != PANEL_RESPONSE_RUN) {
226 227
		goto return_and_close;
	}
228
        
229
        list = g_object_get_data (G_OBJECT (run_dialog), "dentry_list");
230 231
        terminal = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT(w),
							 "terminal"));
232
        
233
        if (g_object_get_data (G_OBJECT (run_dialog), "use_list")) {
234
                char *name;
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
		GtkTreeSelection *selection;
		GtkTreeModel *model;
		GtkTreeIter iter;
		GValue value = {0, };

		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));

		/* just return if nothing selected */
		if ( ! gtk_tree_selection_get_selected (selection, 
							&model, &iter))
			return;

		gtk_tree_model_get_value (model, &iter,
					  COLUMN_NAME,
					  &value);
		name = g_strdup (g_value_get_string (&value));
		g_value_unset (&value);

Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
253 254 255
                if (name != NULL) {
			GError *error = NULL;
                        GnomeDesktopItem *ditem;
256
                        
257 258 259
                        ditem = gnome_desktop_item_new_from_uri (name,
								 GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS,
								 &error);
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
260
			if (ditem != NULL) {
261
                                /* Honor "run in terminal" button */
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
262 263 264 265
				gnome_desktop_item_set_boolean (ditem,
								GNOME_DESKTOP_ITEM_TERMINAL,
								terminal->active);
			}
266

Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
267 268 269 270
                        if (ditem == NULL) {
                                panel_error_dialog ("failed_to_load_desktop",
						    _("Failed to load this program!\n%s"),
						    error->message);
271
				g_clear_error (&error);
272 273 274
			} else if ( ! gnome_desktop_item_launch (ditem,
								 NULL /* file_list */,
								 0 /* flags */,
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
275 276 277 278
								 &error)) {
                                panel_error_dialog ("failed_to_load_desktop",
						    _("Failed to load this program!\n%s"),
						    error->message);
279
				g_clear_error (&error);
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
280 281 282 283 284
			}

			if (ditem != NULL) {
				gnome_desktop_item_unref (ditem);
			}
285 286

			g_free (name);
287 288
                }
        } else {
289
                entry = GTK_ENTRY (g_object_get_data (G_OBJECT (w), "entry"));
290 291 292 293 294 295 296

                s = gtk_entry_get_text(entry);

                if (string_empty (s))
                        goto return_and_close;

                /* evil eggies, do not translate! */
Mark McLoughlin's avatar
Mark McLoughlin committed
297
                if (strcmp (s, "you shall bring us a shrubbery") == 0) {
298 299
                        panel_info_dialog ("ni_ni_ni_ni",
					   "NI! NI! NI! NI! NI! NI!");
300
                        goto return_and_close;
301
                } else if (strcmp (s, "supreme executive power") == 0) {
302 303 304 305 306 307 308
                        panel_info_dialog ("evil",
					   "Listen -- strange women lying in\n"
					   "ponds distributing swords is no\n"
					   "basis for a system of government.\n"
					   "Supreme executive power derives from\n"
					   "a mandate from the masses, not from\n"
					   "some farcical aquatic ceremony!");
309
                        goto return_and_close;
310
                } else if (strcmp (s, "free the fish") == 0) {
311 312
			start_screen_check ();
                        goto return_and_close;
313 314 315
		} else if (strcmp (s, "gegls from outer space") == 0) {
			start_geginv ();
                        goto return_and_close;
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
316 317 318
		} else if (strcmp (s, "End world hunger") == 0) {
			gnome_url_show ("http://www.wfp.org", NULL);
                        goto return_and_close;
319
		}
320 321 322

                /* Somewhat of a hack I suppose */
                if (panel_is_url (s)) {
323
                        gnome_url_show (s, NULL);
324 325 326
                        goto return_and_close;
                }

327
                if ( ! g_shell_parse_argv (s, &temp_argc, &temp_argv, NULL)) {
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
                        panel_error_dialog (_("Failed to execute command:\n"
                                              "%s"), s);
                        goto return_and_close;
                }

                get_environment (&temp_argc, &temp_argv, &envc, &envv);

                if (terminal->active) {
                        char **term_argv;
                        int term_argc;
                        gnome_config_get_vector ("/Gnome/Applications/Terminal",
                                                 &term_argc, &term_argv);
                        if (term_argv) {
                                int i;
                                argv = g_new(char *, term_argc + temp_argc + 1);
                                argc = term_argc + temp_argc;
                                for(i = 0; i < term_argc; i++) {
                                        argv[i] = term_argv[i];
346
                                        term_argv[i] = NULL;
347
                                }
348
                                for(i = term_argc; i < term_argc+temp_argc; i++) {
349
                                        argv[i] = temp_argv[i-term_argc];
350 351
                                        temp_argv[i-term_argc] = NULL;
				}
352
                                argv[i] = NULL;
353
				g_free (term_argv);
354 355 356
                        } else {
                                char *check;
                                int i;
357
                                check = g_find_program_in_path ("gnome-terminal");
358 359 360
                                argv = g_new(char *, 2 + temp_argc + 1);
                                argc = 2 + temp_argc;
                                if(!check) {
361 362
                                        argv[0] = g_strdup ("xterm");
                                        argv[1] = g_strdup ("-e");
363 364
                                } else {
                                        argv[0] = check;
365
                                        argv[1] = g_strdup ("-x");
366
                                }
367
                                for(i = 2; i < 2+temp_argc; i++) {
368
                                        argv[i] = temp_argv[i-2];
369 370
                                        temp_argv[i-2] = NULL;
				}
371 372 373 374
                                argv[i] = NULL;
                        }
                } else {
                        argv = temp_argv;
375
                        temp_argv = NULL;
376 377 378 379 380 381 382 383 384
                        argc = temp_argc;
                }

                if (gnome_execute_async_with_env (g_get_home_dir (),
                                                  argc, argv,
                                                  envc, envv) < 0) {
                        panel_error_dialog(_("Failed to execute command:\n"
                                             "%s\n"
                                             "%s"),
385
                                           s, g_strerror (errno));
386 387 388
                }
        }
        
389
return_and_close:
390 391
	g_strfreev (argv);
	g_strfreev (temp_argv);
392
	g_strfreev (envv);
393
	gtk_widget_destroy (w);
394 395
}

396
static void
397
browse_ok (GtkWidget *widget, GtkFileSelection *fsel)
398
{
399
	const char *fname;
400 401
	GtkWidget *entry;

402
	g_return_if_fail (GTK_IS_FILE_SELECTION (fsel));
403

404
	entry = g_object_get_data (G_OBJECT (fsel), "entry");
405

406 407
	fname = gtk_file_selection_get_filename (fsel);
	if (fname != NULL) {
408
		const char *s = gtk_entry_get_text (GTK_ENTRY (entry));
409 410 411
		if (string_empty (s)) {
			gtk_entry_set_text (GTK_ENTRY (entry), fname);
		} else {
412 413 414
			char *str = g_strconcat (s, " ", fname, NULL);
			gtk_entry_set_text (GTK_ENTRY (entry), str);
			g_free (str);
415 416
		}
	}
417
	gtk_widget_destroy (GTK_WIDGET (fsel));
418 419 420
}

static void
421
browse (GtkWidget *w, GtkWidget *entry)
422
{
423 424
	GtkFileSelection *fsel;

Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
425
	fsel = GTK_FILE_SELECTION(gtk_file_selection_new(_("Choose a program to run")));
426 427
	gtk_window_set_transient_for (GTK_WINDOW (fsel),
				      GTK_WINDOW (run_dialog));
428
	g_object_set_data (G_OBJECT (fsel), "entry", entry);
429

430 431
	g_signal_connect (G_OBJECT (fsel->ok_button), "clicked",
			    G_CALLBACK (browse_ok), fsel);
432 433
	g_signal_connect_swapped (G_OBJECT (fsel->cancel_button), "clicked",
		 		  G_CALLBACK (gtk_widget_destroy), 
434
		 		  G_OBJECT (fsel));
435 436
	panel_signal_connect_object_while_alive
		(G_OBJECT (entry), "destroy",
437
		 G_CALLBACK (gtk_widget_destroy),
438
		 G_OBJECT (fsel));
439

440
	gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
441

442
	gtk_window_present (GTK_WINDOW (fsel));
443 444
}

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
static gboolean
entry_event (GtkEntry * entry, GdkEventKey * event, gpointer data)
{
	if (event->type != GDK_KEY_PRESS)
		return FALSE;

	/* completion */
	if ((event->keyval == GDK_Tab) &&
	    (event->state & GDK_CONTROL_MASK)) {
		gchar* prefix;
		gchar* nprefix = NULL;
		gint pos;

		ensure_completion ();

460
		pos = gtk_editable_get_position (GTK_EDITABLE (entry));
461 462 463 464 465 466 467 468 469 470 471
		prefix = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, pos);

		g_completion_complete (exe_completion, prefix, &nprefix);

		if (nprefix != NULL &&
		    strlen (nprefix) > strlen (prefix)) {
			gtk_editable_insert_text (GTK_EDITABLE (entry),
						  nprefix + pos, 
						  strlen (nprefix) -
						    strlen (prefix),
						  &pos);
472
			gtk_editable_set_position (GTK_EDITABLE (entry), pos);
473 474 475
		} else {
                        gdk_beep ();
                }
476 477 478 479 480 481 482 483 484 485

		g_free (nprefix);
		g_free (prefix);

		return TRUE;
	}

	return FALSE;
}

486 487 488 489 490 491 492
static void
sync_entry_to_list (GtkWidget *dialog)
{
        GtkWidget *clist;
        GtkWidget *entry;
        gboolean blocked;

493 494
        blocked = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog),
						      "sync_entry_to_list_blocked"));
495 496 497
        if (blocked)
                return;
        
498 499
        clist = g_object_get_data (G_OBJECT (dialog), "dentry_list");
        entry = g_object_get_data (G_OBJECT (dialog), "entry");
500 501 502 503 504 505 506

        unset_selected (dialog);
}

static void
sync_list_to_entry (GtkWidget *dialog)
{
507
        GtkWidget *list;
508 509 510
        GtkWidget *entry;
        GtkWidget *terminal_toggle;
        gchar *name;
511 512 513
	GtkTreeSelection *selection;
	GtkTreeModel *model;
	GtkTreeIter iter;
514

515 516 517
        g_object_set_data (G_OBJECT (dialog),
			   "sync_entry_to_list_blocked",
			   GINT_TO_POINTER (TRUE));
518
        
519
        list = g_object_get_data (G_OBJECT (dialog), "dentry_list");
520 521
        entry = g_object_get_data (G_OBJECT (dialog), "entry");
        terminal_toggle = g_object_get_data (G_OBJECT (dialog), "terminal");
522 523 524 525 526 527 528 529 530 531 532 533 534

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));

	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
		GValue value = {0, };

		gtk_tree_model_get_value (model, &iter,
					  COLUMN_NAME,
					  &value);
		name = g_strdup (g_value_get_string (&value));
		g_value_unset (&value);

                if (name != NULL) {
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
535 536
                        GnomeDesktopItem *ditem;

537 538 539
                        ditem = gnome_desktop_item_new_from_uri (name,
								 GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS,
								 NULL /* error */);
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
540 541 542 543 544 545 546 547 548 549 550
                        if (ditem != NULL) {
				gboolean terminal;
                                const char *exec;

				exec = gnome_desktop_item_get_string
					(ditem, GNOME_DESKTOP_ITEM_EXEC);
				if (exec == NULL)
					exec = gnome_desktop_item_get_string
						(ditem, GNOME_DESKTOP_ITEM_URL);
				terminal = gnome_desktop_item_get_boolean
					(ditem, GNOME_DESKTOP_ITEM_TERMINAL);
551 552

                                gtk_entry_set_text (GTK_ENTRY (entry),
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
553
						    sure_string (exec));
554

Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
555 556 557
                                gtk_toggle_button_set_active
					(GTK_TOGGLE_BUTTON (terminal_toggle),
					 terminal);
558
				
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
559
                                gnome_desktop_item_unref (ditem);
560
                        }
561 562

			g_free (name);
563 564 565
                }
        }

566 567 568
	g_object_set_data (G_OBJECT (dialog),
			   "sync_entry_to_list_blocked",
			   GINT_TO_POINTER (FALSE));
569

570 571
	g_object_set_data (G_OBJECT (dialog), "use_list",
			   GINT_TO_POINTER (TRUE));
572 573 574 575 576 577 578 579
}

static void
toggle_contents (GtkWidget *button,
                 GtkWidget *dialog)
{
        gboolean use_advanced;
        
580 581 582 583 584 585 586 587 588 589 590 591
	use_advanced = gconf_client_get_bool (
				panel_gconf_get_client (),
				panel_gconf_general_key (
					panel_gconf_get_profile (), "advanced_run_dialog"),
				NULL);

	gconf_client_set_bool (
		panel_gconf_get_client (),
		panel_gconf_general_key (
			panel_gconf_get_profile (), "advanced_run_dialog"),
		!use_advanced,
		NULL);
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607

        update_contents (dialog);
}

static GtkWidget*
create_toggle_advanced_button (const char *label)
{
        GtkWidget *align;
        GtkWidget *button;

        align = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);

        button = gtk_button_new_with_label (label);

        gtk_container_add (GTK_CONTAINER (align), button);

608 609
        g_signal_connect (G_OBJECT (button), "clicked",
                            G_CALLBACK (toggle_contents),
610 611
                            run_dialog);

612 613 614
        g_object_set_data (G_OBJECT (run_dialog),
			   "advanced_toggle_label",
			   GTK_BIN (button)->child);
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
        
        return align;
}

static void
entry_changed (GtkWidget *entry,
               gpointer   data)
{
        sync_entry_to_list (GTK_WIDGET (data));
}

/* Called when advanced contents are switched to or first shown */
static void
advanced_contents_shown (GtkWidget *vbox,
                         GtkWidget *dialog)
{
        /* does nothing at the moment */
}

634 635 636
static void
activate_run (GtkWidget *entry, GtkWidget *dialog)
{
637
	gtk_dialog_response (GTK_DIALOG (dialog), PANEL_RESPONSE_RUN);
638 639
}

640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
static GtkWidget*
create_advanced_contents (void)
{
        GtkWidget *vbox;
        GtkWidget *entry;
        GtkWidget *gentry;
        GtkWidget *hbox;
        GtkWidget *w;
        
        vbox = gtk_vbox_new (FALSE, 0);

        hbox = gtk_hbox_new(0, FALSE);
	
        gentry = gnome_entry_new ("gnome-run");
        gtk_box_pack_start (GTK_BOX (hbox), gentry, TRUE, TRUE, 0);
        /* 1/4 the width of the first screen should be a good value */
656 657 658
	g_object_set (G_OBJECT (gentry),
		      "width_request", (int)(multiscreen_width (0) / 4),
		      NULL);
659 660 661

        entry = gnome_entry_gtk_entry (GNOME_ENTRY (gentry));

662
        g_signal_connect (G_OBJECT (entry), "event",
663 664 665 666 667
			  G_CALLBACK (entry_event),
			  NULL);
	g_signal_connect (G_OBJECT (entry), "destroy",
			  G_CALLBACK (kill_completion),
			  NULL);
668 669
 
        gtk_combo_set_use_arrows_always (GTK_COMBO (gentry), TRUE);
670
        g_object_set_data (G_OBJECT (run_dialog), "entry", entry);
671

672 673
	g_signal_connect (G_OBJECT (entry), "activate",
			    G_CALLBACK (activate_run),
674
			    run_dialog);
675
        g_signal_connect (G_OBJECT (entry),
676
                            "changed",
677
                            G_CALLBACK (entry_changed),
678 679
                            run_dialog);
        
Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
680
        w = gtk_button_new_with_mnemonic (_("_Browse..."));
681 682
        g_signal_connect(G_OBJECT(w), "clicked",
                           G_CALLBACK (browse), entry);
683 684 685 686 687 688
        gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE,
                            GNOME_PAD_SMALL);

        gtk_box_pack_start (GTK_BOX (vbox), hbox,
                            FALSE, FALSE, GNOME_PAD_SMALL);

Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
689
        w = gtk_check_button_new_with_mnemonic(_("Run in _terminal"));
690
        g_object_set_data (G_OBJECT (run_dialog), "terminal", w);
691 692 693
        gtk_box_pack_start (GTK_BOX (vbox), w,
                            FALSE, FALSE, GNOME_PAD_SMALL);
        
694 695
        g_object_set_data_full (G_OBJECT (run_dialog),
				"advanced",
696 697 698 699 700 701
				g_object_ref (vbox),
				(GDestroyNotify) g_object_unref);

        g_object_set_data_full (G_OBJECT (run_dialog),
				"advanced-entry",
				g_object_ref (entry),
702
				(GDestroyNotify) g_object_unref);
703
        
704
        g_signal_connect (G_OBJECT (vbox),
705 706 707
			  "show",
			  G_CALLBACK (advanced_contents_shown),
			  run_dialog);
708 709 710 711

        return vbox;
}

712 713 714 715 716 717 718
static void
add_columns (GtkTreeView *treeview)
{
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;

	renderer = gtk_cell_renderer_pixbuf_new ();
719 720 721 722 723 724 725
	column = gtk_tree_view_column_new ();
                
        gtk_tree_view_column_pack_start (column, renderer, FALSE);
        gtk_tree_view_column_set_attributes (column, renderer,
                                             "pixbuf", COLUMN_ICON,
                                             NULL);
        
726
	renderer = gtk_cell_renderer_text_new ();
727 728 729 730 731
        gtk_tree_view_column_pack_start (column, renderer, TRUE);

        gtk_tree_view_column_set_attributes (column, renderer,
                                             "text", COLUMN_FULLNAME,
                                             NULL);
732 733
	gtk_tree_view_append_column (treeview, column);
}
734

735
static gboolean
736
add_icon_idle (GtkListStore *list)
737 738
{
	GtkTreeIter *iter;
739 740 741 742 743 744 745 746 747
	gboolean     long_operation = FALSE;
	GdkPixbuf   *pixbuf;
	char        *file;

	do {
		if (!add_icon_iters) {
			add_icon_idle_id = 0;
			return FALSE;
		}
748

749 750
		iter = add_icon_iters->data;
		add_icon_iters = g_slist_remove (add_icon_iters, iter);
751

752 753
		gtk_tree_model_get (GTK_TREE_MODEL (list), iter,
				    COLUMN_ICON_FILE, &file, -1);
754

755 756 757 758 759 760
		pixbuf = panel_make_menu_icon (file, NULL /* fallback */,
					       ICON_SIZE, &long_operation);
		if (pixbuf) {
			gtk_list_store_set (list, iter, COLUMN_ICON, pixbuf, -1);
			g_object_unref (pixbuf);
		}
761

762
		g_free (iter);
763 764

	/* don't go back into the main loop if this wasn't very hard to do */
765
	} while (!long_operation);
766

767
	if (!add_icon_iters) {
768 769 770
		add_icon_idle_id = 0;
		return FALSE;
	}
771 772

	return TRUE;
773 774
}

775 776
/* Called when simple contents are switched to or first shown */
static void
777
fill_list (GtkWidget *list)
778 779 780 781 782
{
        GSList *tmp;
        GSList *files;
        GSList *prev;
        char *prev_name;
783 784
	GtkListStore *store;
	FileRec *all_dir;
785
        
786 787 788 789 790
	/* create list store */
	store = gtk_list_store_new (NUM_COLUMNS,
				    GDK_TYPE_PIXBUF,
				    G_TYPE_STRING,
				    G_TYPE_STRING,
791
				    G_TYPE_STRING,
792 793 794 795 796 797 798 799
				    G_TYPE_STRING);

	all_dir = fr_get_dir ("all-applications:/");
	if (all_dir != NULL) {
		files = g_slist_copy (((DirRec *)all_dir)->recs);
	} else {
		files = NULL;
	}
800

801
	/* Collate */
802
	files = g_slist_sort (files, (GCompareFunc) fr_compare);
803

804 805 806 807 808 809
	/* Strip duplicates */
	tmp = files;
	prev = NULL;
	prev_name = NULL;
	while (tmp) {
		FileRec *fr;
810

811 812 813
		fr = tmp->data;
		if (prev_name && strcmp (fr->fullname, prev_name) == 0) {
			GSList *del = tmp;
814

815 816 817 818 819 820 821 822 823
			prev->next = del->next;
			g_slist_free_1 (del);
			tmp = prev->next;
		} else {
			prev = tmp;
			prev_name = fr->fullname;
			tmp = tmp->next;
		}
	}
824

825 826
	tmp = files;
	while (tmp != NULL) {
827
		GtkTreeIter *iter;
828 829 830 831
		FileRec *fr;

		fr = tmp->data;

832
		iter = g_new0 (GtkTreeIter, 1);
833

834 835 836 837
		gtk_list_store_append (store, iter);
		gtk_list_store_set (store, iter,
				    COLUMN_ICON, NULL,
				    COLUMN_ICON_FILE, fr->icon,
838 839 840 841 842
				    COLUMN_FULLNAME, fr->fullname,
				    COLUMN_COMMENT, fr->comment,
				    COLUMN_NAME, fr->name,
				    -1);

843
		add_icon_iters = g_slist_prepend (add_icon_iters, iter);
844 845 846 847 848 849 850 851 852 853

		tmp = tmp->next;
	}

	g_slist_free (files);

	gtk_tree_view_set_model (GTK_TREE_VIEW (list), 
				 GTK_TREE_MODEL (store));

	add_columns (GTK_TREE_VIEW (list));
854 855 856 857

	add_icon_iters = g_slist_reverse (add_icon_iters);

	if (add_icon_idle_id == 0)
858
		add_icon_idle_id =
859 860 861
			g_idle_add_full (G_PRIORITY_LOW,
					 (GSourceFunc)add_icon_idle,
					 store, NULL);
862 863
}

864
#define DEFAULT_ICON "document-icons/i-executable.png"
865 866 867 868 869 870 871
#define FALLBACK_DEFAULT_ICON "gnome-logo-icon-transparent.png"

static void
unset_pixmap (GtkWidget *gpixmap)
{
        gchar *file;

872 873
        file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP, 
					  DEFAULT_ICON, TRUE, NULL);
874

875 876 877
	if (file == NULL)
		file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP, 
						  DEFAULT_ICON, TRUE, NULL);
878
        if (file == NULL)
879 880
                file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP, 
						  FALLBACK_DEFAULT_ICON, TRUE, NULL);
881 882 883
        if (file == NULL)
                file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP, 
						  FALLBACK_DEFAULT_ICON, TRUE, NULL);
884
        
885 886 887
	gtk_image_set_from_file (GTK_IMAGE (gpixmap), file);

	g_free (file);
888 889 890 891 892 893 894 895 896
}

static void
unset_selected (GtkWidget *dialog)
{
        GtkWidget *label;
        GtkWidget *gpixmap;
        GtkWidget *desc_label;
        GtkWidget *entry;
897
        GtkWidget *list;
898 899
        char *text;
        
900 901 902 903
        label = g_object_get_data (G_OBJECT (dialog), "label");
        gpixmap = g_object_get_data (G_OBJECT (dialog), "pixmap");
        desc_label = g_object_get_data (G_OBJECT (dialog), "desc_label");
        entry = g_object_get_data (G_OBJECT (dialog), "entry");
904
        list = g_object_get_data (G_OBJECT (dialog), "dentry_list");
905
        
906 907 908 909 910 911
	if (entry != NULL) {
		text = gtk_editable_get_chars (GTK_EDITABLE (entry),
					       0, -1);
	} else {
		text = NULL;
	}
912

913
        if ( ! string_empty (text)) {
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935
                char *msg;
                msg = g_strdup_printf (_("Will run '%s'"),
                                       text);
                if (label)
                        gtk_label_set_text (GTK_LABEL (label), msg);
                
                if (desc_label)
                        gtk_label_set_text (GTK_LABEL (desc_label), msg);

                g_free (msg);
        } else {
                if (label)
                        gtk_label_set_text (GTK_LABEL (label), _("No program selected"));
                
                if (desc_label)
                        gtk_label_set_text (GTK_LABEL (desc_label), _("No program selected"));
        }

        g_free (text);
        
        unset_pixmap (gpixmap);

936 937
        g_object_set_data (G_OBJECT (dialog), "use_list",
			   GPOINTER_TO_INT (FALSE));
938 939
	gtk_tree_selection_unselect_all
		(gtk_tree_view_get_selection (GTK_TREE_VIEW (list)));
940 941 942
}

static void
943 944
selection_changed (GtkTreeSelection *selection,
		   gpointer data)
945 946 947 948 949 950
{
        GtkWidget *label;
        GtkWidget *gpixmap;
        GtkWidget *desc_label;
        GtkWidget *dialog = data;
        gchar *name;
951 952 953
	GtkTreeModel *model;
	GtkTreeIter iter;
	GValue value = {0, };
954

955 956 957 958 959 960 961 962
	if ( ! gtk_tree_selection_get_selected (selection, &model, &iter))
		return;

	gtk_tree_model_get_value (model, &iter,
				  COLUMN_NAME,
				  &value);
	name = g_strdup (g_value_get_string (&value));
	g_value_unset (&value);
963

964 965 966
        label = g_object_get_data (G_OBJECT (dialog), "label");
        gpixmap = g_object_get_data (G_OBJECT (dialog), "pixmap");
        desc_label = g_object_get_data (G_OBJECT (dialog), "desc_label");
967

968
        if (name != NULL) {
969 970
                QuickDesktopItem *qitem;

971 972 973
		qitem = quick_desktop_item_load_uri (name /*file */,
						     "Application" /* expected type */,
						     TRUE /* run tryexec */);
974
		if (qitem != NULL) {
975
                        GdkPixbuf *pixbuf;
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
976 977
			char *icon;

978 979
			if (label != NULL)
				gtk_label_set_text (GTK_LABEL (label),
980
						    qitem->name);
981 982 983

			if (desc_label != NULL)
				gtk_label_set_text (GTK_LABEL (desc_label),
984
						    sure_string (qitem->comment));
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
985

986
			icon = gnome_desktop_item_find_icon (qitem->icon,
987
							     48 /* desired size */,
988
							     0 /* flags */);
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
989 990
			if (icon != NULL) {
				pixbuf = gdk_pixbuf_new_from_file (icon, NULL);
991
				g_free (icon);
992 993 994
			} else {
				pixbuf = NULL;
			}
995
                        
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
996
                        if (pixbuf != NULL) {
997
				gtk_image_set_from_pixbuf (GTK_IMAGE (gpixmap), pixbuf);
998 999 1000 1001
                        } else {
                                unset_pixmap (gpixmap);
                        }
                        
1002
			quick_desktop_item_destroy (qitem);
1003
                }
1004 1005

		g_free (name);
1006 1007 1008 1009 1010
        }

        sync_list_to_entry (dialog);
}

1011 1012 1013 1014 1015 1016 1017 1018 1019
static gboolean
add_items_idle (gpointer data)
{
	GtkWidget *list = data;
	add_items_idle_id = 0;
	fill_list (list);
	return FALSE;
}

1020 1021 1022 1023 1024 1025 1026
static GtkWidget*
create_simple_contents (void)
{
        GtkWidget *vbox;
        GtkWidget *w;
        GtkWidget *label;
        GtkWidget *pixmap;
1027
        GtkWidget *list;
1028
        GtkWidget *hbox;
1029
	GtkTreeSelection *selection;
1030 1031 1032
        
        vbox = gtk_vbox_new (FALSE, 1);
        
Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
1033 1034 1035 1036
        label = gtk_label_new_with_mnemonic (_("A_pplications:"));
        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
        gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);

1037 1038
        list = gtk_tree_view_new ();
        g_object_set_data (G_OBJECT (run_dialog), "dentry_list", list);
1039

Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
1040 1041 1042
	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
	gtk_label_set_mnemonic_widget (GTK_LABEL (label), list);

1043
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1044

1045
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1046

1047 1048 1049 1050
        g_signal_connect (G_OBJECT (selection),
			  "changed",
			  G_CALLBACK (selection_changed),
			  run_dialog);
1051 1052
        
        w = gtk_scrolled_window_new (NULL, NULL);
1053 1054
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w),
                                             GTK_SHADOW_IN);
1055 1056 1057
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
                                        GTK_POLICY_AUTOMATIC,
                                        GTK_POLICY_AUTOMATIC);
1058
        gtk_container_add (GTK_CONTAINER (w), list);
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
        
        gtk_box_pack_start (GTK_BOX (vbox), w,
                            TRUE, TRUE, GNOME_PAD_SMALL);


        w = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
        gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
        hbox = gtk_hbox_new (FALSE, 3);
        gtk_container_add (GTK_CONTAINER (w), hbox);
        
1069
        pixmap = gtk_image_new ();
1070
        gtk_box_pack_start (GTK_BOX (hbox), pixmap, FALSE, FALSE, 0);
1071
        g_object_set_data (G_OBJECT (run_dialog), "pixmap", pixmap);
1072 1073 1074 1075
        
        label = gtk_label_new ("");
        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
        gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
1076
        gtk_label_set_selectable (GTK_LABEL (label), TRUE);
1077
        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
1078
        g_object_set_data (G_OBJECT (run_dialog), "desc_label", label);
1079

1080 1081 1082 1083 1084 1085
#if 0
        label = gtk_label_new ("");
        g_object_set_data (G_OBJECT (run_dialog), "label", label);
        gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
#endif

1086 1087
        unset_selected (run_dialog);
        
1088
        g_object_ref (G_OBJECT (vbox));
1089
        
1090 1091 1092 1093
        g_object_set_data_full (G_OBJECT (run_dialog),
				"simple",
				vbox,
				(GtkDestroyNotify) g_object_unref);
1094

1095 1096 1097


	if (add_items_idle_id == 0)
1098 1099 1100
		add_items_idle_id =
			g_idle_add_full (G_PRIORITY_LOW, add_items_idle,
					 list, NULL);
1101

1102
        gtk_box_pack_start (GTK_BOX (GTK_DIALOG (run_dialog)->vbox),
1103 1104 1105
                            vbox,
                            TRUE, TRUE, 0);
        
Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
1106 1107 1108 1109
        w = create_toggle_advanced_button ("");
        gtk_box_pack_start (GTK_BOX (GTK_DIALOG (run_dialog)->vbox),
			    w, FALSE, FALSE, GNOME_PAD_SMALL);
        
1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
        return vbox;
}


static void
update_contents (GtkWidget *dialog)
{
        GtkWidget *advanced = NULL;
        GtkWidget *advanced_toggle;
        gboolean use_advanced;
        GtkWidget *clist;
        
1122 1123 1124 1125 1126 1127
	use_advanced = gconf_client_get_bool (
				panel_gconf_get_client (),
				panel_gconf_general_key (
					panel_gconf_get_profile (), "advanced_run_dialog"),
				NULL);

1128 1129
        advanced_toggle = g_object_get_data (G_OBJECT (dialog),
					     "advanced_toggle_label");
1130

1131
        clist = g_object_get_data (G_OBJECT (dialog), "dentry_list");
1132 1133
        
        if (use_advanced) {
1134
                advanced = g_object_get_data (G_OBJECT (dialog), "advanced");
1135 1136
                
                if (advanced && advanced->parent == NULL) {
1137 1138 1139 1140 1141
			GtkWidget *advanced_entry;

			advanced_entry =
				g_object_get_data (G_OBJECT (dialog), "advanced-entry");

Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
1142
                        gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
1143 1144 1145 1146 1147
                                            advanced,
                                            FALSE, FALSE, 0);
                
                        gtk_widget_show_all (advanced);

1148
                        gtk_widget_grab_focus (advanced_entry);
1149 1150
                }

Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
1151 1152
                gtk_label_set_text_with_mnemonic (GTK_LABEL (advanced_toggle),
                                    		  _("_Advanced <<"));
1153 1154 1155 1156 1157 1158

                gtk_tooltips_set_tip (panel_tooltips, advanced_toggle->parent,
                                      _("Hide the advanced controls below this button."),
                                      NULL);                

        } else {                
1159
                advanced = g_object_get_data (G_OBJECT (dialog), "advanced");
1160 1161 1162
                
                if (advanced && advanced->parent != NULL)
                        gtk_container_remove (GTK_CONTAINER (advanced->parent), advanced);                
Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
1163 1164
                gtk_label_set_text_with_mnemonic (GTK_LABEL (advanced_toggle),
                                    		  _("_Advanced >>"));
1165 1166 1167 1168 1169 1170 1171 1172 1173

                gtk_tooltips_set_tip (panel_tooltips, advanced_toggle->parent,
                                      _("Allow typing in a command line instead of choosing an application from the list"),
                                      NULL);

                gtk_widget_grab_focus (clist);
        }
}

1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
static inline void
register_run_stock_item (void)
{
	static gboolean registered = FALSE;

	if (!registered) {
		GtkIconFactory      *factory;
		GtkIconSet          *execute_icons;

		static GtkStockItem  run_item [] = {
Gediminas Paulauskas's avatar
Gediminas Paulauskas committed
1184
			{ PANEL_STOCK_RUN, N_("_Run"), 0, 0, GETTEXT_PACKAGE },
1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
		};

		execute_icons = gtk_icon_factory_lookup_default (GTK_STOCK_EXECUTE);

		factory = gtk_icon_factory_new ();

		gtk_icon_factory_add (factory, PANEL_STOCK_RUN, execute_icons);

		gtk_icon_factory_add_default (factory);

		gtk_stock_add_static (run_item, 1);

		registered = TRUE;
	}
}

1201 1202 1203 1204
static void
run_dialog_destroyed (GtkWidget *widget)
{
	run_dialog = NULL;
1205

1206 1207 1208
	g_slist_foreach (add_icon_iters, (GFunc)g_free, NULL);
	g_slist_free (add_icon_iters);
	add_icon_iters = NULL;
1209 1210

	if (add_icon_idle_id)
1211 1212
		g_source_remove (add_icon_idle_id);
	add_icon_idle_id = 0;
1213 1214

	if (add_items_idle_id)
1215 1216 1217 1218
		g_source_remove (add_items_idle_id);
	add_items_idle_id = 0;
}

1219
void
1220
show_run_dialog (void)
1221
{
1222 1223
        gboolean  use_advanced;          
	char     *run_icon;
1224

1225 1226 1227
	if (no_run_box)
		return;

1228
	if (run_dialog != NULL) {
1229
		gtk_window_present (GTK_WINDOW (run_dialog));
1230 1231
		return;
	}
1232

1233 1234
	register_run_stock_item ();

1235 1236 1237 1238 1239
	use_advanced = gconf_client_get_bool (
				panel_gconf_get_client (),
				panel_gconf_general_key (
					panel_gconf_get_profile (), "advanced_run_dialog"),
				NULL);
1240
        
1241 1242 1243 1244
	run_dialog = gtk_dialog_new_with_buttons (_("Run Program"),
						  NULL /* parent */,
						  0 /* flags */,
						  GTK_STOCK_HELP,
1245
						  GTK_RESPONSE_HELP,
1246
						  GTK_STOCK_CLOSE,
1247
						  GTK_RESPONSE_CLOSE,
1248 1249
						  PANEL_STOCK_RUN,
						  PANEL_RESPONSE_RUN,
1250
						  NULL);
1251 1252 1253 1254

        /* This is lame in advanced mode, but if you change it on mode
         * toggle it creates weird effects, so always use this policy
         */
1255 1256 1257
	g_object_set (G_OBJECT (run_dialog),
		      "allow_grow", FALSE,
		      "allow_shrink", TRUE,
1258
		      "resizable", TRUE,
1259
		      NULL);
1260 1261 1262 1263 1264

        /* Get some reasonable height in simple list mode */
        if (!use_advanced)
                gtk_window_set_default_size (GTK_WINDOW (run_dialog),
                                             -1, 400);
1265 1266 1267 1268 1269 1270 1271 1272

	run_icon = gnome_program_locate_file (
			NULL, GNOME_FILE_DOMAIN_PIXMAP, "gnome-run.png", TRUE, NULL);
	if (run_icon) {
		gnome_window_icon_set_from_file (GTK_WINDOW (run_dialog), run_icon);
		g_free (run_icon);
	}

1273 1274 1275
	g_signal_connect (G_OBJECT (run_dialog), "destroy",
			  G_CALLBACK (run_dialog_destroyed),
			  NULL);
1276
	gtk_window_set_position (GTK_WINDOW (run_dialog), GTK_WIN_POS_MOUSE);
1277
	gtk_window_set_wmclass (GTK_WINDOW (run_dialog), "run_dialog", "Panel");
1278

1279
	gtk_dialog_set_default_response (GTK_DIALOG (run_dialog), PANEL_RESPONSE_RUN);
1280

1281
        g_signal_connect (G_OBJECT (run_dialog), "response", 
1282
			  G_CALLBACK (run_dialog_response), NULL);
1283

1284 1285 1286 1287
        create_simple_contents ();
        create_advanced_contents ();
        update_contents (run_dialog);
        
1288
	gtk_widget_show_all (run_dialog);
1289
}
1290

1291 1292 1293 1294 1295 1296 1297
void
show_run_dialog_with_text (const char *text)
{
	GtkWidget *entry;

	g_return_if_fail(text != NULL);

1298
	show_run_dialog ();
1299

1300
	if(run_dialog == NULL) {
1301 1302
		return;
	}
1303
        
1304
	entry = g_object_get_data (G_OBJECT (run_dialog), "entry");
1305 1306 1307

	gtk_entry_set_text(GTK_ENTRY(entry), text);
}