gnome-run.c 33.8 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
#include <libgnome/libgnome.h>
#include <libgnomeui/libgnomeui.h>
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
32
#include <libgnomeui/gnome-window-icon.h>
Mark McLoughlin's avatar
Mark McLoughlin committed
33 34 35

#include "gnome-run.h"

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

Mark McLoughlin's avatar
Mark McLoughlin committed
43 44 45 46 47 48 49
#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"
50

51 52
#define ADVANCED_DIALOG_KEY "advanced_run_dialog"

53 54 55 56 57 58 59 60 61 62
#define ICON_SIZE 20

enum {
	COLUMN_ICON,
	COLUMN_FULLNAME,
	COLUMN_COMMENT,
	COLUMN_NAME,
	NUM_COLUMNS
};

63
extern GtkTooltips *panel_tooltips;
64
extern gboolean no_run_box;
65

66 67
static GtkWidget *run_dialog = NULL;

68 69 70
static GList *executables = NULL;
static GCompletion *exe_completion = NULL;

71 72 73 74
static GtkWidget* create_advanced_contents (void);
static void       update_contents          (GtkWidget *dialog);
static void       unset_selected           (GtkWidget *dialog);

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
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;
101
	const char *path;
102 103
	char **pathv;

104
	panel_g_list_deep_free (executables);
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
	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) {
136
		panel_g_list_deep_free (executables);
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
		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) {
165
		panel_g_list_deep_free (envar);
166 167 168 169 170 171 172
		return;
	}

	if (envar == NULL)
		return;

	for (i = 0; i < *argc; i++) {
173 174
		g_free ((*argv)[i]);
		if (i + moveby < *argc) {
175
			(*argv)[i] = (*argv)[i+moveby];
176 177
			(*argv)[i+moveby] = NULL;
		} else {
178
			(*argv)[i] = NULL;
179
		}
180 181 182 183 184 185 186 187 188 189 190 191 192
	}
	*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);
}

193 194 195 196
enum {
	RUN_BUTTON
};

197
static void 
198
run_dialog_response (GtkWidget *w, int response, gpointer data)
199
{
200
	GtkEntry *entry;
201
        GtkWidget *list;
202
	GtkToggleButton *terminal;
203
	char **argv = NULL;
204
	char **temp_argv = NULL;
205
	int argc, temp_argc;
206
	const char *s;
207 208
	char **envv = NULL;
	int envc;
209 210
        gboolean use_advanced;
        
211
        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");
212
        
213
	if (response == GTK_RESPONSE_HELP) {
214
		panel_show_help ("specialobjects", "RUNBUTTON");
215
		/* just return as we don't want to close */
216
		return;
217
	} else if (response == GTK_RESPONSE_CLOSE) {
218 219
		goto return_and_close;
	}
220
        
221
        list = g_object_get_data (G_OBJECT (run_dialog), "dentry_list");
222 223
        terminal = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT(w),
							 "terminal"));
224
        
