r4015: Bugfix: The path entry buffer didn't work in /, due to recent changes
[rox-filer.git] / ROX-Filer / src / minibuffer.c
blob3fe3ae5943f0f39bb490ff7a0bc855f37d871c5d
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, 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 <fnmatch.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <glob.h>
31 #include <stdio.h>
33 #include <sys/types.h>
34 #include <pwd.h>
36 #include <gtk/gtk.h>
37 #include <gdk/gdkkeysyms.h>
39 #include "global.h"
41 #include "options.h"
42 #include "find.h"
43 #include "gui_support.h"
44 #include "support.h"
45 #include "minibuffer.h"
46 #include "filer.h"
47 #include "display.h"
48 #include "main.h"
49 #include "action.h"
50 #include "diritem.h"
51 #include "type.h"
52 #include "view_iface.h"
54 static GList *shell_history = NULL;
56 /* Static prototypes */
57 static gint key_press_event(GtkWidget *widget,
58 GdkEventKey *event,
59 FilerWindow *filer_window);
60 static void changed(GtkEditable *mini, FilerWindow *filer_window);
61 static gboolean find_next_match(FilerWindow *filer_window,
62 const char *pattern,
63 int dir);
64 static gboolean find_exact_match(FilerWindow *filer_window,
65 const gchar *pattern);
66 static gboolean matches(ViewIter *iter, const char *pattern);
67 static void search_in_dir(FilerWindow *filer_window, int dir);
68 static const gchar *mini_contents(FilerWindow *filer_window);
69 static void show_help(FilerWindow *filer_window);
70 static gboolean grab_focus(GtkWidget *minibuffer);
71 static gboolean select_if_glob(ViewIter *iter, gpointer data);
73 /****************************************************************
74 * EXTERNAL INTERFACE *
75 ****************************************************************/
77 static Option o_filer_beep_fail, o_filer_beep_multi;
79 void minibuffer_init(void)
81 option_add_int(&o_filer_beep_fail, "filer_beep_fail", 1);
82 option_add_int(&o_filer_beep_multi, "filer_beep_multi", 1);
85 /* Creates the minibuffer widgets, setting the appropriate fields
86 * in filer_window.
88 void create_minibuffer(FilerWindow *filer_window)
90 GtkWidget *hbox, *label, *mini;
92 hbox = gtk_hbox_new(FALSE, 0);
94 gtk_box_pack_start(GTK_BOX(hbox),
95 new_help_button((HelpFunc) show_help, filer_window),
96 FALSE, TRUE, 0);
98 label = gtk_label_new("");
99 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 2);
101 mini = gtk_entry_new();
102 gtk_box_pack_start(GTK_BOX(hbox), mini, TRUE, TRUE, 0);
103 gtk_widget_set_name(mini, "fixed-style");
104 g_signal_connect(mini, "key_press_event",
105 G_CALLBACK(key_press_event), filer_window);
106 g_signal_connect(mini, "changed", G_CALLBACK(changed), filer_window);
108 /* Grabbing focus musn't select the text... */
109 g_signal_connect_swapped(mini, "grab-focus",
110 G_CALLBACK(grab_focus), mini);
112 filer_window->minibuffer = mini;
113 filer_window->minibuffer_label = label;
114 filer_window->minibuffer_area = hbox;
117 void minibuffer_show(FilerWindow *filer_window, MiniType mini_type)
119 GtkEntry *mini;
120 int pos = -1;
121 ViewIter cursor;
123 g_return_if_fail(filer_window != NULL);
124 g_return_if_fail(filer_window->minibuffer != NULL);
126 mini = GTK_ENTRY(filer_window->minibuffer);
127 entry_set_error(filer_window->minibuffer, FALSE);
129 filer_window->mini_type = MINI_NONE;
130 gtk_label_set_text(GTK_LABEL(filer_window->minibuffer_label),
131 mini_type == MINI_PATH ? _("Goto:") :
132 mini_type == MINI_SHELL ? _("Shell:") :
133 mini_type == MINI_SELECT_IF ? _("Select If:") :
134 mini_type == MINI_SELECT_BY_NAME ? _("Select Named:") :
135 mini_type == MINI_FILTER ? _("Pattern:") :
136 "?");
138 switch (mini_type)
140 case MINI_PATH:
141 view_show_cursor(filer_window->view);
142 view_get_cursor(filer_window->view, &cursor);
143 view_set_base(filer_window->view, &cursor);
145 gtk_entry_set_text(mini,
146 make_path(filer_window->sym_path, ""));
147 if (filer_window->temp_show_hidden)
149 filer_window->temp_show_hidden = FALSE;
150 display_update_hidden(filer_window);
152 break;
153 case MINI_SELECT_IF:
154 gtk_entry_set_text(mini, "");
155 filer_window->mini_cursor_base = -1; /* History */
156 break;
157 case MINI_SELECT_BY_NAME:
158 gtk_entry_set_text(mini, "*.");
159 filer_window->mini_cursor_base = -1; /* History */
160 view_select_if(filer_window->view, select_if_glob, "*.");
161 break;
162 case MINI_FILTER:
163 if(filer_window->filter!=FILER_SHOW_GLOB ||
164 !filer_window->filter_string)
165 gtk_entry_set_text(mini, "*");
166 else
167 gtk_entry_set_text(mini,
168 filer_window->filter_string);
169 break;
170 case MINI_SHELL:
172 DirItem *item;
173 view_get_cursor(filer_window->view, &cursor);
174 item = cursor.peek(&cursor);
175 pos = 0;
176 if (view_count_selected(filer_window->view) > 0)
177 gtk_entry_set_text(mini, " \"$@\"");
178 else if (item)
180 guchar *tmp;
182 tmp = g_strconcat(" ", item->leafname, NULL);
183 gtk_entry_set_text(mini, tmp);
184 g_free(tmp);
186 else
187 gtk_entry_set_text(mini, "");
188 filer_window->mini_cursor_base = -1; /* History */
189 break;
191 default:
192 g_warning("Bad minibuffer type\n");
193 return;
196 filer_window->mini_type = mini_type;
198 gtk_editable_set_position(GTK_EDITABLE(mini), pos);
200 gtk_widget_show_all(filer_window->minibuffer_area);
202 gtk_widget_grab_focus(filer_window->minibuffer);
205 void minibuffer_hide(FilerWindow *filer_window)
207 filer_window->mini_type = MINI_NONE;
209 gtk_widget_hide(filer_window->minibuffer_area);
211 gtk_widget_child_focus(filer_window->window, GTK_DIR_TAB_FORWARD);
213 if (filer_window->temp_show_hidden)
215 DirItem *item;
216 ViewIter iter;
218 view_get_cursor(filer_window->view, &iter);
219 item = iter.peek(&iter);
221 if (item == NULL || item->leafname[0] != '.')
222 display_update_hidden(filer_window);
223 filer_window->temp_show_hidden = FALSE;
227 /* Insert this leafname at the cursor (replacing the selection, if any).
228 * Must be in SHELL mode.
230 void minibuffer_add(FilerWindow *filer_window, const gchar *leafname)
232 guchar *esc;
233 GtkEditable *edit = GTK_EDITABLE(filer_window->minibuffer);
234 GtkEntry *entry = GTK_ENTRY(edit);
235 int pos;
237 g_return_if_fail(filer_window->mini_type == MINI_SHELL);
239 esc = shell_escape(leafname);
241 gtk_editable_delete_selection(edit);
242 pos = gtk_editable_get_position(edit);
244 if (pos > 0 && gtk_entry_get_text(entry)[pos - 1] != ' ')
245 gtk_editable_insert_text(edit, " ", 1, &pos);
247 gtk_editable_insert_text(edit, esc, strlen(esc), &pos);
248 gtk_editable_set_position(edit, pos);
250 g_free(esc);
254 /****************************************************************
255 * INTERNAL FUNCTIONS *
256 ****************************************************************/
258 static void show_help(FilerWindow *filer_window)
260 switch (filer_window->mini_type)
262 case MINI_PATH:
263 info_message(
264 _("Enter the name of a file and I'll display "
265 "it for you. Press Tab to fill in the longest "
266 "match. Escape to close the minibuffer."));
267 break;
268 case MINI_SHELL:
269 info_message(
270 _("Enter a shell command to execute. Click "
271 "on a file to add it to the buffer."));
272 break;
273 case MINI_SELECT_BY_NAME:
274 info_message(
275 _("Enter a file name pattern to select all matching files:\n\n"
276 "? means any character\n"
277 "* means zero or more characters\n"
278 "[aA] means 'a' or 'A'\n"
279 "[a-z] means any character from a to z (lowercase)\n"
280 "*.png means any name ending in '.png'"));
281 break;
282 case MINI_SELECT_IF:
283 show_condition_help(NULL);
284 break;
285 case MINI_FILTER:
286 info_message(
287 _("Enter a pattern to match for files to "
288 "be shown. An empty filter turns the "
289 "filter off."));
290 break;
291 default:
292 g_warning("Unknown minibuffer type!");
293 break;
298 /* PATH ENTRY */
300 static void path_return_pressed(FilerWindow *filer_window, GdkEventKey *event)
302 const gchar *path, *pattern;
303 int flags = OPEN_FROM_MINI | OPEN_SAME_WINDOW;
304 ViewIter iter;
305 DirItem *item;
307 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
308 pattern = g_basename(path);
310 view_get_cursor(filer_window->view, &iter);
312 item = iter.peek(&iter);
313 if (item == NULL || !matches(&iter, pattern))
315 gdk_beep();
316 return;
319 if ((event->state & GDK_SHIFT_MASK) != 0)
320 flags |= OPEN_SHIFT;
322 filer_openitem(filer_window, &iter, flags);
325 /* Use the cursor item to fill in the minibuffer.
326 * If there are multiple matches then fill in as much as possible and
327 * (possibly) beep.
329 static void complete(FilerWindow *filer_window)
331 GtkEntry *entry;
332 DirItem *item, *other;
333 int shortest_stem = -1;
334 int current_stem;
335 const gchar *text, *leaf;
336 ViewIter cursor, iter;
338 view_get_cursor(filer_window->view, &cursor);
339 item = cursor.peek(&cursor);
341 if (!item)
343 gdk_beep();
344 return;
347 entry = GTK_ENTRY(filer_window->minibuffer);
349 text = gtk_entry_get_text(entry);
350 leaf = strrchr(text, '/');
351 if (!leaf)
353 gdk_beep();
354 return;
357 leaf++;
358 if (!matches(&cursor, leaf))
360 gdk_beep();
361 return;
364 current_stem = strlen(leaf);
366 /* Find the longest other match of this name. It it's longer than
367 * the currently entered text then complete only up to that length.
369 view_get_iter(filer_window->view, &iter, 0);
370 while ((other = iter.next(&iter)))
372 int stem = 0;
374 if (iter.i == cursor.i) /* XXX */
375 continue;
377 while (other->leafname[stem] && item->leafname[stem])
379 if (other->leafname[stem] != item->leafname[stem])
380 break;
381 stem++;
384 /* stem is the index of the first difference */
385 if (stem >= current_stem &&
386 (shortest_stem == -1 || stem < shortest_stem))
387 shortest_stem = stem;
390 if (current_stem == shortest_stem)
392 /* We didn't add anything... */
393 if (o_filer_beep_fail.int_value)
394 gdk_beep();
396 else if (current_stem < shortest_stem)
398 guchar *extra;
399 gint tmp_pos;
401 extra = g_strndup(item->leafname + current_stem,
402 shortest_stem - current_stem);
404 tmp_pos = entry->text_length;
405 gtk_editable_insert_text(GTK_EDITABLE(entry), extra, -1,
406 &tmp_pos);
408 g_free(extra);
409 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
411 if (o_filer_beep_multi.int_value)
412 gdk_beep();
414 else
416 const guchar *new;
418 new = make_path(filer_window->sym_path, item->leafname);
420 if (item->base_type == TYPE_DIRECTORY &&
421 (item->flags & ITEM_FLAG_APPDIR) == 0)
422 new = make_path(new, "");
424 gtk_entry_set_text(entry, new);
425 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
429 static void path_changed(FilerWindow *filer_window)
431 GtkWidget *mini = filer_window->minibuffer;
432 const char *rawnew, *leaf;
433 char *path;
434 char *new = NULL;
435 gboolean error = FALSE;
437 rawnew = gtk_entry_get_text(GTK_ENTRY(mini));
438 if (!*rawnew)
440 /* Entry may be blank because we're in the middle of changing
441 * to something else...
443 entry_set_error(mini, FALSE);
444 return;
447 switch (rawnew[0])
449 case '/':
450 new=g_strdup(rawnew);
451 break;
453 case '~':
454 if (!rawnew[1] || rawnew[1]=='/')
456 new=g_strconcat(g_get_home_dir(), "/",
457 rawnew[1]? rawnew+2: "", "/",
458 NULL);
460 else
462 const char *sl;
463 gchar *username;
464 struct passwd *passwd;
467 /* Need to lookup user name */
468 for(sl=rawnew+2; *sl && *sl!='/'; sl++)
470 username=g_strndup(rawnew+1, sl-rawnew-1);
471 passwd=getpwnam(username);
472 g_free(username);
474 if(passwd)
476 new=g_strconcat(passwd->pw_dir, "/",
477 sl+1, "/",
478 NULL);
480 else
481 new=g_strdup(rawnew);
483 break;
485 default:
486 new=g_strdup(rawnew);
487 break;
491 leaf = g_basename(new);
492 if (leaf == new)
493 path = g_strdup("/");
494 else
495 path = g_path_get_dirname(new);
497 if (strcmp(path, filer_window->sym_path) != 0)
499 /* The new path is in a different directory */
500 struct stat info;
502 if (stat_with_timeout(path, &info) == 0 &&
503 S_ISDIR(info.st_mode))
505 filer_change_to(filer_window, path, leaf);
507 else
508 error = TRUE;
510 else
512 if (*leaf == '.')
514 filer_window->temp_show_hidden = TRUE;
515 display_update_hidden(filer_window);
518 if (find_exact_match(filer_window, leaf) == FALSE &&
519 find_next_match(filer_window, leaf, 0) == FALSE)
520 error = TRUE;
523 g_free(new);
524 g_free(path);
526 entry_set_error(mini, error);
529 /* Look for an exact match, and move the cursor to it if found.
530 * TRUE on success.
532 static gboolean find_exact_match(FilerWindow *filer_window,
533 const gchar *pattern)
535 DirItem *item;
536 ViewIter iter;
537 ViewIface *view = filer_window->view;
539 view_get_iter(view, &iter, 0);
541 while ((item = iter.next(&iter)))
543 if (strcmp(item->leafname, pattern) == 0)
545 view_cursor_to_iter(view, &iter);
546 return TRUE;
550 return FALSE;
553 /* Find the next item in the view that matches 'pattern'. Start from
554 * cursor_base and loop at either end. dir is 1 for a forward search,
555 * -1 for backwards. 0 means forwards, but may stay the same.
557 * Does not automatically update cursor_base.
559 * Returns TRUE if a match is found.
561 static gboolean find_next_match(FilerWindow *filer_window,
562 const char *pattern,
563 int dir)
565 ViewIface *view = filer_window->view;
566 ViewIter iter;
568 if (view_count_items(view) < 1)
569 return FALSE;
571 view_get_iter(view, &iter,
572 VIEW_ITER_FROM_BASE |
573 (dir >= 0 ? 0 : VIEW_ITER_BACKWARDS));
575 if (dir != 0)
576 iter.next(&iter); /* Don't look at the base itself */
578 while (iter.next(&iter))
580 if (matches(&iter, pattern))
582 view_cursor_to_iter(view, &iter);
583 return TRUE;
587 /* No matches (except possibly base itself) */
588 view_get_iter(view, &iter,
589 VIEW_ITER_FROM_BASE | VIEW_ITER_ONE_ONLY |
590 (dir >= 0 ? 0 : VIEW_ITER_BACKWARDS));
592 view_cursor_to_iter(view, &iter);
594 return FALSE;
597 static gboolean matches(ViewIter *iter, const char *pattern)
599 DirItem *item;
601 item = iter->peek(iter);
603 return strncasecmp(item->leafname, pattern, strlen(pattern)) == 0;
606 /* Find next match and set base for future matches. */
607 static void search_in_dir(FilerWindow *filer_window, int dir)
609 const char *path, *pattern;
610 ViewIter iter;
612 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
613 pattern = g_basename(path);
615 view_get_cursor(filer_window->view, &iter);
616 view_set_base(filer_window->view, &iter);
617 find_next_match(filer_window, pattern, dir);
618 view_get_cursor(filer_window->view, &iter);
619 view_set_base(filer_window->view, &iter);
622 /* SHELL COMMANDS */
624 static void add_to_history(const gchar *line)
626 guchar *last;
628 last = shell_history ? (guchar *) shell_history->data : NULL;
630 if (last && strcmp(last, line) == 0)
631 return; /* Duplicating last entry */
633 shell_history = g_list_prepend(shell_history, g_strdup(line));
636 static void shell_done(FilerWindow *filer_window)
638 if (filer_exists(filer_window))
639 filer_update_dir(filer_window, TRUE);
642 /* Given a list of matches, return the longest stem. g_free() the result.
643 * Special chars are escaped. If there is only a single (non-dir) match
644 * then a trailing space is added.
646 static guchar *best_match(FilerWindow *filer_window, glob_t *matches)
648 gchar *first = matches->gl_pathv[0];
649 int i;
650 int longest, path_len;
651 guchar *path, *tmp;
653 longest = strlen(first);
655 for (i = 1; i < matches->gl_pathc; i++)
657 int j;
658 guchar *m = matches->gl_pathv[i];
660 for (j = 0; j < longest; j++)
661 if (m[j] != first[j])
662 longest = j;
665 path_len = strlen(filer_window->sym_path);
666 if (strncmp(filer_window->sym_path, first, path_len) == 0 &&
667 first[path_len] == '/' && first[path_len + 1])
669 path = g_strndup(first + path_len + 1, longest - path_len - 1);
671 else
672 path = g_strndup(first, longest);
674 tmp = shell_escape(path);
675 g_free(path);
677 if (matches->gl_pathc == 1 && tmp[strlen(tmp) - 1] != '/')
679 path = g_strdup_printf("%s ", tmp);
680 g_free(tmp);
681 return path;
684 return tmp;
687 static void shell_tab(FilerWindow *filer_window)
689 const gchar *entry;
690 int i;
691 int pos;
692 GString *leaf;
693 glob_t matches;
694 int leaf_start;
696 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
697 pos = gtk_editable_get_position(GTK_EDITABLE(filer_window->minibuffer));
698 leaf = g_string_new(NULL);
700 for (i = 0; i < pos; i++)
702 guchar c = entry[i];
704 if (leaf->len == 0)
705 leaf_start = i;
707 if (c == ' ')
709 g_string_truncate(leaf, 0);
710 continue;
712 else if (c == '\\' && i + 1 < pos)
713 c = entry[++i];
714 else if (c == '"' || c == '\'')
716 for (++i; i < pos; i++)
718 guchar cc = entry[i];
720 if (cc == '\\' && i + 1 < pos)
721 cc = entry[++i];
722 else if (cc == c)
723 break;
724 g_string_append_c(leaf, cc);
726 continue;
729 g_string_append_c(leaf, c);
732 if (leaf->len == 0)
733 leaf_start = pos;
735 if (leaf->str[0] != '/' && leaf->str[0] != '~')
737 g_string_prepend_c(leaf, '/');
738 g_string_prepend(leaf, filer_window->sym_path);
741 g_string_append_c(leaf, '*');
743 if (glob(leaf->str,
744 #ifdef GLOB_TILDE
745 GLOB_TILDE |
746 #endif
747 GLOB_MARK, NULL, &matches) == 0)
749 if (matches.gl_pathc > 0)
751 guchar *best;
752 GtkEditable *edit =
753 GTK_EDITABLE(filer_window->minibuffer);
755 best = best_match(filer_window, &matches);
757 gtk_editable_delete_text(edit, leaf_start, pos);
758 gtk_editable_insert_text(edit, best, strlen(best),
759 &leaf_start);
760 gtk_editable_set_position(edit, leaf_start);
762 g_free(best);
764 if (matches.gl_pathc != 1)
765 gdk_beep();
767 globfree(&matches);
770 g_string_free(leaf, TRUE);
773 static void run_child(gpointer unused)
775 /* Ensure output is noticed - send stdout to stderr */
776 dup2(STDERR_FILENO, STDOUT_FILENO);
777 close_on_exec(STDOUT_FILENO, FALSE);
780 /* Either execute the command or make it the default run action */
781 static void shell_return_pressed(FilerWindow *filer_window)
783 GPtrArray *argv;
784 const gchar *entry;
785 GError *error = NULL;
786 pid_t child;
787 DirItem *item;
788 ViewIter iter;
790 entry = mini_contents(filer_window);
792 if (!entry)
793 goto out;
795 add_to_history(entry);
797 argv = g_ptr_array_new();
798 g_ptr_array_add(argv, "sh");
799 g_ptr_array_add(argv, "-c");
800 g_ptr_array_add(argv, (gchar *) entry);
801 g_ptr_array_add(argv, "sh");
803 view_get_iter(filer_window->view, &iter, 0);
804 while ((item = iter.next(&iter)))
806 if (view_get_selected(filer_window->view, &iter))
807 g_ptr_array_add(argv, item->leafname);
810 g_ptr_array_add(argv, NULL);
812 if (!g_spawn_async_with_pipes(filer_window->sym_path,
813 (gchar **) argv->pdata, NULL,
814 G_SPAWN_DO_NOT_REAP_CHILD |
815 G_SPAWN_SEARCH_PATH,
816 run_child, NULL, /* Child setup fn */
817 &child, /* Child PID */
818 NULL, NULL, NULL, /* Standard pipes */
819 &error))
821 delayed_error("%s", error ? error->message : "(null)");
822 g_error_free(error);
824 else
825 on_child_death(child, (CallbackFn) shell_done, filer_window);
827 g_ptr_array_free(argv, TRUE);
829 out:
830 minibuffer_hide(filer_window);
833 /* Move through the shell history */
834 static void shell_recall(FilerWindow *filer_window, int dir)
836 guchar *command;
837 int pos = filer_window->mini_cursor_base;
839 pos += dir;
840 if (pos >= 0)
842 command = g_list_nth_data(shell_history, pos);
843 if (!command)
844 return;
846 else
847 command = "";
849 if (pos < -1)
850 pos = -1;
851 filer_window->mini_cursor_base = pos;
853 gtk_entry_set_text(GTK_ENTRY(filer_window->minibuffer), command);
856 /* SELECT IF */
858 typedef struct {
859 FindInfo info;
860 FilerWindow *filer_window;
861 FindCondition *cond;
862 } SelectData;
864 static gboolean select_if_test(ViewIter *iter, gpointer user_data)
866 DirItem *item;
867 SelectData *data = user_data;
869 item = iter->peek(iter);
870 g_return_val_if_fail(item != NULL, FALSE);
872 data->info.leaf = item->leafname;
873 data->info.fullpath = make_path(data->filer_window->sym_path,
874 data->info.leaf);
876 return mc_lstat(data->info.fullpath, &data->info.stats) == 0 &&
877 find_test_condition(data->cond, &data->info);
880 static void select_return_pressed(FilerWindow *filer_window, guint etime)
882 const gchar *entry;
883 SelectData data;
885 entry = mini_contents(filer_window);
887 if (!entry)
888 goto out;
890 add_to_history(entry);
892 data.cond = find_compile(entry);
893 if (!data.cond)
895 delayed_error(_("Invalid Find condition"));
896 return;
899 data.info.now = time(NULL);
900 data.info.prune = FALSE; /* (don't care) */
901 data.filer_window = filer_window;
903 view_select_if(filer_window->view, select_if_test, &data);
905 find_condition_free(data.cond);
906 out:
907 minibuffer_hide(filer_window);
910 static void filter_return_pressed(FilerWindow *filer_window, guint etime)
912 const gchar *entry;
914 entry = mini_contents(filer_window);
916 if (entry && *entry && strcmp(entry, "*")!=0) {
917 display_set_filter(filer_window, FILER_SHOW_GLOB,
918 entry);
919 } else {
920 display_set_filter(filer_window, FILER_SHOW_ALL, NULL);
922 minibuffer_hide(filer_window);
926 /* EVENT HANDLERS */
928 static gint key_press_event(GtkWidget *widget,
929 GdkEventKey *event,
930 FilerWindow *filer_window)
932 if (event->keyval == GDK_Escape)
934 if (filer_window->mini_type == MINI_SHELL)
936 const gchar *line;
938 line = mini_contents(filer_window);
939 if (line)
940 add_to_history(line);
943 minibuffer_hide(filer_window);
944 return TRUE;
947 switch (filer_window->mini_type)
949 case MINI_PATH:
950 switch (event->keyval)
952 case GDK_Up:
953 search_in_dir(filer_window, -1);
954 break;
955 case GDK_Down:
956 search_in_dir(filer_window, 1);
957 break;
958 case GDK_Return:
959 case GDK_KP_Enter:
960 path_return_pressed(filer_window,
961 event);
962 break;
963 case GDK_Tab:
964 complete(filer_window);
965 break;
966 default:
967 return FALSE;
969 break;
971 case MINI_SHELL:
972 switch (event->keyval)
974 case GDK_Up:
975 shell_recall(filer_window, 1);
976 break;
977 case GDK_Down:
978 shell_recall(filer_window, -1);
979 break;
980 case GDK_Tab:
981 shell_tab(filer_window);
982 break;
983 case GDK_Return:
984 case GDK_KP_Enter:
985 shell_return_pressed(filer_window);
986 break;
987 default:
988 return FALSE;
990 break;
991 case MINI_SELECT_IF:
992 switch (event->keyval)
994 case GDK_Up:
995 shell_recall(filer_window, 1);
996 break;
997 case GDK_Down:
998 shell_recall(filer_window, -1);
999 break;
1000 case GDK_Tab:
1001 break;
1002 case GDK_Return:
1003 case GDK_KP_Enter:
1004 select_return_pressed(filer_window,
1005 event->time);
1006 break;
1007 default:
1008 return FALSE;
1010 break;
1011 case MINI_SELECT_BY_NAME:
1012 switch (event->keyval)
1014 case GDK_Up:
1015 filer_next_selected(filer_window, -1);
1016 break;
1017 case GDK_Down:
1018 filer_next_selected(filer_window, 1);
1019 break;
1020 case GDK_Tab:
1021 break;
1022 case GDK_Return:
1023 case GDK_KP_Enter:
1024 minibuffer_hide(filer_window);
1025 break;
1026 default:
1027 return FALSE;
1029 break;
1031 case MINI_FILTER:
1032 switch (event->keyval)
1034 case GDK_Return:
1035 case GDK_KP_Enter:
1036 filter_return_pressed(filer_window,
1037 event->time);
1038 break;
1039 default:
1040 return FALSE;
1042 break;
1043 default:
1044 break;
1047 return TRUE;
1050 static gboolean select_if_glob(ViewIter *iter, gpointer data)
1052 DirItem *item;
1053 const char *pattern = (char *) data;
1055 item = iter->peek(iter);
1056 g_return_val_if_fail(item != NULL, FALSE);
1058 return fnmatch(pattern, item->leafname, 0) == 0;
1061 static void changed(GtkEditable *mini, FilerWindow *filer_window)
1063 switch (filer_window->mini_type)
1065 case MINI_PATH:
1066 path_changed(filer_window);
1067 return;
1068 case MINI_SELECT_IF:
1069 set_find_string_colour(GTK_WIDGET(mini),
1070 gtk_entry_get_text(
1071 GTK_ENTRY(filer_window->minibuffer)));
1072 return;
1073 case MINI_SELECT_BY_NAME:
1074 view_select_if(filer_window->view,
1075 select_if_glob,
1076 (gpointer) gtk_entry_get_text(
1077 GTK_ENTRY(filer_window->minibuffer)));
1078 return;
1079 default:
1080 break;
1084 /* Returns a string (which must NOT be freed), or NULL if the buffer
1085 * is blank (whitespace only).
1087 static const gchar *mini_contents(FilerWindow *filer_window)
1089 const gchar *entry, *c;
1091 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
1093 for (c = entry; *c; c++)
1094 if (!g_ascii_isspace(*c))
1095 return entry;
1097 return NULL;
1100 /* This is a really ugly hack to get around Gtk+-2.0's broken auto-select
1101 * behaviour.
1103 static gboolean grab_focus(GtkWidget *minibuffer)
1105 GtkWidgetClass *class;
1107 class = GTK_WIDGET_CLASS(gtk_type_class(GTK_TYPE_WIDGET));
1109 class->grab_focus(minibuffer);
1111 g_signal_stop_emission(minibuffer,
1112 g_signal_lookup("grab_focus", G_OBJECT_TYPE(minibuffer)), 0);
1115 return 1;