r1996: Cope slightly better with invalid filenames in various places (reported by
[rox-filer.git] / ROX-Filer / src / minibuffer.c
blobe7e78e22d79e2014d12ea0c46509481ca33dd5bc
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 "find.h"
39 #include "gui_support.h"
40 #include "support.h"
41 #include "minibuffer.h"
42 #include "filer.h"
43 #include "display.h"
44 #include "main.h"
45 #include "action.h"
46 #include "diritem.h"
47 #include "type.h"
48 #include "view_iface.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(ViewIter *iter, 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 GtkEntry *mini;
115 int pos = -1;
116 ViewIter cursor;
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 switch (mini_type)
133 case MINI_PATH:
134 view_show_cursor(filer_window->view);
135 view_get_cursor(filer_window->view, &cursor);
136 view_set_base(filer_window->view, &cursor);
138 gtk_entry_set_text(mini,
139 make_path(filer_window->sym_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:
152 DirItem *item;
153 view_get_cursor(filer_window->view, &cursor);
154 item = cursor.peek(&cursor);
155 pos = 0;
156 if (view_count_selected(filer_window->view) > 0)
157 gtk_entry_set_text(mini, " \"$@\"");
158 else if (item)
160 guchar *tmp;
162 tmp = g_strconcat(" ", item->leafname, NULL);
163 gtk_entry_set_text(mini, tmp);
164 g_free(tmp);
166 else
167 gtk_entry_set_text(mini, "");
168 filer_window->mini_cursor_base = -1; /* History */
169 break;
171 default:
172 g_warning("Bad minibuffer type\n");
173 return;
176 filer_window->mini_type = mini_type;
178 gtk_editable_set_position(GTK_EDITABLE(mini), pos);
180 gtk_widget_show_all(filer_window->minibuffer_area);
182 gtk_widget_grab_focus(filer_window->minibuffer);
185 void minibuffer_hide(FilerWindow *filer_window)
187 filer_window->mini_type = MINI_NONE;
189 gtk_widget_hide(filer_window->minibuffer_area);
191 gtk_widget_child_focus(filer_window->window, GTK_DIR_TAB_FORWARD);
193 if (filer_window->temp_show_hidden)
195 DirItem *item;
196 ViewIter iter;
198 view_get_cursor(filer_window->view, &iter);
199 item = iter.peek(&iter);
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 const gchar *path, *pattern;
268 int flags = OPEN_FROM_MINI | OPEN_SAME_WINDOW;
269 ViewIter iter;
270 DirItem *item;
272 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
273 pattern = g_basename(path);
275 view_get_cursor(filer_window->view, &iter);
277 item = iter.peek(&iter);
278 if (item == NULL || !matches(&iter, pattern))
280 gdk_beep();
281 return;
284 if ((event->state & GDK_SHIFT_MASK) != 0)
285 flags |= OPEN_SHIFT;
287 filer_openitem(filer_window, &iter, flags);
290 /* Use the cursor item to fill in the minibuffer.
291 * If there are multiple matches then fill in as much as possible and
292 * (possibly) beep.
294 static void complete(FilerWindow *filer_window)
296 GtkEntry *entry;
297 DirItem *item, *other;
298 int shortest_stem = -1;
299 int current_stem;
300 const gchar *text, *leaf;
301 ViewIter cursor, iter;
303 view_get_cursor(filer_window->view, &cursor);
304 item = cursor.peek(&cursor);
306 if (!item)
308 gdk_beep();
309 return;
312 entry = GTK_ENTRY(filer_window->minibuffer);
314 text = gtk_entry_get_text(entry);
315 leaf = strrchr(text, '/');
316 if (!leaf)
318 gdk_beep();
319 return;
322 leaf++;
323 if (!matches(&cursor, leaf))
325 gdk_beep();
326 return;
329 current_stem = strlen(leaf);
331 /* Find the longest other match of this name. It it's longer than
332 * the currently entered text then complete only up to that length.
334 view_get_iter(filer_window->view, &iter, 0);
335 while ((other = iter.next(&iter)))
337 int stem = 0;
339 if (iter.i == cursor.i) /* XXX */
340 continue;
342 while (other->leafname[stem] && item->leafname[stem])
344 if (other->leafname[stem] != item->leafname[stem])
345 break;
346 stem++;
349 /* stem is the index of the first difference */
350 if (stem >= current_stem &&
351 (shortest_stem == -1 || stem < shortest_stem))
352 shortest_stem = stem;
355 if (current_stem == shortest_stem)
357 /* We didn't add anything... */
358 if (o_filer_beep_fail.int_value)
359 gdk_beep();
361 else if (current_stem < shortest_stem)
363 guchar *extra;
364 gint tmp_pos;
366 extra = g_strndup(item->leafname + current_stem,
367 shortest_stem - current_stem);
369 tmp_pos = entry->text_length;
370 gtk_editable_insert_text(GTK_EDITABLE(entry), extra, -1,
371 &tmp_pos);
373 g_free(extra);
374 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
376 if (o_filer_beep_multi.int_value)
377 gdk_beep();
379 else
381 GString *new;
383 new = make_path(filer_window->sym_path, item->leafname);
385 if (item->base_type == TYPE_DIRECTORY &&
386 (item->flags & ITEM_FLAG_APPDIR) == 0)
387 g_string_append_c(new, '/');
389 gtk_entry_set_text(entry, new->str);
390 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
394 static void path_changed(FilerWindow *filer_window)
396 GtkWidget *mini = filer_window->minibuffer;
397 const char *new, *leaf;
398 char *path;
399 gboolean error = FALSE;
401 new = gtk_entry_get_text(GTK_ENTRY(mini));
403 if (!*new)
405 /* Entry may be blank because we're in the middle of changing
406 * to something else...
408 entry_set_error(mini, FALSE);
409 return;
412 leaf = g_basename(new);
413 if (leaf == new)
414 path = g_strdup("/");
415 else
416 path = g_dirname(new);
418 if (strcmp(path, filer_window->sym_path) != 0)
420 /* The new path is in a different directory */
421 struct stat info;
423 if (mc_stat(path, &info) == 0 && S_ISDIR(info.st_mode))
424 filer_change_to(filer_window, path, leaf);
425 else
426 error = TRUE;
428 else
430 if (*leaf == '.')
432 if (!filer_window->show_hidden)
434 filer_window->temp_show_hidden = TRUE;
435 display_set_hidden(filer_window, TRUE);
438 else if (filer_window->temp_show_hidden)
440 display_set_hidden(filer_window, FALSE);
441 filer_window->temp_show_hidden = FALSE;
444 if (find_exact_match(filer_window, leaf) == FALSE &&
445 find_next_match(filer_window, leaf, 0) == FALSE)
446 error = TRUE;
449 g_free(path);
451 entry_set_error(mini, error);
454 /* Look for an exact match, and move the cursor to it if found.
455 * TRUE on success.
457 static gboolean find_exact_match(FilerWindow *filer_window,
458 const gchar *pattern)
460 DirItem *item;
461 ViewIter iter;
462 ViewIface *view = filer_window->view;
464 view_get_iter(view, &iter, 0);
466 while ((item = iter.next(&iter)))
468 if (strcmp(item->leafname, pattern) == 0)
470 view_cursor_to_iter(view, &iter);
471 return TRUE;
475 return FALSE;
478 /* Find the next item in the view that matches 'pattern'. Start from
479 * cursor_base and loop at either end. dir is 1 for a forward search,
480 * -1 for backwards. 0 means forwards, but may stay the same.
482 * Does not automatically update cursor_base.
484 * Returns TRUE if a match is found.
486 static gboolean find_next_match(FilerWindow *filer_window,
487 const char *pattern,
488 int dir)
490 ViewIface *view = filer_window->view;
491 ViewIter iter;
493 if (view_count_items(view) < 1)
494 return FALSE;
496 view_get_iter(view, &iter,
497 VIEW_ITER_FROM_BASE |
498 (dir >= 0 ? 0 : VIEW_ITER_BACKWARDS));
500 if (dir != 0)
501 iter.next(&iter); /* Don't look at the base itself */
503 while (iter.next(&iter))
505 if (matches(&iter, pattern))
507 view_cursor_to_iter(view, &iter);
508 return TRUE;
512 /* No matches (except possibly base itself) */
513 view_get_iter(view, &iter,
514 VIEW_ITER_FROM_BASE | VIEW_ITER_ONE_ONLY |
515 (dir >= 0 ? 0 : VIEW_ITER_BACKWARDS));
517 view_cursor_to_iter(view, &iter);
519 return FALSE;
522 static gboolean matches(ViewIter *iter, const char *pattern)
524 DirItem *item;
526 item = iter->peek(iter);
528 return strncmp(item->leafname, pattern, strlen(pattern)) == 0;
531 /* Find next match and set base for future matches. */
532 static void search_in_dir(FilerWindow *filer_window, int dir)
534 const char *path, *pattern;
535 ViewIter iter;
537 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
538 pattern = g_basename(path);
540 view_get_cursor(filer_window->view, &iter);
541 view_set_base(filer_window->view, &iter);
542 find_next_match(filer_window, pattern, dir);
543 view_get_cursor(filer_window->view, &iter);
544 view_set_base(filer_window->view, &iter);
547 /* SHELL COMMANDS */
549 static void add_to_history(const gchar *line)
551 guchar *last;
553 last = shell_history ? (guchar *) shell_history->data : NULL;
555 if (last && strcmp(last, line) == 0)
556 return; /* Duplicating last entry */
558 shell_history = g_list_prepend(shell_history, g_strdup(line));
561 static void shell_done(FilerWindow *filer_window)
563 if (filer_exists(filer_window))
564 filer_update_dir(filer_window, TRUE);
567 /* Given a list of matches, return the longest stem. g_free() the result.
568 * Special chars are escaped. If there is only a single (non-dir) match
569 * then a trailing space is added.
571 static guchar *best_match(FilerWindow *filer_window, glob_t *matches)
573 gchar *first = matches->gl_pathv[0];
574 int i;
575 int longest, path_len;
576 guchar *path, *tmp;
578 longest = strlen(first);
580 for (i = 1; i < matches->gl_pathc; i++)
582 int j;
583 guchar *m = matches->gl_pathv[i];
585 for (j = 0; j < longest; j++)
586 if (m[j] != first[j])
587 longest = j;
590 path_len = strlen(filer_window->sym_path);
591 if (strncmp(filer_window->sym_path, first, path_len) == 0 &&
592 first[path_len] == '/' && first[path_len + 1])
594 path = g_strndup(first + path_len + 1, longest - path_len - 1);
596 else
597 path = g_strndup(first, longest);
599 tmp = shell_escape(path);
600 g_free(path);
602 if (matches->gl_pathc == 1 && tmp[strlen(tmp) - 1] != '/')
604 path = g_strdup_printf("%s ", tmp);
605 g_free(tmp);
606 return path;
609 return tmp;
612 static void shell_tab(FilerWindow *filer_window)
614 const gchar *entry;
615 int i;
616 int pos;
617 GString *leaf;
618 glob_t matches;
619 int leaf_start;
621 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
622 pos = gtk_editable_get_position(GTK_EDITABLE(filer_window->minibuffer));
623 leaf = g_string_new(NULL);
625 for (i = 0; i < pos; i++)
627 guchar c = entry[i];
629 if (leaf->len == 0)
630 leaf_start = i;
632 if (c == ' ')
634 g_string_truncate(leaf, 0);
635 continue;
637 else if (c == '\\' && i + 1 < pos)
638 c = entry[++i];
639 else if (c == '"' || c == '\'')
641 for (++i; i < pos; i++)
643 guchar cc = entry[i];
645 if (cc == '\\' && i + 1 < pos)
646 cc = entry[++i];
647 else if (cc == c)
648 break;
649 g_string_append_c(leaf, cc);
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->sym_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 static void run_child(gpointer unused)
700 /* Ensure output is noticed - send stdout to stderr */
701 dup2(STDERR_FILENO, STDOUT_FILENO);
702 close_on_exec(STDOUT_FILENO, FALSE);
705 /* Either execute the command or make it the default run action */
706 static void shell_return_pressed(FilerWindow *filer_window)
708 GPtrArray *argv;
709 const gchar *entry;
710 GError *error = NULL;
711 pid_t child;
712 DirItem *item;
713 ViewIter iter;
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 view_get_iter(filer_window->view, &iter, 0);
729 while ((item = iter.next(&iter)))
731 if (view_get_selected(filer_window->view, &iter))
732 g_ptr_array_add(argv, item->leafname);
735 g_ptr_array_add(argv, NULL);
737 if (!g_spawn_async_with_pipes(filer_window->sym_path,
738 (gchar **) argv->pdata, NULL,
739 G_SPAWN_DO_NOT_REAP_CHILD |
740 G_SPAWN_SEARCH_PATH,
741 run_child, NULL, /* Child setup fn */
742 &child, /* Child PID */
743 NULL, NULL, NULL, /* Standard pipes */
744 &error))
746 delayed_error("%s", error ? error->message : "(null)");
747 g_error_free(error);
749 else
750 on_child_death(child, (CallbackFn) shell_done, filer_window);
752 g_ptr_array_free(argv, TRUE);
754 out:
755 minibuffer_hide(filer_window);
758 /* Move through the shell history */
759 static void shell_recall(FilerWindow *filer_window, int dir)
761 guchar *command;
762 int pos = filer_window->mini_cursor_base;
764 pos += dir;
765 if (pos >= 0)
767 command = g_list_nth_data(shell_history, pos);
768 if (!command)
769 return;
771 else
772 command = "";
774 if (pos < -1)
775 pos = -1;
776 filer_window->mini_cursor_base = pos;
778 gtk_entry_set_text(GTK_ENTRY(filer_window->minibuffer), command);
781 /* SELECT IF */
783 typedef struct {
784 FindInfo info;
785 FilerWindow *filer_window;
786 FindCondition *cond;
787 } SelectData;
789 static gboolean select_if_test(ViewIter *iter, gpointer user_data)
791 DirItem *item;
792 SelectData *data = user_data;
794 item = iter->peek(iter);
795 g_return_val_if_fail(item != NULL, FALSE);
797 data->info.leaf = item->leafname;
798 data->info.fullpath = make_path(data->filer_window->sym_path,
799 data->info.leaf)->str;
801 return lstat(data->info.fullpath, &data->info.stats) == 0 &&
802 find_test_condition(data->cond, &data->info);
805 static void select_return_pressed(FilerWindow *filer_window, guint etime)
807 const gchar *entry;
808 SelectData data;
810 entry = mini_contents(filer_window);
812 if (!entry)
813 goto out;
815 add_to_history(entry);
817 data.cond = find_compile(entry);
818 if (!data.cond)
820 delayed_error(_("Invalid Find condition"));
821 return;
824 data.info.now = time(NULL);
825 data.info.prune = FALSE; /* (don't care) */
826 data.filer_window = filer_window;
828 view_select_if(filer_window->view, select_if_test, &data);
830 find_condition_free(data.cond);
831 out:
832 minibuffer_hide(filer_window);
836 /* EVENT HANDLERS */
838 static gint key_press_event(GtkWidget *widget,
839 GdkEventKey *event,
840 FilerWindow *filer_window)
842 if (event->keyval == GDK_Escape)
844 if (filer_window->mini_type == MINI_SHELL)
846 const gchar *line;
848 line = mini_contents(filer_window);
849 if (line)
850 add_to_history(line);
853 minibuffer_hide(filer_window);
854 return TRUE;
857 switch (filer_window->mini_type)
859 case MINI_PATH:
860 switch (event->keyval)
862 case GDK_Up:
863 search_in_dir(filer_window, -1);
864 break;
865 case GDK_Down:
866 search_in_dir(filer_window, 1);
867 break;
868 case GDK_Return:
869 case GDK_KP_Enter:
870 path_return_pressed(filer_window,
871 event);
872 break;
873 case GDK_Tab:
874 complete(filer_window);
875 break;
876 default:
877 return FALSE;
879 break;
881 case MINI_SHELL:
882 switch (event->keyval)
884 case GDK_Up:
885 shell_recall(filer_window, 1);
886 break;
887 case GDK_Down:
888 shell_recall(filer_window, -1);
889 break;
890 case GDK_Tab:
891 shell_tab(filer_window);
892 break;
893 case GDK_Return:
894 case GDK_KP_Enter:
895 shell_return_pressed(filer_window);
896 break;
897 default:
898 return FALSE;
900 break;
901 case MINI_SELECT_IF:
902 switch (event->keyval)
904 case GDK_Up:
905 shell_recall(filer_window, 1);
906 break;
907 case GDK_Down:
908 shell_recall(filer_window, -1);
909 break;
910 case GDK_Tab:
911 break;
912 case GDK_Return:
913 case GDK_KP_Enter:
914 select_return_pressed(filer_window,
915 event->time);
916 break;
917 default:
918 return FALSE;
920 break;
921 default:
922 break;
925 return TRUE;
928 static void changed(GtkEditable *mini, FilerWindow *filer_window)
930 switch (filer_window->mini_type)
932 case MINI_PATH:
933 path_changed(filer_window);
934 return;
935 case MINI_SELECT_IF:
936 set_find_string_colour(GTK_WIDGET(mini),
937 gtk_entry_get_text(
938 GTK_ENTRY(filer_window->minibuffer)));
939 return;
940 default:
941 break;
945 /* Returns a string (which must NOT be freed), or NULL if the buffer
946 * is blank (whitespace only).
948 static const gchar *mini_contents(FilerWindow *filer_window)
950 const gchar *entry, *c;
952 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
954 for (c = entry; *c; c++)
955 if (!isspace(*c))
956 return entry;
958 return NULL;
961 /* This is a really ugly hack to get around Gtk+-2.0's broken auto-select
962 * behaviour.
964 static gboolean grab_focus(GtkWidget *minibuffer)
966 GtkWidgetClass *class;
968 class = GTK_WIDGET_CLASS(gtk_type_class(GTK_TYPE_WIDGET));
970 class->grab_focus(minibuffer);
972 g_signal_stop_emission(minibuffer,
973 g_signal_lookup("grab_focus", G_OBJECT_TYPE(minibuffer)), 0);
976 return 1;