225
        if (g_object_get_data (G_OBJECT (run_dialog), "use_list")) {
226
                char *name;
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
		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
245 246 247
                if (name != NULL) {
			GError *error = NULL;
                        GnomeDesktopItem *ditem;
248
                        
249 250 251
                        ditem = gnome_desktop_item_new_from_uri (name,
								 GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS,
								 &error);
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
252
			if (ditem != NULL) {
253
                                /* Honor "run in terminal" button */
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
254 255 256 257
				gnome_desktop_item_set_boolean (ditem,
								GNOME_DESKTOP_ITEM_TERMINAL,
								terminal->active);
			}
258

Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
259 260 261 262
                        if (ditem == NULL) {
                                panel_error_dialog ("failed_to_load_desktop",
						    _("Failed to load this program!\n%s"),
						    error->message);
263
				g_clear_error (&error);
264 265 266
			} else if ( ! gnome_desktop_item_launch (ditem,
								 NULL /* file_list */,
								 0 /* flags */,
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
267 268 269 270
								 &error)) {
                                panel_error_dialog ("failed_to_load_desktop",
						    _("Failed to load this program!\n%s"),
						    error->message);
271
				g_clear_error (&error);
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
272 273 274 275 276
			}

			if (ditem != NULL) {
				gnome_desktop_item_unref (ditem);
			}
277 278

			g_free (name);
279 280
                }
        } else {
281
                entry = GTK_ENTRY (g_object_get_data (G_OBJECT (w), "entry"));
282 283 284 285 286 287 288 289 290 291

                s = gtk_entry_get_text(entry);

                if (string_empty (s))
                        goto return_and_close;

                /* evil eggies, do not translate! */
                if (strcmp (s, "time shall be unixey") == 0) {
                        foobar_widget_global_set_clock_format ("%s");
                        goto return_and_close;
292
                } else if (strcmp (s, "you shall bring us a shrubbery") == 0) {
293 294
                        panel_info_dialog ("ni_ni_ni_ni",
					   "NI! NI! NI! NI! NI! NI!");
295
                        goto return_and_close;
296
                } else if (strcmp (s, "supreme executive power") == 0) {
297 298 299 300 301 302 303
                        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!");
304
                        goto return_and_close;
305
                } else if (strcmp (s, "free the fish") == 0) {
306 307
			start_screen_check ();
                        goto return_and_close;
308 309 310
		} else if (strcmp (s, "gegls from outer space") == 0) {
			start_geginv ();
                        goto return_and_close;
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
311 312 313
		} else if (strcmp (s, "End world hunger") == 0) {
			gnome_url_show ("http://www.wfp.org", NULL);
                        goto return_and_close;
314
		}
315 316 317

                /* Somewhat of a hack I suppose */
                if (panel_is_url (s)) {
318
                        gnome_url_show (s, NULL);
319 320 321
                        goto return_and_close;
                }

322
                if ( ! g_shell_parse_argv (s, &temp_argc, &temp_argv, NULL)) {
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
                        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];
341
                                        term_argv[i] = NULL;
342
                                }
343
                                for(i = term_argc; i < term_argc+temp_argc; i++) {
344
                                        argv[i] = temp_argv[i-term_argc];
345 346
                                        temp_argv[i-term_argc] = NULL;
				}
347
                                argv[i] = NULL;
348
				g_free (term_argv);
349 350 351
                        } else {
                                char *check;
                                int i;
352
                                check = g_find_program_in_path ("gnome-terminal");
353 354 355
                                argv = g_new(char *, 2 + temp_argc + 1);
                                argc = 2 + temp_argc;
                                if(!check) {
356 357
                                        argv[0] = g_strdup ("xterm");
                                        argv[1] = g_strdup ("-e");
358 359
                                } else {
                                        argv[0] = check;
360
                                        argv[1] = g_strdup ("-x");
361
                                }
362
                                for(i = 2; i < 2+temp_argc; i++) {
363
                                        argv[i] = temp_argv[i-2];
364 365
                                        temp_argv[i-2] = NULL;
				}
366 367 368 369
                                argv[i] = NULL;
                        }
                } else {
                        argv = temp_argv;
370
                        temp_argv = NULL;
371 372 373 374 375 376 377 378 379
                        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"),
380
                                           s, g_strerror (errno));
381 382 383
                }
        }
        
384
return_and_close:
385 386
	g_strfreev (argv);
	g_strfreev (temp_argv);
387
	g_strfreev (envv);
388
	gtk_widget_destroy (w);
389 390
}

391
static void
392
browse_ok (GtkWidget *widget, GtkFileSelection *fsel)
393
{
394
	const char *fname;
395 396
	GtkWidget *entry;

397
	g_return_if_fail (GTK_IS_FILE_SELECTION (fsel));
398

399
	entry = g_object_get_data (G_OBJECT (fsel), "entry");
400

401 402
	fname = gtk_file_selection_get_filename (fsel);
	if (fname != NULL) {
403
		const char *s = gtk_entry_get_text (GTK_ENTRY (entry));
404 405 406
		if (string_empty (s)) {
			gtk_entry_set_text (GTK_ENTRY (entry), fname);
		} else {
407 408 409
			char *str = g_strconcat (s, " ", fname, NULL);
			gtk_entry_set_text (GTK_ENTRY (entry), str);
			g_free (str);
410 411
		}
	}
412
	gtk_widget_destroy (GTK_WIDGET (fsel));
413 414 415
}

