1
/* -*- c-basic-offset: 8; indent-tabs-mode: t -*-
4
* Copyright (C) 2003 Frank Worsley <fworsley@shaw.ca>
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License as
8
* published by the Free Software Foundation; either version 2 of the
9
* License, or (at your option) any later version.
11
* This program is distributed in the hope that it will be useful, but
12
* WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22
* Frank Worsley <fworsley@shaw.ca>
25
* Havoc Pennington <hp@pobox.com>
26
* George Lebl <jirka@5z.com>
27
* Mark McLoughlin <mark@skynet.ie>
28
* Tom Tromey (Copyright (C) 1998)
33
#include "panel-run-dialog.h"
38
#include <sys/types.h>
41
#include <glib/gi18n.h>
43
#include <gdk/gdkkeysyms.h>
44
#include <gmenu-tree.h>
46
#include <libpanel-util/panel-error.h>
47
#include <libpanel-util/panel-glib.h>
48
#include <libpanel-util/panel-gtk.h>
49
#include <libpanel-util/panel-keyfile.h>
50
#include <libpanel-util/panel-show.h>
51
#include <libpanel-util/panel-xdg.h>
54
#include "panel-util.h"
55
#include "panel-globals.h"
56
#include "panel-enums.h"
57
#include "panel-stock-icons.h"
58
#include "panel-multiscreen.h"
60
#include "panel-lockdown.h"
61
#include "panel-xutils.h"
62
#include "panel-icon-names.h"
63
#include "panel-schemas.h"
66
GtkWidget *run_dialog;
68
GSettings *run_settings;
74
GtkWidget *run_button;
75
GtkWidget *file_button;
76
GtkWidget *list_expander;
77
GtkWidget *terminal_checkbox;
78
GtkWidget *program_label;
79
GtkWidget *program_list;
83
GtkListStore *program_list_store;
86
GList *possible_executables;
87
GList *completion_items;
88
GCompletion *completion;
90
GSList *add_icon_paths;
91
int add_items_idle_id;
92
int find_command_idle_id;
93
gboolean use_program_list;
94
gboolean completion_started;
111
static PanelRunDialog *static_dialog = NULL;
113
#define PANEL_RUN_MAX_HISTORY 20
115
static GtkTreeModel *
116
_panel_run_get_recent_programs_list (PanelRunDialog *dialog)
122
list = gtk_list_store_new (1, G_TYPE_STRING);
124
commands = g_settings_get_strv (dialog->run_settings,
125
PANEL_RUN_HISTORY_KEY);
127
for (i = 0; commands[i] != NULL; i++) {
129
gtk_list_store_prepend (list, &iter);
130
gtk_list_store_set (list, &iter, 0, commands[i], -1);
133
g_strfreev (commands);
135
return GTK_TREE_MODEL (list);
139
_panel_run_save_recent_programs_list (PanelRunDialog *dialog,
147
commands = g_settings_get_strv (dialog->run_settings,
148
PANEL_RUN_HISTORY_KEY);
150
/* do not save the same command twice in a row */
151
if (g_strcmp0 (commands[0], last_command) == 0)
154
for (i = 0; commands[i] != NULL; i++);
155
size = MIN (i + 1, PANEL_RUN_MAX_HISTORY);
157
new_commands = g_new (char *, size + 1);
159
new_commands[0] = last_command;
160
new_commands[size] = NULL; /* last item */
162
for (i = 1; i < size; i++)
163
new_commands[i] = commands[i-1];
165
g_settings_set_strv (dialog->run_settings,
166
PANEL_RUN_HISTORY_KEY,
167
(const char **) new_commands);
169
g_free (new_commands); /* we don't own the strings */
170
g_strfreev (commands);
174
panel_run_dialog_destroy (PanelRunDialog *dialog)
178
dialog->changed_id = 0;
180
g_object_unref (dialog->list_expander);
182
g_slist_foreach (dialog->add_icon_paths, (GFunc) gtk_tree_path_free, NULL);
183
g_slist_free (dialog->add_icon_paths);
184
dialog->add_icon_paths = NULL;
187
g_object_unref (dialog->gicon);
188
dialog->gicon = NULL;
190
g_free (dialog->desktop_path);
191
dialog->desktop_path = NULL;
192
g_free (dialog->item_name);
193
dialog->item_name = NULL;
195
if (dialog->add_items_idle_id)
196
g_source_remove (dialog->add_items_idle_id);
197
dialog->add_items_idle_id = 0;
199
if (dialog->find_command_idle_id)
200
g_source_remove (dialog->find_command_idle_id);
201
dialog->find_command_idle_id = 0;
203
if (dialog->dir_hash)
204
g_hash_table_destroy (dialog->dir_hash);
205
dialog->dir_hash = NULL;
207
for (l = dialog->possible_executables; l; l = l->next)
209
g_list_free (dialog->possible_executables);
210
dialog->possible_executables = NULL;
212
for (l = dialog->completion_items; l; l = l->next)
214
g_list_free (dialog->completion_items);
215
dialog->completion_items = NULL;
217
if (dialog->completion)
218
g_completion_free (dialog->completion);
219
dialog->completion = NULL;
221
if (dialog->run_settings)
222
g_object_unref (dialog->run_settings);
223
dialog->run_settings = NULL;
229
panel_run_dialog_get_combo_text (PanelRunDialog *dialog)
233
entry = gtk_bin_get_child (GTK_BIN (dialog->combobox));
235
return gtk_entry_get_text (GTK_ENTRY (entry));
239
panel_run_dialog_set_default_icon (PanelRunDialog *dialog, gboolean set_drag)
241
gtk_image_set_from_icon_name (GTK_IMAGE (dialog->pixmap),
243
GTK_ICON_SIZE_DIALOG);
246
gtk_drag_source_set_icon_name (dialog->run_dialog,
247
PANEL_ICON_LAUNCHER);
251
panel_run_dialog_set_icon (PanelRunDialog *dialog,
255
if (!force && gicon && dialog->gicon &&
256
gicon == dialog->gicon)
260
g_object_unref (dialog->gicon);
261
dialog->gicon = NULL;
264
dialog->gicon = g_object_ref (gicon);
265
gtk_image_set_from_gicon (GTK_IMAGE (dialog->pixmap),
266
gicon, GTK_ICON_SIZE_DIALOG);
267
gtk_drag_source_set_icon_gicon (dialog->run_dialog, gicon);
269
panel_run_dialog_set_default_icon (dialog, TRUE);
274
command_is_executable (const char *command,
283
result = g_shell_parse_argv (command, &argc, &argv, NULL);
288
path = g_find_program_in_path (argv[0]);
295
/* If we pass an absolute path to g_find_program it just returns
296
* that absolute path without checking if it is executable. Also
297
* make sure its a regular file so we don't try to launch
298
* directories or device nodes.
300
if (!g_file_test (path, G_FILE_TEST_IS_EXECUTABLE) ||
301
!g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
318
* Set the DISPLAY variable, to be use by g_spawn_async.
321
set_environment (gpointer display)
323
g_setenv ("DISPLAY", display, TRUE);
327
dummy_child_watch (GPid pid,
331
/* Nothing, this is just to ensure we don't double fork
333
* https://bugzilla.gnome.org/show_bug.cgi?id=675789
339
* panel_run_dialog_prepend_terminal_to_vector:
340
* @argc: a pointer to the vector size
341
* @argv: a pointer to the vector
343
* Description: Prepends a terminal (either the one configured as default in
344
* the user's GNOME setup, or one of the common xterm emulators) to the passed
345
* in vector, modifying it in the process. The vector should be allocated with
346
* #g_malloc, as this will #g_free the original vector. Also all elements must
347
* have been allocated separately. That is the standard glib/GNOME way of
348
* doing vectors however. If the integer that @argc points to is negative, the
349
* size will first be computed. Also note that passing in pointers to a vector
350
* that is empty, will just create a new vector for you.
352
/* TODO: throw out this function if there ever is a proper GAppInfo port */
354
panel_run_dialog_prepend_terminal_to_vector (int *argc, char ***argv)
359
char **term_argv = NULL;
363
gchar *terminal = NULL;
367
g_return_if_fail (argc != NULL);
368
g_return_if_fail (argv != NULL);
376
/* compute size if not given */
378
for (i = 0; the_argv[i] != NULL; i++)
383
settings = g_settings_new ("org.gnome.desktop.default-applications.terminal");
384
terminal = g_settings_get_string (settings, "exec");
390
exec_flag = g_settings_get_string (settings, "exec-arg");
392
if (exec_flag == NULL)
393
command_line = g_strdup (terminal);
395
command_line = g_strdup_printf ("%s %s", terminal,
398
g_shell_parse_argv (command_line,
403
g_free (command_line);
408
g_object_unref (settings);
410
if (term_argv == NULL) {
414
term_argv = g_new0 (char *, 3);
416
check = g_find_program_in_path ("gnome-terminal");
418
term_argv[0] = check;
419
/* Note that gnome-terminal takes -x and
420
* as -e in gnome-terminal is broken we use that. */
421
term_argv[1] = g_strdup ("-x");
424
check = g_find_program_in_path ("nxterm");
426
check = g_find_program_in_path ("color-xterm");
428
check = g_find_program_in_path ("rxvt");
430
check = g_find_program_in_path ("xterm");
432
check = g_find_program_in_path ("dtterm");
434
g_warning (_("Cannot find a terminal, using "
435
"xterm, even if it may not work"));
436
check = g_strdup ("xterm");
438
term_argv[0] = check;
439
term_argv[1] = g_strdup ("-e");
443
real_argc = term_argc + *argc;
444
real_argv = g_new (char *, real_argc + 1);
446
for (i = 0; i < term_argc; i++)
447
real_argv[i] = term_argv[i];
449
for (j = 0; j < *argc; j++, i++)
450
real_argv[i] = (char *)the_argv[j];
458
/* we use g_free here as we sucked all the inner strings
459
* out from it into real_argv */
464
panel_run_dialog_launch_command (PanelRunDialog *dialog,
466
const char *locale_command)
470
GError *error = NULL;
476
if (!command_is_executable (locale_command, &argc, &argv))
479
screen = gtk_window_get_screen (GTK_WINDOW (dialog->run_dialog));
481
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->terminal_checkbox)))
482
panel_run_dialog_prepend_terminal_to_vector (&argc, &argv);
484
display = gdk_screen_make_display_name (screen);
486
result = g_spawn_async (NULL, /* working directory */
489
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
498
primary = g_markup_printf_escaped (_("Could not run command '%s'"),
500
panel_error_dialog (GTK_WINDOW (dialog->run_dialog), NULL,
501
"cannot_spawn_command", TRUE,
502
primary, error->message);
505
g_error_free (error);
507
g_child_watch_add (pid, dummy_child_watch, NULL);
517
panel_run_dialog_execute (PanelRunDialog *dialog)
525
command = g_strdup (panel_run_dialog_get_combo_text (dialog));
526
command = g_strchug (command);
528
if (!command || !command [0]) {
533
/* evil eggies, do not translate! */
534
if (!strcmp (command, "free the fish")) {
535
start_screen_check ();
538
gtk_widget_destroy (dialog->run_dialog);
543
disk = g_locale_from_utf8 (command, -1, NULL, NULL, &error);
545
if (!disk || error) {
548
primary = g_strdup_printf (_("Could not convert '%s' from UTF-8"),
550
panel_error_dialog (GTK_WINDOW (dialog->run_dialog), NULL,
551
"cannot_convert_command_from_utf8", TRUE,
552
primary, error->message);
555
g_error_free (error);
561
scheme = g_uri_parse_scheme (disk);
562
/* if it's an absolute path or not a URI, it's possibly an executable,
563
* so try it before displaying it */
564
if (g_path_is_absolute (disk) || !scheme)
565
result = panel_run_dialog_launch_command (dialog, command, disk);
572
file = panel_util_get_file_optional_homedir (command);
573
uri = g_file_get_uri (file);
574
g_object_unref (file);
576
screen = gtk_window_get_screen (GTK_WINDOW (dialog->run_dialog));
577
result = panel_show_uri (screen, uri,
578
gtk_get_current_event_time (), NULL);
584
/* only save working commands in history */
585
_panel_run_save_recent_programs_list (dialog, command);
587
/* only close the dialog if we successfully showed or launched
589
gtk_widget_destroy (dialog->run_dialog);
598
panel_run_dialog_response (PanelRunDialog *dialog,
600
GtkWidget *run_dialog)
603
dialog->completion_started = FALSE;
606
case GTK_RESPONSE_OK:
607
panel_run_dialog_execute (dialog);
609
case GTK_RESPONSE_CANCEL:
610
gtk_widget_destroy (dialog->run_dialog);
617
/* only quote the string if really needed */
619
quote_string (const char *s)
623
for (p = s; *p != '\0'; p++) {
624
if ((*p >= 'a' && *p <= 'z') ||
625
(*p >= 'A' && *p <= 'Z') ||
626
(*p >= '0' && *p <= '9') ||
627
strchr ("-_./=:", *p) != NULL)
630
return g_shell_quote (s);
637
panel_run_dialog_append_file_utf8 (PanelRunDialog *dialog,
644
/* Don't allow filenames beginning with '-' */
645
if (!file || !file[0] || file[0] == '-')
648
quoted = quote_string (file);
649
entry = gtk_bin_get_child (GTK_BIN (dialog->combobox));
650
text = gtk_entry_get_text (GTK_ENTRY (entry));
651
if (text && text [0]) {
652
temp = g_strconcat (text, " ", quoted, NULL);
653
gtk_entry_set_text (GTK_ENTRY (entry), temp);
656
gtk_entry_set_text (GTK_ENTRY (entry), quoted);
662
panel_run_dialog_append_file (PanelRunDialog *dialog,
670
utf8_file = g_filename_to_utf8 (file, -1, NULL, NULL, NULL);
673
panel_run_dialog_append_file_utf8 (dialog, utf8_file);
679
fuzzy_command_match (const char *cmd1,
686
g_return_val_if_fail (cmd1 && cmd2, TRUE);
690
if (!strcmp (cmd1, cmd2))
693
/* find basename of exec from desktop item.
694
strip of all arguments after the initial command */
695
tokens = g_strsplit (cmd1, " ", -1);
696
if (!tokens || !tokens [0]) {
701
word1 = g_path_get_basename (tokens [0]);
704
/* same for the user command */
705
tokens = g_strsplit (cmd2, " ", -1);
706
word2 = g_path_get_basename (tokens [0]);
707
if (!tokens || !tokens [0]) {
715
if (!strcmp (word1, word2)) {
729
panel_run_dialog_make_all_list_visible (GtkTreeModel *model,
734
gtk_list_store_set (GTK_LIST_STORE (model), iter,
735
COLUMN_VISIBLE, TRUE,
742
panel_run_dialog_find_command_idle (PanelRunDialog *dialog)
752
model = GTK_TREE_MODEL (dialog->program_list_store);
753
path = gtk_tree_path_new_first ();
755
if (!path || !gtk_tree_model_get_iter (model, &iter, path)) {
757
gtk_tree_path_free (path);
759
panel_run_dialog_set_icon (dialog, NULL, FALSE);
761
dialog->find_command_idle_id = 0;
765
text = g_strdup (panel_run_dialog_get_combo_text (dialog));
773
char *comment = NULL;
776
gtk_tree_model_get (model, &iter,
780
COLUMN_COMMENT, &comment,
783
if (!fuzzy && exec && icon &&
784
fuzzy_command_match (text, exec, &fuzzy)) {
786
g_object_unref (found_icon);
789
found_icon = g_object_ref (icon);
790
found_name = g_strdup (name);
792
gtk_list_store_set (dialog->program_list_store,
794
COLUMN_VISIBLE, TRUE,
796
} else if (panel_g_utf8_strstrcase (exec, text) != NULL ||
797
panel_g_utf8_strstrcase (name, text) != NULL ||
798
panel_g_utf8_strstrcase (comment, text) != NULL) {
799
gtk_list_store_set (dialog->program_list_store,
801
COLUMN_VISIBLE, TRUE,
804
gtk_list_store_set (dialog->program_list_store,
806
COLUMN_VISIBLE, FALSE,
811
g_object_unref (icon);
815
} while (gtk_tree_model_iter_next (model, &iter));
817
if (gtk_tree_model_get_iter (gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->program_list)),
819
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (dialog->program_list),
820
path, NULL, FALSE, 0, 0);
822
gtk_tree_path_free (path);
824
panel_run_dialog_set_icon (dialog, found_icon, FALSE);
825
//FIXME update dialog->program_label
828
g_object_unref (found_icon);
831
g_free (dialog->item_name);
832
dialog->item_name = found_name;
834
dialog->find_command_idle_id = 0;
839
compare_applications (GMenuTreeEntry *a,
842
return g_utf8_collate (g_app_info_get_display_name ((GAppInfo*)(gmenu_tree_entry_get_app_info (a))),
843
g_app_info_get_display_name ((GAppInfo*)(gmenu_tree_entry_get_app_info (b))));
846
static GSList *get_all_applications_from_dir (GMenuTreeDirectory *directory,
850
get_all_applications_from_alias (GMenuTreeAlias *alias,
853
switch (gmenu_tree_alias_get_aliased_item_type (alias)) {
854
case GMENU_TREE_ITEM_ENTRY:
855
/* pass on the reference */
856
list = g_slist_append (list, gmenu_tree_alias_get_aliased_entry (alias));
859
case GMENU_TREE_ITEM_DIRECTORY: {
860
GMenuTreeDirectory *directory = gmenu_tree_alias_get_aliased_directory (alias);
861
list = get_all_applications_from_dir (directory, list);
862
gmenu_tree_item_unref (directory);
874
get_all_applications_from_dir (GMenuTreeDirectory *directory,
878
GMenuTreeItemType next_type;
880
iter = gmenu_tree_directory_iter (directory);
882
while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID) {
884
case GMENU_TREE_ITEM_ENTRY:
885
list = g_slist_append (list, gmenu_tree_iter_get_entry (iter));
888
case GMENU_TREE_ITEM_DIRECTORY: {
889
GMenuTreeDirectory *dir = gmenu_tree_iter_get_directory (iter);
890
list = get_all_applications_from_dir (dir, list);
891
gmenu_tree_item_unref (dir);
895
case GMENU_TREE_ITEM_ALIAS: {
896
GMenuTreeAlias *alias = gmenu_tree_iter_get_alias (iter);
897
list = get_all_applications_from_alias (alias, list);
898
gmenu_tree_item_unref (alias);
907
gmenu_tree_iter_unref (iter);
913
get_all_applications (void)
916
GMenuTreeDirectory *root;
919
tree = gmenu_tree_new ("applications.menu", GMENU_TREE_FLAGS_SORT_DISPLAY_NAME);
921
if (!gmenu_tree_load_sync (tree, NULL))
924
root = gmenu_tree_get_root_directory (tree);
926
retval = get_all_applications_from_dir (root, NULL);
928
gmenu_tree_item_unref (root);
929
g_object_unref (tree);
931
retval = g_slist_sort (retval,
932
(GCompareFunc) compare_applications);
938
panel_run_dialog_add_items_idle (PanelRunDialog *dialog)
940
GtkCellRenderer *renderer;
941
GtkTreeViewColumn *column;
942
GtkTreeModel *model_filter;
943
GSList *all_applications;
946
const char *prev_name;
948
/* create list store */
949
dialog->program_list_store = gtk_list_store_new (NUM_COLUMNS,
957
all_applications = get_all_applications ();
959
/* Strip duplicates */
961
for (l = all_applications; l; l = next) {
962
GMenuTreeEntry *entry = l->data;
963
const char *entry_name;
964
GDesktopAppInfo *app_info;
968
app_info = gmenu_tree_entry_get_app_info (entry);
970
entry_name = g_app_info_get_display_name (G_APP_INFO (app_info));
971
if (prev_name && entry_name && strcmp (entry_name, prev_name) == 0) {
972
gmenu_tree_item_unref (entry);
974
all_applications = g_slist_delete_link (all_applications, l);
976
prev_name = entry_name;
980
for (l = all_applications; l; l = l->next) {
981
GMenuTreeEntry *entry = l->data;
986
app_info = G_APP_INFO (gmenu_tree_entry_get_app_info (entry));
988
gtk_list_store_append (dialog->program_list_store, &iter);
989
gtk_list_store_set (dialog->program_list_store, &iter,
990
COLUMN_ICON, g_app_info_get_icon (app_info),
991
COLUMN_NAME, g_app_info_get_display_name (app_info),
992
COLUMN_COMMENT, g_app_info_get_description (app_info),
993
COLUMN_EXEC, g_app_info_get_executable (app_info),
994
COLUMN_PATH, gmenu_tree_entry_get_desktop_file_path (entry),
995
COLUMN_VISIBLE, TRUE,
998
path = gtk_tree_model_get_path (GTK_TREE_MODEL (dialog->program_list_store), &iter);
1000
dialog->add_icon_paths = g_slist_prepend (dialog->add_icon_paths, path);
1002
gmenu_tree_item_unref (entry);
1004
g_slist_free (all_applications);
1006
model_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->program_list_store),
1008
gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (model_filter),
1011
gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->program_list),
1013
//FIXME use the same search than the fuzzy one?
1014
gtk_tree_view_set_search_column (GTK_TREE_VIEW (dialog->program_list),
1017
renderer = gtk_cell_renderer_pixbuf_new ();
1018
column = gtk_tree_view_column_new ();
1019
gtk_tree_view_column_pack_start (column, renderer, FALSE);
1020
gtk_tree_view_column_set_attributes (column, renderer,
1021
"gicon", COLUMN_ICON,
1024
renderer = gtk_cell_renderer_text_new ();
1025
gtk_tree_view_column_pack_start (column, renderer, TRUE);
1026
gtk_tree_view_column_set_attributes (column, renderer,
1027
"text", COLUMN_NAME,
1030
gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->program_list), column);
1032
dialog->add_icon_paths = g_slist_reverse (dialog->add_icon_paths);
1034
dialog->add_items_idle_id = 0;
1039
remove_parameters (const char *exec)
1044
str = g_string_new (exec);
1046
while ((p = strstr (str->str, "%"))) {
1049
g_string_erase (str, p - str->str, 1);
1064
g_string_erase (str, p - str->str, 2);
1072
g_string_free (str, FALSE);
1078
program_list_selection_changed (GtkTreeSelection *selection,
1079
PanelRunDialog *dialog)
1081
GtkTreeModel *filter_model;
1082
GtkTreeModel *child_model;
1084
GtkTreeIter filter_iter;
1086
char *path, *stripped;
1091
if (!gtk_tree_selection_get_selected (selection, &filter_model,
1095
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (filter_model),
1096
&iter, &filter_iter);
1099
child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1100
gtk_tree_model_get (child_model, &iter,
1107
key_file = g_key_file_new ();
1109
if (!g_key_file_load_from_file (key_file, path,
1110
G_KEY_FILE_NONE, NULL)) {
1111
g_key_file_free (key_file);
1116
dialog->use_program_list = TRUE;
1117
if (dialog->desktop_path)
1118
g_free (dialog->desktop_path);
1119
dialog->desktop_path = g_strdup (path);
1120
if (dialog->item_name)
1121
g_free (dialog->item_name);
1122
dialog->item_name = NULL;
1124
/* Order is important here. We have to set the text first so that the
1125
* drag source is enabled, otherwise the drag icon can't be set by
1126
* panel_run_dialog_set_icon.
1128
entry = gtk_bin_get_child (GTK_BIN (dialog->combobox));
1129
temp = panel_key_file_get_string (key_file, "Exec");
1131
stripped = remove_parameters (temp);
1132
gtk_entry_set_text (GTK_ENTRY (entry), stripped);
1135
temp = panel_key_file_get_string (key_file, "URL");
1136
gtk_entry_set_text (GTK_ENTRY (entry), sure_string (temp));
1140
temp = panel_key_file_get_locale_string (key_file, "Icon");
1141
if (!PANEL_GLIB_STR_EMPTY (temp)) {
1144
stripped = panel_xdg_icon_remove_extension (temp);
1145
gicon = g_themed_icon_new (stripped);
1146
panel_run_dialog_set_icon (dialog, gicon, FALSE);
1147
g_object_unref (gicon);
1150
panel_run_dialog_set_icon (dialog, NULL, FALSE);
1154
temp = panel_key_file_get_locale_string (key_file, "Comment");
1155
//FIXME: if sure_string () == "", we should display "Will run..." as in entry_changed()
1156
gtk_label_set_text (GTK_LABEL (dialog->program_label),
1157
sure_string (temp));
1160
terminal = panel_key_file_get_boolean (key_file, "Terminal", FALSE);
1161
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->terminal_checkbox),
1164
g_key_file_free (key_file);
1170
program_list_selection_activated (GtkTreeView *view,
1172
GtkTreeViewColumn *column,
1173
PanelRunDialog *dialog)
1175
GtkTreeSelection *selection;
1177
/* update the entry with the info from the selection */
1178
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->program_list));
1179
program_list_selection_changed (selection, dialog);
1181
/* now launch the command */
1182
gtk_dialog_response (GTK_DIALOG (dialog->run_dialog), GTK_RESPONSE_OK);
1187
panel_run_dialog_setup_program_list (PanelRunDialog *dialog,
1190
GtkTreeSelection *selection;
1192
dialog->program_list = PANEL_GTK_BUILDER_GET (gui, "program_list");
1193
dialog->program_label = PANEL_GTK_BUILDER_GET (gui, "program_label");
1194
dialog->main_box = PANEL_GTK_BUILDER_GET (gui, "main_box");
1196
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->program_list));
1197
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1199
g_signal_connect (selection, "changed",
1200
G_CALLBACK (program_list_selection_changed),
1203
g_signal_connect (dialog->program_list, "row-activated",
1204
G_CALLBACK (program_list_selection_activated),
1209
panel_run_dialog_setup_list_expander (PanelRunDialog *dialog,
1212
dialog->list_expander = PANEL_GTK_BUILDER_GET (gui, "list_expander");
1214
/* Ref the expander so it doesn't get destroyed when it is
1215
* removed from the visible area of the dialog box. */
1216
g_object_ref (dialog->list_expander);
1218
g_settings_bind (dialog->run_settings,
1219
PANEL_RUN_SHOW_LIST_KEY,
1220
dialog->list_expander,
1222
G_SETTINGS_BIND_DEFAULT);
1226
panel_run_dialog_update_program_list (GSettings *settings,
1228
PanelRunDialog *dialog)
1234
enabled = g_settings_get_boolean (dialog->run_settings,
1235
PANEL_RUN_ENABLE_LIST_KEY);
1237
parent = gtk_widget_get_parent (dialog->list_expander);
1240
if (dialog->program_list_store == NULL) {
1241
/* start loading the list of applications */
1242
dialog->add_items_idle_id =
1243
g_idle_add_full (G_PRIORITY_LOW,
1244
(GSourceFunc) panel_run_dialog_add_items_idle,
1249
gtk_box_pack_end (GTK_BOX (dialog->main_box),
1250
dialog->list_expander,
1254
gtk_container_remove (GTK_CONTAINER (parent),
1255
dialog->list_expander);
1258
shown = g_settings_get_boolean (dialog->run_settings,
1259
PANEL_RUN_SHOW_LIST_KEY);
1261
if (enabled && shown) {
1262
gtk_window_resize (GTK_WINDOW (dialog->run_dialog), 100, 300);
1263
gtk_window_set_resizable (GTK_WINDOW (dialog->run_dialog), TRUE);
1264
gtk_widget_grab_focus (dialog->program_list);
1266
gtk_window_set_resizable (GTK_WINDOW (dialog->run_dialog), FALSE);
1267
gtk_widget_grab_focus (dialog->combobox);
1272
file_button_browse_response (GtkWidget *chooser,
1274
PanelRunDialog *dialog)
1278
if (response == GTK_RESPONSE_OK) {
1279
file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
1280
panel_run_dialog_append_file (dialog, file);
1284
gtk_widget_destroy (chooser);
1286
gtk_widget_grab_focus (dialog->combobox);
1290
file_button_clicked (GtkButton *button,
1291
PanelRunDialog *dialog)
1295
chooser = gtk_file_chooser_dialog_new (_("Choose a file to append to the command..."),
1296
GTK_WINDOW (dialog->run_dialog),
1297
GTK_FILE_CHOOSER_ACTION_OPEN,
1298
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1299
GTK_STOCK_OK, GTK_RESPONSE_OK,
1302
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser),
1305
gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK);
1306
gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE);
1308
g_signal_connect (chooser, "response",
1309
G_CALLBACK (file_button_browse_response), dialog);
1311
gtk_window_present (GTK_WINDOW (chooser));
1315
panel_run_dialog_setup_file_button (PanelRunDialog *dialog,
1318
dialog->file_button = PANEL_GTK_BUILDER_GET (gui, "file_button");
1320
g_signal_connect (dialog->file_button, "clicked",
1321
G_CALLBACK (file_button_clicked),
1326
fill_files_from (const char *dirname,
1327
const char *dirprefix,
1329
GList *existing_items)
1333
struct dirent *dent;
1336
dir = opendir (dirname);
1341
while ((dent = readdir (dir))) {
1346
if (!dent->d_name ||
1347
dent->d_name [0] != prefix)
1350
file = g_build_filename (dirname, dent->d_name, NULL);
1354
#ifdef HAVE_STRUCT_DIRENT_D_TYPE
1355
/* don't use g_file_test at first so we don't stat() */
1356
dent->d_type == DT_DIR ||
1357
(dent->d_type == DT_LNK &&
1358
g_file_test (file, G_FILE_TEST_IS_DIR))
1360
g_file_test (file, G_FILE_TEST_IS_DIR)
1367
item = g_build_filename (dirprefix, dent->d_name, suffix, NULL);
1369
list = g_list_prepend (list, item);
1378
fill_possible_executables (void)
1386
path = g_getenv ("PATH");
1388
if (!path || !path [0])
1391
pathv = g_strsplit (path, ":", 0);
1393
for (i = 0; pathv [i]; i++) {
1398
dir = g_dir_open (pathv [i], 0, NULL);
1403
while ((file = g_dir_read_name (dir))) {
1404
filename = g_build_filename (pathv [i], file, NULL);
1405
list = g_list_prepend (list, filename);
1417
fill_executables (GList *possible_executables,
1418
GList *existing_items,
1426
for (l = possible_executables; l; l = l->next) {
1427
const char *filename;
1431
basename = g_path_get_basename (filename);
1433
if (basename [0] == prefix &&
1434
g_file_test (filename, G_FILE_TEST_IS_REGULAR) &&
1435
g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE)) {
1437
if (g_list_find_custom (existing_items, basename,
1438
(GCompareFunc) strcmp)) {
1443
list = g_list_prepend (list, basename);
1453
panel_run_dialog_update_completion (PanelRunDialog *dialog,
1464
g_assert (text != NULL && *text != '\0' && !g_ascii_isspace (*text));
1469
if (!dialog->completion) {
1470
dialog->completion = g_completion_new (NULL);
1471
dialog->possible_executables = fill_possible_executables ();
1472
dialog->dir_hash = g_hash_table_new_full (g_str_hash,
1477
buf = g_path_get_basename (text);
1480
if (prefix == '/' || prefix == '.')
1483
if (text [0] == '/') {
1484
/* complete against absolute path */
1485
dirname = g_path_get_dirname (text);
1486
dirprefix = g_strdup (dirname);
1488
/* complete against relative path and executable name */
1489
if (!strchr (text, '/')) {
1490
executables = fill_executables (dialog->possible_executables,
1491
dialog->completion_items,
1493
dirprefix = g_strdup ("");
1495
dirprefix = g_path_get_dirname (text);
1498
dirname = g_build_filename (g_get_home_dir (), dirprefix, NULL);
1501
key = g_strdup_printf ("%s%c%c", dirprefix, G_DIR_SEPARATOR, prefix);
1503
if (!g_hash_table_lookup (dialog->dir_hash, key)) {
1504
g_hash_table_insert (dialog->dir_hash, key, dialog);
1506
list = fill_files_from (dirname, dirprefix, prefix,
1507
dialog->completion_items);
1512
list = g_list_concat (list, executables);
1520
g_completion_add_items (dialog->completion, list);
1522
dialog->completion_items = g_list_concat (dialog->completion_items,
1527
entry_event (GtkEditable *entry,
1529
PanelRunDialog *dialog)
1531
GtkTreeSelection *selection;
1533
char *nospace_prefix;
1538
if (event->type != GDK_KEY_PRESS)
1541
/* if user typed something we're not using the list anymore */
1542
dialog->use_program_list = FALSE;
1543
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->program_list));
1544
gtk_tree_selection_unselect_all (selection);
1546
if (!g_settings_get_boolean (dialog->run_settings,
1547
PANEL_RUN_ENABLE_COMPLETION_KEY))
1550
/* tab completion */
1551
if (event->keyval == GDK_KEY_Tab) {
1552
gtk_editable_get_selection_bounds (entry, &pos, &tmp);
1554
if (dialog->completion_started &&
1557
tmp == strlen (gtk_entry_get_text (GTK_ENTRY (entry)))) {
1558
gtk_editable_select_region (entry, 0, 0);
1559
gtk_editable_set_position (entry, -1);
1563
} else if (event->length > 0) {
1565
gtk_editable_get_selection_bounds (entry, &pos, &tmp);
1567
if (dialog->completion_started &&
1570
tmp == strlen (gtk_entry_get_text (GTK_ENTRY (entry)))) {
1571
temp = gtk_editable_get_chars (entry, 0, pos);
1572
prefix = g_strconcat (temp, event->string, NULL);
1574
} else if (pos == tmp &&
1575
tmp == strlen (gtk_entry_get_text (GTK_ENTRY (entry)))) {
1576
prefix = g_strconcat (gtk_entry_get_text (GTK_ENTRY (entry)),
1577
event->string, NULL);
1582
nospace_prefix = prefix;
1583
while (*nospace_prefix != '\0' &&
1584
g_ascii_isspace (*nospace_prefix))
1586
if (*nospace_prefix == '\0')
1589
panel_run_dialog_update_completion (dialog, nospace_prefix);
1591
if (!dialog->completion) {
1596
pos = strlen (prefix);
1599
g_completion_complete_utf8 (dialog->completion, nospace_prefix,
1606
temp = g_strndup (prefix, nospace_prefix - prefix);
1609
prefix = g_strconcat (temp, nprefix, NULL);
1611
g_signal_handler_block (dialog->combobox,
1612
dialog->changed_id);
1613
gtk_editable_delete_text (entry, 0, -1);
1614
g_signal_handler_unblock (dialog->combobox,
1615
dialog->changed_id);
1617
gtk_editable_insert_text (entry,
1618
prefix, strlen (prefix),
1621
gtk_editable_set_position (entry, pos);
1622
gtk_editable_select_region (entry, pos, -1);
1624
dialog->completion_started = TRUE;
1640
combobox_changed (GtkComboBox *combobox,
1641
PanelRunDialog *dialog)
1643
gboolean program_list_enabled;
1648
program_list_enabled = g_settings_get_boolean (dialog->run_settings,
1649
PANEL_RUN_ENABLE_LIST_KEY);
1651
text = g_strdup (panel_run_dialog_get_combo_text (dialog));
1654
while (*start != '\0' && g_ascii_isspace (*start))
1657
/* update item name to use for dnd */
1658
if (!dialog->use_program_list) {
1659
if (dialog->desktop_path) {
1660
g_free (dialog->desktop_path);
1661
dialog->desktop_path = NULL;
1663
if (dialog->item_name) {
1664
g_free (dialog->item_name);
1665
dialog->item_name = NULL;
1669
/* desensitize run button if no text entered */
1670
if (!start || !start [0]) {
1673
gtk_widget_set_sensitive (dialog->run_button, FALSE);
1674
gtk_drag_source_unset (dialog->run_dialog);
1676
if (program_list_enabled)
1677
gtk_label_set_text (GTK_LABEL (dialog->program_label),
1678
_("Select an application to view its description."));
1680
panel_run_dialog_set_default_icon (dialog, FALSE);
1682
if (dialog->find_command_idle_id) {
1683
g_source_remove (dialog->find_command_idle_id);
1684
dialog->find_command_idle_id = 0;
1687
if (program_list_enabled) {
1691
gtk_tree_model_foreach (GTK_TREE_MODEL (dialog->program_list_store),
1692
panel_run_dialog_make_all_list_visible,
1695
path = gtk_tree_path_new_first ();
1696
if (gtk_tree_model_get_iter (gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->program_list)),
1698
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (dialog->program_list),
1701
gtk_tree_path_free (path);
1707
gtk_widget_set_sensitive (dialog->run_button, TRUE);
1708
gtk_drag_source_set (dialog->run_dialog,
1712
gtk_drag_source_add_uri_targets (dialog->run_dialog);
1714
if (program_list_enabled &&
1715
!dialog->use_program_list) {
1716
msg = g_strdup_printf (_("Will run command: '%s'"),
1718
gtk_label_set_text (GTK_LABEL (dialog->program_label), msg);
1722
/* look up icon for the command */
1723
if (program_list_enabled &&
1724
!dialog->use_program_list &&
1725
!dialog->find_command_idle_id)
1726
dialog->find_command_idle_id =
1727
g_idle_add_full (G_PRIORITY_LOW,
1728
(GSourceFunc) panel_run_dialog_find_command_idle,
1735
entry_drag_data_received (GtkEditable *entry,
1736
GdkDragContext *context,
1739
GtkSelectionData *selection_data,
1742
PanelRunDialog *dialog)
1748
if (gtk_selection_data_get_format (selection_data) != 8 || gtk_selection_data_get_length (selection_data) == 0) {
1749
g_warning (_("URI list dropped on run dialog had wrong format (%d) or length (%d)\n"),
1750
gtk_selection_data_get_format (selection_data),
1751
gtk_selection_data_get_length (selection_data));
1755
uris = g_uri_list_extract_uris ((const char *)gtk_selection_data_get_data (selection_data));
1758
gtk_drag_finish (context, FALSE, FALSE, time);
1762
for (i = 0; uris [i]; i++) {
1763
if (!uris [i] || !uris [i][0])
1766
file = g_filename_from_uri (uris [i], NULL, NULL);
1768
/* FIXME: I assume the file is in utf8 encoding if coming from a URI? */
1770
panel_run_dialog_append_file_utf8 (dialog, file);
1773
panel_run_dialog_append_file_utf8 (dialog, uris [i]);
1777
gtk_drag_finish (context, TRUE, FALSE, time);
1781
panel_run_dialog_setup_entry (PanelRunDialog *dialog,
1788
dialog->combobox = PANEL_GTK_BUILDER_GET (gui, "comboboxentry");
1790
entry = gtk_bin_get_child (GTK_BIN (dialog->combobox));
1791
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
1793
gtk_combo_box_set_model (GTK_COMBO_BOX (dialog->combobox),
1794
_panel_run_get_recent_programs_list (dialog));
1795
gtk_combo_box_set_entry_text_column
1796
(GTK_COMBO_BOX (dialog->combobox), 0);
1798
screen = gtk_window_get_screen (GTK_WINDOW (dialog->run_dialog));
1800
/* 1/4 the width of the first monitor should be a good value */
1801
width_request = panel_multiscreen_width (screen, 0) / 4;
1802
g_object_set (G_OBJECT (dialog->combobox),
1803
"width_request", width_request,
1806
g_signal_connect (entry, "key-press-event",
1807
G_CALLBACK (entry_event), dialog);
1809
dialog->changed_id = g_signal_connect (dialog->combobox, "changed",
1810
G_CALLBACK (combobox_changed),
1813
gtk_drag_dest_unset (dialog->combobox);
1815
gtk_drag_dest_set (dialog->combobox,
1816
GTK_DEST_DEFAULT_MOTION|GTK_DEST_DEFAULT_HIGHLIGHT,
1819
gtk_drag_dest_add_uri_targets (dialog->combobox);
1821
g_signal_connect (dialog->combobox, "drag_data_received",
1822
G_CALLBACK (entry_drag_data_received), dialog);
1826
panel_run_dialog_create_desktop_file (PanelRunDialog *dialog)
1829
gboolean exec = FALSE;
1837
text = g_strdup (panel_run_dialog_get_combo_text (dialog));
1839
if (!text || !text [0]) {
1844
key_file = panel_key_file_new_desktop ();
1845
disk = g_locale_from_utf8 (text, -1, NULL, NULL, NULL);
1847
scheme = g_uri_parse_scheme (disk);
1848
/* if it's an absolute path or not a URI, it's possibly an executable */
1849
if (g_path_is_absolute (disk) || !scheme)
1850
exec = command_is_executable (disk, NULL, NULL);
1854
panel_key_file_set_string (key_file, "Type", "Application");
1855
panel_key_file_set_string (key_file, "Exec", text);
1856
name = g_strdup (text);
1861
file = panel_util_get_file_optional_homedir (disk);
1862
uri = g_file_get_uri (file);
1863
g_object_unref (file);
1865
panel_key_file_set_string (key_file, "Type", "Link");
1866
panel_key_file_set_string (key_file, "URL", uri);
1872
panel_key_file_set_locale_string (key_file, "Name",
1873
(dialog->item_name) ?
1874
dialog->item_name : text);
1876
panel_key_file_set_boolean (key_file, "Terminal",
1877
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->terminal_checkbox)));
1881
icon = panel_util_get_icon_name_from_g_icon (dialog->gicon);
1883
panel_key_file_set_locale_string (key_file, "Icon",
1887
panel_key_file_set_locale_string (key_file, "Icon",
1888
PANEL_ICON_LAUNCHER);
1890
save_uri = panel_make_unique_desktop_uri (g_get_tmp_dir (), name);
1891
disk = g_filename_from_uri (save_uri, NULL, NULL);
1893
if (!disk || !panel_key_file_to_file (key_file, disk, NULL)) {
1898
g_key_file_free (key_file);
1907
pixmap_drag_data_get (GtkWidget *run_dialog,
1908
GdkDragContext *context,
1909
GtkSelectionData *selection_data,
1912
PanelRunDialog *dialog)
1916
if (dialog->use_program_list && dialog->desktop_path)
1917
uri = g_filename_to_uri (dialog->desktop_path, NULL, NULL);
1919
uri = panel_run_dialog_create_desktop_file (dialog);
1922
gtk_selection_data_set (selection_data,
1923
gtk_selection_data_get_target (selection_data), 8,
1924
(unsigned char *) uri, strlen (uri));
1930
panel_run_dialog_setup_pixmap (PanelRunDialog *dialog,
1933
dialog->pixmap = PANEL_GTK_BUILDER_GET (gui, "icon_pixmap");
1935
g_signal_connect (dialog->run_dialog, "drag_data_get",
1936
G_CALLBACK (pixmap_drag_data_get),
1940
static PanelRunDialog *
1941
panel_run_dialog_new (GdkScreen *screen,
1943
guint32 activate_time)
1945
PanelRunDialog *dialog;
1947
dialog = g_new0 (PanelRunDialog, 1);
1949
dialog->run_dialog = PANEL_GTK_BUILDER_GET (gui, "panel_run_dialog");
1951
dialog->run_settings = g_settings_new (PANEL_RUN_SCHEMA);
1953
g_signal_connect_swapped (dialog->run_dialog, "response",
1954
G_CALLBACK (panel_run_dialog_response), dialog);
1956
g_signal_connect_swapped (dialog->run_dialog, "destroy",
1957
G_CALLBACK (panel_run_dialog_destroy), dialog);
1959
dialog->run_button = PANEL_GTK_BUILDER_GET (gui, "run_button");
1960
dialog->terminal_checkbox = PANEL_GTK_BUILDER_GET (gui, "terminal_checkbox");
1962
panel_run_dialog_setup_pixmap (dialog, gui);
1963
panel_run_dialog_setup_entry (dialog, gui);
1964
panel_run_dialog_setup_file_button (dialog, gui);
1965
panel_run_dialog_setup_program_list (dialog, gui);
1966
panel_run_dialog_setup_list_expander (dialog, gui);
1968
gtk_window_set_icon_name (GTK_WINDOW (dialog->run_dialog),
1970
panel_run_dialog_set_default_icon (dialog, FALSE);
1972
g_signal_connect (dialog->run_settings, "changed::"PANEL_RUN_ENABLE_LIST_KEY,
1973
G_CALLBACK (panel_run_dialog_update_program_list), dialog);
1974
g_signal_connect (dialog->run_settings, "changed::"PANEL_RUN_SHOW_LIST_KEY,
1975
G_CALLBACK (panel_run_dialog_update_program_list), dialog);
1977
panel_run_dialog_update_program_list (dialog->run_settings, NULL, dialog);
1979
gtk_widget_set_sensitive (dialog->run_button, FALSE);
1981
gtk_dialog_set_default_response (GTK_DIALOG (dialog->run_dialog),
1984
gtk_window_set_screen (GTK_WINDOW (dialog->run_dialog), screen);
1986
gtk_widget_grab_focus (dialog->combobox);
1987
gtk_widget_realize (dialog->run_dialog);
1988
gdk_x11_window_set_user_time (gtk_widget_get_window (dialog->run_dialog),
1990
gtk_widget_show (dialog->run_dialog);
1996
panel_run_dialog_static_dialog_destroyed (PanelRunDialog *dialog)
1998
/* just reset the static dialog to NULL for next time */
1999
static_dialog = NULL;
2003
panel_run_dialog_present (GdkScreen *screen,
2004
guint32 activate_time)
2008
if (panel_lockdown_get_disable_command_line_s ())
2011
if (static_dialog) {
2012
gtk_window_set_screen (GTK_WINDOW (static_dialog->run_dialog), screen);
2013
gtk_window_present_with_time (GTK_WINDOW (static_dialog->run_dialog),
2015
gtk_widget_grab_focus (static_dialog->combobox);
2019
gui = gtk_builder_new ();
2020
gtk_builder_set_translation_domain (gui, GETTEXT_PACKAGE);
2021
gtk_builder_add_from_resource (gui,
2022
PANEL_RESOURCE_PATH "panel-run-dialog.ui",
2025
static_dialog = panel_run_dialog_new (screen, gui, activate_time);
2027
g_signal_connect_swapped (static_dialog->run_dialog, "destroy",
2028
G_CALLBACK (panel_run_dialog_static_dialog_destroyed),
2031
g_object_unref (gui);