r3359: Added 'Select by Name'.
[rox-filer.git] / ROX-Filer / src / minibuffer.c
blobc681a6b3af14d248bb9908a4fb1fcc6b2510bd54
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, 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 <gtk/gtk.h>
34 #include <gdk/gdkkeysyms.h>
36 #include "global.h"
38 #include "options.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"
49 #include "view_iface.h"
51 static GList *shell_history = NULL;
53 /* Static prototypes */
54 static gint key_press_event(GtkWidget *widget,
55 GdkEventKey *event,
56 FilerWindow *filer_window);
57 static void changed(GtkEditable *mini, FilerWindow *filer_window);
58 static gboolean find_next_match(FilerWindow *filer_window,
59 const char *pattern,
60 int dir);
61 static gboolean find_exact_match(FilerWindow *filer_window,
62 const gchar *pattern);
63 static gboolean matches(ViewIter *iter, const char *pattern);
64 static void search_in_dir(FilerWindow *filer_window, int dir);
65 static const gchar *mini_contents(FilerWindow *filer_window);
66 static void show_help(FilerWindow *filer_window);
67 static gboolean grab_focus(GtkWidget *minibuffer);
68 static gboolean select_if_glob(ViewIter *iter, gpointer data);
70 /****************************************************************
71 * EXTERNAL INTERFACE *
72 ****************************************************************/
74 static Option o_filer_beep_fail, o_filer_beep_multi;
76 void minibuffer_init(void)
78 option_add_int(&o_filer_beep_fail, "filer_beep_fail", 1);
79 option_add_int(&o_filer_beep_multi, "filer_beep_multi", 1);
82 /* Creates the minibuffer widgets, setting the appropriate fields
83 * in filer_window.
85 void create_minibuffer(FilerWindow *filer_window)
87 GtkWidget *hbox, *label, *mini;
89 hbox = gtk_hbox_new(FALSE, 0);
91 gtk_box_pack_start(GTK_BOX(hbox),
92 new_help_button((HelpFunc) show_help, filer_window),
93 FALSE, TRUE, 0);
95 label = gtk_label_new("");
96 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 2);
98 mini = gtk_entry_new();
99 gtk_box_pack_start(GTK_BOX(hbox), mini, TRUE, TRUE, 0);
100 gtk_widget_set_name(mini, "fixed-style");
101 g_signal_connect(mini, "key_press_event",
102 G_CALLBACK(key_press_event), filer_window);
103 g_signal_connect(mini, "changed", G_CALLBACK(changed), filer_window);
105 /* Grabbing focus musn't select the text... */
106 g_signal_connect_swapped(mini, "grab-focus",
107 G_CALLBACK(grab_focus), mini);
109 filer_window->minibuffer = mini;
110 filer_window->minibuffer_label = label;
111 filer_window->minibuffer_area = hbox;
114 void minibuffer_show(FilerWindow *filer_window, MiniType mini_type)
116 GtkEntry *mini;
117 int pos = -1;
118 ViewIter cursor;
120 g_return_if_fail(filer_window != NULL);
121 g_return_if_fail(filer_window->minibuffer != NULL);
123 mini = GTK_ENTRY(filer_window->minibuffer);
124 entry_set_error(filer_window->minibuffer, FALSE);
126 filer_window->mini_type = MINI_NONE;
127 gtk_label_set_text(GTK_LABEL(filer_window->minibuffer_label),
128 mini_type == MINI_PATH ? _("Goto:") :
129 mini_type == MINI_SHELL ? _("Shell:") :
130 mini_type == MINI_SELECT_IF ? _("Select If:") :
131 mini_type == MINI_SELECT_BY_NAME ? _("Select Named:") :
132 mini_type == MINI_FILTER ? _("Pattern:") :
133 "?");
135 switch (mini_type)
137 case MINI_PATH:
138 view_show_cursor(filer_window->view);
139 view_get_cursor(filer_window->view, &cursor);
140 view_set_base(filer_window->view, &cursor);
142 gtk_entry_set_text(mini,
143 make_path(filer_window->sym_path, ""));
144 if (filer_window->temp_show_hidden)
146 filer_window->temp_show_hidden = FALSE;
147 display_update_hidden(filer_window);
149 break;
150 case MINI_SELECT_IF:
151 gtk_entry_set_text(mini, "");
152 filer_window->mini_cursor_base = -1; /* History */
153 break;
154 case MINI_SELECT_BY_NAME:
155 gtk_entry_set_text(mini, "*.");
156 filer_window->mini_cursor_base = -1; /* History */
157 view_select_if(filer_window->view, select_if_glob, "*.");
158 break;
159 case MINI_FILTER:
160 if(filer_window->filter!=FILER_SHOW_GLOB ||
161 !filer_window->filter_string)
162 gtk_entry_set_text(mini, "");
163 else
164 gtk_entry_set_text(mini,
165 filer_window->filter_string);
166 break;
167 case MINI_SHELL:
169 DirItem *item;
170 view_get_cursor(filer_window->view, &cursor);
171 item = cursor.peek(&cursor);
172 pos = 0;
173 if (view_count_selected(filer_window->view) > 0)
174 gtk_entry_set_text(mini, " \"$@\"");
175 else if (item)
177 guchar *tmp;
179 tmp = g_strconcat(" ", item->leafname, NULL);
180 gtk_entry_set_text(mini, tmp);
181 g_free(tmp);
183 else
184 gtk_entry_set_text(mini, "");
185 filer_window->mini_cursor_base = -1; /* History */
186 break;
188 default:
189 g_warning("Bad minibuffer type\n");
190 return;
193 filer_window->mini_type = mini_type;
195 gtk_editable_set_position(GTK_EDITABLE(mini), pos);
197 gtk_widget_show_all(filer_window->minibuffer_area);
199 gtk_widget_grab_focus(filer_window->minibuffer);
202 void minibuffer_hide(FilerWindow *filer_window)
204 filer_window->mini_type = MINI_NONE;
206 gtk_widget_hide(filer_window->minibuffer_area);
208 gtk_widget_child_focus(filer_window->window, GTK_DIR_TAB_FORWARD);
210 if (filer_window->temp_show_hidden)
212 DirItem *item;
213 ViewIter iter;
215 view_get_cursor(filer_window->view, &iter);
216 item = iter.peek(&iter);
218 if (item == NULL || item->leafname[0] != '.')
219 display_update_hidden(filer_window);
220 filer_window->temp_show_hidden = FALSE;
224 /* Insert this leafname at the cursor (replacing the selection, if any).
225 * Must be in SHELL mode.
227 void minibuffer_add(FilerWindow *filer_window, const gchar *leafname)
229 guchar *esc;
230 GtkEditable *edit = GTK_EDITABLE(filer_window->minibuffer);
231 GtkEntry *entry = GTK_ENTRY(edit);
232 int pos;
234 g_return_if_fail(filer_window->mini_type == MINI_SHELL);
236 esc = shell_escape(leafname);
238 gtk_editable_delete_selection(edit);
239 pos = gtk_editable_get_position(edit);
241 if (pos > 0 && gtk_entry_get_text(entry)[pos - 1] != ' ')
242 gtk_editable_insert_text(edit, " ", 1, &pos);
244 gtk_editable_insert_text(edit, esc, strlen(esc), &pos);
245 gtk_editable_set_position(edit, pos);
247 g_free(esc);
251 /****************************************************************
252 * INTERNAL FUNCTIONS *
253 ****************************************************************/
255 static void show_help(FilerWindow *filer_window)
257 switch (filer_window->mini_type)
259 case MINI_PATH:
260 info_message(
261 _("Enter the name of a file and I'll display "
262 "it for you. Press Tab to fill in the longest "
263 "match. Escape to close the minibuffer."));
264 break;
265 case MINI_SHELL:
266 info_message(
267 _("Enter a shell command to execute. Click "
268 "on a file to add it to the buffer."));
269 break;
270 case MINI_SELECT_BY_NAME:
271 info_message(
272 _("Enter a file name pattern to select all matching files:\n\n"
273 "? means any character\n"
274 "* means zero or more characters\n"
275 "[aA] means 'a' or 'A'\n"
276 "[a-z] means any character from a to z (lowercase)\n"
277 "*.png means any name ending in '.png')"));
278 break;
279 case MINI_SELECT_IF:
280 show_condition_help(NULL);
281 break;
282 case MINI_FILTER:
283 info_message(
284 _("Enter a pattern to match for files to "
285 "be shown. An empty filter turns the "
286 "filter off."));
287 break;
288 default:
289 g_warning("Unknown minibuffer type!");
290 break;
295 /* PATH ENTRY */
297 static void path_return_pressed(FilerWindow *filer_window, GdkEventKey *event)
299 const gchar *path, *pattern;
300 int flags = OPEN_FROM_MINI | OPEN_SAME_WINDOW;
301 ViewIter iter;
302 DirItem *item;
304 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
305 pattern = g_basename(path);
307 view_get_cursor(filer_window->view, &iter);
309 item = iter.peek(&iter);
310 if (item == NULL || !matches(&iter, pattern))
312 gdk_beep();
313 return;
316 if ((event->state & GDK_SHIFT_MASK) != 0)
317 flags |= OPEN_SHIFT;
319 filer_openitem(filer_window, &iter, flags);
322 /* Use the cursor item to fill in the minibuffer.
323 * If there are multiple matches then fill in as much as possible and
324 * (possibly) beep.
326 static void complete(FilerWindow *filer_window)
328 GtkEntry *entry;
329 DirItem *item, *other;
330 int shortest_stem = -1;
331 int current_stem;
332 const gchar *text, *leaf;
333 ViewIter cursor, iter;
335 view_get_cursor(filer_window->view, &cursor);
336 item = cursor.peek(&cursor);
338 if (!item)
340 gdk_beep();
341 return;
344 entry = GTK_ENTRY(filer_window->minibuffer);
346 text = gtk_entry_get_text(entry);
347 leaf = strrchr(text, '/');
348 if (!leaf)
350 gdk_beep();
351 return;
354 leaf++;
355 if (!matches(&cursor, leaf))
357 gdk_beep();
358 return;
361 current_stem = strlen(leaf);
363 /* Find the longest other match of this name. It it's longer than
364 * the currently entered text then complete only up to that length.
366 view_get_iter(filer_window->view, &iter, 0);
367 while ((other = iter.next(&iter)))
369 int stem = 0;
371 if (iter.i == cursor.i) /* XXX */
372 continue;
374 while (other->leafname[stem] && item->leafname[stem])
376 if (other->leafname[stem] != item->leafname[stem])
377 break;
378 stem++;
381 /* stem is the index of the first difference */
382 if (stem >= current_stem &&
383 (shortest_stem == -1 || stem < shortest_stem))
384 shortest_stem = stem;
387 if (current_stem == shortest_stem)
389 /* We didn't add anything... */
390 if (o_filer_beep_fail.int_value)
391 gdk_beep();
393 else if (current_stem < shortest_stem)
395 guchar *extra;
396 gint tmp_pos;
398 extra = g_strndup(item->leafname + current_stem,
399 shortest_stem - current_stem);
401 tmp_pos = entry->text_length;
402 gtk_editable_insert_text(GTK_EDITABLE(entry), extra, -1,
403 &tmp_pos);
405 g_free(extra);
406 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
408 if (o_filer_beep_multi.int_value)
409 gdk_beep();
411 else
413 const guchar *new;
415 new = make_path(filer_window->sym_path, item->leafname);
417 if (item->base_type == TYPE_DIRECTORY &&
418 (item->flags & ITEM_FLAG_APPDIR) == 0)
419 new = make_path(new, "");
421 gtk_entry_set_text(entry, new);
422 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
426 static void path_changed(FilerWindow *filer_window)
428 GtkWidget *mini = filer_window->minibuffer;
429 const char *new, *leaf;
430 char *path;
431 gboolean error = FALSE;
433 new = gtk_entry_get_text(GTK_ENTRY(mini));
435 if (!*new)
437 /* Entry may be blank because we're in the middle of changing
438 * to something else...
440 entry_set_error(mini, FALSE);
441 return;
444 leaf = g_basename(new);
445 if (leaf == new)
446 path = g_strdup("/");
447 else
448 path = g_path_get_dirname(new);
450 if (strcmp(path, filer_window->sym_path) != 0)
452 /* The new path is in a different directory */
453 struct stat info;
455 if (stat_with_timeout(path, &info) == 0 &&
456 S_ISDIR(info.st_mode))
458 filer_change_to(filer_window, path, leaf);
460 else
461 error = TRUE;
463 else
465 if (*leaf == '.')
467 filer_window->temp_show_hidden = TRUE;
468 display_update_hidden(filer_window);
471 if (find_exact_match(filer_window, leaf) == FALSE &&
472 find_next_match(filer_window, leaf, 0) == FALSE)
473 error = TRUE;
476 g_free(path);
478 entry_set_error(mini, error);
481 /* Look for an exact match, and move the cursor to it if found.
482 * TRUE on success.
484 static gboolean find_exact_match(FilerWindow *filer_window,
485 const gchar *pattern)
487 DirItem *item;
488 ViewIter iter;
489 ViewIface *view = filer_window->view;
491 view_get_iter(view, &iter, 0);
493 while ((item = iter.next(&iter)))
495 if (strcmp(item->leafname, pattern) == 0)
497 view_cursor_to_iter(view, &iter);
498 return TRUE;
502 return FALSE;
505 /* Find the next item in the view that matches 'pattern'. Start from
506 * cursor_base and loop at either end. dir is 1 for a forward search,
507 * -1 for backwards. 0 means forwards, but may stay the same.
509 * Does not automatically update cursor_base.
511 * Returns TRUE if a match is found.
513 static gboolean find_next_match(FilerWindow *filer_window,
514 const char *pattern,
515 int dir)
517 ViewIface *view = filer_window->view;
518 ViewIter iter;
520 if (view_count_items(view) < 1)
521 return FALSE;
523 view_get_iter(view, &iter,
524 VIEW_ITER_FROM_BASE |
525 (dir >= 0 ? 0 : VIEW_ITER_BACKWARDS));
527 if (dir != 0)
528 iter.next(&iter); /* Don't look at the base itself */
530 while (iter.next(&iter))
532 if (matches(&iter, pattern))
534 view_cursor_to_iter(view, &iter);
535 return TRUE;
539 /* No matches (except possibly base itself) */
540 view_get_iter(view, &iter,
541 VIEW_ITER_FROM_BASE | VIEW_ITER_ONE_ONLY |
542 (dir >= 0 ? 0 : VIEW_ITER_BACKWARDS));
544 view_cursor_to_iter(view, &iter);
546 return FALSE;
549 static gboolean matches(ViewIter *iter, const char *pattern)
551 DirItem *item;
553 item = iter->peek(iter);
555 return strncmp(item->leafname, pattern, strlen(pattern)) == 0;
558 /* Find next match and set base for future matches. */
559 static void search_in_dir(FilerWindow *filer_window, int dir)
561 const char *path, *pattern;
562 ViewIter iter;
564 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
565 pattern = g_basename(path);
567 view_get_cursor(filer_window->view, &iter);
568 view_set_base(filer_window->view, &iter);
569 find_next_match(filer_window, pattern, dir);
570 view_get_cursor(filer_window->view, &iter);
571 view_set_base(filer_window->view, &iter);
574 /* SHELL COMMANDS */
576 static void add_to_history(const gchar *line)
578 guchar *last;
580 last = shell_history ? (guchar *) shell_history->data : NULL;
582 if (last && strcmp(last, line) == 0)
583 return; /* Duplicating last entry */
585 shell_history = g_list_prepend(shell_history, g_strdup(line));
588 static void shell_done(FilerWindow *filer_window)
590 if (filer_exists(filer_window))
591 filer_update_dir(filer_window, TRUE);
594 /* Given a list of matches, return the longest stem. g_free() the result.
595 * Special chars are escaped. If there is only a single (non-dir) match
596 * then a trailing space is added.
598 static guchar *best_match(FilerWindow *filer_window, glob_t *matches)
600 gchar *first = matches->gl_pathv[0];
601 int i;
602 int longest, path_len;
603 guchar *path, *tmp;
605 longest = strlen(first);
607 for (i = 1; i < matches->gl_pathc; i++)
609 int j;
610 guchar *m = matches->gl_pathv[i];
612 for (j = 0; j < longest; j++)
613 if (m[j] != first[j])
614 longest = j;
617 path_len = strlen(filer_window->sym_path);
618 if (strncmp(filer_window->sym_path, first, path_len) == 0 &&
619 first[path_len] == '/' && first[path_len + 1])
621 path = g_strndup(first + path_len + 1, longest - path_len - 1);
623 else
624 path = g_strndup(first, longest);
626 tmp = shell_escape(path);
627 g_free(path);
629 if (matches->gl_pathc == 1 && tmp[strlen(tmp) - 1] != '/')
631 path = g_strdup_printf("%s ", tmp);
632 g_free(tmp);
633 return path;
636 return tmp;
639 static void shell_tab(FilerWindow *filer_window)
641 const gchar *entry;
642 int i;
643 int pos;
644 GString *leaf;
645 glob_t matches;
646 int leaf_start;
648 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
649 pos = gtk_editable_get_position(GTK_EDITABLE(filer_window->minibuffer));
650 leaf = g_string_new(NULL);
652 for (i = 0; i < pos; i++)
654 guchar c = entry[i];
656 if (leaf->len == 0)
657 leaf_start = i;
659 if (c == ' ')
661 g_string_truncate(leaf, 0);
662 continue;
664 else if (c == '\\' && i + 1 < pos)
665 c = entry[++i];
666 else if (c == '"' || c == '\'')
668 for (++i; i < pos; i++)
670 guchar cc = entry[i];
672 if (cc == '\\' && i + 1 < pos)
673 cc = entry[++i];
674 else if (cc == c)
675 break;
676 g_string_append_c(leaf, cc);
678 continue;
681 g_string_append_c(leaf, c);
684 if (leaf->len == 0)
685 leaf_start = pos;
687 if (leaf->str[0] != '/' && leaf->str[0] != '~')
689 g_string_prepend_c(leaf, '/');
690 g_string_prepend(leaf, filer_window->sym_path);
693 g_string_append_c(leaf, '*');
695 if (glob(leaf->str,
696 #ifdef GLOB_TILDE
697 GLOB_TILDE |
698 #endif
699 GLOB_MARK, NULL, &matches) == 0)
701 if (matches.gl_pathc > 0)
703 guchar *best;
704 GtkEditable *edit =
705 GTK_EDITABLE(filer_window->minibuffer);
707 best = best_match(filer_window, &matches);
709 gtk_editable_delete_text(edit, leaf_start, pos);
710 gtk_editable_insert_text(edit, best, strlen(best),
711 &leaf_start);
712 gtk_editable_set_position(edit, leaf_start);
714 g_free(best);
716 if (matches.gl_pathc != 1)
717 gdk_beep();
719 globfree(&matches);
722 g_string_free(leaf, TRUE);
725 static void run_child(gpointer unused)
727 /* Ensure output is noticed - send stdout to stderr */
728 dup2(STDERR_FILENO, STDOUT_FILENO);
729 close_on_exec(STDOUT_FILENO, FALSE);
732 /* Either execute the command or make it the default run action */
733 static void shell_return_pressed(FilerWindow *filer_window)
735 GPtrArray *argv;
736 const gchar *entry;
737 GError *error = NULL;
738 pid_t child;
739 DirItem *item;
740 ViewIter iter;
742 entry = mini_contents(filer_window);
744 if (!entry)
745 goto out;
747 add_to_history(entry);
749 argv = g_ptr_array_new();
750 g_ptr_array_add(argv, "sh");
751 g_ptr_array_add(argv, "-c");
752 g_ptr_array_add(argv, (gchar *) entry);
753 g_ptr_array_add(argv, "sh");
755 view_get_iter(filer_window->view, &iter, 0);
756 while ((item = iter.next(&iter)))
758 if (view_get_selected(filer_window->view, &iter))
759 g_ptr_array_add(argv, item->leafname);
762 g_ptr_array_add(argv, NULL);
764 if (!g_spawn_async_with_pipes(filer_window->sym_path,
765 (gchar **) argv->pdata, NULL,
766 G_SPAWN_DO_NOT_REAP_CHILD |
767 G_SPAWN_SEARCH_PATH,
768 run_child, NULL, /* Child setup fn */
769 &child, /* Child PID */
770 NULL, NULL, NULL, /* Standard pipes */
771 &error))
773 delayed_error("%s", error ? error->message : "(null)");
774 g_error_free(error);
776 else
777 on_child_death(child, (CallbackFn) shell_done, filer_window);
779 g_ptr_array_free(argv, TRUE);
781 out:
782 minibuffer_hide(filer_window);
785 /* Move through the shell history */
786 static void shell_recall(FilerWindow *filer_window, int dir)
788 guchar *command;
789 int pos = filer_window->mini_cursor_base;
791 pos += dir;
792 if (pos >= 0)
794 command = g_list_nth_data(shell_history, pos);
795 if (!command)
796 return;
798 else
799 command = "";
801 if (pos < -1)
802 pos = -1;
803 filer_window->mini_cursor_base = pos;
805 gtk_entry_set_text(GTK_ENTRY(filer_window->minibuffer), command);
808 /* SELECT IF */
810 typedef struct {
811 FindInfo info;
812 FilerWindow *filer_window;
813 FindCondition *cond;
814 } SelectData;
816 static gboolean select_if_test(ViewIter *iter, gpointer user_data)
818 DirItem *item;
819 SelectData *data = user_data;
821 item = iter->peek(iter);
822 g_return_val_if_fail(item != NULL, FALSE);
824 data->info.leaf = item->leafname;
825 data->info.fullpath = make_path(data->filer_window->sym_path,
826 data->info.leaf);
828 return mc_lstat(data->info.fullpath, &data->info.stats) == 0 &&
829 find_test_condition(data->cond, &data->info);
832 static void select_return_pressed(FilerWindow *filer_window, guint etime)
834 const gchar *entry;
835 SelectData data;
837 entry = mini_contents(filer_window);
839 if (!entry)
840 goto out;
842 add_to_history(entry);
844 data.cond = find_compile(entry);
845 if (!data.cond)
847 delayed_error(_("Invalid Find condition"));
848 return;
851 data.info.now = time(NULL);
852 data.info.prune = FALSE; /* (don't care) */
853 data.filer_window = filer_window;
855 view_select_if(filer_window->view, select_if_test, &data);
857 find_condition_free(data.cond);
858 out:
859 minibuffer_hide(filer_window);
862 static void filter_return_pressed(FilerWindow *filer_window, guint etime)
864 const gchar *entry;
866 entry = mini_contents(filer_window);
868 if (entry && *entry) {
869 display_set_filter(filer_window, FILER_SHOW_GLOB,
870 entry);
871 } else {
872 display_set_filter(filer_window, FILER_SHOW_ALL, NULL);
874 minibuffer_hide(filer_window);
878 /* EVENT HANDLERS */
880 static gint key_press_event(GtkWidget *widget,
881 GdkEventKey *event,
882 FilerWindow *filer_window)
884 if (event->keyval == GDK_Escape)
886 if (filer_window->mini_type == MINI_SHELL)
888 const gchar *line;
890 line = mini_contents(filer_window);
891 if (line)
892 add_to_history(line);
895 minibuffer_hide(filer_window);
896 return TRUE;
899 switch (filer_window->mini_type)
901 case MINI_PATH:
902 switch (event->keyval)
904 case GDK_Up:
905 search_in_dir(filer_window, -1);
906 break;
907 case GDK_Down:
908 search_in_dir(filer_window, 1);
909 break;
910 case GDK_Return:
911 case GDK_KP_Enter:
912 path_return_pressed(filer_window,
913 event);
914 break;
915 case GDK_Tab:
916 complete(filer_window);
917 break;
918 default:
919 return FALSE;
921 break;
923 case MINI_SHELL:
924 switch (event->keyval)
926 case GDK_Up:
927 shell_recall(filer_window, 1);
928 break;
929 case GDK_Down:
930 shell_recall(filer_window, -1);
931 break;
932 case GDK_Tab:
933 shell_tab(filer_window);
934 break;
935 case GDK_Return:
936 case GDK_KP_Enter:
937 shell_return_pressed(filer_window);
938 break;
939 default:
940 return FALSE;
942 break;
943 case MINI_SELECT_IF:
944 switch (event->keyval)
946 case GDK_Up:
947 shell_recall(filer_window, 1);
948 break;
949 case GDK_Down:
950 shell_recall(filer_window, -1);
951 break;
952 case GDK_Tab:
953 break;
954 case GDK_Return:
955 case GDK_KP_Enter:
956 select_return_pressed(filer_window,
957 event->time);
958 break;
959 default:
960 return FALSE;
962 break;
963 case MINI_SELECT_BY_NAME:
964 switch (event->keyval)
966 case GDK_Up:
967 filer_next_selected(filer_window, -1);
968 break;
969 case GDK_Down:
970 filer_next_selected(filer_window, 1);
971 break;
972 case GDK_Tab:
973 break;
974 case GDK_Return:
975 case GDK_KP_Enter:
976 minibuffer_hide(filer_window);
977 break;
978 default:
979 return FALSE;
981 break;
983 case MINI_FILTER:
984 switch (event->keyval)
986 case GDK_Return:
987 case GDK_KP_Enter:
988 filter_return_pressed(filer_window,
989 event->time);
990 break;
991 default:
992 return FALSE;
994 break;
995 default:
996 break;
999 return TRUE;
1002 static gboolean select_if_glob(ViewIter *iter, gpointer data)
1004 DirItem *item;
1005 const char *pattern = (char *) data;
1007 item = iter->peek(iter);
1008 g_return_val_if_fail(item != NULL, FALSE);
1010 return fnmatch(pattern, item->leafname, 0) == 0;
1013 static void changed(GtkEditable *mini, FilerWindow *filer_window)
1015 switch (filer_window->mini_type)
1017 case MINI_PATH:
1018 path_changed(filer_window);
1019 return;
1020 case MINI_SELECT_IF:
1021 set_find_string_colour(GTK_WIDGET(mini),
1022 gtk_entry_get_text(
1023 GTK_ENTRY(filer_window->minibuffer)));
1024 return;
1025 case MINI_SELECT_BY_NAME:
1026 view_select_if(filer_window->view,
1027 select_if_glob,
1028 (gpointer) gtk_entry_get_text(
1029 GTK_ENTRY(filer_window->minibuffer)));
1030 return;
1031 default:
1032 break;
1036 /* Returns a string (which must NOT be freed), or NULL if the buffer
1037 * is blank (whitespace only).
1039 static const gchar *mini_contents(FilerWindow *filer_window)
1041 const gchar *entry, *c;
1043 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
1045 for (c = entry; *c; c++)
1046 if (!g_ascii_isspace(*c))
1047 return entry;
1049 return NULL;
1052 /* This is a really ugly hack to get around Gtk+-2.0's broken auto-select
1053 * behaviour.
1055 static gboolean grab_focus(GtkWidget *minibuffer)
1057 GtkWidgetClass *class;
1059 class = GTK_WIDGET_CLASS(gtk_type_class(GTK_TYPE_WIDGET));
1061 class->grab_focus(minibuffer);
1063 g_signal_stop_emission(minibuffer,
1064 g_signal_lookup("grab_focus", G_OBJECT_TYPE(minibuffer)), 0);
1067 return 1;