static void
416
browse (GtkWidget *w, GtkWidget *entry)
417
{
418 419 420
	GtkFileSelection *fsel;

	fsel = GTK_FILE_SELECTION(gtk_file_selection_new(_("Browse...")));
421 422
	gtk_window_set_transient_for (GTK_WINDOW (fsel),
				      GTK_WINDOW (run_dialog));
423
	g_object_set_data (G_OBJECT (fsel), "entry", entry);
424

425 426
	g_signal_connect (G_OBJECT (fsel->ok_button), "clicked",
			    G_CALLBACK (browse_ok), fsel);
427 428
	g_signal_connect_swapped (G_OBJECT (fsel->cancel_button), "clicked",
		 		  G_CALLBACK (gtk_widget_destroy), 
429
		 		  G_OBJECT (fsel));
430 431
	panel_signal_connect_object_while_alive
		(G_OBJECT (entry), "destroy",
432
		 G_CALLBACK (gtk_widget_destroy),
433
		 G_OBJECT (fsel));
434

435
	gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
436

437
	gtk_window_present (GTK_WINDOW (fsel));
438 439
}

440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
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 ();

455
		pos = gtk_editable_get_position (GTK_EDITABLE (entry));
456 457 458 459 460 461 462 463 464 465 466
		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);
467
			gtk_editable_set_position (GTK_EDITABLE (entry), pos);
468 469 470
		} else {
                        gdk_beep ();
                }
471 472 473 474 475 476 477 478 479 480

		g_free (nprefix);
		g_free (prefix);

		return TRUE;
	}

	return FALSE;
}

481 482 483 484 485 486 487
static void
sync_entry_to_list (GtkWidget *dialog)
{
        GtkWidget *clist;
        GtkWidget *entry;
        gboolean blocked;

488 489
        blocked = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog),
						      "sync_entry_to_list_blocked"));
490 491 492
        if (blocked)
                return;
        
493 494
        clist = g_object_get_data (G_OBJECT (dialog), "dentry_list");
        entry = g_object_get_data (G_OBJECT (dialog), "entry");
495 496 497 498 499 500 501

        unset_selected (dialog);
}

static void
sync_list_to_entry (GtkWidget *dialog)
{
502
        GtkWidget *list;
503 504 505
        GtkWidget *entry;
        GtkWidget *terminal_toggle;
        gchar *name;
506 507 508
	GtkTreeSelection *selection;
	GtkTreeModel *model;
	GtkTreeIter iter;
509

510 511 512
        g_object_set_data (G_OBJECT (dialog),
			   "sync_entry_to_list_blocked",
			   GINT_TO_POINTER (TRUE));
513
        
514
        list = g_object_get_data (G_OBJECT (dialog), "dentry_list");
515 516
        entry = g_object_get_data (G_OBJECT (dialog), "entry");
        terminal_toggle = g_object_get_data (G_OBJECT (dialog), "terminal");
517 518 519 520 521 522 523 524 525 526 527 528 529

	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
530 531
                        GnomeDesktopItem *ditem;

532 533 534
                        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
535 536 537 538 539 540 541 542 543 544 545
                        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);
546 547

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

Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
550 551 552
                                gtk_toggle_button_set_active
					(GTK_TOGGLE_BUTTON (terminal_toggle),
					 terminal);
553
				
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
554
                                gnome_desktop_item_unref (ditem);
555
                        }
556 557

			g_free (name);
558 559 560
                }
        }

561 562 563
	g_object_set_data (G_OBJECT (dialog),
			   "sync_entry_to_list_blocked",
			   GINT_TO_POINTER (FALSE));
564

565 566
	g_object_set_data (G_OBJECT (dialog), "use_list",
			   GINT_TO_POINTER (TRUE));
567 568 569 570 571 572 573 574
}

static void
toggle_contents (GtkWidget *button,
                 GtkWidget *dialog)
{
        gboolean use_advanced;
        
575
        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");
576

577
        gnome_config_set_bool ("/panel/State/"ADVANCED_DIALOG_KEY, !use_advanced);
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
        gnome_config_sync ();

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

595 596
        g_signal_connect (G_OBJECT (button), "clicked",
                            G_CALLBACK (toggle_contents),
597 598
                            run_dialog);

599 600 601
        g_object_set_data (G_OBJECT (run_dialog),
			   "advanced_toggle_label",
			   GTK_BIN (button)->child);
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
        
        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 */
}

