r1354: Code tidying in various places (Bernard Jungen).
[rox-filer.git] / ROX-Filer / src / minibuffer.c
blobc44302caff1fb94b363a10a57915c7221b3ed07e
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);
122 entry_set_error(filer_window->minibuffer, FALSE);
124 filer_window->mini_type = MINI_NONE;
125 gtk_label_set_text(GTK_LABEL(filer_window->minibuffer_label),
126 mini_type == MINI_PATH ? _("Goto:") :
127 mini_type == MINI_SHELL ? _("Shell:") :
128 mini_type == MINI_SELECT_IF ? _("Select If:") :
129 "?");
131 collection = filer_window->collection;
132 switch (mini_type)
134 case MINI_PATH:
135 collection_move_cursor(collection, 0, 0);
136 filer_window->mini_cursor_base =
137 MAX(collection->cursor_item, 0);
138 gtk_entry_set_text(mini,
139 make_path(filer_window->path, "")->str);
140 if (filer_window->temp_show_hidden)
142 display_set_hidden(filer_window, FALSE);
143 filer_window->temp_show_hidden = FALSE;
145 break;
146 case MINI_SELECT_IF:
147 gtk_entry_set_text(mini, "");
148 filer_window->mini_cursor_base = -1; /* History */
149 break;
150 case MINI_SHELL:
151 pos = 0;
152 i = collection->cursor_item;
153 if (collection->number_selected)
154 gtk_entry_set_text(mini, " \"$@\"");
155 else if (i > -1 && i < collection->number_of_items)
157 DirItem *item = (DirItem *)
158 collection->items[i].data;
159 guchar *tmp;
161 tmp = g_strconcat(" ", item->leafname, NULL);
162 gtk_entry_set_text(mini, tmp);
163 g_free(tmp);
165 else
166 gtk_entry_set_text(mini, "");
167 filer_window->mini_cursor_base = -1; /* History */
168 break;
169 default:
170 g_warning("Bad minibuffer type\n");
171 return;
174 filer_window->mini_type = mini_type;
176 gtk_editable_set_position(GTK_EDITABLE(mini), pos);
178 gtk_widget_show_all(filer_window->minibuffer_area);
180 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
181 filer_window->minibuffer);
184 void minibuffer_hide(FilerWindow *filer_window)
186 filer_window->mini_type = MINI_NONE;
188 gtk_widget_hide(filer_window->minibuffer_area);
189 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
190 GTK_WIDGET(filer_window->collection));
192 if (filer_window->temp_show_hidden)
194 Collection *collection = filer_window->collection;
195 int i = collection->cursor_item;
196 DirItem *item = NULL;
198 if (i >= 0 && i < collection->number_of_items)
199 item = (DirItem *) collection->items[i].data;
201 if (item == NULL || item->leafname[0] != '.')
202 display_set_hidden(filer_window, FALSE);
203 filer_window->temp_show_hidden = FALSE;
207 /* Insert this leafname at the cursor (replacing the selection, if any).
208 * Must be in SHELL mode.
210 void minibuffer_add(FilerWindow *filer_window, const gchar *leafname)
212 guchar *esc;
213 GtkEditable *edit = GTK_EDITABLE(filer_window->minibuffer);
214 GtkEntry *entry = GTK_ENTRY(edit);
215 int pos;
217 g_return_if_fail(filer_window->mini_type == MINI_SHELL);
219 esc = shell_escape(leafname);
221 gtk_editable_delete_selection(edit);
222 pos = gtk_editable_get_position(edit);
224 if (pos > 0 && gtk_entry_get_text(entry)[pos - 1] != ' ')
225 gtk_editable_insert_text(edit, " ", 1, &pos);
227 gtk_editable_insert_text(edit, esc, strlen(esc), &pos);
228 gtk_editable_set_position(edit, pos);
230 g_free(esc);
234 /****************************************************************
235 * INTERNAL FUNCTIONS *
236 ****************************************************************/
238 static void show_help(FilerWindow *filer_window)
240 switch (filer_window->mini_type)
242 case MINI_PATH:
243 info_message(
244 _("Enter the name of a file and I'll display "
245 "it for you. Press Tab to fill in the longest "
246 "match. Escape to close the minibuffer."));
247 break;
248 case MINI_SHELL:
249 info_message(
250 _("Enter a shell command to execute. Click "
251 "on a file to add it to the buffer."));
252 break;
253 case MINI_SELECT_IF:
254 show_condition_help(NULL);
255 break;
256 default:
257 g_warning("Unknown minibuffer type!");
258 break;
263 /* PATH ENTRY */
265 static void path_return_pressed(FilerWindow *filer_window, GdkEventKey *event)
267 Collection *collection = filer_window->collection;
268 int item = collection->cursor_item;
269 const gchar *path, *pattern;
270 int flags = OPEN_FROM_MINI | OPEN_SAME_WINDOW;
272 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
273 pattern = g_basename(path);
275 if (item == -1 || item >= collection->number_of_items ||
276 !matches(collection, item, pattern))
278 gdk_beep();
279 return;
282 if ((event->state & GDK_SHIFT_MASK) != 0)
283 flags |= OPEN_SHIFT;
285 filer_openitem(filer_window, item, flags);
288 /* Use the cursor item to fill in the minibuffer.
289 * If there are multiple matches then fill in as much as possible and
290 * (possibly) beep.
292 static void complete(FilerWindow *filer_window)
294 GtkEntry *entry;
295 Collection *collection = filer_window->collection;
296 int cursor = collection->cursor_item;
297 DirItem *item;
298 int shortest_stem = -1;
299 int current_stem;
300 int other;
301 const gchar *text, *leaf;
303 if (cursor < 0 || cursor >= collection->number_of_items)
305 gdk_beep();
306 return;
309 entry = GTK_ENTRY(filer_window->minibuffer);
311 item = (DirItem *) collection->items[cursor].data;
313 text = gtk_entry_get_text(entry);
314 leaf = strrchr(text, '/');
315 if (!leaf)
317 gdk_beep();
318 return;
321 leaf++;
322 if (!matches(collection, cursor, leaf))
324 gdk_beep();
325 return;
328 current_stem = strlen(leaf);
330 /* Find the longest other match of this name. It it's longer than
331 * the currently entered text then complete only up to that length.
333 for (other = 0; other < collection->number_of_items; other++)
335 DirItem *other_item = (DirItem *) collection->items[other].data;
336 int stem = 0;
338 if (other == cursor)
339 continue;
341 while (other_item->leafname[stem] && item->leafname[stem])
343 if (other_item->leafname[stem] != item->leafname[stem])
344 break;
345 stem++;
348 /* stem is the index of the first difference */
349 if (stem >= current_stem &&
350 (shortest_stem == -1 || stem < shortest_stem))
351 shortest_stem = stem;
354 if (current_stem == shortest_stem)
356 /* We didn't add anything... */
357 if (o_filer_beep_fail.int_value)
358 gdk_beep();
360 else if (current_stem < shortest_stem)
362 guchar *extra;
363 gint tmp_pos;
365 extra = g_strndup(item->leafname + current_stem,
366 shortest_stem - current_stem);
368 tmp_pos = entry->text_length;
369 gtk_editable_insert_text(GTK_EDITABLE(entry), extra, -1,
370 &tmp_pos);
372 g_free(extra);
373 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
375 if (o_filer_beep_multi.int_value)
376 gdk_beep();
378 else
380 GString *new;
382 new = make_path(filer_window->path, item->leafname);
384 if (item->base_type == TYPE_DIRECTORY &&
385 (item->flags & ITEM_FLAG_APPDIR) == 0)
386 g_string_append_c(new, '/');
388 gtk_entry_set_text(entry, new->str);
389 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
393 /* This is an idle function because Gtk+ 2.0 changes text in a entry
394 * with two signals; one to blank it and one to put the new text in.
396 static gboolean path_changed(gpointer data)
398 FilerWindow *filer_window = (FilerWindow *) data;
399 GtkWidget *mini = filer_window->minibuffer;
400 const char *new, *leaf;
401 char *path, *real;
402 gboolean error = FALSE;
404 new = gtk_entry_get_text(GTK_ENTRY(mini));
406 leaf = g_basename(new);
407 if (leaf == new)
408 path = g_strdup("/");
409 else
410 path = g_dirname(new);
411 real = pathdup(path);
412 g_free(path);
414 if (strcmp(real, filer_window->path) != 0)
416 /* The new path is in a different directory */
417 struct stat info;
419 if (mc_stat(real, &info) == 0 && S_ISDIR(info.st_mode))
420 filer_change_to(filer_window, real, leaf);
421 else
422 error = TRUE;
424 else
426 if (*leaf == '.')
428 if (!filer_window->show_hidden)
430 filer_window->temp_show_hidden = TRUE;
431 display_set_hidden(filer_window, TRUE);
434 else if (filer_window->temp_show_hidden)
436 display_set_hidden(filer_window, FALSE);
437 filer_window->temp_show_hidden = FALSE;
440 if (find_exact_match(filer_window, leaf) == FALSE &&
441 find_next_match(filer_window, leaf, 0) == FALSE)
442 error = TRUE;
445 g_free(real);
447 entry_set_error(mini, error);
449 return FALSE;
452 /* Look for an exact match, and move the cursor to it if found.
453 * TRUE on success.
455 static gboolean find_exact_match(FilerWindow *filer_window,
456 const gchar *pattern)
458 Collection *collection = filer_window->collection;
459 int i;
461 for (i = 0; i < collection->number_of_items; i++)
463 DirItem *item = (DirItem *) collection->items[i].data;
465 if (strcmp(item->leafname, pattern) == 0)
467 collection_set_cursor_item(collection, i);
468 return TRUE;
472 return FALSE;
475 /* Find the next item in the collection that matches 'pattern'. Start from
476 * mini_cursor_base and loop at either end. dir is 1 for a forward search,
477 * -1 for backwards. 0 means forwards, but may stay the same.
479 * Does not automatically update mini_cursor_base.
481 * Returns TRUE if a match is found.
483 static gboolean find_next_match(FilerWindow *filer_window,
484 const char *pattern,
485 int dir)
487 Collection *collection = filer_window->collection;
488 int base = filer_window->mini_cursor_base;
489 int item = base;
490 gboolean retval = TRUE;
492 if (collection->number_of_items < 1)
493 return FALSE;
495 if (base < 0 || base>= collection->number_of_items)
496 filer_window->mini_cursor_base = base = 0;
500 /* Step to the next item */
501 item += dir;
503 if (item >= collection->number_of_items)
504 item = 0;
505 else if (item < 0)
506 item = collection->number_of_items - 1;
508 if (dir == 0)
509 dir = 1;
510 else if (item == base)
512 retval = FALSE;
513 break; /* No (other) matches at all */
515 } while (!matches(collection, item, pattern));
517 collection_set_cursor_item(collection, item);
519 return retval;
522 static gboolean matches(Collection *collection,
523 int item_number, const char *pattern)
525 DirItem *item = (DirItem *) collection->items[item_number].data;
527 return strncmp(item->leafname, pattern, strlen(pattern)) == 0;
530 /* Find next match and set base for future matches. */
531 static void search_in_dir(FilerWindow *filer_window, int dir)
533 const char *path, *pattern;
535 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
536 pattern = g_basename(path);
538 filer_window->mini_cursor_base = filer_window->collection->cursor_item;
539 find_next_match(filer_window, pattern, dir);
540 filer_window->mini_cursor_base = filer_window->collection->cursor_item;
543 /* SHELL COMMANDS */
545 static void add_to_history(const gchar *line)
547 guchar *last;
549 last = shell_history ? (guchar *) shell_history->data : NULL;
551 if (last && strcmp(last, line) == 0)
552 return; /* Duplicating last entry */
554 shell_history = g_list_prepend(shell_history, g_strdup(line));
557 static void shell_done(FilerWindow *filer_window)
559 if (filer_exists(filer_window))
560 filer_update_dir(filer_window, TRUE);
563 /* Given a list of matches, return the longest stem. g_free() the result.
564 * Special chars are escaped. If there is only a single (non-dir) match
565 * then a trailing space is added.
567 static guchar *best_match(FilerWindow *filer_window, glob_t *matches)
569 gchar *first = matches->gl_pathv[0];
570 int i;
571 int longest, path_len;
572 guchar *path, *tmp;
574 longest = strlen(first);
576 for (i = 1; i < matches->gl_pathc; i++)
578 int j;
579 guchar *m = matches->gl_pathv[i];
581 for (j = 0; j < longest; j++)
582 if (m[j] != first[j])
583 longest = j;
586 path_len = strlen(filer_window->path);
587 if (strncmp(filer_window->path, first, path_len) == 0 &&
588 first[path_len] == '/' && first[path_len + 1])
590 path = g_strndup(first + path_len + 1, longest - path_len - 1);
592 else
593 path = g_strndup(first, longest);
595 tmp = shell_escape(path);
596 g_free(path);
598 if (matches->gl_pathc == 1 && tmp[strlen(tmp) - 1] != '/')
600 path = g_strdup_printf("%s ", tmp);
601 g_free(tmp);
602 return path;
605 return tmp;
608 static void shell_tab(FilerWindow *filer_window)
610 const gchar *entry;
611 int i;
612 gchar quote;
613 int pos;
614 GString *leaf;
615 glob_t matches;
616 int leaf_start;
618 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
619 pos = gtk_editable_get_position(GTK_EDITABLE(filer_window->minibuffer));
620 leaf = g_string_new(NULL);
622 quote = '\0';
623 for (i = 0; i < pos; i++)
625 guchar c = entry[i];
627 if (leaf->len == 0)
628 leaf_start = i;
630 if (c == ' ')
632 g_string_truncate(leaf, 0);
633 continue;
635 else if (c == '\\' && i + 1 < pos)
636 c = entry[++i];
637 else if (c == '"' || c == '\'')
639 guchar cc;
641 for (++i; i < pos; i++)
643 cc = entry[i];
645 if (cc == '\\' && i + 1 < pos)
646 cc = entry[++i];
647 else if (entry[i] == c)
648 break;
649 g_string_append_c(leaf, entry[i]);
651 continue;
654 g_string_append_c(leaf, c);
657 if (leaf->len == 0)
658 leaf_start = pos;
660 if (leaf->str[0] != '/' && leaf->str[0] != '~')
662 g_string_prepend_c(leaf, '/');
663 g_string_prepend(leaf, filer_window->path);
666 g_string_append_c(leaf, '*');
668 if (glob(leaf->str,
669 #ifdef GLOB_TILDE
670 GLOB_TILDE |
671 #endif
672 GLOB_MARK, NULL, &matches) == 0)
674 if (matches.gl_pathc > 0)
676 guchar *best;
677 GtkEditable *edit =
678 GTK_EDITABLE(filer_window->minibuffer);
680 best = best_match(filer_window, &matches);
682 gtk_editable_delete_text(edit, leaf_start, pos);
683 gtk_editable_insert_text(edit, best, strlen(best),
684 &leaf_start);
685 gtk_editable_set_position(edit, leaf_start);
687 g_free(best);
689 if (matches.gl_pathc != 1)
690 gdk_beep();
692 globfree(&matches);
695 g_string_free(leaf, TRUE);
698 /* Either execute the command or make it the default run action */
699 static void shell_return_pressed(FilerWindow *filer_window)
701 GPtrArray *argv;
702 int i;
703 const gchar *entry;
704 Collection *collection = filer_window->collection;
705 pid_t child;
707 entry = mini_contents(filer_window);
709 if (!entry)
710 goto out;
712 add_to_history(entry);
714 argv = g_ptr_array_new();
715 g_ptr_array_add(argv, "sh");
716 g_ptr_array_add(argv, "-c");
717 g_ptr_array_add(argv, (gchar *) entry);
718 g_ptr_array_add(argv, "sh");
720 for (i = 0; i < collection->number_of_items; i++)
722 DirItem *item = (DirItem *) collection->items[i].data;
723 if (collection->items[i].selected)
724 g_ptr_array_add(argv, item->leafname);
727 g_ptr_array_add(argv, NULL);
729 /* XXX: Use spawn */
731 child = fork();
733 switch (child)
735 case -1:
736 delayed_error(_("Failed to create child process"));
737 break;
738 case 0: /* Child */
739 /* Ensure output is noticed - send stdout to stderr */
740 dup2(STDERR_FILENO, STDOUT_FILENO);
741 close_on_exec(STDOUT_FILENO, FALSE);
742 if (chdir(filer_window->path))
743 g_printerr("chdir(%s) failed: %s\n",
744 filer_window->path,
745 g_strerror(errno));
746 execvp((char *) argv->pdata[0],
747 (char **) argv->pdata);
748 g_printerr("execvp(%s, ...) failed: %s\n",
749 (char *) argv->pdata[0],
750 g_strerror(errno));
751 _exit(0);
752 default:
753 on_child_death(child,
754 (CallbackFn) shell_done, filer_window);
755 break;
758 g_ptr_array_free(argv, TRUE);
760 out:
761 minibuffer_hide(filer_window);
764 /* Move through the shell history */
765 static void shell_recall(FilerWindow *filer_window, int dir)
767 guchar *command;
768 int pos = filer_window->mini_cursor_base;
770 pos += dir;
771 if (pos >= 0)
773 command = g_list_nth_data(shell_history, pos);
774 if (!command)
775 return;
777 else
778 command = "";
780 if (pos < -1)
781 pos = -1;
782 filer_window->mini_cursor_base = pos;
784 gtk_entry_set_text(GTK_ENTRY(filer_window->minibuffer), command);
787 /* SELECT IF */
789 static void select_return_pressed(FilerWindow *filer_window, guint etime)
791 FindCondition *cond;
792 int i, n;
793 const gchar *entry;
794 Collection *collection = filer_window->collection;
795 FindInfo info;
797 entry = mini_contents(filer_window);
799 if (!entry)
800 goto out;
802 add_to_history(entry);
804 cond = find_compile(entry);
805 if (!cond)
807 delayed_error(_("Invalid Find condition"));
808 return;
811 info.now = time(NULL);
812 info.prune = FALSE; /* (don't care) */
813 n = collection->number_of_items;
815 collection->block_selection_changed++;
817 /* If an item at the start is selected then we could lose the
818 * primary selection after checking that item and then need to
819 * gain it again at the end. Therefore, if anything is selected
820 * then select the last item until the end of the search.
822 if (collection->number_selected)
823 collection_select_item(collection, n - 1);
825 for (i = 0; i < n; i++)
827 DirItem *item = (DirItem *) collection->items[i].data;
829 info.leaf = item->leafname;
830 info.fullpath = make_path(filer_window->path, info.leaf)->str;
832 if (lstat(info.fullpath, &info.stats) == 0 &&
833 find_test_condition(cond, &info))
834 collection_select_item(collection, i);
835 else
836 collection_unselect_item(collection, i);
839 find_condition_free(cond);
841 collection_unblock_selection_changed(collection, etime, TRUE);
842 out:
843 minibuffer_hide(filer_window);
847 /* EVENT HANDLERS */
849 static gint key_press_event(GtkWidget *widget,
850 GdkEventKey *event,
851 FilerWindow *filer_window)
853 if (event->keyval == GDK_Escape)
855 if (filer_window->mini_type == MINI_SHELL)
857 const gchar *line;
859 line = mini_contents(filer_window);
860 if (line)
861 add_to_history(line);
864 minibuffer_hide(filer_window);
865 return TRUE;
868 switch (filer_window->mini_type)
870 case MINI_PATH:
871 switch (event->keyval)
873 case GDK_Up:
874 search_in_dir(filer_window, -1);
875 break;
876 case GDK_Down:
877 search_in_dir(filer_window, 1);
878 break;
879 case GDK_Return:
880 case GDK_KP_Enter:
881 path_return_pressed(filer_window,
882 event);
883 break;
884 case GDK_Tab:
885 complete(filer_window);
886 break;
887 default:
888 return FALSE;
890 break;
892 case MINI_SHELL:
893 switch (event->keyval)
895 case GDK_Up:
896 shell_recall(filer_window, 1);
897 break;
898 case GDK_Down:
899 shell_recall(filer_window, -1);
900 break;
901 case GDK_Tab:
902 shell_tab(filer_window);
903 break;
904 case GDK_Return:
905 case GDK_KP_Enter:
906 shell_return_pressed(filer_window);
907 break;
908 default:
909 return FALSE;
911 break;
912 case MINI_SELECT_IF:
913 switch (event->keyval)
915 case GDK_Up:
916 shell_recall(filer_window, 1);
917 break;
918 case GDK_Down:
919 shell_recall(filer_window, -1);
920 break;
921 case GDK_Tab:
922 break;
923 case GDK_Return:
924 case GDK_KP_Enter:
925 select_return_pressed(filer_window,
926 event->time);
927 break;
928 default:
929 return FALSE;
931 break;
932 default:
933 break;
936 return TRUE;
939 static void changed(GtkEditable *mini, FilerWindow *filer_window)
941 switch (filer_window->mini_type)
943 case MINI_PATH:
944 gtk_idle_add(path_changed, filer_window);
945 return;
946 case MINI_SELECT_IF:
947 set_find_string_colour(GTK_WIDGET(mini),
948 gtk_entry_get_text(
949 GTK_ENTRY(filer_window->minibuffer)));
950 return;
951 default:
952 break;
956 /* Returns a string (which must NOT be freed), or NULL if the buffer
957 * is blank (whitespace only).
959 static const gchar *mini_contents(FilerWindow *filer_window)
961 const gchar *entry, *c;
963 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
965 for (c = entry; *c; c++)
966 if (!isspace(*c))
967 return entry;
969 return NULL;
972 /* This is a really ugly hack to get around Gtk+-2.0's broken auto-select
973 * behaviour.
975 static gboolean grab_focus(GtkWidget *minibuffer)
977 GtkWidgetClass *class;
979 class = GTK_WIDGET_CLASS(gtk_type_class(GTK_TYPE_WIDGET));
981 class->grab_focus(minibuffer);
983 g_signal_stop_emission(minibuffer,
984 g_signal_lookup("grab_focus", G_OBJECT_TYPE(minibuffer)), 0);
987 return 1;