r1322: Converted MaskedPixmap to GObject.
[rox-filer.git] / ROX-Filer / src / minibuffer.c
blob5eb11c5f543a57d3c858fbb7967d24b81b9fcac9
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* minibuffer.c - for handling the path entry box at the bottom */
24 #include "config.h"
26 #include <string.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <glob.h>
30 #include <stdio.h>
32 #include <gtk/gtk.h>
33 #include <gdk/gdkkeysyms.h>
35 #include "global.h"
37 #include "options.h"
38 #include "collection.h"
39 #include "find.h"
40 #include "gui_support.h"
41 #include "support.h"
42 #include "minibuffer.h"
43 #include "filer.h"
44 #include "display.h"
45 #include "main.h"
46 #include "action.h"
47 #include "diritem.h"
48 #include "type.h"
50 static GList *shell_history = NULL;
52 /* Static prototypes */
53 static gint key_press_event(GtkWidget *widget,
54 GdkEventKey *event,
55 FilerWindow *filer_window);
56 static void changed(GtkEditable *mini, FilerWindow *filer_window);
57 static gboolean find_next_match(FilerWindow *filer_window,
58 const char *pattern,
59 int dir);
60 static gboolean find_exact_match(FilerWindow *filer_window,
61 const gchar *pattern);
62 static gboolean matches(Collection *collection, int item, const char *pattern);
63 static void search_in_dir(FilerWindow *filer_window, int dir);
64 static const gchar *mini_contents(FilerWindow *filer_window);
65 static void show_help(FilerWindow *filer_window);
66 static gboolean grab_focus(GtkWidget *minibuffer);
68 /****************************************************************
69 * EXTERNAL INTERFACE *
70 ****************************************************************/
72 static Option o_filer_beep_fail, o_filer_beep_multi;
74 void minibuffer_init(void)
76 option_add_int(&o_filer_beep_fail, "filer_beep_fail", 1);
77 option_add_int(&o_filer_beep_multi, "filer_beep_multi", 1);
80 /* Creates the minibuffer widgets, setting the appropriate fields
81 * in filer_window.
83 void create_minibuffer(FilerWindow *filer_window)
85 GtkWidget *hbox, *label, *mini;
87 hbox = gtk_hbox_new(FALSE, 0);
89 gtk_box_pack_start(GTK_BOX(hbox),
90 new_help_button((HelpFunc) show_help, filer_window),
91 FALSE, TRUE, 0);
93 label = gtk_label_new("");
94 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 2);
96 mini = gtk_entry_new();
97 gtk_box_pack_start(GTK_BOX(hbox), mini, TRUE, TRUE, 0);
98 gtk_widget_set_name(mini, "fixed-style");
99 g_signal_connect(mini, "key_press_event",
100 G_CALLBACK(key_press_event), filer_window);
101 g_signal_connect(mini, "changed", G_CALLBACK(changed), filer_window);
103 /* Grabbing focus musn't select the text... */
104 g_signal_connect_swapped(mini, "grab-focus",
105 G_CALLBACK(grab_focus), mini);
107 filer_window->minibuffer = mini;
108 filer_window->minibuffer_label = label;
109 filer_window->minibuffer_area = hbox;
112 void minibuffer_show(FilerWindow *filer_window, MiniType mini_type)
114 Collection *collection;
115 GtkEntry *mini;
116 int pos = -1, i;
118 g_return_if_fail(filer_window != NULL);
119 g_return_if_fail(filer_window->minibuffer != NULL);
121 mini = GTK_ENTRY(filer_window->minibuffer);
123 filer_window->mini_type = MINI_NONE;
124 gtk_label_set_text(GTK_LABEL(filer_window->minibuffer_label),
125 mini_type == MINI_PATH ? _("Goto:") :
126 mini_type == MINI_SHELL ? _("Shell:") :
127 mini_type == MINI_SELECT_IF ? _("Select If:") :
128 "?");
130 collection = filer_window->collection;
131 switch (mini_type)
133 case MINI_PATH:
134 collection_move_cursor(collection, 0, 0);
135 filer_window->mini_cursor_base =
136 MAX(collection->cursor_item, 0);
137 gtk_entry_set_text(mini,
138 make_path(filer_window->path, "")->str);
139 if (filer_window->temp_show_hidden)
141 display_set_hidden(filer_window, FALSE);
142 filer_window->temp_show_hidden = FALSE;
144 break;
145 case MINI_SELECT_IF:
146 gtk_entry_set_text(mini, "");
147 filer_window->mini_cursor_base = -1; /* History */
148 break;
149 case MINI_SHELL:
150 pos = 0;
151 i = collection->cursor_item;
152 if (collection->number_selected)
153 gtk_entry_set_text(mini, " \"$@\"");
154 else if (i > -1 && i < collection->number_of_items)
156 DirItem *item = (DirItem *)
157 collection->items[i].data;
158 guchar *tmp;
160 tmp = g_strconcat(" ", item->leafname, NULL);
161 gtk_entry_set_text(mini, tmp);
162 g_free(tmp);
164 else
165 gtk_entry_set_text(mini, "");
166 filer_window->mini_cursor_base = -1; /* History */
167 break;
168 default:
169 g_warning("Bad minibuffer type\n");
170 return;
173 filer_window->mini_type = mini_type;
175 gtk_editable_set_position(GTK_EDITABLE(mini), pos);
177 gtk_widget_show_all(filer_window->minibuffer_area);
179 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
180 filer_window->minibuffer);
183 void minibuffer_hide(FilerWindow *filer_window)
185 filer_window->mini_type = MINI_NONE;
187 gtk_widget_hide(filer_window->minibuffer_area);
188 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
189 GTK_WIDGET(filer_window->collection));
191 if (filer_window->temp_show_hidden)
193 Collection *collection = filer_window->collection;
194 int i = collection->cursor_item;
195 DirItem *item = NULL;
197 if (i >= 0 && i < collection->number_of_items)
198 item = (DirItem *) collection->items[i].data;
200 if (item == NULL || item->leafname[0] != '.')
201 display_set_hidden(filer_window, FALSE);
202 filer_window->temp_show_hidden = FALSE;
206 /* Insert this leafname at the cursor (replacing the selection, if any).
207 * Must be in SHELL mode.
209 void minibuffer_add(FilerWindow *filer_window, const gchar *leafname)
211 guchar *esc;
212 GtkEditable *edit = GTK_EDITABLE(filer_window->minibuffer);
213 GtkEntry *entry = GTK_ENTRY(edit);
214 int pos;
216 g_return_if_fail(filer_window->mini_type == MINI_SHELL);
218 esc = shell_escape(leafname);
220 gtk_editable_delete_selection(edit);
221 pos = gtk_editable_get_position(edit);
223 if (pos > 0 && gtk_entry_get_text(entry)[pos - 1] != ' ')
224 gtk_editable_insert_text(edit, " ", 1, &pos);
226 gtk_editable_insert_text(edit, esc, strlen(esc), &pos);
227 gtk_editable_set_position(edit, pos);
229 g_free(esc);
233 /****************************************************************
234 * INTERNAL FUNCTIONS *
235 ****************************************************************/
237 static void show_help(FilerWindow *filer_window)
239 const gchar *message;
241 gtk_widget_grab_focus(filer_window->minibuffer);
243 switch (filer_window->mini_type)
245 case MINI_PATH:
246 message = _("Enter the name of a file and I'll display "
247 "it for you. Press Tab to fill in the longest "
248 "match. Escape to close the minibuffer.");
249 break;
250 case MINI_SHELL:
251 message = _("Enter a shell command to execute. Click "
252 "on a file to add it to the buffer.");
253 break;
254 case MINI_SELECT_IF:
255 show_condition_help(NULL);
256 return;
257 default:
258 message = "?!?";
259 break;
262 delayed_error("%s", message);
266 /* PATH ENTRY */
268 static void path_return_pressed(FilerWindow *filer_window, GdkEventKey *event)
270 Collection *collection = filer_window->collection;
271 int item = collection->cursor_item;
272 const gchar *path, *pattern;
273 int flags = OPEN_FROM_MINI | OPEN_SAME_WINDOW;
275 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
276 pattern = strrchr(path, '/');
277 if (pattern)
278 pattern++;
279 else
280 pattern = path;
282 if (item == -1 || item >= collection->number_of_items ||
283 !matches(collection, item, pattern))
285 gdk_beep();
286 return;
289 if ((event->state & GDK_SHIFT_MASK) != 0)
290 flags |= OPEN_SHIFT;
292 filer_openitem(filer_window, item, flags);
295 /* Use the cursor item to fill in the minibuffer.
296 * If there are multiple matches then fill in as much as possible and
297 * (possibly) beep.
299 static void complete(FilerWindow *filer_window)
301 GtkEntry *entry;
302 Collection *collection = filer_window->collection;
303 int cursor = collection->cursor_item;
304 DirItem *item;
305 int shortest_stem = -1;
306 int current_stem;
307 int other;
308 const gchar *text, *leaf;
310 if (cursor < 0 || cursor >= collection->number_of_items)
312 gdk_beep();
313 return;
316 entry = GTK_ENTRY(filer_window->minibuffer);
318 item = (DirItem *) collection->items[cursor].data;
320 text = gtk_entry_get_text(entry);
321 leaf = strrchr(text, '/');
322 if (!leaf)
324 gdk_beep();
325 return;
328 leaf++;
329 if (!matches(collection, cursor, leaf))
331 gdk_beep();
332 return;
335 current_stem = strlen(leaf);
337 /* Find the longest other match of this name. It it's longer than
338 * the currently entered text then complete only up to that length.
340 for (other = 0; other < collection->number_of_items; other++)
342 DirItem *other_item = (DirItem *) collection->items[other].data;
343 int stem = 0;
345 if (other == cursor)
346 continue;
348 while (other_item->leafname[stem] && item->leafname[stem])
350 if (other_item->leafname[stem] != item->leafname[stem])
351 break;
352 stem++;
355 /* stem is the index of the first difference */
356 if (stem >= current_stem &&
357 (shortest_stem == -1 || stem < shortest_stem))
358 shortest_stem = stem;
361 if (current_stem == shortest_stem)
363 /* We didn't add anything... */
364 if (o_filer_beep_fail.int_value)
365 gdk_beep();
367 else if (current_stem < shortest_stem)
369 guchar *extra;
370 gint tmp_pos;
372 extra = g_strndup(item->leafname + current_stem,
373 shortest_stem - current_stem);
375 tmp_pos = entry->text_length;
376 gtk_editable_insert_text(GTK_EDITABLE(entry), extra, -1,
377 &tmp_pos);
379 g_free(extra);
380 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
382 if (o_filer_beep_multi.int_value)
383 gdk_beep();
385 else
387 GString *new;
389 new = make_path(filer_window->path, item->leafname);
391 if (item->base_type == TYPE_DIRECTORY &&
392 (item->flags & ITEM_FLAG_APPDIR) == 0)
393 g_string_append_c(new, '/');
395 gtk_entry_set_text(entry, new->str);
396 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
400 /* This is an idle function because Gtk+ 2.0 changes text in a entry
401 * with two signals; one to blank it and one to put the new text in.
403 static gboolean path_changed(gpointer data)
405 FilerWindow *filer_window = (FilerWindow *) data;
406 GtkWidget *mini = filer_window->minibuffer;
407 const char *new, *leaf;
408 char *path, *real;
410 new = gtk_entry_get_text(GTK_ENTRY(mini));
412 leaf = g_basename(new);
413 if (leaf == new)
414 path = g_strdup("/");
415 else
416 path = g_dirname(new);
417 real = pathdup(path);
418 g_free(path);
420 if (strcmp(real, filer_window->path) != 0)
422 /* The new path is in a different directory */
423 struct stat info;
425 if (mc_stat(real, &info) == 0 && S_ISDIR(info.st_mode))
426 filer_change_to(filer_window, real, leaf);
427 else
428 gdk_beep();
430 else
432 if (*leaf == '.')
434 if (!filer_window->show_hidden)
436 filer_window->temp_show_hidden = TRUE;
437 display_set_hidden(filer_window, TRUE);
440 else if (filer_window->temp_show_hidden)
442 display_set_hidden(filer_window, FALSE);
443 filer_window->temp_show_hidden = FALSE;
446 if (find_exact_match(filer_window, leaf) == FALSE &&
447 find_next_match(filer_window, leaf, 0) == FALSE)
448 gdk_beep();
451 g_free(real);
453 return FALSE;
456 /* Look for an exact match, and move the cursor to it if found.
457 * TRUE on success.
459 static gboolean find_exact_match(FilerWindow *filer_window,
460 const gchar *pattern)
462 Collection *collection = filer_window->collection;
463 int i;
465 for (i = 0; i < collection->number_of_items; i++)
467 DirItem *item = (DirItem *) collection->items[i].data;
469 if (strcmp(item->leafname, pattern) == 0)
471 collection_set_cursor_item(collection, i);
472 return TRUE;
476 return FALSE;
479 /* Find the next item in the collection that matches 'pattern'. Start from
480 * mini_cursor_base and loop at either end. dir is 1 for a forward search,
481 * -1 for backwards. 0 means forwards, but may stay the same.
483 * Does not automatically update mini_cursor_base.
485 * Returns TRUE if a match is found.
487 static gboolean find_next_match(FilerWindow *filer_window,
488 const char *pattern,
489 int dir)
491 Collection *collection = filer_window->collection;
492 int base = filer_window->mini_cursor_base;
493 int item = base;
494 gboolean retval = TRUE;
496 if (collection->number_of_items < 1)
497 return FALSE;
499 if (base < 0 || base>= collection->number_of_items)
500 filer_window->mini_cursor_base = base = 0;
504 /* Step to the next item */
505 item += dir;
507 if (item >= collection->number_of_items)
508 item = 0;
509 else if (item < 0)
510 item = collection->number_of_items - 1;
512 if (dir == 0)
513 dir = 1;
514 else if (item == base)
516 retval = FALSE;
517 break; /* No (other) matches at all */
519 } while (!matches(collection, item, pattern));
521 collection_set_cursor_item(collection, item);
523 return retval;
526 static gboolean matches(Collection *collection,
527 int item_number, const char *pattern)
529 DirItem *item = (DirItem *) collection->items[item_number].data;
531 return strncmp(item->leafname, pattern, strlen(pattern)) == 0;
534 /* Find next match and set base for future matches. */
535 static void search_in_dir(FilerWindow *filer_window, int dir)
537 const char *path, *pattern;
539 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
540 pattern = strrchr(path, '/');
541 if (pattern)
542 pattern++;
543 else
544 pattern = path;
546 filer_window->mini_cursor_base = filer_window->collection->cursor_item;
547 find_next_match(filer_window, pattern, dir);
548 filer_window->mini_cursor_base = filer_window->collection->cursor_item;
551 /* SHELL COMMANDS */
553 static void add_to_history(const gchar *line)
555 guchar *last;
557 last = shell_history ? (guchar *) shell_history->data : NULL;
559 if (last && strcmp(last, line) == 0)
560 return; /* Duplicating last entry */
562 shell_history = g_list_prepend(shell_history, g_strdup(line));
565 static void shell_done(FilerWindow *filer_window)
567 if (filer_exists(filer_window))
568 filer_update_dir(filer_window, TRUE);
571 /* Given a list of matches, return the longest stem. g_free() the result.
572 * Special chars are escaped. If there is only a single (non-dir) match
573 * then a trailing space is added.
575 static guchar *best_match(FilerWindow *filer_window, glob_t *matches)
577 gchar *first = matches->gl_pathv[0];
578 int i;
579 int longest, path_len;
580 guchar *path, *tmp;
582 longest = strlen(first);
584 for (i = 1; i < matches->gl_pathc; i++)
586 int j;
587 guchar *m = matches->gl_pathv[i];
589 for (j = 0; j < longest; j++)
590 if (m[j] != first[j])
591 longest = j;
594 path_len = strlen(filer_window->path);
595 if (strncmp(filer_window->path, first, path_len) == 0 &&
596 first[path_len] == '/' && first[path_len + 1])
598 path = g_strndup(first + path_len + 1, longest - path_len - 1);
600 else
601 path = g_strndup(first, longest);
603 tmp = shell_escape(path);
604 g_free(path);
606 if (matches->gl_pathc == 1 && tmp[strlen(tmp) - 1] != '/')
608 path = g_strdup_printf("%s ", tmp);
609 g_free(tmp);
610 return path;
613 return tmp;
616 static void shell_tab(FilerWindow *filer_window)
618 const gchar *entry;
619 int i;
620 gchar quote;
621 int pos;
622 GString *leaf;
623 glob_t matches;
624 int leaf_start;
626 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
627 pos = gtk_editable_get_position(GTK_EDITABLE(filer_window->minibuffer));
628 leaf = g_string_new(NULL);
630 quote = '\0';
631 for (i = 0; i < pos; i++)
633 guchar c = entry[i];
635 if (leaf->len == 0)
636 leaf_start = i;
638 if (c == ' ')
640 g_string_truncate(leaf, 0);
641 continue;
643 else if (c == '\\' && i + 1 < pos)
644 c = entry[++i];
645 else if (c == '"' || c == '\'')
647 guchar cc;
649 for (++i; i < pos; i++)
651 cc = entry[i];
653 if (cc == '\\' && i + 1 < pos)
654 cc = entry[++i];
655 else if (entry[i] == c)
656 break;
657 g_string_append_c(leaf, entry[i]);
659 continue;
662 g_string_append_c(leaf, c);
665 if (leaf->len == 0)
666 leaf_start = pos;
668 if (leaf->str[0] != '/' && leaf->str[0] != '~')
670 g_string_prepend_c(leaf, '/');
671 g_string_prepend(leaf, filer_window->path);
674 g_string_append_c(leaf, '*');
676 if (glob(leaf->str,
677 #ifdef GLOB_TILDE
678 GLOB_TILDE |
679 #endif
680 GLOB_MARK, NULL, &matches) == 0)
682 if (matches.gl_pathc > 0)
684 guchar *best;
685 GtkEditable *edit =
686 GTK_EDITABLE(filer_window->minibuffer);
688 best = best_match(filer_window, &matches);
690 gtk_editable_delete_text(edit, leaf_start, pos);
691 gtk_editable_insert_text(edit, best, strlen(best),
692 &leaf_start);
693 gtk_editable_set_position(edit, leaf_start);
695 g_free(best);
697 if (matches.gl_pathc != 1)
698 gdk_beep();
700 globfree(&matches);
703 g_string_free(leaf, TRUE);
706 /* Either execute the command or make it the default run action */
707 static void shell_return_pressed(FilerWindow *filer_window)
709 GPtrArray *argv;
710 int i;
711 const gchar *entry;
712 Collection *collection = filer_window->collection;
713 int child;
715 entry = mini_contents(filer_window);
717 if (!entry)
718 goto out;
720 add_to_history(entry);
722 argv = g_ptr_array_new();
723 g_ptr_array_add(argv, "sh");
724 g_ptr_array_add(argv, "-c");
725 g_ptr_array_add(argv, (gchar *) entry);
726 g_ptr_array_add(argv, "sh");
728 for (i = 0; i < collection->number_of_items; i++)
730 DirItem *item = (DirItem *) collection->items[i].data;
731 if (collection->items[i].selected)
732 g_ptr_array_add(argv, item->leafname);
735 g_ptr_array_add(argv, NULL);
737 /* XXX: Use spawn */
739 child = fork();
741 switch (child)
743 case -1:
744 delayed_error(_("Failed to create child process"));
745 break;
746 case 0: /* Child */
747 /* Ensure output is noticed - send stdout to stderr */
748 dup2(STDERR_FILENO, STDOUT_FILENO);
749 close_on_exec(STDOUT_FILENO, FALSE);
750 if (chdir(filer_window->path))
751 g_printerr("chdir(%s) failed: %s\n",
752 filer_window->path,
753 g_strerror(errno));
754 execvp((char *) argv->pdata[0],
755 (char **) argv->pdata);
756 g_printerr("execvp(%s, ...) failed: %s\n",
757 (char *) argv->pdata[0],
758 g_strerror(errno));
759 _exit(0);
760 default:
761 on_child_death(child,
762 (CallbackFn) shell_done, filer_window);
763 break;
766 g_ptr_array_free(argv, TRUE);
768 out:
769 minibuffer_hide(filer_window);
772 /* Move through the shell history */
773 static void shell_recall(FilerWindow *filer_window, int dir)
775 guchar *command;
776 int pos = filer_window->mini_cursor_base;
778 pos += dir;
779 if (pos >= 0)
781 command = g_list_nth_data(shell_history, pos);
782 if (!command)
783 return;
785 else
786 command = "";
788 if (pos < -1)
789 pos = -1;
790 filer_window->mini_cursor_base = pos;
792 gtk_entry_set_text(GTK_ENTRY(filer_window->minibuffer), command);
795 /* SELECT IF */
797 static void select_return_pressed(FilerWindow *filer_window, guint etime)
799 FindCondition *cond;
800 int i, n;
801 const gchar *entry;
802 Collection *collection = filer_window->collection;
803 FindInfo info;
805 entry = mini_contents(filer_window);
807 if (!entry)
808 goto out;
810 add_to_history(entry);
812 cond = find_compile(entry);
813 if (!cond)
815 delayed_error(_("Invalid Find condition"));
816 return;
819 info.now = time(NULL);
820 info.prune = FALSE; /* (don't care) */
821 n = collection->number_of_items;
823 collection->block_selection_changed++;
825 /* If an item at the start is selected then we could lose the
826 * primary selection after checking that item and then need to
827 * gain it again at the end. Therefore, if anything is selected
828 * then select the last item until the end of the search.
830 if (collection->number_selected)
831 collection_select_item(collection, n - 1);
833 for (i = 0; i < n; i++)
835 DirItem *item = (DirItem *) collection->items[i].data;
837 info.leaf = item->leafname;
838 info.fullpath = make_path(filer_window->path, info.leaf)->str;
840 if (lstat(info.fullpath, &info.stats) == 0 &&
841 find_test_condition(cond, &info))
842 collection_select_item(collection, i);
843 else
844 collection_unselect_item(collection, i);
847 find_condition_free(cond);
849 collection_unblock_selection_changed(collection, etime, TRUE);
850 out:
851 minibuffer_hide(filer_window);
855 /* EVENT HANDLERS */
857 static gint key_press_event(GtkWidget *widget,
858 GdkEventKey *event,
859 FilerWindow *filer_window)
861 if (event->keyval == GDK_Escape)
863 if (filer_window->mini_type == MINI_SHELL)
865 const gchar *line;
867 line = mini_contents(filer_window);
868 if (line)
869 add_to_history(line);
872 minibuffer_hide(filer_window);
873 return TRUE;
876 switch (filer_window->mini_type)
878 case MINI_PATH:
879 switch (event->keyval)
881 case GDK_Up:
882 search_in_dir(filer_window, -1);
883 break;
884 case GDK_Down:
885 search_in_dir(filer_window, 1);
886 break;
887 case GDK_Return:
888 case GDK_KP_Enter:
889 path_return_pressed(filer_window,
890 event);
891 break;
892 case GDK_Tab:
893 complete(filer_window);
894 break;
895 default:
896 return FALSE;
898 break;
900 case MINI_SHELL:
901 switch (event->keyval)
903 case GDK_Up:
904 shell_recall(filer_window, 1);
905 break;
906 case GDK_Down:
907 shell_recall(filer_window, -1);
908 break;
909 case GDK_Tab:
910 shell_tab(filer_window);
911 break;
912 case GDK_Return:
913 case GDK_KP_Enter:
914 shell_return_pressed(filer_window);
915 break;
916 default:
917 return FALSE;
919 break;
920 case MINI_SELECT_IF:
921 switch (event->keyval)
923 case GDK_Up:
924 shell_recall(filer_window, 1);
925 break;
926 case GDK_Down:
927 shell_recall(filer_window, -1);
928 break;
929 case GDK_Tab:
930 break;
931 case GDK_Return:
932 case GDK_KP_Enter:
933 select_return_pressed(filer_window,
934 event->time);
935 break;
936 default:
937 return FALSE;
939 break;
940 default:
941 break;
944 return TRUE;
947 static void changed(GtkEditable *mini, FilerWindow *filer_window)
949 switch (filer_window->mini_type)
951 case MINI_PATH:
952 gtk_idle_add(path_changed, filer_window);
953 return;
954 case MINI_SELECT_IF:
955 set_find_string_colour(GTK_WIDGET(mini),
956 gtk_entry_get_text(
957 GTK_ENTRY(filer_window->minibuffer)));
958 return;
959 default:
960 break;
964 /* Returns a string (which must NOT be freed), or NULL if the buffer
965 * is blank (whitespace only).
967 static const gchar *mini_contents(FilerWindow *filer_window)
969 const gchar *entry, *c;
971 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
973 for (c = entry; *c; c++)
974 if (!isspace(*c))
975 return entry;
977 return NULL;
980 /* This is a really ugly hack to get around Gtk+-2.0's broken auto-select
981 * behaviour.
983 static gboolean grab_focus(GtkWidget *minibuffer)
985 GtkWidgetClass *class;
987 class = GTK_WIDGET_CLASS(gtk_type_class(GTK_TYPE_WIDGET));
989 class->grab_focus(minibuffer);
991 g_signal_stop_emission(minibuffer,
992 g_signal_lookup("grab_focus", G_OBJECT_TYPE(minibuffer)), 0);
995 return 1;