621 622 623 624 625 626
static void
activate_run (GtkWidget *entry, GtkWidget *dialog)
{
	gtk_dialog_response (GTK_DIALOG (dialog), RUN_BUTTON);
}

627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
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 */
643 644 645
	g_object_set (G_OBJECT (gentry),
		      "width_request", (int)(multiscreen_width (0) / 4),
		      NULL);
646 647 648

        entry = gnome_entry_gtk_entry (GNOME_ENTRY (gentry));

649
        g_signal_connect (G_OBJECT (entry), "event",
650 651 652 653 654
			  G_CALLBACK (entry_event),
			  NULL);
	g_signal_connect (G_OBJECT (entry), "destroy",
			  G_CALLBACK (kill_completion),
			  NULL);
655 656 657
 
        gtk_window_set_focus (GTK_WINDOW (run_dialog), entry);
        gtk_combo_set_use_arrows_always (GTK_COMBO (gentry), TRUE);
658
        g_object_set_data (G_OBJECT (run_dialog), "entry", entry);
659

660 661
	g_signal_connect (G_OBJECT (entry), "activate",
			    G_CALLBACK (activate_run),
662
			    run_dialog);
663
        g_signal_connect (G_OBJECT (entry),
664
                            "changed",
665
                            G_CALLBACK (entry_changed),
666 667 668
                            run_dialog);
        
        w = gtk_button_new_with_label(_("Browse..."));
669 670
        g_signal_connect(G_OBJECT(w), "clicked",
                           G_CALLBACK (browse), entry);
671 672 673 674 675 676 677
        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);

        w = gtk_check_button_new_with_label(_("Run in terminal"));
678
        g_object_set_data (G_OBJECT (run_dialog), "terminal", w);
679 680 681
        gtk_box_pack_start (GTK_BOX (vbox), w,
                            FALSE, FALSE, GNOME_PAD_SMALL);
        
682
        g_object_ref (G_OBJECT (vbox));
683
        
684 685 686
        g_object_set_data_full (G_OBJECT (run_dialog),
				"advanced",
				vbox,
687
				(GDestroyNotify) g_object_unref);
688
        
689
        g_signal_connect (G_OBJECT (vbox),
690 691 692
			  "show",
			  G_CALLBACK (advanced_contents_shown),
			  run_dialog);
693 694 695 696 697 698 699 700

        return vbox;
}

static int
sort_by_name (FileRec *fra,
              FileRec *frb)
{
701 702 703 704
	/* FIXME: there is no utf8 strcoll afaik, so
	 * we just strcmp for now, this is evil, but
	 * it works mostly somewhat */
        return strcmp (fra->fullname, frb->fullname);
705 706
}

707 708 709 710 711 712 713
static void
add_columns (GtkTreeView *treeview)
{
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;

	renderer = gtk_cell_renderer_pixbuf_new ();
714 715 716 717 718 719 720 721
	column = gtk_tree_view_column_new ();
        gtk_tree_view_column_set_title (column, _("Applications"));
                
        gtk_tree_view_column_pack_start (column, renderer, FALSE);
        gtk_tree_view_column_set_attributes (column, renderer,
                                             "pixbuf", COLUMN_ICON,
                                             NULL);
        
722
	renderer = gtk_cell_renderer_text_new ();
723 724 725 726 727
        gtk_tree_view_column_pack_start (column, renderer, TRUE);

        gtk_tree_view_column_set_attributes (column, renderer,
                                             "text", COLUMN_FULLNAME,
                                             NULL);
728 729
	gtk_tree_view_append_column (treeview, column);
}
730 731 732

