r286: Added some truncation code (not finished yet).
[rox-filer/ma.git] / ROX-Filer / src / minibuffer.c
blob56958237e75b2b58de62cb059b58f5c7d5618920
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
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 "collection.h"
36 #include "find.h"
37 #include "gui_support.h"
38 #include "support.h"
39 #include "minibuffer.h"
40 #include "filer.h"
41 #include "main.h"
42 #include "action.h"
44 static GList *shell_history = NULL;
46 /* Static prototypes */
47 static gint key_press_event(GtkWidget *widget,
48 GdkEventKey *event,
49 FilerWindow *filer_window);
50 static void changed(GtkEditable *mini, FilerWindow *filer_window);
51 static void find_next_match(FilerWindow *filer_window, char *pattern, int dir);
52 static gboolean matches(Collection *collection, int item, char *pattern);
53 static void search_in_dir(FilerWindow *filer_window, int dir);
54 static guchar *mini_contents(FilerWindow *filer_window);
55 static void show_help(FilerWindow *filer_window);
58 /****************************************************************
59 * EXTERNAL INTERFACE *
60 ****************************************************************/
63 /* Creates the minibuffer widgets, setting the appropriate fields
64 * in filer_window.
66 void create_minibuffer(FilerWindow *filer_window)
68 MaskedPixmap *mp;
69 GtkWidget *hbox, *label, *mini, *help, *icon;
71 hbox = gtk_hbox_new(FALSE, 0);
73 mp = g_fscache_lookup(pixmap_cache,
74 make_path(getenv("APP_DIR"), "pixmaps/help.xpm")->str);
75 icon = gtk_pixmap_new(mp->pixmap, mp->mask);
76 g_fscache_data_unref(pixmap_cache, mp);
77 help = gtk_button_new();
78 gtk_container_add(GTK_CONTAINER(help), icon);
79 gtk_box_pack_start(GTK_BOX(hbox), help, FALSE, TRUE, 0);
80 gtk_signal_connect_object(GTK_OBJECT(help), "clicked",
81 GTK_SIGNAL_FUNC(show_help), (GtkObject *) filer_window);
83 label = gtk_label_new(NULL);
84 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 2);
86 mini = gtk_entry_new();
87 gtk_box_pack_start(GTK_BOX(hbox), mini, TRUE, TRUE, 0);
88 gtk_widget_set_style(mini, fixed_style);
89 gtk_signal_connect(GTK_OBJECT(mini), "key_press_event",
90 GTK_SIGNAL_FUNC(key_press_event), filer_window);
91 gtk_signal_connect(GTK_OBJECT(mini), "changed",
92 GTK_SIGNAL_FUNC(changed), filer_window);
94 filer_window->minibuffer = mini;
95 filer_window->minibuffer_label = label;
96 filer_window->minibuffer_area = hbox;
99 void minibuffer_show(FilerWindow *filer_window, MiniType mini_type)
101 Collection *collection;
102 GtkEntry *mini;
104 g_return_if_fail(filer_window != NULL);
105 g_return_if_fail(filer_window->minibuffer != NULL);
107 mini = GTK_ENTRY(filer_window->minibuffer);
109 filer_window->mini_type = MINI_NONE;
110 gtk_label_set_text(GTK_LABEL(filer_window->minibuffer_label),
111 mini_type == MINI_PATH ? _("Goto:") :
112 mini_type == MINI_SHELL ? _("Shell:") :
113 mini_type == MINI_RUN_ACTION ? _("Run Action:") :
114 mini_type == MINI_SELECT_IF ? _("Select If:") :
115 "?");
117 collection = filer_window->collection;
118 collection_move_cursor(collection, 0, 0); /* Turn the cursor on */
119 switch (mini_type)
121 case MINI_PATH:
122 filer_window->mini_cursor_base =
123 MAX(collection->cursor_item, 0);
124 gtk_entry_set_text(mini,
125 make_path(filer_window->path, "")->str);
126 break;
127 case MINI_SHELL:
128 case MINI_RUN_ACTION:
129 case MINI_SELECT_IF:
130 filer_window->mini_cursor_base = -1; /* History */
131 gtk_entry_set_text(mini, "");
132 break;
133 default:
134 g_warning("Bad minibuffer type\n");
135 return;
138 filer_window->mini_type = mini_type;
140 gtk_entry_set_position(mini, -1);
142 gtk_widget_show_all(filer_window->minibuffer_area);
144 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
145 filer_window->minibuffer);
148 void minibuffer_hide(FilerWindow *filer_window)
150 filer_window->mini_type = MINI_NONE;
152 gtk_widget_hide(filer_window->minibuffer_area);
153 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
154 GTK_WIDGET(filer_window->collection));
157 /* Insert this leafname at the cursor (replacing the selection, if any).
158 * Must be in SHELL or RUN_ACTION mode.
160 void minibuffer_add(FilerWindow *filer_window, guchar *leafname)
162 guchar *esc;
163 GtkEditable *edit = GTK_EDITABLE(filer_window->minibuffer);
164 GtkEntry *entry = GTK_ENTRY(edit);
165 int pos;
167 g_return_if_fail(filer_window->mini_type == MINI_SHELL ||
168 filer_window->mini_type == MINI_RUN_ACTION);
170 esc = shell_escape(leafname);
172 gtk_editable_delete_selection(edit);
173 pos = gtk_editable_get_position(edit);
175 if (pos > 0 && gtk_entry_get_text(entry)[pos - 1] != ' ')
176 gtk_editable_insert_text(edit, " ", 1, &pos);
178 gtk_editable_insert_text(edit, esc, strlen(esc), &pos);
179 gtk_editable_set_position(edit, pos);
181 g_free(esc);
185 /****************************************************************
186 * INTERNAL FUNCTIONS *
187 ****************************************************************/
189 static void show_help(FilerWindow *filer_window)
191 guchar *message;
193 gtk_widget_grab_focus(filer_window->minibuffer);
195 switch (filer_window->mini_type)
197 case MINI_PATH:
198 message = _("Enter the name of a file and I'll display "
199 "it for you. Press Tab to fill in the longest "
200 "match. Escape to close the minibuffer.");
201 break;
202 case MINI_SHELL:
203 message = _("Enter a shell command to execute. Click "
204 "on a file to add it to the buffer.");
205 break;
206 case MINI_RUN_ACTION:
207 message =
208 _("To set the run action for a file, either:\n"
209 "- Drag a file to an application directory (eg drag an image to the "
210 "Gimp), or\n" "- Enter a shell command which contains a \"$1\""
211 "where the name of the file should go (eg ` gimp \"$1\" ')");
212 break;
213 case MINI_SELECT_IF:
214 show_condition_help();
215 return;
216 default:
217 message = "?!?";
218 break;
221 delayed_error(PROJECT, message);
225 /* PATH ENTRY */
227 static void path_return_pressed(FilerWindow *filer_window, GdkEventKey *event)
229 Collection *collection = filer_window->collection;
230 int item = collection->cursor_item;
231 char *path, *pattern;
232 int flags = OPEN_FROM_MINI | OPEN_SAME_WINDOW;
234 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
235 pattern = strrchr(path, '/');
236 if (pattern)
237 pattern++;
238 else
239 pattern = path;
241 if (item == -1 || item >= collection->number_of_items ||
242 !matches(collection, item, pattern))
244 gdk_beep();
245 return;
248 if ((event->state & GDK_SHIFT_MASK) != 0)
249 flags |= OPEN_SHIFT;
251 filer_openitem(filer_window, item, flags);
254 /* Use the cursor item to fill in the minibuffer.
255 * If there are multiple matches the fill in as much as possible and beep.
257 static void complete(FilerWindow *filer_window)
259 GtkEntry *entry;
260 Collection *collection = filer_window->collection;
261 int cursor = collection->cursor_item;
262 DirItem *item;
263 int shortest_stem = -1;
264 int current_stem;
265 int other;
266 guchar *text, *leaf;
268 if (cursor < 0 || cursor >= collection->number_of_items)
270 gdk_beep();
271 return;
274 entry = GTK_ENTRY(filer_window->minibuffer);
276 item = (DirItem *) collection->items[cursor].data;
278 text = gtk_entry_get_text(entry);
279 leaf = strrchr(text, '/');
280 if (!leaf)
282 gdk_beep();
283 return;
286 leaf++;
287 if (!matches(collection, cursor, leaf))
289 gdk_beep();
290 return;
293 current_stem = strlen(leaf);
295 /* Find the longest other match of this name. It it's longer than
296 * the currently entered text then complete only up to that length.
298 for (other = 0; other < collection->number_of_items; other++)
300 DirItem *other_item = (DirItem *) collection->items[other].data;
301 int stem = 0;
303 if (other == cursor)
304 continue;
306 while (other_item->leafname[stem] && item->leafname[stem])
308 if (other_item->leafname[stem] != item->leafname[stem])
309 break;
310 stem++;
313 /* stem is the index of the first difference */
314 if (stem >= current_stem &&
315 (shortest_stem == -1 || stem < shortest_stem))
316 shortest_stem = stem;
319 if (current_stem == shortest_stem)
320 gdk_beep();
321 else if (current_stem < shortest_stem)
323 guchar *extra;
325 extra = g_strndup(item->leafname + current_stem,
326 shortest_stem - current_stem);
327 gtk_entry_append_text(entry, extra);
328 g_free(extra);
329 gdk_beep();
331 else
333 GString *new;
335 new = make_path(filer_window->path, item->leafname);
337 if (item->base_type == TYPE_DIRECTORY &&
338 (item->flags & ITEM_FLAG_APPDIR) == 0)
339 g_string_append_c(new, '/');
341 gtk_entry_set_text(entry, new->str);
345 static void path_changed(GtkEditable *mini, FilerWindow *filer_window)
347 char *new, *slash;
348 char *path, *real;
350 new = gtk_entry_get_text(GTK_ENTRY(mini));
351 if (*new == '/')
352 new = g_strdup(new);
353 else
354 new = g_strdup_printf("/%s", new);
356 slash = strrchr(new, '/');
357 *slash = '\0';
359 if (*new == '\0')
360 path = "/";
361 else
362 path = new;
364 real = pathdup(path);
366 if (strcmp(real, filer_window->path) != 0)
368 struct stat info;
369 if (mc_stat(real, &info) == 0 && S_ISDIR(info.st_mode))
370 filer_change_to(filer_window, real, slash + 1);
371 else
372 gdk_beep();
374 else
376 Collection *collection = filer_window->collection;
377 int item;
379 find_next_match(filer_window, slash + 1, 0);
380 item = collection->cursor_item;
381 if (item != -1 && !matches(collection, item, slash + 1))
382 gdk_beep();
385 g_free(real);
386 g_free(new);
389 /* Find the next item in the collection that matches 'pattern'. Start from
390 * mini_cursor_base and loop at either end. dir is 1 for a forward search,
391 * -1 for backwards. 0 means forwards, but may stay the same.
393 * Does not automatically update mini_cursor_base.
395 static void find_next_match(FilerWindow *filer_window, char *pattern, int dir)
397 Collection *collection = filer_window->collection;
398 int base = filer_window->mini_cursor_base;
399 int item = base;
401 if (collection->number_of_items < 1)
402 return;
404 if (base < 0 || base>= collection->number_of_items)
405 filer_window->mini_cursor_base = base = 0;
409 /* Step to the next item */
410 item += dir;
412 if (item >= collection->number_of_items)
413 item = 0;
414 else if (item < 0)
415 item = collection->number_of_items - 1;
417 if (dir == 0)
418 dir = 1;
419 else if (item == base)
420 break; /* No (other) matches at all */
423 } while (!matches(collection, item, pattern));
425 collection_set_cursor_item(collection, item);
428 static gboolean matches(Collection *collection, int item_number, char *pattern)
430 DirItem *item = (DirItem *) collection->items[item_number].data;
432 return strncmp(item->leafname, pattern, strlen(pattern)) == 0;
435 /* Find next match and set base for future matches. */
436 static void search_in_dir(FilerWindow *filer_window, int dir)
438 char *path, *pattern;
440 path = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
441 pattern = strrchr(path, '/');
442 if (pattern)
443 pattern++;
444 else
445 pattern = path;
447 filer_window->mini_cursor_base = filer_window->collection->cursor_item;
448 find_next_match(filer_window, pattern, dir);
449 filer_window->mini_cursor_base = filer_window->collection->cursor_item;
452 /* SHELL COMMANDS */
454 static void add_to_history(guchar *line)
456 guchar *last;
458 last = shell_history ? (guchar *) shell_history->data : NULL;
460 if (last && strcmp(last, line) == 0)
461 return; /* Duplicating last entry */
463 shell_history = g_list_prepend(shell_history, g_strdup(line));
466 static void shell_done(FilerWindow *filer_window)
468 if (filer_exists(filer_window))
469 filer_update_dir(filer_window, TRUE);
472 /* Given a list of matches, return the longest stem. g_free() the result.
473 * Special chars are escaped. If there is only a single (non-dir) match
474 * then a trailing space is added.
476 static guchar *best_match(FilerWindow *filer_window, glob_t *matches)
478 gchar *first = matches->gl_pathv[0];
479 int i;
480 int longest, path_len;
481 guchar *path, *tmp;
483 longest = strlen(first);
485 for (i = 1; i < matches->gl_pathc; i++)
487 int j;
488 guchar *m = matches->gl_pathv[i];
490 for (j = 0; j < longest; j++)
491 if (m[j] != first[j])
492 longest = j;
495 path_len = strlen(filer_window->path);
496 if (strncmp(filer_window->path, first, path_len) == 0 &&
497 first[path_len] == '/' && first[path_len + 1])
499 path = g_strndup(first + path_len + 1, longest - path_len - 1);
501 else
502 path = g_strndup(first, longest);
504 tmp = shell_escape(path);
505 g_free(path);
507 if (matches->gl_pathc == 1 && tmp[strlen(tmp) - 1] != '/')
509 path = g_strdup_printf("%s ", tmp);
510 g_free(tmp);
511 return path;
514 return tmp;
517 static void shell_tab(FilerWindow *filer_window)
519 int i;
520 guchar *entry;
521 guchar quote;
522 int pos;
523 GString *leaf;
524 glob_t matches;
525 int leaf_start;
527 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
528 pos = gtk_editable_get_position(GTK_EDITABLE(filer_window->minibuffer));
529 leaf = g_string_new(NULL);
531 quote = '\0';
532 for (i = 0; i < pos; i++)
534 guchar c = entry[i];
536 if (leaf->len == 0)
537 leaf_start = i;
539 if (c == ' ')
541 g_string_truncate(leaf, 0);
542 continue;
544 else if (c == '\\' && i + 1 < pos)
545 c = entry[++i];
546 else if (c == '"' || c == '\'')
548 guchar cc;
550 for (++i; i < pos; i++)
552 cc = entry[i];
554 if (cc == '\\' && i + 1 < pos)
555 cc = entry[++i];
556 else if (entry[i] == c)
557 break;
558 g_string_append_c(leaf, entry[i]);
560 continue;
563 g_string_append_c(leaf, c);
566 if (leaf->len == 0)
567 leaf_start = pos;
569 if (leaf->str[0] != '/')
571 g_string_prepend_c(leaf, '/');
572 g_string_prepend(leaf, filer_window->path);
575 g_string_append_c(leaf, '*');
577 if (glob(leaf->str,
578 #ifdef GLOB_TILDE
579 GLOB_TILDE |
580 #endif
581 GLOB_MARK, NULL, &matches) == 0)
583 if (matches.gl_pathc > 0)
585 guchar *best;
586 GtkEditable *edit =
587 GTK_EDITABLE(filer_window->minibuffer);
589 best = best_match(filer_window, &matches);
591 gtk_editable_delete_text(edit, leaf_start, pos);
592 gtk_editable_insert_text(edit, best, strlen(best),
593 &leaf_start);
594 gtk_editable_set_position(edit, leaf_start);
596 g_free(best);
598 if (matches.gl_pathc != 1)
599 gdk_beep();
601 globfree(&matches);
604 g_string_free(leaf, TRUE);
607 /* This is called from shell_return_pressed() so that the command is already
608 * in the history. Returns TRUE if the buffer should be closed.
610 gboolean set_run_action(FilerWindow *filer_window, guchar *command)
612 Collection *collection = filer_window->collection;
613 int n = collection->cursor_item;
614 DirItem *item;
615 guchar *path, *tmp;
616 FILE *file;
617 int error = 0, len;
619 if (!strchr(command, '$'))
621 show_help(filer_window);
622 return FALSE;
625 if (n < 0 || n >= collection->number_of_items)
627 delayed_error(PROJECT,
628 _("You must have the cursor on the item to use for '$1'."));
629 return FALSE;
632 item = (DirItem *) collection->items[n].data;
633 path = type_ask_which_action(item->mime_type->media_type,
634 item->mime_type->subtype);
636 if (!path)
637 return TRUE;
639 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
640 len = strlen(tmp);
642 file = fopen(path, "wb");
643 if (fwrite(tmp, 1, len, file) < len)
644 error = errno;
645 if (fclose(file) && error == 0)
646 error = errno;
647 if (chmod(path, 0777))
648 error = errno;
650 if (error)
651 report_error(PROJECT, g_strerror(errno));
653 g_free(tmp);
655 return TRUE;
658 /* Either execute the command or make it the default run action */
659 static void shell_return_pressed(FilerWindow *filer_window)
661 GPtrArray *argv;
662 int i;
663 guchar *entry;
664 Collection *collection = filer_window->collection;
665 int child;
667 entry = mini_contents(filer_window);
669 if (!entry)
670 goto out;
672 add_to_history(entry);
674 if (filer_window->mini_type == MINI_RUN_ACTION)
676 if (set_run_action(filer_window, entry))
677 goto out;
678 else
679 return;
682 argv = g_ptr_array_new();
683 g_ptr_array_add(argv, "sh");
684 g_ptr_array_add(argv, "-c");
685 g_ptr_array_add(argv, entry);
686 g_ptr_array_add(argv, "sh");
688 for (i = 0; i < collection->number_of_items; i++)
690 DirItem *item = (DirItem *) collection->items[i].data;
691 if (collection->items[i].selected)
692 g_ptr_array_add(argv, item->leafname);
695 g_ptr_array_add(argv, NULL);
697 child = fork();
699 switch (child)
701 case -1:
702 delayed_error(PROJECT, _("Failed to create "
703 "child process"));
704 break;
705 case 0: /* Child */
706 dup2(to_error_log, STDOUT_FILENO);
707 close_on_exec(STDOUT_FILENO, FALSE);
708 dup2(to_error_log, STDERR_FILENO);
709 close_on_exec(STDERR_FILENO, FALSE);
710 if (chdir(filer_window->path))
711 g_printerr("chdir(%s) failed: %s\n",
712 filer_window->path,
713 g_strerror(errno));
714 execvp((char *) argv->pdata[0],
715 (char **) argv->pdata);
716 g_printerr("execvp(%s, ...) failed: %s\n",
717 (char *) argv->pdata[0],
718 g_strerror(errno));
719 _exit(0);
720 default:
721 on_child_death(child,
722 (CallbackFn) shell_done, filer_window);
723 break;
726 g_ptr_array_free(argv, TRUE);
728 out:
729 minibuffer_hide(filer_window);
732 /* Move through the shell history */
733 static void shell_recall(FilerWindow *filer_window, int dir)
735 guchar *command;
736 int pos = filer_window->mini_cursor_base;
738 pos += dir;
739 if (pos >= 0)
741 command = g_list_nth_data(shell_history, pos);
742 if (!command)
743 return;
745 else
746 command = "";
748 if (pos < -1)
749 pos = -1;
750 filer_window->mini_cursor_base = pos;
752 gtk_entry_set_text(GTK_ENTRY(filer_window->minibuffer), command);
755 /* SELECT IF */
757 static void select_return_pressed(FilerWindow *filer_window)
759 FindCondition *cond;
760 int i, n;
761 guchar *entry;
762 Collection *collection = filer_window->collection;
763 FindInfo info;
765 entry = mini_contents(filer_window);
767 if (!entry)
768 goto out;
770 add_to_history(entry);
772 cond = find_compile(entry);
773 if (!cond)
775 delayed_error(PROJECT, "Invalid Find condition");
776 return;
779 info.now = time(NULL);
780 info.prune = FALSE; /* (don't care) */
781 n = collection->number_of_items;
783 /* If an item at the start is selected then we could lose the
784 * primary selection after checking that item and then need to
785 * gain it again at the end. Therefore, if anything is selected
786 * then select the last item until the end of the search.
788 if (collection->number_selected)
789 collection_select_item(collection, n - 1);
791 for (i = 0; i < n; i++)
793 DirItem *item = (DirItem *) collection->items[i].data;
795 info.leaf = item->leafname;
796 info.fullpath = make_path(filer_window->path, info.leaf)->str;
798 if (lstat(info.fullpath, &info.stats) == 0 &&
799 find_test_condition(cond, &info))
800 collection_select_item(collection, i);
801 else
802 collection_unselect_item(collection, i);
805 find_condition_free(cond);
807 out:
808 minibuffer_hide(filer_window);
812 /* EVENT HANDLERS */
814 static gint key_press_event(GtkWidget *widget,
815 GdkEventKey *event,
816 FilerWindow *filer_window)
818 if (event->keyval == GDK_Escape)
820 if (filer_window->mini_type == MINI_SHELL)
822 guchar *line;
824 line = mini_contents(filer_window);
825 if (line)
826 add_to_history(line);
829 minibuffer_hide(filer_window);
830 return TRUE;
833 switch (filer_window->mini_type)
835 case MINI_PATH:
836 switch (event->keyval)
838 case GDK_Up:
839 search_in_dir(filer_window, -1);
840 break;
841 case GDK_Down:
842 search_in_dir(filer_window, 1);
843 break;
844 case GDK_Return:
845 path_return_pressed(filer_window,
846 event);
847 break;
848 case GDK_Tab:
849 complete(filer_window);
850 break;
851 default:
852 return FALSE;
854 break;
856 case MINI_SHELL:
857 case MINI_RUN_ACTION:
858 switch (event->keyval)
860 case GDK_Up:
861 shell_recall(filer_window, 1);
862 break;
863 case GDK_Down:
864 shell_recall(filer_window, -1);
865 break;
866 case GDK_Tab:
867 shell_tab(filer_window);
868 break;
869 case GDK_Return:
870 shell_return_pressed(filer_window);
871 break;
872 default:
873 return FALSE;
875 break;
876 case MINI_SELECT_IF:
877 switch (event->keyval)
879 case GDK_Up:
880 shell_recall(filer_window, 1);
881 break;
882 case GDK_Down:
883 shell_recall(filer_window, -1);
884 break;
885 case GDK_Tab:
886 return TRUE;
887 case GDK_Return:
888 select_return_pressed(filer_window);
889 break;
890 default:
891 return FALSE;
893 break;
894 default:
895 break;
898 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event");
899 return TRUE;
902 static void changed(GtkEditable *mini, FilerWindow *filer_window)
904 switch (filer_window->mini_type)
906 case MINI_PATH:
907 path_changed(mini, filer_window);
908 return;
909 default:
910 break;
914 /* Returns a string (which must NOT be freed), or NULL if the buffer
915 * is blank (whitespace only).
917 static guchar *mini_contents(FilerWindow *filer_window)
919 guchar *entry, *c;
921 entry = gtk_entry_get_text(GTK_ENTRY(filer_window->minibuffer));
923 for (c = entry; *c; c++)
924 if (!isspace(*c))
925 return entry;
927 return NULL;