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)
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
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 */
29 #include <gdk/gdkkeysyms.h>
31 #include "collection.h"
33 #include "minibuffer.h"
36 /* Static prototypes */
37 static gint
key_press_event(GtkWidget
*widget
,
39 FilerWindow
*filer_window
);
40 static void changed(GtkEditable
*mini
, FilerWindow
*filer_window
);
41 static void find_next_match(FilerWindow
*filer_window
, char *pattern
, int dir
);
42 static gboolean
matches(Collection
*collection
, int item
, char *pattern
);
43 static void search_in_dir(FilerWindow
*filer_window
, int dir
);
46 /****************************************************************
47 * EXTERNAL INTERFACE *
48 ****************************************************************/
51 GtkWidget
*create_minibuffer(FilerWindow
*filer_window
)
55 mini
= gtk_entry_new();
56 gtk_signal_connect(GTK_OBJECT(mini
), "key_press_event",
57 GTK_SIGNAL_FUNC(key_press_event
), filer_window
);
58 gtk_signal_connect(GTK_OBJECT(mini
), "changed",
59 GTK_SIGNAL_FUNC(changed
), filer_window
);
64 void minibuffer_show(FilerWindow
*filer_window
)
66 Collection
*collection
;
69 g_return_if_fail(filer_window
!= NULL
);
70 g_return_if_fail(filer_window
->minibuffer
!= NULL
);
72 collection
= filer_window
->collection
;
73 filer_window
->mini_cursor_base
= MAX(collection
->cursor_item
, 0);
75 mini
= GTK_ENTRY(filer_window
->minibuffer
);
77 gtk_entry_set_text(mini
, make_path(filer_window
->path
, "")->str
);
78 gtk_entry_set_position(mini
, -1);
80 gtk_widget_show(filer_window
->minibuffer
);
82 gtk_window_set_focus(GTK_WINDOW(filer_window
->window
),
83 filer_window
->minibuffer
);
86 void minibuffer_hide(FilerWindow
*filer_window
)
88 gtk_widget_hide(filer_window
->minibuffer
);
89 gtk_window_set_focus(GTK_WINDOW(filer_window
->window
),
90 GTK_WIDGET(filer_window
->collection
));
94 /****************************************************************
95 * INTERNAL FUNCTIONS *
96 ****************************************************************/
98 static void return_pressed(FilerWindow
*filer_window
, GdkEventKey
*event
)
100 Collection
*collection
= filer_window
->collection
;
101 int item
= collection
->cursor_item
;
102 char *path
, *pattern
;
103 int flags
= OPEN_FROM_MINI
| OPEN_SAME_WINDOW
;
105 path
= gtk_entry_get_text(GTK_ENTRY(filer_window
->minibuffer
));
106 pattern
= strrchr(path
, '/');
112 if (item
== -1 || !matches(collection
, item
, pattern
))
118 if ((event
->state
& GDK_SHIFT_MASK
) != 0)
121 filer_openitem(filer_window
, item
, flags
);
124 /* Use the cursor item to fill in the minibuffer.
125 * If there are multiple matches the fill in as much as possible and beep.
127 static void complete(FilerWindow
*filer_window
)
130 Collection
*collection
= filer_window
->collection
;
131 int cursor
= collection
->cursor_item
;
133 int shortest_stem
= -1;
138 if (cursor
< 0 || cursor
>= collection
->number_of_items
)
144 entry
= GTK_ENTRY(filer_window
->minibuffer
);
146 item
= (DirItem
*) collection
->items
[cursor
].data
;
148 text
= gtk_entry_get_text(entry
);
149 leaf
= strrchr(text
, '/');
157 if (!matches(collection
, cursor
, leaf
))
163 current_stem
= strlen(leaf
);
165 /* Find the longest other match of this name. It it's longer than
166 * the currently entered text then complete only up to that length.
168 for (other
= 0; other
< collection
->number_of_items
; other
++)
170 DirItem
*other_item
= (DirItem
*) collection
->items
[other
].data
;
176 while (other_item
->leafname
[stem
] && item
->leafname
[stem
])
178 if (other_item
->leafname
[stem
] != item
->leafname
[stem
])
183 /* stem is the index of the first difference */
184 if (stem
>= current_stem
&&
185 (shortest_stem
== -1 || stem
< shortest_stem
))
186 shortest_stem
= stem
;
189 if (current_stem
== shortest_stem
)
191 else if (current_stem
< shortest_stem
)
195 extra
= g_strndup(item
->leafname
+ current_stem
,
196 shortest_stem
- current_stem
);
197 gtk_entry_append_text(entry
, extra
);
205 new = make_path(filer_window
->path
, item
->leafname
);
207 if (item
->base_type
== TYPE_DIRECTORY
&&
208 (item
->flags
& ITEM_FLAG_APPDIR
) == 0)
209 g_string_append_c(new, '/');
211 gtk_entry_set_text(entry
, new->str
);
215 static gint
key_press_event(GtkWidget
*widget
,
217 FilerWindow
*filer_window
)
219 switch (event
->keyval
)
222 minibuffer_hide(filer_window
);
225 search_in_dir(filer_window
, -1);
228 search_in_dir(filer_window
, 1);
231 return_pressed(filer_window
, event
);
234 complete(filer_window
);
240 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget
), "key_press_event");
244 static void changed(GtkEditable
*mini
, FilerWindow
*filer_window
)
249 new = gtk_entry_get_text(GTK_ENTRY(mini
));
253 new = g_strdup_printf("/%s", new);
255 slash
= strrchr(new, '/');
263 real
= pathdup(path
);
265 if (strcmp(real
, filer_window
->path
) != 0)
268 if (mc_stat(real
, &info
) == 0 && S_ISDIR(info
.st_mode
))
269 filer_change_to(filer_window
, real
, slash
+ 1);
275 Collection
*collection
= filer_window
->collection
;
278 find_next_match(filer_window
, slash
+ 1, 0);
279 item
= collection
->cursor_item
;
280 if (item
!= -1 && !matches(collection
, item
, slash
+ 1))
288 /* Find the next item in the collection that matches 'pattern'. Start from
289 * mini_cursor_base and loop at either end. dir is 1 for a forward search,
290 * -1 for backwards. 0 means forwards, but may stay the same.
292 * Does not automatically update mini_cursor_base.
294 static void find_next_match(FilerWindow
*filer_window
, char *pattern
, int dir
)
296 Collection
*collection
= filer_window
->collection
;
297 int base
= filer_window
->mini_cursor_base
;
300 if (collection
->number_of_items
< 1)
303 if (base
< 0 || base
>= collection
->number_of_items
)
304 filer_window
->mini_cursor_base
= base
= 0;
308 /* Step to the next item */
311 if (item
>= collection
->number_of_items
)
314 item
= collection
->number_of_items
- 1;
318 else if (item
== base
)
319 break; /* No (other) matches at all */
322 } while (!matches(collection
, item
, pattern
));
324 collection_set_cursor_item(collection
, item
);
327 static gboolean
matches(Collection
*collection
, int item_number
, char *pattern
)
329 DirItem
*item
= (DirItem
*) collection
->items
[item_number
].data
;
331 return strncmp(item
->leafname
, pattern
, strlen(pattern
)) == 0;
334 /* Find next match and set base for future matches. */
335 static void search_in_dir(FilerWindow
*filer_window
, int dir
)
337 char *path
, *pattern
;
339 path
= gtk_entry_get_text(GTK_ENTRY(filer_window
->minibuffer
));
340 pattern
= strrchr(path
, '/');
346 filer_window
->mini_cursor_base
= filer_window
->collection
->cursor_item
;
347 find_next_match(filer_window
, pattern
, dir
);
348 filer_window
->mini_cursor_base
= filer_window
->collection
->cursor_item
;