/* Called when simple contents are switched to or first shown */
static void
733
fill_list (GtkWidget *list)
734 735 736 737 738
{
        GSList *tmp;
        GSList *files;
        GSList *prev;
        char *prev_name;
739 740
	GtkListStore *store;
	FileRec *all_dir;
741
        
742 743 744 745 746 747 748 749 750 751 752 753 754
	/* create list store */
	store = gtk_list_store_new (NUM_COLUMNS,
				    GDK_TYPE_PIXBUF,
				    G_TYPE_STRING,
				    G_TYPE_STRING,
				    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;
	}
755

756 757
	/* Collate */
	files = g_slist_sort (files, (GCompareFunc) sort_by_name);
758

759 760 761 762 763 764
	/* Strip duplicates */
	tmp = files;
	prev = NULL;
	prev_name = NULL;
	while (tmp) {
		FileRec *fr;
765

766 767 768
		fr = tmp->data;
		if (prev_name && strcmp (fr->fullname, prev_name) == 0) {
			GSList *del = tmp;
769

770 771 772 773 774 775 776 777 778
			prev->next = del->next;
			g_slist_free_1 (del);
			tmp = prev->next;
		} else {
			prev = tmp;
			prev_name = fr->fullname;
			tmp = tmp->next;
		}
	}
779

780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797
	tmp = files;
	while (tmp != NULL) {
		GtkTreeIter iter;
		FileRec *fr;
		GdkPixbuf *pixbuf;
		char *icon;

		fr = tmp->data;

		icon = gnome_desktop_item_find_icon (fr->icon,
						     ICON_SIZE /* desired size */,
						     0 /* flags */);
		if (icon != NULL) {
			pixbuf = gdk_pixbuf_new_from_file (icon, NULL);
			g_free (icon);
		} else {
			pixbuf = NULL;
		}
798

799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
		if (pixbuf != NULL &&
		    (gdk_pixbuf_get_width (pixbuf) != ICON_SIZE ||
		     gdk_pixbuf_get_height (pixbuf) != ICON_SIZE)) {
			GdkPixbuf *scaled;
			scaled = gdk_pixbuf_scale_simple (pixbuf,
							  ICON_SIZE,
							  ICON_SIZE,
							  GDK_INTERP_BILINEAR);
			g_object_unref (G_OBJECT (pixbuf));
			pixbuf = scaled;
		}

		gtk_list_store_append (store, &iter);
		gtk_list_store_set (store, &iter,
				    COLUMN_ICON, pixbuf,
				    COLUMN_FULLNAME, fr->fullname,
				    COLUMN_COMMENT, fr->comment,
				    COLUMN_NAME, fr->name,
				    -1);

		if (pixbuf != NULL)
			g_object_unref (G_OBJECT (pixbuf));

		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));
831 832
}

833
#define DEFAULT_ICON "document-icons/i-executable.png"
834 835 836 837 838 839 840
#define FALLBACK_DEFAULT_ICON "gnome-logo-icon-transparent.png"

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

841 842
        file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP, 
					  DEFAULT_ICON, TRUE, NULL);
843

844 845 846
	if (file == NULL)
		file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP, 
						  DEFAULT_ICON, TRUE, NULL);
847
        if (file == NULL)
848 849
                file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP, 
						  FALLBACK_DEFAULT_ICON, TRUE, NULL);
850 851 852
        if (file == NULL)
                file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP, 
						  FALLBACK_DEFAULT_ICON, TRUE, NULL);
853
        
854 855 856
	gtk_image_set_from_file (GTK_IMAGE (gpixmap), file);

	g_free (file);
857 858 859 860 861 862 863 864 865
}

static void
unset_selected (GtkWidget *dialog)
{
        GtkWidget *label;
        GtkWidget *gpixmap;
        GtkWidget *desc_label;
        GtkWidget *entry;
866
        GtkWidget *list;
867 868
        char *text;
        
869 870 871 872
        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");
873
        list = g_object_get_data (G_OBJECT (dialog), "dentry_list");
874
        
875 876 877 878 879 880
	if (entry != NULL) {
		text = gtk_editable_get_chars (GTK_EDITABLE (entry),
					       0, -1);
	} else {
		text = NULL;
	}
881

882
        if ( ! string_empty (text)) {
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
                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);

905 906
        g_object_set_data (G_OBJECT (dialog), "use_list",
			   GPOINTER_TO_INT (FALSE));
907 908
	gtk_tree_selection_unselect_all
		(gtk_tree_view_get_selection (GTK_TREE_VIEW (list)));
909 910 911
}

static void
912 913
selection_changed (GtkTreeSelection *selection,
		   gpointer data)
914 915 916 917 918 919
{
        GtkWidget *label;
        GtkWidget *gpixmap;
        GtkWidget *desc_label;
        GtkWidget *dialog = data;
        gchar *name;
920 921 922
	GtkTreeModel *model;
	GtkTreeIter iter;
	GValue value = {0, };
923

924 925 926 927 928 929 930 931
	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);
