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>
32
#include "panel-run-dialog.h"
37
#include <sys/types.h>
40
#include <glib/gi18n.h>
42
#include <gdk/gdkkeysyms.h>
43
#include <gmenu-tree.h>
45
#include <libgnome-desktop/gnome-desktop-utils.h>
47
#include <libpanel-util/panel-error.h>
48
#include <libpanel-util/panel-glib.h>
49
#include <libpanel-util/panel-gtk.h>
50
#include <libpanel-util/panel-keyfile.h>
51
#include <libpanel-util/panel-show.h>
52
#include <libpanel-util/panel-xdg.h>
55
#include "panel-util.h"
56
#include "panel-globals.h"
57
#include "panel-enums.h"
58
#include "panel-stock-icons.h"
59
#include "panel-multiscreen.h"
61
#include "panel-lockdown.h"
62
#include "panel-xutils.h"
63
#include "panel-icon-names.h"
64
#include "panel-schemas.h"
67
GtkWidget *run_dialog;
69
GSettings *run_settings;
75
GtkWidget *run_button;
76
GtkWidget *file_button;
77
GtkWidget *list_expander;
78
GtkWidget *terminal_checkbox;
79
GtkWidget *program_label;
80
GtkWidget *program_list;
84
GtkListStore *program_list_store;
87
GList *possible_executables;
88
GList *completion_items;
89
GCompletion *completion;
91
GSList *add_icon_paths;
92
int add_items_idle_id;
93
int find_command_idle_id;
94
gboolean use_program_list;
95
gboolean completion_started;
112
static PanelRunDialog *static_dialog = NULL;
114
#define PANEL_RUN_MAX_HISTORY 20
116
static GtkTreeModel *
117
_panel_run_get_recent_programs_list (PanelRunDialog *dialog)
123
list = gtk_list_store_new (1, G_TYPE_STRING);
125
commands = g_settings_get_strv (dialog->run_settings,
126
PANEL_RUN_HISTORY_KEY);
128
for (i = 0; commands[i] != NULL; i++) {
130
gtk_list_store_prepend (list, &iter);
131
gtk_list_store_set (list, &iter, 0, commands[i], -1);
134
g_strfreev (commands);
136
return GTK_TREE_MODEL (list);
140
_panel_run_save_recent_programs_list (PanelRunDialog *dialog,
148
commands = g_settings_get_strv (dialog->run_settings,
149
PANEL_RUN_HISTORY_KEY);
151
/* do not save the same command twice in a row */
152
if (g_strcmp0 (commands[0], last_command) == 0)
155
for (i = 0; commands[i] != NULL; i++);
156
size = MIN (i + 1, PANEL_RUN_MAX_HISTORY);
158
new_commands = g_new (char *, size + 1);
160
new_commands[0] = last_command;
161
new_commands[size] = NULL; /* last item */
163
for (i = 1; i < size; i++)
164
new_commands[i] = commands[i-1];
166
g_settings_set_strv (dialog->run_settings,
167
PANEL_RUN_HISTORY_KEY,
168
(const char **) new_commands);
170
g_free (new_commands); /* we don't own the strings */
171
g_strfreev (commands);
175
panel_run_dialog_destroy (PanelRunDialog *dialog)
179
dialog->changed_id = 0;
181
g_object_unref (dialog->list_expander);
183
g_slist_foreach (dialog->add_icon_paths, (GFunc) gtk_tree_path_free, NULL);
184
g_slist_free (dialog->add_icon_paths);
185
dialog->add_icon_paths = NULL;
188
g_object_unref (dialog->gicon);
189
dialog->gicon = NULL;
191
g_free (dialog->desktop_path);
192
dialog->desktop_path = NULL;
193
g_free (dialog->item_name);
194
dialog->item_name = NULL;
196
if (dialog->add_items_idle_id)
197
g_source_remove (dialog->add_items_idle_id);
198
dialog->add_items_idle_id = 0;
200
if (dialog->find_command_idle_id)
201
g_source_remove (dialog->find_command_idle_id);
202
dialog->find_command_idle_id = 0;
204
if (dialog->dir_hash)
205
g_hash_table_destroy (dialog->dir_hash);
206
dialog->dir_hash = NULL;
208
for (l = dialog->possible_executables; l; l = l->next)
210
g_list_free (dialog->possible_executables);
211
dialog->possible_executables = NULL;
213
for (l = dialog->completion_items; l; l = l->next)
215
g_list_free (dialog->completion_items);
216
dialog->completion_items = NULL;
218
if (dialog->completion)
219
g_completion_free (dialog->completion);
220
dialog->completion = NULL;
222
if (dialog->run_settings)
223
g_object_unref (dialog->run_settings);
224
dialog->run_settings = NULL;
230
panel_run_dialog_get_combo_text (PanelRunDialog *dialog)
234
entry = gtk_bin_get_child (GTK_BIN (dialog->combobox));
236
return gtk_entry_get_text (GTK_ENTRY (entry));
240
panel_run_dialog_set_default_icon (PanelRunDialog *dialog, gboolean set_drag)
242
gtk_image_set_from_icon_name (GTK_IMAGE (dialog->pixmap),
244
GTK_ICON_SIZE_DIALOG);
247
gtk_drag_source_set_icon_name (dialog->run_dialog,
248
PANEL_ICON_LAUNCHER);
252
panel_run_dialog_set_icon (PanelRunDialog *dialog,
256
if (!force && gicon && dialog->gicon &&
257
gicon == dialog->gicon)
261
g_object_unref (dialog->gicon);
262
dialog->gicon = NULL;
265
dialog->gicon = g_object_ref (gicon);
266
gtk_image_set_from_gicon (GTK_IMAGE (dialog->pixmap),
267
gicon, GTK_ICON_SIZE_DIALOG);
268
gtk_drag_source_set_icon_gicon (dialog->run_dialog, gicon);
270
panel_run_dialog_set_default_icon (dialog, TRUE);
275
command_is_executable (const char *command,
284
result = g_shell_parse_argv (command, &argc, &argv, NULL);
289
path = g_find_program_in_path (argv[0]);
296
/* If we pass an absolute path to g_find_program it just returns
297
* that absolute path without checking if it is executable. Also
298
* make sure its a regular file so we don't try to launch
299
* directories or device nodes.
301
if (!g_file_test (path, G_FILE_TEST_IS_EXECUTABLE) ||
302
!g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
319
* Set the DISPLAY variable, to be use by g_spawn_async.
322
set_environment (gpointer display)
324
g_setenv ("DISPLAY", display, TRUE);
328
dummy_child_watch (GPid pid,
332
/* Nothing, this is just to ensure we don't double fork
334
* https://bugzilla.gnome.org/show_bug.cgi?id=675789
339
panel_run_dialog_launch_command (PanelRunDialog *dialog,
341
const char *locale_command)
345
GError *error = NULL;
351
if (!command_is_executable (locale_command, &argc, &argv))
354
screen = gtk_window_get_screen (GTK_WINDOW (dialog->run_dialog));
356
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->terminal_checkbox)))
357
gnome_desktop_prepend_terminal_to_vector (&argc, &argv);
359
display = gdk_screen_make_display_name (screen);
361
result = g_spawn_async (NULL, /* working directory */
364
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
373
primary = g_markup_printf_escaped (_("Could not run command '%s'"),
375
panel_error_dialog (GTK_WINDOW (dialog->run_dialog), NULL,
376
"cannot_spawn_command", TRUE,
377
primary, error->message);
380
g_error_free (error);
382
g_child_watch_add (pid, dummy_child_watch, NULL);
392
panel_run_dialog_execute (PanelRunDialog *dialog)
400
command = g_strdup (panel_run_dialog_get_combo_text (dialog));
401
command = g_strchug (command);
403
if (!command || !command [0]) {
408
/* evil eggies, do not translate! */
409
if (!strcmp (command, "free the fish")) {
410
start_screen_check ();
413
gtk_widget_destroy (dialog->run_dialog);
418
disk = g_locale_from_utf8 (command, -1, NULL, NULL, &error);
420
if (!disk || error) {
423
primary = g_strdup_printf (_("Could not convert '%s' from UTF-8"),
425
panel_error_dialog (GTK_WINDOW (dialog->run_dialog), NULL,
426
"cannot_convert_command_from_utf8", TRUE,
427
primary, error->message);
430
g_error_free (error);
436
scheme = g_uri_parse_scheme (disk);
437
/* if it's an absolute path or not a URI, it's possibly an executable,
438
* so try it before displaying it */
439
if (g_path_is_absolute (disk) || !scheme)
440
result = panel_run_dialog_launch_command (dialog, command, disk);
447
file = panel_util_get_file_optional_homedir (command);
448
uri = g_file_get_uri (file);
449
g_object_unref (file);
451
screen = gtk_window_get_screen (GTK_WINDOW (dialog->run_dialog));
452
result = panel_show_uri (screen, uri,
453
gtk_get_current_event_time (), NULL);
459
/* only save working commands in history */
460
_panel_run_save_recent_programs_list (dialog, command);
462
/* only close the dialog if we successfully showed or launched
464
gtk_widget_destroy (dialog->run_dialog);
473
panel_run_dialog_response (PanelRunDialog *dialog,
475
GtkWidget *run_dialog)
478
dialog->completion_started = FALSE;
481
case GTK_RESPONSE_OK:
482
panel_run_dialog_execute (dialog);
484
case GTK_RESPONSE_CANCEL:
485
gtk_widget_destroy (dialog->run_dialog);
492
/* only quote the string if really needed */
494
quote_string (const char *s)
498
for (p = s; *p != '\0'; p++) {
499
if ((*p >= 'a' && *p <= 'z') ||
500
(*p >= 'A' && *p <= 'Z') ||
501
(*p >= '0' && *p <= '9') ||
502
strchr ("-_./=:", *p) != NULL)
505
return g_shell_quote (s);
512
panel_run_dialog_append_file_utf8 (PanelRunDialog *dialog,
519
/* Don't allow filenames beginning with '-' */
520
if (!file || !file[0] || file[0] == '-')
523
quoted = quote_string (file);
524
entry = gtk_bin_get_child (GTK_BIN (dialog->combobox));
525
text = gtk_entry_get_text (GTK_ENTRY (entry));
526
if (text && text [0]) {
527
temp = g_strconcat (text, " ", quoted, NULL);
528
gtk_entry_set_text (GTK_ENTRY (entry), temp);
531
gtk_entry_set_text (GTK_ENTRY (entry), quoted);
537
panel_run_dialog_append_file (PanelRunDialog *dialog,
545
utf8_file = g_filename_to_utf8 (file, -1, NULL, NULL, NULL);
548
panel_run_dialog_append_file_utf8 (dialog, utf8_file);
554
fuzzy_command_match (const char *cmd1,
561
g_return_val_if_fail (cmd1 && cmd2, TRUE);
565
if (!strcmp (cmd1, cmd2))
568
/* find basename of exec from desktop item.
569
strip of all arguments after the initial command */
570
tokens = g_strsplit (cmd1, " ", -1);
571
if (!tokens || !tokens [0]) {
576
word1 = g_path_get_basename (tokens [0]);
579
/* same for the user command */
580
tokens = g_strsplit (cmd2, " ", -1);
581
word2 = g_path_get_basename (tokens [0]);
582
if (!tokens || !tokens [0]) {
590
if (!strcmp (word1, word2)) {
604
panel_run_dialog_make_all_list_visible (GtkTreeModel *model,
609
gtk_list_store_set (GTK_LIST_STORE (model), iter,
610
COLUMN_VISIBLE, TRUE,
617
panel_run_dialog_find_command_idle (PanelRunDialog *dialog)
627
model = GTK_TREE_MODEL (dialog->program_list_store);
628
path = gtk_tree_path_new_first ();
630
if (!path || !gtk_tree_model_get_iter (model, &iter, path)) {
632
gtk_tree_path_free (path);
634
panel_run_dialog_set_icon (dialog, NULL, FALSE);
636
dialog->find_command_idle_id = 0;
640
text = g_strdup (panel_run_dialog_get_combo_text (dialog));
648
char *comment = NULL;
651
gtk_tree_model_get (model, &iter,
655
COLUMN_COMMENT, &comment,
658
if (!fuzzy && exec && icon &&
659
fuzzy_command_match (text, exec, &fuzzy)) {
661
g_object_unref (found_icon);
664
found_icon = g_object_ref (icon);
665
found_name = g_strdup (name);
667
gtk_list_store_set (dialog->program_list_store,
669
COLUMN_VISIBLE, TRUE,
671
} else if (panel_g_utf8_strstrcase (exec, text) != NULL ||
672
panel_g_utf8_strstrcase (name, text) != NULL ||
673
panel_g_utf8_strstrcase (comment, text) != NULL) {
674
gtk_list_store_set (dialog->program_list_store,
676
COLUMN_VISIBLE, TRUE,
679
gtk_list_store_set (dialog->program_list_store,
681
COLUMN_VISIBLE, FALSE,
686
g_object_unref (icon);
690
} while (gtk_tree_model_iter_next (model, &iter));
692
if (gtk_tree_model_get_iter (gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->program_list)),
694
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (dialog->program_list),
695
path, NULL, FALSE, 0, 0);
697
gtk_tree_path_free (path);
699
panel_run_dialog_set_icon (dialog, found_icon, FALSE);
700
//FIXME update dialog->program_label
703
g_object_unref (found_icon);
706
g_free (dialog->item_name);
707
dialog->item_name = found_name;
709
dialog->find_command_idle_id = 0;
714
compare_applications (GMenuTreeEntry *a,
717
return g_utf8_collate (g_app_info_get_display_name ((GAppInfo*)(gmenu_tree_entry_get_app_info (a))),
718
g_app_info_get_display_name ((GAppInfo*)(gmenu_tree_entry_get_app_info (b))));
721
static GSList *get_all_applications_from_dir (GMenuTreeDirectory *directory,
725
get_all_applications_from_alias (GMenuTreeAlias *alias,
728
switch (gmenu_tree_alias_get_aliased_item_type (alias)) {
729
case GMENU_TREE_ITEM_ENTRY:
730
/* pass on the reference */
731
list = g_slist_append (list, gmenu_tree_alias_get_aliased_entry (alias));
734
case GMENU_TREE_ITEM_DIRECTORY: {
735
GMenuTreeDirectory *directory = gmenu_tree_alias_get_aliased_directory (alias);
736
list = get_all_applications_from_dir (directory, list);
737
gmenu_tree_item_unref (directory);
749
get_all_applications_from_dir (GMenuTreeDirectory *directory,
753
GMenuTreeItemType next_type;
755
iter = gmenu_tree_directory_iter (directory);
757
while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID) {
759
case GMENU_TREE_ITEM_ENTRY:
760
list = g_slist_append (list, gmenu_tree_iter_get_entry (iter));
763
case GMENU_TREE_ITEM_DIRECTORY: {
764
GMenuTreeDirectory *dir = gmenu_tree_iter_get_directory (iter);
765
list = get_all_applications_from_dir (dir, list);
766
gmenu_tree_item_unref (dir);
770
case GMENU_TREE_ITEM_ALIAS: {
771
GMenuTreeAlias *alias = gmenu_tree_iter_get_alias (iter);
772
list = get_all_applications_from_alias (alias, list);
773
gmenu_tree_item_unref (alias);
782
gmenu_tree_iter_unref (iter);
788
get_all_applications (void)
791
GMenuTreeDirectory *root;
794
tree = gmenu_tree_new ("applications.menu", GMENU_TREE_FLAGS_SORT_DISPLAY_NAME);
796
if (!gmenu_tree_load_sync (tree, NULL))
799
root = gmenu_tree_get_root_directory (tree);
801
retval = get_all_applications_from_dir (root, NULL);
803
gmenu_tree_item_unref (root);
804
g_object_unref (tree);
806
retval = g_slist_sort (retval,
807
(GCompareFunc) compare_applications);
813
panel_run_dialog_add_items_idle (PanelRunDialog *dialog)
815
GtkCellRenderer *renderer;
816
GtkTreeViewColumn *column;
817
GtkTreeModel *model_filter;
818
GSList *all_applications;
821
const char *prev_name;
823
/* create list store */
824
dialog->program_list_store = gtk_list_store_new (NUM_COLUMNS,
832
all_applications = get_all_applications ();
834
/* Strip duplicates */
836
for (l = all_applications; l; l = next) {
837
GMenuTreeEntry *entry = l->data;
838
const char *entry_name;
839
GDesktopAppInfo *app_info;
843
app_info = gmenu_tree_entry_get_app_info (entry);
845
entry_name = g_app_info_get_display_name (G_APP_INFO (app_info));
846
if (prev_name && entry_name && strcmp (entry_name, prev_name) == 0) {
847
gmenu_tree_item_unref (entry);
849
all_applications = g_slist_delete_link (all_applications, l);
851
prev_name = entry_name;
855
for (l = all_applications; l; l = l->next) {
856
GMenuTreeEntry *entry = l->data;
861
app_info = G_APP_INFO (gmenu_tree_entry_get_app_info (entry));
863
gtk_list_store_append (dialog->program_list_store, &iter);
864
gtk_list_store_set (dialog->program_list_store, &iter,
865
COLUMN_ICON, g_app_info_get_icon (app_info),
866
COLUMN_NAME, g_app_info_get_display_name (app_info),
867
COLUMN_COMMENT, g_app_info_get_description (app_info),
868
COLUMN_EXEC, g_app_info_get_executable (app_info),
869
COLUMN_PATH, gmenu_tree_entry_get_desktop_file_path (entry),
870
COLUMN_VISIBLE, TRUE,
873
path = gtk_tree_model_get_path (GTK_TREE_MODEL (dialog->program_list_store), &iter);
875
dialog->add_icon_paths = g_slist_prepend (dialog->add_icon_paths, path);
877
gmenu_tree_item_unref (entry);
879
g_slist_free (all_applications);
881
model_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->program_list_store),
883
gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (model_filter),
886
gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->program_list),
888
//FIXME use the same search than the fuzzy one?
889
gtk_tree_view_set_search_column (GTK_TREE_VIEW (dialog->program_list),
892
renderer = gtk_cell_renderer_pixbuf_new ();
893
column = gtk_tree_view_column_new ();
894
gtk_tree_view_column_pack_start (column, renderer, FALSE);
895
gtk_tree_view_column_set_attributes (column, renderer,
896
"gicon", COLUMN_ICON,
899
renderer = gtk_cell_renderer_text_new ();
900
gtk_tree_view_column_pack_start (column, renderer, TRUE);
901
gtk_tree_view_column_set_attributes (column, renderer,
905
gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->program_list), column);
907
dialog->add_icon_paths = g_slist_reverse (dialog->add_icon_paths);
909
dialog->add_items_idle_id = 0;
914
remove_parameters (const char *exec)
919
str = g_string_new (exec);
921
while ((p = strstr (str->str, "%"))) {
924
g_string_erase (str, p - str->str, 1);
939
g_string_erase (str, p - str->str, 2);
947
g_string_free (str, FALSE);
953
program_list_selection_changed (GtkTreeSelection *selection,
954
PanelRunDialog *dialog)
956
GtkTreeModel *filter_model;
957
GtkTreeModel *child_model;
959
GtkTreeIter filter_iter;
961
char *path, *stripped;
966
if (!gtk_tree_selection_get_selected (selection, &filter_model,
970
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (filter_model),
971
&iter, &filter_iter);
974
child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
975
gtk_tree_model_get (child_model, &iter,
982
key_file = g_key_file_new ();
984
if (!g_key_file_load_from_file (key_file, path,
985
G_KEY_FILE_NONE, NULL)) {
986
g_key_file_free (key_file);
991
dialog->use_program_list = TRUE;
992
if (dialog->desktop_path)
993
g_free (dialog->desktop_path);
994
dialog->desktop_path = g_strdup (path);
995
if (dialog->item_name)
996
g_free (dialog->item_name);
997
dialog->item_name = NULL;
999
/* Order is important here. We have to set the text first so that the
1000
* drag source is enabled, otherwise the drag icon can't be set by
1001
* panel_run_dialog_set_icon.
1003
entry = gtk_bin_get_child (GTK_BIN (dialog->combobox));
1004
temp = panel_key_file_get_string (key_file, "Exec");
1006
stripped = remove_parameters (temp);
1007
gtk_entry_set_text (GTK_ENTRY (entry), stripped);
1010
temp = panel_key_file_get_string (key_file, "URL");
1011
gtk_entry_set_text (GTK_ENTRY (entry), sure_string (temp));
1015
temp = panel_key_file_get_locale_string (key_file, "Icon");
1016
if (!PANEL_GLIB_STR_EMPTY (temp)) {
1019
stripped = panel_xdg_icon_remove_extension (temp);
1020
gicon = g_themed_icon_new (stripped);
1021
panel_run_dialog_set_icon (dialog, gicon, FALSE);
1022
g_object_unref (gicon);
1025
panel_run_dialog_set_icon (dialog, NULL, FALSE);
1029
temp = panel_key_file_get_locale_string (key_file, "Comment");
1030
//FIXME: if sure_string () == "", we should display "Will run..." as in entry_changed()
1031
gtk_label_set_text (GTK_LABEL (dialog->program_label),
1032
sure_string (temp));
1035
terminal = panel_key_file_get_boolean (key_file, "Terminal", FALSE);
1036
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->terminal_checkbox),
1039
g_key_file_free (key_file);
1045
program_list_selection_activated (GtkTreeView *view,
1047
GtkTreeViewColumn *column,
1048
PanelRunDialog *dialog)
1050
GtkTreeSelection *selection;
1052
/* update the entry with the info from the selection */
1053
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->program_list));
1054
program_list_selection_changed (selection, dialog);
1056
/* now launch the command */
1057
gtk_dialog_response (GTK_DIALOG (dialog->run_dialog), GTK_RESPONSE_OK);
1062
panel_run_dialog_setup_program_list (PanelRunDialog *dialog,
1065
GtkTreeSelection *selection;
1067
dialog->program_list = PANEL_GTK_BUILDER_GET (gui, "program_list");
1068
dialog->program_label = PANEL_GTK_BUILDER_GET (gui, "program_label");
1069
dialog->main_box = PANEL_GTK_BUILDER_GET (gui, "main_box");
1071
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->program_list));
1072
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1074
g_signal_connect (selection, "changed",
1075
G_CALLBACK (program_list_selection_changed),
1078
g_signal_connect (dialog->program_list, "row-activated",
1079
G_CALLBACK (program_list_selection_activated),
1084
panel_run_dialog_setup_list_expander (PanelRunDialog *dialog,
1087
dialog->list_expander = PANEL_GTK_BUILDER_GET (gui, "list_expander");
1089
/* Ref the expander so it doesn't get destroyed when it is
1090
* removed from the visible area of the dialog box. */
1091
g_object_ref (dialog->list_expander);
1093
g_settings_bind (dialog->run_settings,
1094
PANEL_RUN_SHOW_LIST_KEY,
1095
dialog->list_expander,
1097
G_SETTINGS_BIND_DEFAULT);
1101
panel_run_dialog_update_program_list (GSettings *settings,
1103
PanelRunDialog *dialog)
1109
enabled = g_settings_get_boolean (dialog->run_settings,
1110
PANEL_RUN_ENABLE_LIST_KEY);
1112
parent = gtk_widget_get_parent (dialog->list_expander);
1115
if (dialog->program_list_store == NULL) {
1116
/* start loading the list of applications */
1117
dialog->add_items_idle_id =
1118
g_idle_add_full (G_PRIORITY_LOW,
1119
(GSourceFunc) panel_run_dialog_add_items_idle,
1124
gtk_box_pack_end (GTK_BOX (dialog->main_box),
1125
dialog->list_expander,
1129
gtk_container_remove (GTK_CONTAINER (parent),
1130
dialog->list_expander);
1133
shown = g_settings_get_boolean (dialog->run_settings,
1134
PANEL_RUN_SHOW_LIST_KEY);
1136
if (enabled && shown) {
1137
gtk_window_resize (GTK_WINDOW (dialog->run_dialog), 100, 300);
1138
gtk_window_set_resizable (GTK_WINDOW (dialog->run_dialog), TRUE);
1139
gtk_widget_grab_focus (dialog->program_list);
1141
gtk_window_set_resizable (GTK_WINDOW (dialog->run_dialog), FALSE);
1142
gtk_widget_grab_focus (dialog->combobox);
1147
file_button_browse_response (GtkWidget *chooser,
1149
PanelRunDialog *dialog)
1153
if (response == GTK_RESPONSE_OK) {
1154
file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
1155
panel_run_dialog_append_file (dialog, file);
1159
gtk_widget_destroy (chooser);
1161
gtk_widget_grab_focus (dialog->combobox);
1165
file_button_clicked (GtkButton *button,
1166
PanelRunDialog *dialog)
1170
chooser = gtk_file_chooser_dialog_new (_("Choose a file to append to the command..."),
1171
GTK_WINDOW (dialog->run_dialog),
1172
GTK_FILE_CHOOSER_ACTION_OPEN,
1173
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1174
GTK_STOCK_OK, GTK_RESPONSE_OK,
1177
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser),
1180
gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK);
1181
gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE);
1183
g_signal_connect (chooser, "response",
1184
G_CALLBACK (file_button_browse_response), dialog);
1186
gtk_window_present (GTK_WINDOW (chooser));
1190
panel_run_dialog_setup_file_button (PanelRunDialog *dialog,
1193
dialog->file_button = PANEL_GTK_BUILDER_GET (gui, "file_button");
1195
g_signal_connect (dialog->file_button, "clicked",
1196
G_CALLBACK (file_button_clicked),
1201
fill_files_from (const char *dirname,
1202
const char *dirprefix,
1204
GList *existing_items)
1208
struct dirent *dent;
1211
dir = opendir (dirname);
1216
while ((dent = readdir (dir))) {
1221
if (!dent->d_name ||
1222
dent->d_name [0] != prefix)
1225
file = g_build_filename (dirname, dent->d_name, NULL);
1229
#ifdef HAVE_STRUCT_DIRENT_D_TYPE
1230
/* don't use g_file_test at first so we don't stat() */
1231
dent->d_type == DT_DIR ||
1232
(dent->d_type == DT_LNK &&
1233
g_file_test (file, G_FILE_TEST_IS_DIR))
1235
g_file_test (file, G_FILE_TEST_IS_DIR)
1242
item = g_build_filename (dirprefix, dent->d_name, suffix, NULL);
1244
list = g_list_prepend (list, item);
1253
fill_possible_executables (void)
1261
path = g_getenv ("PATH");
1263
if (!path || !path [0])
1266
pathv = g_strsplit (path, ":", 0);
1268
for (i = 0; pathv [i]; i++) {
1273
dir = g_dir_open (pathv [i], 0, NULL);
1278
while ((file = g_dir_read_name (dir))) {
1279
filename = g_build_filename (pathv [i], file, NULL);
1280
list = g_list_prepend (list, filename);
1292
fill_executables (GList *possible_executables,
1293
GList *existing_items,
1301
for (l = possible_executables; l; l = l->next) {
1302
const char *filename;
1306
basename = g_path_get_basename (filename);
1308
if (basename [0] == prefix &&
1309
g_file_test (filename, G_FILE_TEST_IS_REGULAR) &&
1310
g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE)) {
1312
if (g_list_find_custom (existing_items, basename,
1313
(GCompareFunc) strcmp)) {
1318
list = g_list_prepend (list, basename);
1328
panel_run_dialog_update_completion (PanelRunDialog *dialog,
1339
g_assert (text != NULL && *text != '\0' && !g_ascii_isspace (*text));
1344
if (!dialog->completion) {
1345
dialog->completion = g_completion_new (NULL);
1346
dialog->possible_executables = fill_possible_executables ();
1347
dialog->dir_hash = g_hash_table_new_full (g_str_hash,
1352
buf = g_path_get_basename (text);
1355
if (prefix == '/' || prefix == '.')
1358
if (text [0] == '/') {
1359
/* complete against absolute path */
1360
dirname = g_path_get_dirname (text);
1361
dirprefix = g_strdup (dirname);
1363
/* complete against relative path and executable name */
1364
if (!strchr (text, '/')) {
1365
executables = fill_executables (dialog->possible_executables,
1366
dialog->completion_items,
1368
dirprefix = g_strdup ("");
1370
dirprefix = g_path_get_dirname (text);
1373
dirname = g_build_filename (g_get_home_dir (), dirprefix, NULL);
1376
key = g_strdup_printf ("%s%c%c", dirprefix, G_DIR_SEPARATOR, prefix);
1378
if (!g_hash_table_lookup (dialog->dir_hash, key)) {
1379
g_hash_table_insert (dialog->dir_hash, key, dialog);
1381
list = fill_files_from (dirname, dirprefix, prefix,
1382
dialog->completion_items);
1387
list = g_list_concat (list, executables);
1395
g_completion_add_items (dialog->completion, list);
1397
dialog->completion_items = g_list_concat (dialog->completion_items,
1402
entry_event (GtkEditable *entry,
1404
PanelRunDialog *dialog)
1406
GtkTreeSelection *selection;
1408
char *nospace_prefix;
1413
if (event->type != GDK_KEY_PRESS)
1416
/* if user typed something we're not using the list anymore */
1417
dialog->use_program_list = FALSE;
1418
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->program_list));
1419
gtk_tree_selection_unselect_all (selection);
1421
if (!g_settings_get_boolean (dialog->run_settings,
1422
PANEL_RUN_ENABLE_COMPLETION_KEY))
1425
/* tab completion */
1426
if (event->keyval == GDK_KEY_Tab) {
1427
gtk_editable_get_selection_bounds (entry, &pos, &tmp);
1429
if (dialog->completion_started &&
1432
tmp == strlen (gtk_entry_get_text (GTK_ENTRY (entry)))) {
1433
gtk_editable_select_region (entry, 0, 0);
1434
gtk_editable_set_position (entry, -1);
1438
} else if (event->length > 0) {
1440
gtk_editable_get_selection_bounds (entry, &pos, &tmp);
1442
if (dialog->completion_started &&
1445
tmp == strlen (gtk_entry_get_text (GTK_ENTRY (entry)))) {
1446
temp = gtk_editable_get_chars (entry, 0, pos);
1447
prefix = g_strconcat (temp, event->string, NULL);
1449
} else if (pos == tmp &&
1450
tmp == strlen (gtk_entry_get_text (GTK_ENTRY (entry)))) {
1451
prefix = g_strconcat (gtk_entry_get_text (GTK_ENTRY (entry)),
1452
event->string, NULL);
1457
nospace_prefix = prefix;
1458
while (*nospace_prefix != '\0' &&
1459
g_ascii_isspace (*nospace_prefix))
1461
if (*nospace_prefix == '\0')
1464
panel_run_dialog_update_completion (dialog, nospace_prefix);
1466
if (!dialog->completion) {
1471
pos = strlen (prefix);
1474
g_completion_complete_utf8 (dialog->completion, nospace_prefix,
1481
temp = g_strndup (prefix, nospace_prefix - prefix);
1484
prefix = g_strconcat (temp, nprefix, NULL);
1486
g_signal_handler_block (dialog->combobox,
1487
dialog->changed_id);
1488
gtk_editable_delete_text (entry, 0, -1);
1489
g_signal_handler_unblock (dialog->combobox,
1490
dialog->changed_id);
1492
gtk_editable_insert_text (entry,
1493
prefix, strlen (prefix),
1496
gtk_editable_set_position (entry, pos);
1497
gtk_editable_select_region (entry, pos, -1);
1499
dialog->completion_started = TRUE;
1515
combobox_changed (GtkComboBox *combobox,
1516
PanelRunDialog *dialog)
1518
gboolean program_list_enabled;
1523
program_list_enabled = g_settings_get_boolean (dialog->run_settings,
1524
PANEL_RUN_ENABLE_LIST_KEY);
1526
text = g_strdup (panel_run_dialog_get_combo_text (dialog));
1529
while (*start != '\0' && g_ascii_isspace (*start))
1532
/* update item name to use for dnd */
1533
if (!dialog->use_program_list) {
1534
if (dialog->desktop_path) {
1535
g_free (dialog->desktop_path);
1536
dialog->desktop_path = NULL;
1538
if (dialog->item_name) {
1539
g_free (dialog->item_name);
1540
dialog->item_name = NULL;
1544
/* desensitize run button if no text entered */
1545
if (!start || !start [0]) {
1548
gtk_widget_set_sensitive (dialog->run_button, FALSE);
1549
gtk_drag_source_unset (dialog->run_dialog);
1551
if (program_list_enabled)
1552
gtk_label_set_text (GTK_LABEL (dialog->program_label),
1553
_("Select an application to view its description."));
1555
panel_run_dialog_set_default_icon (dialog, FALSE);
1557
if (dialog->find_command_idle_id) {
1558
g_source_remove (dialog->find_command_idle_id);
1559
dialog->find_command_idle_id = 0;
1562
if (program_list_enabled) {
1566
gtk_tree_model_foreach (GTK_TREE_MODEL (dialog->program_list_store),
1567
panel_run_dialog_make_all_list_visible,
1570
path = gtk_tree_path_new_first ();
1571
if (gtk_tree_model_get_iter (gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->program_list)),
1573
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (dialog->program_list),
1576
gtk_tree_path_free (path);
1582
gtk_widget_set_sensitive (dialog->run_button, TRUE);
1583
gtk_drag_source_set (dialog->run_dialog,
1587
gtk_drag_source_add_uri_targets (dialog->run_dialog);
1589
if (program_list_enabled &&
1590
!dialog->use_program_list) {
1591
msg = g_strdup_printf (_("Will run command: '%s'"),
1593
gtk_label_set_text (GTK_LABEL (dialog->program_label), msg);
1597
/* look up icon for the command */
1598
if (program_list_enabled &&
1599
!dialog->use_program_list &&
1600
!dialog->find_command_idle_id)
1601
dialog->find_command_idle_id =
1602
g_idle_add_full (G_PRIORITY_LOW,
1603
(GSourceFunc) panel_run_dialog_find_command_idle,
1610
entry_drag_data_received (GtkEditable *entry,
1611
GdkDragContext *context,
1614
GtkSelectionData *selection_data,
1617
PanelRunDialog *dialog)
1623
if (gtk_selection_data_get_format (selection_data) != 8 || gtk_selection_data_get_length (selection_data) == 0) {
1624
g_warning (_("URI list dropped on run dialog had wrong format (%d) or length (%d)\n"),
1625
gtk_selection_data_get_format (selection_data),
1626
gtk_selection_data_get_length (selection_data));
1630
uris = g_uri_list_extract_uris ((const char *)gtk_selection_data_get_data (selection_data));
1633
gtk_drag_finish (context, FALSE, FALSE, time);
1637
for (i = 0; uris [i]; i++) {
1638
if (!uris [i] || !uris [i][0])
1641
file = g_filename_from_uri (uris [i], NULL, NULL);
1643
/* FIXME: I assume the file is in utf8 encoding if coming from a URI? */
1645
panel_run_dialog_append_file_utf8 (dialog, file);
1648
panel_run_dialog_append_file_utf8 (dialog, uris [i]);
1652
gtk_drag_finish (context, TRUE, FALSE, time);
1656
panel_run_dialog_setup_entry (PanelRunDialog *dialog,
1663
dialog->combobox = PANEL_GTK_BUILDER_GET (gui, "comboboxentry");
1665
entry = gtk_bin_get_child (GTK_BIN (dialog->combobox));
1666
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
1668
gtk_combo_box_set_model (GTK_COMBO_BOX (dialog->combobox),
1669
_panel_run_get_recent_programs_list (dialog));
1670
gtk_combo_box_set_entry_text_column
1671
(GTK_COMBO_BOX (dialog->combobox), 0);
1673
screen = gtk_window_get_screen (GTK_WINDOW (dialog->run_dialog));
1675
/* 1/4 the width of the first monitor should be a good value */
1676
width_request = panel_multiscreen_width (screen, 0) / 4;
1677
g_object_set (G_OBJECT (dialog->combobox),
1678
"width_request", width_request,
1681
g_signal_connect (entry, "key-press-event",
1682
G_CALLBACK (entry_event), dialog);
1684
dialog->changed_id = g_signal_connect (dialog->combobox, "changed",
1685
G_CALLBACK (combobox_changed),
1688
gtk_drag_dest_unset (dialog->combobox);
1690
gtk_drag_dest_set (dialog->combobox,
1691
GTK_DEST_DEFAULT_MOTION|GTK_DEST_DEFAULT_HIGHLIGHT,
1694
gtk_drag_dest_add_uri_targets (dialog->combobox);
1696
g_signal_connect (dialog->combobox, "drag_data_received",
1697
G_CALLBACK (entry_drag_data_received), dialog);
1701
panel_run_dialog_create_desktop_file (PanelRunDialog *dialog)
1704
gboolean exec = FALSE;
1712
text = g_strdup (panel_run_dialog_get_combo_text (dialog));
1714
if (!text || !text [0]) {
1719
key_file = panel_key_file_new_desktop ();
1720
disk = g_locale_from_utf8 (text, -1, NULL, NULL, NULL);
1722
scheme = g_uri_parse_scheme (disk);
1723
/* if it's an absolute path or not a URI, it's possibly an executable */
1724
if (g_path_is_absolute (disk) || !scheme)
1725
exec = command_is_executable (disk, NULL, NULL);
1729
panel_key_file_set_string (key_file, "Type", "Application");
1730
panel_key_file_set_string (key_file, "Exec", text);
1731
name = g_strdup (text);
1736
file = panel_util_get_file_optional_homedir (disk);
1737
uri = g_file_get_uri (file);
1738
g_object_unref (file);
1740
panel_key_file_set_string (key_file, "Type", "Link");
1741
panel_key_file_set_string (key_file, "URL", uri);
1747
panel_key_file_set_locale_string (key_file, "Name",
1748
(dialog->item_name) ?
1749
dialog->item_name : text);
1751
panel_key_file_set_boolean (key_file, "Terminal",
1752
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->terminal_checkbox)));
1756
icon = panel_util_get_icon_name_from_g_icon (dialog->gicon);
1758
panel_key_file_set_locale_string (key_file, "Icon",
1762
panel_key_file_set_locale_string (key_file, "Icon",
1763
PANEL_ICON_LAUNCHER);
1765
save_uri = panel_make_unique_desktop_uri (g_get_tmp_dir (), name);
1766
disk = g_filename_from_uri (save_uri, NULL, NULL);
1768
if (!disk || !panel_key_file_to_file (key_file, disk, NULL)) {
1773
g_key_file_free (key_file);
1782
pixmap_drag_data_get (GtkWidget *run_dialog,
1783
GdkDragContext *context,
1784
GtkSelectionData *selection_data,
1787
PanelRunDialog *dialog)
1791
if (dialog->use_program_list && dialog->desktop_path)
1792
uri = g_filename_to_uri (dialog->desktop_path, NULL, NULL);
1794
uri = panel_run_dialog_create_desktop_file (dialog);
1797
gtk_selection_data_set (selection_data,
1798
gtk_selection_data_get_target (selection_data), 8,
1799
(unsigned char *) uri, strlen (uri));
1805
panel_run_dialog_setup_pixmap (PanelRunDialog *dialog,
1808
dialog->pixmap = PANEL_GTK_BUILDER_GET (gui, "icon_pixmap");
1810
g_signal_connect (dialog->run_dialog, "drag_data_get",
1811
G_CALLBACK (pixmap_drag_data_get),
1815
static PanelRunDialog *
1816
panel_run_dialog_new (GdkScreen *screen,
1818
guint32 activate_time)
1820
PanelRunDialog *dialog;
1822
dialog = g_new0 (PanelRunDialog, 1);
1824
dialog->run_dialog = PANEL_GTK_BUILDER_GET (gui, "panel_run_dialog");
1826
dialog->run_settings = g_settings_new (PANEL_RUN_SCHEMA);
1828
g_signal_connect_swapped (dialog->run_dialog, "response",
1829
G_CALLBACK (panel_run_dialog_response), dialog);
1831
g_signal_connect_swapped (dialog->run_dialog, "destroy",
1832
G_CALLBACK (panel_run_dialog_destroy), dialog);
1834
dialog->run_button = PANEL_GTK_BUILDER_GET (gui, "run_button");
1835
dialog->terminal_checkbox = PANEL_GTK_BUILDER_GET (gui, "terminal_checkbox");
1837
panel_run_dialog_setup_pixmap (dialog, gui);
1838
panel_run_dialog_setup_entry (dialog, gui);
1839
panel_run_dialog_setup_file_button (dialog, gui);
1840
panel_run_dialog_setup_program_list (dialog, gui);
1841
panel_run_dialog_setup_list_expander (dialog, gui);
1843
gtk_window_set_icon_name (GTK_WINDOW (dialog->run_dialog),
1845
panel_run_dialog_set_default_icon (dialog, FALSE);
1847
g_signal_connect (dialog->run_settings, "changed::"PANEL_RUN_ENABLE_LIST_KEY,
1848
G_CALLBACK (panel_run_dialog_update_program_list), dialog);
1849
g_signal_connect (dialog->run_settings, "changed::"PANEL_RUN_SHOW_LIST_KEY,
1850
G_CALLBACK (panel_run_dialog_update_program_list), dialog);
1852
panel_run_dialog_update_program_list (dialog->run_settings, NULL, dialog);
1854
gtk_widget_set_sensitive (dialog->run_button, FALSE);
1856
gtk_dialog_set_default_response (GTK_DIALOG (dialog->run_dialog),
1859
gtk_window_set_screen (GTK_WINDOW (dialog->run_dialog), screen);
1861
gtk_widget_grab_focus (dialog->combobox);
1862
gtk_widget_realize (dialog->run_dialog);
1863
gdk_x11_window_set_user_time (gtk_widget_get_window (dialog->run_dialog),
1865
gtk_widget_show (dialog->run_dialog);
1871
panel_run_dialog_static_dialog_destroyed (PanelRunDialog *dialog)
1873
/* just reset the static dialog to NULL for next time */
1874
static_dialog = NULL;
1878
panel_run_dialog_present (GdkScreen *screen,
1879
guint32 activate_time)
1883
if (panel_lockdown_get_disable_command_line_s ())
1886
if (static_dialog) {
1887
gtk_window_set_screen (GTK_WINDOW (static_dialog->run_dialog), screen);
1888
gtk_window_present_with_time (GTK_WINDOW (static_dialog->run_dialog),
1890
gtk_widget_grab_focus (static_dialog->combobox);
1894
gui = gtk_builder_new ();
1895
gtk_builder_set_translation_domain (gui, GETTEXT_PACKAGE);
1896
gtk_builder_add_from_resource (gui,
1897
PANEL_RESOURCE_PATH "panel-run-dialog.ui",
1900
static_dialog = panel_run_dialog_new (screen, gui, activate_time);
1902
g_signal_connect_swapped (static_dialog->run_dialog, "destroy",
1903
G_CALLBACK (panel_run_dialog_static_dialog_destroyed),
1906
g_object_unref (gui);