932

933 934 935
        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");
936

937
        if (name != NULL) {
938 939
                QuickDesktopItem *qitem;

940 941 942
		qitem = quick_desktop_item_load_uri (name /*file */,
						     "Application" /* expected type */,
						     TRUE /* run tryexec */);
943
		if (qitem != NULL) {
944
                        GdkPixbuf *pixbuf;
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
945 946
			char *icon;

947 948
			if (label != NULL)
				gtk_label_set_text (GTK_LABEL (label),
949
						    qitem->name);
950 951 952

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

955
			icon = gnome_desktop_item_find_icon (qitem->icon,
956
							     48 /* desired size */,
957
							     0 /* flags */);
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
958 959
			if (icon != NULL) {
				pixbuf = gdk_pixbuf_new_from_file (icon, NULL);
960
				g_free (icon);
961 962 963
			} else {
				pixbuf = NULL;
			}
964
                        
Jiri (George) Lebl's avatar
Jiri (George) Lebl committed
965
                        if (pixbuf != NULL) {
966
				gtk_image_set_from_pixbuf (GTK_IMAGE (gpixmap), pixbuf);
967 968 969 970
                        } else {
                                unset_pixmap (gpixmap);
                        }
                        
971
			quick_desktop_item_destroy (qitem);
972
                }
973 974

		g_free (name);
975 976 977 978 979 980 981 982 983 984 985 986
        }

        sync_list_to_entry (dialog);
}

static GtkWidget*
create_simple_contents (void)
{
        GtkWidget *vbox;
        GtkWidget *w;
        GtkWidget *label;
        GtkWidget *pixmap;
987
        GtkWidget *list;
988
        GtkWidget *hbox;
989
	GtkTreeSelection *selection;
990 991 992
        
        vbox = gtk_vbox_new (FALSE, 1);
        
993 994
        list = gtk_tree_view_new ();
        g_object_set_data (G_OBJECT (run_dialog), "dentry_list", list);
995

996
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
997

998
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
999

1000 1001 1002 1003
        g_signal_connect (G_OBJECT (selection),
			  "changed",
			  G_CALLBACK (selection_changed),
			  run_dialog);
1004 1005
        
        w = gtk_scrolled_window_new (NULL, NULL);
1006 1007
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w),
                                             GTK_SHADOW_IN);
1008 1009 1010
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
                                        GTK_POLICY_AUTOMATIC,
                                        GTK_POLICY_AUTOMATIC);
1011
        gtk_container_add (GTK_CONTAINER (w), list);
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
        
        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);
        
1022
        pixmap = gtk_image_new ();
1023
        gtk_box_pack_start (GTK_BOX (hbox), pixmap, FALSE, FALSE, 0);
1024
        g_object_set_data (G_OBJECT (run_dialog), "pixmap", pixmap);
1025 1026 1027 1028 1029
        
        label = gtk_label_new ("");
        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
        gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1030
        g_object_set_data (G_OBJECT (run_dialog), "desc_label", label);        
1031 1032 1033

#if 0
        label = gtk_label_new ("");
1034
        g_object_set_data (G_OBJECT (run_dialog), "label", label);
1035 1036 1037 1038 1039 1040
        gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
#endif

        unset_selected (run_dialog);
        
        w = create_toggle_advanced_button ("");
1041
        gtk_box_pack_end (GTK_BOX (GTK_DIALOG (run_dialog)->vbox), w,
1042 1043
                          FALSE, FALSE, GNOME_PAD_SMALL);
        
1044
        g_object_ref (G_OBJECT (vbox));
1045
        
1046 1047 1048 1049
        g_object_set_data_full (G_OBJECT (run_dialog),
				"simple",
				vbox,
				(GtkDestroyNotify) g_object_unref);
1050

1051
	fill_list (list);
1052

1053
        gtk_box_pack_start (GTK_BOX (GTK_DIALOG (run_dialog)->vbox),
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
                            vbox,
                            TRUE, TRUE, 0);
        
        return vbox;
}


static void
update_contents (GtkWidget *dialog)
{
        GtkWidget *advanced = NULL;
        GtkWidget *advanced_toggle;
        gboolean use_advanced;
        GtkWidget *clist;
        
1069
        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");        
1070 1071
        advanced_toggle = g_object_get_data (G_OBJECT (dialog),
					     "advanced_toggle_label");
1072

1073
        clist = g_object_get_data (G_OBJECT (dialog), "dentry_list");
1074 1075
        
        if (use_advanced) {
1076
                advanced = g_object_get_data (G_OBJECT (dialog), "advanced");
1077 1078
                
                if (advanced && advanced->parent == NULL) {
1079
                        gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
                                            advanced,
                                            FALSE, FALSE, 0);
                
                        gtk_widget_show_all (advanced);

                        gtk_widget_grab_focus (advanced);
                }

                gtk_label_set_text (GTK_LABEL (advanced_toggle),
                                    _("Hide advanced options"));

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

        } else {                
1096
                advanced = g_object_get_data (G_OBJECT (dialog), "advanced");
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
                
                if (advanced && advanced->parent != NULL)
                        gtk_container_remove (GTK_CONTAINER (advanced->parent), advanced);                
                gtk_label_set_text (GTK_LABEL (advanced_toggle),
                                    _("Advanced..."));

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

1111
void
1112
show_run_dialog (void)
1113
{
1114
        gboolean use_advanced;          
1115

1116 1117 1118
	if (no_run_box)
		return;

1119
	if(run_dialog != NULL) {
1120
		gtk_window_present (GTK_WINDOW (run_dialog));
1121 1122
		return;
	}
1123

1124
        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");
1125
        
1126 1127 1128 1129
	run_dialog = gtk_dialog_new_with_buttons (_("Run Program"),
						  NULL /* parent */,
						  0 /* flags */,
						  GTK_STOCK_HELP,
1130
						  GTK_RESPONSE_HELP,
1131
						  GTK_STOCK_CLOSE,
1132
						  GTK_RESPONSE_CLOSE,
1133 1134 1135 1136 1137
						  /* FIXME: how the hell do we get a different label but
						   * the execute stock icon */
						  GTK_STOCK_EXECUTE,
						  RUN_BUTTON,
						  NULL);
1138 1139 1140 1141

        /* This is lame in advanced mode, but if you change it on mode
         * toggle it creates weird effects, so always use this policy
         */
1142 1143 1144
	g_object_set (G_OBJECT (run_dialog),
		      "allow_grow", FALSE,
		      "allow_shrink", TRUE,
1145
		      "resizable", TRUE,
1146
		      NULL);
1147 1148 1149 1150 1151 1152

        /* Get some reasonable height in simple list mode */
        if (!use_advanced)
                gtk_window_set_default_size (GTK_WINDOW (run_dialog),
                                             -1, 400);
        
1153
	gnome_window_icon_set_from_file (GTK_WINDOW (run_dialog),
Jacob Berkman's avatar
Jacob Berkman committed
1154
					 GNOME_ICONDIR"/gnome-run.png");
1155 1156
	g_signal_connect(G_OBJECT(run_dialog), "destroy",
			   G_CALLBACK(gtk_widget_destroyed),
1157
			   &run_dialog);
1158
	gtk_window_set_position (GTK_WINDOW (run_dialog), GTK_WIN_POS_MOUSE);
1159
	gtk_window_set_wmclass (GTK_WINDOW (run_dialog), "run_dialog", "Panel");
1160

1161 1162
	gtk_dialog_set_default_response (GTK_DIALOG (run_dialog), 
					 RUN_BUTTON);
1163

1164
        g_signal_connect (G_OBJECT (run_dialog), "response", 
1165
			  G_CALLBACK (run_dialog_response), NULL);
1166

1167 1168 1169 1170
        create_simple_contents ();
        create_advanced_contents ();
        update_contents (run_dialog);
        
1171
	gtk_widget_show_all (run_dialog);
1172
}
1173

1174 1175 1176 1177 1178 1179 1180
void
show_run_dialog_with_text (const char *text)
{
	GtkWidget *entry;

	g_return_if_fail(text != NULL);

1181
	show_run_dialog ();
1182

1183
	if(run_dialog == NULL) {
1184 1185
		return;
	}
1186
        
1187
	entry = g_object_get_data (G_OBJECT (run_dialog), "entry");
1188 1189 1190

	gtk_entry_set_text(GTK_ENTRY(entry), text);
}