Mercurial > geeqie
annotate src/ui_tabcomp.c @ 1385:b4e6fee484f7
Fix utf8/locale conversion warning: the path passed to isdir() is not in UTF8 (if local fs is not in UFT8) so do not use it (isdir() uses stat_utf8() which calls path_from_utf8()).
author | zas_ |
---|---|
date | Fri, 06 Mar 2009 14:53:32 +0000 |
parents | 31a747f9d268 |
children | 2496a345c452 |
rev | line source |
---|---|
9 | 1 /* |
2 * (SLIK) SimpLIstic sKin functions | |
69
31759d770628
Fri Oct 13 10:27:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
12
diff
changeset
|
3 * (C) 2006 John Ellis |
1284 | 4 * Copyright (C) 2008 - 2009 The Geeqie Team |
9 | 5 * |
6 * Author: John Ellis | |
7 * | |
8 * This software is released under the GNU General Public License (GNU GPL). | |
9 * Please read the included file COPYING for more information. | |
10 * This software comes with no warranty of any kind, use at your own risk! | |
11 */ | |
12 | |
13 #ifdef HAVE_CONFIG_H | |
14 # include "config.h" | |
15 #endif | |
16 #include "intl.h" | |
17 | |
18 #include <stdio.h> | |
19 #include <stdlib.h> | |
20 #include <string.h> | |
21 #include <unistd.h> | |
22 #include <sys/types.h> | |
23 #include <dirent.h> | |
24 | |
25 #include <gdk/gdk.h> | |
26 #include <gtk/gtk.h> | |
27 #include <gdk-pixbuf/gdk-pixbuf.h> | |
28 | |
281 | 29 #include "main.h" |
9 | 30 #include "ui_tabcomp.h" |
31 | |
902 | 32 #include "history_list.h" |
1022
9962b24b6b43
Move miscellaneous functions to their own files (new misc.[ch]).
zas_
parents:
991
diff
changeset
|
33 #include "misc.h" /* expand_tilde() */ |
9 | 34 #include "ui_fileops.h" |
35 #include "ui_spinner.h" | |
36 #include "ui_utildlg.h" | |
37 | |
38 #include <gdk/gdkkeysyms.h> /* for key values */ | |
39 | |
40 | |
41 /* define this to enable a pop-up menu that shows possible matches | |
42 * #define TAB_COMPLETION_ENABLE_POPUP_MENU | |
43 */ | |
44 #define TAB_COMPLETION_ENABLE_POPUP_MENU 1 | |
45 #define TAB_COMP_POPUP_MAX 500 | |
46 | |
47 #ifdef TAB_COMPLETION_ENABLE_POPUP_MENU | |
48 #include "ui_menu.h" | |
49 #endif | |
50 | |
51 | |
52 /* ---------------------------------------------------------------- | |
53 Tab completion routines, can be connected to any gtkentry widget | |
54 using the tab_completion_add_to_entry() function. | |
726 | 55 Use remove_trailing_slash() to strip the trailing G_DIR_SEPARATOR. |
9 | 56 ----------------------------------------------------------------*/ |
57 | |
58 typedef struct _TabCompData TabCompData; | |
59 struct _TabCompData | |
60 { | |
61 GtkWidget *entry; | |
62 gchar *dir_path; | |
63 GList *file_list; | |
64 void (*enter_func)(const gchar *, gpointer); | |
65 void (*tab_func)(const gchar *, gpointer); | |
1167
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
66 void (*tab_append_func)(const gchar *, gpointer, gint); |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
67 |
9 | 68 gpointer enter_data; |
69 gpointer tab_data; | |
1167
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
70 gpointer tab_append_data; |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
71 |
9 | 72 GtkWidget *combo; |
73 gint has_history; | |
74 gchar *history_key; | |
75 gint history_levels; | |
76 | |
77 FileDialog *fd; | |
78 gchar *fd_title; | |
79 gint fd_folders_only; | |
80 GtkWidget *fd_button; | |
81 }; | |
82 | |
83 | |
84 static void tab_completion_select_show(TabCompData *td); | |
85 | |
86 static void tab_completion_free_list(TabCompData *td) | |
87 { | |
88 GList *list; | |
89 | |
90 g_free(td->dir_path); | |
91 td->dir_path = NULL; | |
92 | |
93 list = td->file_list; | |
94 | |
516 | 95 while (list) |
9 | 96 { |
97 g_free(list->data); | |
98 list = list->next; | |
99 } | |
100 | |
101 g_list_free(td->file_list); | |
102 td->file_list = NULL; | |
103 } | |
104 | |
105 static void tab_completion_read_dir(TabCompData *td, const gchar *path) | |
106 { | |
442 | 107 DIR *dp; |
108 struct dirent *dir; | |
109 GList *list = NULL; | |
9 | 110 gchar *pathl; |
111 | |
112 tab_completion_free_list(td); | |
113 | |
114 pathl = path_from_utf8(path); | |
442 | 115 dp = opendir(pathl); |
9 | 116 if (!dp) |
442 | 117 { |
118 /* dir not found */ | |
1385
b4e6fee484f7
Fix utf8/locale conversion warning: the path passed to isdir() is not in UTF8 (if local fs is not in UFT8) so do not use it (isdir() uses stat_utf8() which calls path_from_utf8()).
zas_
parents:
1382
diff
changeset
|
119 g_free(pathl); |
442 | 120 return; |
121 } | |
122 while ((dir = readdir(dp)) != NULL) | |
123 { | |
69
31759d770628
Fri Oct 13 10:27:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
12
diff
changeset
|
124 gchar *name = dir->d_name; |
31759d770628
Fri Oct 13 10:27:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
12
diff
changeset
|
125 if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0) |
31759d770628
Fri Oct 13 10:27:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
12
diff
changeset
|
126 { |
1385
b4e6fee484f7
Fix utf8/locale conversion warning: the path passed to isdir() is not in UTF8 (if local fs is not in UFT8) so do not use it (isdir() uses stat_utf8() which calls path_from_utf8()).
zas_
parents:
1382
diff
changeset
|
127 gchar *abspath = g_build_filename(pathl, name, NULL); |
1370
0011d6859ec5
Add a directory separator at the end of names in tab completion list to indicate directories.
zas_
parents:
1365
diff
changeset
|
128 |
1385
b4e6fee484f7
Fix utf8/locale conversion warning: the path passed to isdir() is not in UTF8 (if local fs is not in UFT8) so do not use it (isdir() uses stat_utf8() which calls path_from_utf8()).
zas_
parents:
1382
diff
changeset
|
129 if (g_file_test(abspath, G_FILE_TEST_IS_DIR)) |
1370
0011d6859ec5
Add a directory separator at the end of names in tab completion list to indicate directories.
zas_
parents:
1365
diff
changeset
|
130 { |
0011d6859ec5
Add a directory separator at the end of names in tab completion list to indicate directories.
zas_
parents:
1365
diff
changeset
|
131 gchar *dname = g_strconcat(name, G_DIR_SEPARATOR_S, NULL); |
0011d6859ec5
Add a directory separator at the end of names in tab completion list to indicate directories.
zas_
parents:
1365
diff
changeset
|
132 list = g_list_prepend(list, path_to_utf8(dname)); |
1371 | 133 g_free(dname); |
1370
0011d6859ec5
Add a directory separator at the end of names in tab completion list to indicate directories.
zas_
parents:
1365
diff
changeset
|
134 } |
0011d6859ec5
Add a directory separator at the end of names in tab completion list to indicate directories.
zas_
parents:
1365
diff
changeset
|
135 else |
0011d6859ec5
Add a directory separator at the end of names in tab completion list to indicate directories.
zas_
parents:
1365
diff
changeset
|
136 { |
0011d6859ec5
Add a directory separator at the end of names in tab completion list to indicate directories.
zas_
parents:
1365
diff
changeset
|
137 list = g_list_prepend(list, path_to_utf8(name)); |
0011d6859ec5
Add a directory separator at the end of names in tab completion list to indicate directories.
zas_
parents:
1365
diff
changeset
|
138 } |
0011d6859ec5
Add a directory separator at the end of names in tab completion list to indicate directories.
zas_
parents:
1365
diff
changeset
|
139 g_free(abspath); |
69
31759d770628
Fri Oct 13 10:27:22 2006 John Ellis <johne@verizon.net>
gqview
parents:
12
diff
changeset
|
140 } |
9 | 141 } |
442 | 142 closedir(dp); |
9 | 143 |
144 td->dir_path = g_strdup(path); | |
145 td->file_list = list; | |
1385
b4e6fee484f7
Fix utf8/locale conversion warning: the path passed to isdir() is not in UTF8 (if local fs is not in UFT8) so do not use it (isdir() uses stat_utf8() which calls path_from_utf8()).
zas_
parents:
1382
diff
changeset
|
146 g_free(pathl); |
9 | 147 } |
148 | |
149 static void tab_completion_destroy(GtkWidget *widget, gpointer data) | |
150 { | |
151 TabCompData *td = data; | |
152 | |
153 tab_completion_free_list(td); | |
154 g_free(td->history_key); | |
155 | |
156 if (td->fd) file_dialog_close(td->fd); | |
157 g_free(td->fd_title); | |
158 | |
159 g_free(td); | |
160 } | |
161 | |
991 | 162 static gchar *tab_completion_get_text(TabCompData *td) |
9 | 163 { |
164 gchar *text; | |
165 | |
166 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(td->entry))); | |
167 | |
168 if (text[0] == '~') | |
169 { | |
170 gchar *t = text; | |
720
d8a88f279aca
Use expand_tilde() instead of simple concatenation, it allows correct expansion
zas_
parents:
673
diff
changeset
|
171 text = expand_tilde(text); |
9 | 172 g_free(t); |
173 } | |
174 | |
991 | 175 return text; |
176 } | |
177 | |
178 static gint tab_completion_emit_enter_signal(TabCompData *td) | |
179 { | |
180 gchar *text; | |
181 if (!td->enter_func) return FALSE; | |
182 | |
183 text = tab_completion_get_text(td); | |
9 | 184 td->enter_func(text, td->enter_data); |
185 g_free(text); | |
186 | |
187 return TRUE; | |
188 } | |
189 | |
190 static void tab_completion_emit_tab_signal(TabCompData *td) | |
191 { | |
192 gchar *text; | |
193 if (!td->tab_func) return; | |
194 | |
991 | 195 text = tab_completion_get_text(td); |
9 | 196 td->tab_func(text, td->tab_data); |
197 g_free(text); | |
198 } | |
199 | |
200 #ifdef TAB_COMPLETION_ENABLE_POPUP_MENU | |
201 | |
202 static gint tab_completion_popup_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) | |
203 { | |
204 TabCompData *td = data; | |
205 | |
206 if (event->keyval == GDK_Tab || | |
207 event->keyval == GDK_BackSpace || | |
208 (event->keyval >= 0x20 && event->keyval <= 0xFF) ) | |
209 { | |
210 if (event->keyval >= 0x20 && event->keyval <= 0xFF) | |
211 { | |
212 gchar buf[2]; | |
213 gint p = -1; | |
214 | |
215 buf[0] = event->keyval; | |
216 buf[1] = '\0'; | |
217 gtk_editable_insert_text(GTK_EDITABLE(td->entry), buf, 1, &p); | |
218 gtk_editable_set_position(GTK_EDITABLE(td->entry), -1); | |
219 } | |
220 | |
221 /*close the menu */ | |
222 gtk_menu_popdown(GTK_MENU(widget)); | |
223 /* doing this does not emit the "selection done" signal, unref it ourselves */ | |
1043 | 224 #if GTK_CHECK_VERSION(2,12,0) |
225 g_object_unref(widget); | |
226 #else | |
9 | 227 gtk_widget_unref(widget); |
1043 | 228 #endif |
9 | 229 return TRUE; |
230 } | |
231 | |
232 return FALSE; | |
233 } | |
234 | |
235 static void tab_completion_popup_cb(GtkWidget *widget, gpointer data) | |
236 { | |
237 gchar *name = data; | |
238 TabCompData *td; | |
239 gchar *buf; | |
240 | |
241 td = g_object_get_data(G_OBJECT(widget), "tab_completion_data"); | |
242 if (!td) return; | |
243 | |
721
b736a2e3129b
tab_completion_popup_cb(): use g_build_filename() and simplify code.
zas_
parents:
720
diff
changeset
|
244 buf = g_build_filename(td->dir_path, name, NULL); |
9 | 245 gtk_entry_set_text(GTK_ENTRY(td->entry), buf); |
246 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf)); | |
247 g_free(buf); | |
248 | |
249 tab_completion_emit_tab_signal(td); | |
250 } | |
251 | |
252 static void tab_completion_popup_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) | |
253 { | |
254 TabCompData *td = data; | |
255 gint height; | |
256 PangoLayout *layout; | |
257 PangoRectangle strong_pos, weak_pos; | |
258 gint length; | |
259 gint xoffset, yoffset; | |
12
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
260 GtkRequisition req; |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
261 GdkScreen *screen; |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
262 gint monitor_num; |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
263 GdkRectangle monitor; |
9 | 264 |
265 gdk_window_get_origin(td->entry->window, x, y); | |
266 | |
12
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
267 screen = gtk_widget_get_screen(GTK_WIDGET(menu)); |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
268 monitor_num = gdk_screen_get_monitor_at_window(screen, td->entry->window); |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
269 gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor); |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
270 |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
271 gtk_widget_size_request(GTK_WIDGET(menu), &req); |
9 | 272 |
273 length = strlen(gtk_entry_get_text(GTK_ENTRY(td->entry))); | |
274 gtk_entry_get_layout_offsets(GTK_ENTRY(td->entry), &xoffset, &yoffset); | |
275 | |
276 layout = gtk_entry_get_layout(GTK_ENTRY(td->entry)); | |
277 pango_layout_get_cursor_pos(layout, length, &strong_pos, &weak_pos); | |
12
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
278 |
9 | 279 *x += strong_pos.x / PANGO_SCALE + xoffset; |
12
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
280 |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
281 height = MIN(td->entry->requisition.height, td->entry->allocation.height); |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
282 |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
283 if (req.height > monitor.y + monitor.height - *y - height && |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
284 *y - monitor.y > monitor.y + monitor.height - *y) |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
285 { |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
286 height = MIN(*y - monitor.y, req.height); |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
287 gtk_widget_set_size_request(GTK_WIDGET(menu), -1, height); |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
288 *y -= height; |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
289 } |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
290 else |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
291 { |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
292 *y += height; |
147f4c4b9025
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
293 } |
9 | 294 } |
295 | |
296 static void tab_completion_popup_list(TabCompData *td, GList *list) | |
297 { | |
298 GtkWidget *menu; | |
299 GList *work; | |
300 GdkEvent *event; | |
301 guint32 etime; | |
302 gint ebutton; | |
303 gint count = 0; | |
304 | |
305 if (!list) return; | |
306 | |
307 #if 0 | |
308 /* | |
309 * well, the menu would be too long anyway... | |
310 * (listing /dev causes gtk+ window allocation errors, -> too big a window) | |
311 * this is why menu popups are disabled, this really should be a popup scrollable listview. | |
312 */ | |
313 if (g_list_length(list) > 200) return; | |
314 #endif | |
315 | |
316 menu = popup_menu_short_lived(); | |
317 | |
318 work = list; | |
319 while (work && count < TAB_COMP_POPUP_MAX) | |
320 { | |
321 gchar *name = work->data; | |
322 GtkWidget *item; | |
323 | |
324 item = menu_item_add_simple(menu, name, G_CALLBACK(tab_completion_popup_cb), name); | |
325 g_object_set_data(G_OBJECT(item), "tab_completion_data", td); | |
326 | |
327 work = work->next; | |
328 count++; | |
329 } | |
330 | |
331 g_signal_connect(G_OBJECT(menu), "key_press_event", | |
332 G_CALLBACK(tab_completion_popup_key_press), td); | |
333 | |
334 /* peek at the current event to get the time, etc. */ | |
335 event = gtk_get_current_event(); | |
336 | |
337 if (event && event->type == GDK_BUTTON_RELEASE) | |
338 { | |
339 ebutton = event->button.button; | |
340 } | |
341 else | |
342 { | |
343 ebutton = 0; | |
344 } | |
345 | |
346 if (event) | |
347 { | |
348 etime = gdk_event_get_time(event); | |
349 gdk_event_free(event); | |
350 } | |
351 else | |
352 { | |
353 etime = 0; | |
354 } | |
355 | |
356 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, | |
357 tab_completion_popup_pos_cb, td, ebutton, etime); | |
358 } | |
359 | |
360 #ifndef CASE_SORT | |
361 #define CASE_SORT strcmp | |
362 #endif | |
363 | |
364 static gint simple_sort(gconstpointer a, gconstpointer b) | |
365 { | |
442 | 366 return CASE_SORT((gchar *)a, (gchar *)b); |
9 | 367 } |
368 | |
369 #endif | |
370 | |
371 static gint tab_completion_do(TabCompData *td) | |
372 { | |
373 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(td->entry)); | |
374 const gchar *entry_file; | |
375 gchar *entry_dir; | |
376 gchar *ptr; | |
377 gint home_exp = FALSE; | |
378 | |
379 /* home dir expansion */ | |
380 if (entry_text[0] == '~') | |
381 { | |
720
d8a88f279aca
Use expand_tilde() instead of simple concatenation, it allows correct expansion
zas_
parents:
673
diff
changeset
|
382 entry_dir = expand_tilde(entry_text); |
9 | 383 home_exp = TRUE; |
384 } | |
385 else | |
386 { | |
387 entry_dir = g_strdup(entry_text); | |
388 } | |
389 | |
390 if (isfile(entry_dir)) | |
391 { | |
392 if (home_exp) | |
393 { | |
394 gtk_entry_set_text(GTK_ENTRY(td->entry), entry_dir); | |
395 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(entry_dir)); | |
396 } | |
397 g_free(entry_dir); | |
398 return home_exp; | |
399 } | |
722
9a145206ec2c
tab_completion_do(): use g_build_filename(), G_DIR_SEPARATOR, G_DIR_SEPARATOR_S.
zas_
parents:
721
diff
changeset
|
400 |
1382
31a747f9d268
Fix tab completion when entering "/et[TAB]" it was changed to "et", this is fixed.
zas_
parents:
1371
diff
changeset
|
401 entry_file = filename_from_path(entry_text); |
31a747f9d268
Fix tab completion when entering "/et[TAB]" it was changed to "et", this is fixed.
zas_
parents:
1371
diff
changeset
|
402 |
9 | 403 if (isdir(entry_dir) && strcmp(entry_file, ".") != 0 && strcmp(entry_file, "..") != 0) |
404 { | |
405 ptr = entry_dir + strlen(entry_dir) - 1; | |
722
9a145206ec2c
tab_completion_do(): use g_build_filename(), G_DIR_SEPARATOR, G_DIR_SEPARATOR_S.
zas_
parents:
721
diff
changeset
|
406 if (ptr[0] == G_DIR_SEPARATOR) |
9 | 407 { |
408 if (home_exp) | |
409 { | |
410 gtk_entry_set_text(GTK_ENTRY(td->entry), entry_dir); | |
411 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(entry_dir)); | |
412 } | |
413 | |
414 tab_completion_read_dir(td, entry_dir); | |
415 td->file_list = g_list_sort(td->file_list, simple_sort); | |
416 if (td->file_list && !td->file_list->next) | |
417 { | |
418 gchar *buf; | |
419 const gchar *file; | |
420 | |
421 file = td->file_list->data; | |
722
9a145206ec2c
tab_completion_do(): use g_build_filename(), G_DIR_SEPARATOR, G_DIR_SEPARATOR_S.
zas_
parents:
721
diff
changeset
|
422 buf = g_build_filename(entry_dir, file, NULL); |
9 | 423 if (isdir(buf)) |
424 { | |
722
9a145206ec2c
tab_completion_do(): use g_build_filename(), G_DIR_SEPARATOR, G_DIR_SEPARATOR_S.
zas_
parents:
721
diff
changeset
|
425 gchar *tmp = g_strconcat(buf, G_DIR_SEPARATOR_S, NULL); |
9 | 426 g_free(buf); |
722
9a145206ec2c
tab_completion_do(): use g_build_filename(), G_DIR_SEPARATOR, G_DIR_SEPARATOR_S.
zas_
parents:
721
diff
changeset
|
427 buf = tmp; |
9 | 428 } |
429 gtk_entry_set_text(GTK_ENTRY(td->entry), buf); | |
430 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf)); | |
431 g_free(buf); | |
432 } | |
433 | |
434 #ifdef TAB_COMPLETION_ENABLE_POPUP_MENU | |
435 | |
436 else | |
437 { | |
438 tab_completion_popup_list(td, td->file_list); | |
439 } | |
440 #endif | |
441 | |
442 g_free(entry_dir); | |
443 return home_exp; | |
444 } | |
445 else | |
446 { | |
722
9a145206ec2c
tab_completion_do(): use g_build_filename(), G_DIR_SEPARATOR, G_DIR_SEPARATOR_S.
zas_
parents:
721
diff
changeset
|
447 gchar *buf = g_strconcat(entry_dir, G_DIR_SEPARATOR_S, NULL); |
9 | 448 gtk_entry_set_text(GTK_ENTRY(td->entry), buf); |
449 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf)); | |
450 g_free(buf); | |
451 g_free(entry_dir); | |
452 return TRUE; | |
453 } | |
454 } | |
455 | |
456 ptr = (gchar *)filename_from_path(entry_dir); | |
457 if (ptr > entry_dir) ptr--; | |
458 ptr[0] = '\0'; | |
1382
31a747f9d268
Fix tab completion when entering "/et[TAB]" it was changed to "et", this is fixed.
zas_
parents:
1371
diff
changeset
|
459 |
9 | 460 if (strlen(entry_dir) == 0) |
461 { | |
462 g_free(entry_dir); | |
722
9a145206ec2c
tab_completion_do(): use g_build_filename(), G_DIR_SEPARATOR, G_DIR_SEPARATOR_S.
zas_
parents:
721
diff
changeset
|
463 entry_dir = g_strdup(G_DIR_SEPARATOR_S); /* FIXME: win32 */ |
9 | 464 } |
465 | |
466 if (isdir(entry_dir)) | |
467 { | |
468 GList *list; | |
469 GList *poss = NULL; | |
470 gint l = strlen(entry_file); | |
471 | |
472 if (!td->dir_path || !td->file_list || strcmp(td->dir_path, entry_dir) != 0) | |
473 { | |
474 tab_completion_read_dir(td, entry_dir); | |
475 } | |
476 | |
477 list = td->file_list; | |
516 | 478 while (list) |
9 | 479 { |
480 gchar *file = list->data; | |
481 if (strncmp(entry_file, file, l) == 0) | |
482 { | |
483 poss = g_list_prepend(poss, file); | |
484 } | |
485 list = list->next; | |
486 } | |
487 | |
488 if (poss) | |
489 { | |
490 if (!poss->next) | |
491 { | |
492 gchar *file = poss->data; | |
493 gchar *buf; | |
494 | |
722
9a145206ec2c
tab_completion_do(): use g_build_filename(), G_DIR_SEPARATOR, G_DIR_SEPARATOR_S.
zas_
parents:
721
diff
changeset
|
495 buf = g_build_filename(entry_dir, file, NULL); |
9 | 496 gtk_entry_set_text(GTK_ENTRY(td->entry), buf); |
497 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf)); | |
498 g_free(buf); | |
499 g_list_free(poss); | |
500 g_free(entry_dir); | |
501 return TRUE; | |
502 } | |
503 else | |
504 { | |
734
e6ebae313d46
Fix up some types, make some signed vs unsigned warnings quiet.
zas_
parents:
726
diff
changeset
|
505 gsize c = strlen(entry_file); |
9 | 506 gint done = FALSE; |
507 gchar *test_file = poss->data; | |
508 | |
509 while (!done) | |
510 { | |
511 list = poss; | |
512 if (!list) done = TRUE; | |
516 | 513 while (list && !done) |
9 | 514 { |
515 gchar *file = list->data; | |
516 if (strlen(file) < c || strncmp(test_file, file, c) != 0) | |
517 { | |
518 done = TRUE; | |
519 } | |
520 list = list->next; | |
521 } | |
522 c++; | |
523 } | |
524 c -= 2; | |
525 if (c > 0) | |
526 { | |
527 gchar *file; | |
528 gchar *buf; | |
529 file = g_strdup(test_file); | |
530 file[c] = '\0'; | |
722
9a145206ec2c
tab_completion_do(): use g_build_filename(), G_DIR_SEPARATOR, G_DIR_SEPARATOR_S.
zas_
parents:
721
diff
changeset
|
531 buf = g_build_filename(entry_dir, file, NULL); |
9 | 532 gtk_entry_set_text(GTK_ENTRY(td->entry), buf); |
533 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf)); | |
534 | |
535 #ifdef TAB_COMPLETION_ENABLE_POPUP_MENU | |
536 | |
537 poss = g_list_sort(poss, simple_sort); | |
538 tab_completion_popup_list(td, poss); | |
539 | |
540 #endif | |
541 | |
542 g_free(file); | |
543 g_free(buf); | |
544 g_list_free(poss); | |
545 g_free(entry_dir); | |
546 return TRUE; | |
547 } | |
548 } | |
549 g_list_free(poss); | |
550 } | |
551 } | |
552 | |
553 g_free(entry_dir); | |
554 | |
555 return FALSE; | |
556 } | |
557 | |
558 static gint tab_completion_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data) | |
559 { | |
560 TabCompData *td = data; | |
561 gint stop_signal = FALSE; | |
562 | |
563 switch (event->keyval) | |
564 { | |
442 | 565 case GDK_Tab: |
9 | 566 if (!(event->state & GDK_CONTROL_MASK)) |
567 { | |
568 if (tab_completion_do(td)) | |
569 { | |
570 tab_completion_emit_tab_signal(td); | |
571 } | |
572 stop_signal = TRUE; | |
573 } | |
574 break; | |
575 case GDK_Return: case GDK_KP_Enter: | |
576 if (td->fd_button && | |
577 (event->state & GDK_CONTROL_MASK)) | |
578 { | |
579 tab_completion_select_show(td); | |
580 stop_signal = TRUE; | |
581 } | |
582 else if (tab_completion_emit_enter_signal(td)) | |
583 { | |
584 stop_signal = TRUE; | |
585 } | |
586 break; | |
587 default: | |
588 break; | |
589 } | |
590 | |
591 if (stop_signal) g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event"); | |
592 | |
593 return (stop_signal); | |
594 } | |
595 | |
596 static void tab_completion_button_pressed(GtkWidget *widget, gpointer data) | |
597 { | |
598 TabCompData *td; | |
599 GtkWidget *entry = data; | |
600 | |
601 td = g_object_get_data(G_OBJECT(entry), "tab_completion_data"); | |
602 | |
603 if (!td) return; | |
604 | |
605 if (!GTK_WIDGET_HAS_FOCUS(entry)) | |
606 { | |
607 gtk_widget_grab_focus(entry); | |
608 } | |
609 | |
610 if (tab_completion_do(td)) | |
611 { | |
612 tab_completion_emit_tab_signal(td); | |
613 } | |
614 } | |
615 | |
616 static void tab_completion_button_size_allocate(GtkWidget *button, GtkAllocation *allocation, gpointer data) | |
617 { | |
618 GtkWidget *parent = data; | |
619 | |
620 if (allocation->height > parent->allocation.height) | |
621 { | |
622 GtkAllocation button_allocation; | |
623 | |
624 button_allocation = button->allocation; | |
625 button_allocation.height = parent->allocation.height; | |
626 button_allocation.y = parent->allocation.y + | |
627 (parent->allocation.height - parent->allocation.height) / 2; | |
628 gtk_widget_size_allocate(button, &button_allocation); | |
629 } | |
630 } | |
631 | |
632 static GtkWidget *tab_completion_create_complete_button(GtkWidget *entry, GtkWidget *parent) | |
633 { | |
634 GtkWidget *button; | |
635 GtkWidget *icon; | |
636 GdkPixbuf *pixbuf; | |
637 | |
638 button = gtk_button_new(); | |
639 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS); | |
640 g_signal_connect(G_OBJECT(button), "size_allocate", | |
641 G_CALLBACK(tab_completion_button_size_allocate), parent); | |
642 g_signal_connect(G_OBJECT(button), "clicked", | |
643 G_CALLBACK(tab_completion_button_pressed), entry); | |
644 | |
645 pixbuf = gdk_pixbuf_new_from_inline(-1, icon_tabcomp, FALSE, NULL); | |
646 icon = gtk_image_new_from_pixbuf(pixbuf); | |
1043 | 647 g_object_unref(pixbuf); |
648 | |
9 | 649 gtk_container_add(GTK_CONTAINER(button), icon); |
650 gtk_widget_show(icon); | |
651 | |
652 return button; | |
653 } | |
654 | |
655 /* | |
656 *---------------------------------------------------------------------------- | |
657 * public interface | |
658 *---------------------------------------------------------------------------- | |
659 */ | |
660 | |
661 GtkWidget *tab_completion_new_with_history(GtkWidget **entry, const gchar *text, | |
662 const gchar *history_key, gint max_levels, | |
663 void (*enter_func)(const gchar *, gpointer), gpointer data) | |
664 { | |
665 GtkWidget *box; | |
666 GtkWidget *combo; | |
667 GtkWidget *combo_entry; | |
668 GtkWidget *button; | |
669 GList *work; | |
670 TabCompData *td; | |
671 gint n = 0; | |
672 | |
673 box = gtk_hbox_new(FALSE, 0); | |
674 | |
675 combo = gtk_combo_box_entry_new_text(); | |
676 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0); | |
677 gtk_widget_show(combo); | |
678 | |
679 combo_entry = GTK_BIN(combo)->child; | |
680 #if 0 | |
681 gtk_combo_set_case_sensitive(GTK_COMBO(combo), TRUE); | |
682 gtk_combo_set_use_arrows(GTK_COMBO(combo), FALSE); | |
683 #endif | |
684 | |
685 button = tab_completion_create_complete_button(combo_entry, combo); | |
686 gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0); | |
687 gtk_widget_show(button); | |
688 | |
689 tab_completion_add_to_entry(combo_entry, enter_func, data); | |
690 | |
691 td = g_object_get_data(G_OBJECT(combo_entry), "tab_completion_data"); | |
692 if (!td) return NULL; /* this should never happen! */ | |
693 | |
694 td->combo = combo; | |
695 td->has_history = TRUE; | |
696 td->history_key = g_strdup(history_key); | |
697 td->history_levels = max_levels; | |
698 | |
699 work = history_list_get_by_key(td->history_key); | |
700 | |
701 work = history_list_get_by_key(history_key); | |
702 while (work) | |
703 { | |
704 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), (gchar *)work->data); | |
705 work = work->next; | |
706 n++; | |
707 } | |
708 | |
709 if (text) | |
710 { | |
711 gtk_entry_set_text(GTK_ENTRY(combo_entry), text); | |
712 } | |
713 else if (n > 0) | |
714 { | |
715 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); | |
716 } | |
717 | |
718 if (entry) *entry = combo_entry; | |
719 return box; | |
720 } | |
721 | |
722 const gchar *tab_completion_set_to_last_history(GtkWidget *entry) | |
723 { | |
724 TabCompData *td = g_object_get_data(G_OBJECT(entry), "tab_completion_data"); | |
725 const gchar *buf; | |
726 | |
727 if (!td || !td->has_history) return NULL; | |
728 | |
729 buf = history_list_find_last_path_by_key(td->history_key); | |
730 if (buf) | |
731 { | |
732 gtk_entry_set_text(GTK_ENTRY(td->entry), buf); | |
733 } | |
734 | |
735 return buf; | |
736 } | |
737 | |
738 void tab_completion_append_to_history(GtkWidget *entry, const gchar *path) | |
739 { | |
740 TabCompData *td; | |
741 GtkTreeModel *store; | |
742 GList *work; | |
1167
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
743 gint n = 0; |
9 | 744 |
745 td = g_object_get_data(G_OBJECT(entry), "tab_completion_data"); | |
746 | |
747 if (!path) return; | |
748 | |
749 if (!td || !td->has_history) return; | |
750 | |
751 history_list_add_to_key(td->history_key, path, td->history_levels); | |
752 | |
753 gtk_combo_box_set_active(GTK_COMBO_BOX(td->combo), -1); | |
754 | |
755 store = gtk_combo_box_get_model(GTK_COMBO_BOX(td->combo)); | |
756 gtk_list_store_clear(GTK_LIST_STORE(store)); | |
757 | |
758 work = history_list_get_by_key(td->history_key); | |
759 while (work) | |
760 { | |
761 gtk_combo_box_append_text(GTK_COMBO_BOX(td->combo), (gchar *)work->data); | |
762 work = work->next; | |
1167
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
763 n++; |
9 | 764 } |
1167
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
765 |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
766 if (td->tab_append_func) { |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
767 td->tab_append_func(path, td->tab_append_data, n); |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
768 } |
9 | 769 } |
770 | |
771 GtkWidget *tab_completion_new(GtkWidget **entry, const gchar *text, | |
772 void (*enter_func)(const gchar *, gpointer), gpointer data) | |
773 { | |
774 GtkWidget *hbox; | |
775 GtkWidget *button; | |
776 GtkWidget *newentry; | |
777 | |
778 hbox = gtk_hbox_new(FALSE, 0); | |
779 | |
780 newentry = gtk_entry_new(); | |
781 if (text) gtk_entry_set_text(GTK_ENTRY(newentry), text); | |
782 gtk_box_pack_start(GTK_BOX(hbox), newentry, TRUE, TRUE, 0); | |
783 gtk_widget_show(newentry); | |
784 | |
785 button = tab_completion_create_complete_button(newentry, newentry); | |
786 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); | |
787 gtk_widget_show(button); | |
788 | |
789 tab_completion_add_to_entry(newentry, enter_func, data); | |
790 | |
791 if (entry) *entry = newentry; | |
792 return hbox; | |
793 } | |
794 | |
795 void tab_completion_add_to_entry(GtkWidget *entry, void (*enter_func)(const gchar *, gpointer), gpointer data) | |
796 { | |
797 TabCompData *td; | |
798 if (!entry) | |
799 { | |
673
fbebf5cf4a55
Do not use printf() directly but use new wrapper function log_printf() instead.
zas_
parents:
516
diff
changeset
|
800 log_printf("Tab completion error: entry != NULL\n"); |
9 | 801 return; |
802 } | |
803 | |
804 td = g_new0(TabCompData, 1); | |
1365
249bf204004a
When g_new0() is used, drop redundant initializations to NULL, FALSE or 0.
zas_
parents:
1284
diff
changeset
|
805 |
9 | 806 td->entry = entry; |
807 td->enter_func = enter_func; | |
808 td->enter_data = data; | |
809 | |
810 g_object_set_data(G_OBJECT(td->entry), "tab_completion_data", td); | |
811 | |
812 g_signal_connect(G_OBJECT(entry), "key_press_event", | |
813 G_CALLBACK(tab_completion_key_pressed), td); | |
814 g_signal_connect(G_OBJECT(entry), "destroy", | |
815 G_CALLBACK(tab_completion_destroy), td); | |
816 } | |
817 | |
818 void tab_completion_add_tab_func(GtkWidget *entry, void (*tab_func)(const gchar *, gpointer), gpointer data) | |
819 { | |
820 TabCompData *td = g_object_get_data(G_OBJECT(entry), "tab_completion_data"); | |
821 | |
822 if (!td) return; | |
823 | |
824 td->tab_func = tab_func; | |
825 td->tab_data = data; | |
826 } | |
827 | |
1167
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
828 /* Add a callback function called when a new entry is appended to the list */ |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
829 void tab_completion_add_append_func(GtkWidget *entry, void (*tab_append_func)(const gchar *, gpointer, gint), gpointer data) |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
830 { |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
831 TabCompData *td = g_object_get_data(G_OBJECT(entry), "tab_completion_data"); |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
832 |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
833 if (!td) return; |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
834 |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
835 td->tab_append_func = tab_append_func; |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
836 td->tab_append_data = data; |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
837 } |
e812b1a7adda
Add a back button in the toolbar: it allows to go back and forth between two directories. Experimental, please test and comment on ml.
zas_
parents:
1055
diff
changeset
|
838 |
9 | 839 gchar *remove_trailing_slash(const gchar *path) |
840 { | |
841 gint l; | |
260
249a9a6cd27f
Improve remove_trailing_slash() so it allocates no more than
zas_
parents:
254
diff
changeset
|
842 |
9 | 843 if (!path) return NULL; |
844 | |
260
249a9a6cd27f
Improve remove_trailing_slash() so it allocates no more than
zas_
parents:
254
diff
changeset
|
845 l = strlen(path); |
726 | 846 while (l > 1 && path[l - 1] == G_DIR_SEPARATOR) l--; |
9 | 847 |
260
249a9a6cd27f
Improve remove_trailing_slash() so it allocates no more than
zas_
parents:
254
diff
changeset
|
848 return g_strndup(path, l); |
9 | 849 } |
850 | |
851 static void tab_completion_select_cancel_cb(FileDialog *fd, gpointer data) | |
852 { | |
853 TabCompData *td = data; | |
854 | |
855 td->fd = NULL; | |
856 file_dialog_close(fd); | |
857 } | |
858 | |
859 static void tab_completion_select_ok_cb(FileDialog *fd, gpointer data) | |
860 { | |
861 TabCompData *td = data; | |
862 | |
863 gtk_entry_set_text(GTK_ENTRY(td->entry), gtk_entry_get_text(GTK_ENTRY(fd->entry))); | |
864 | |
865 tab_completion_select_cancel_cb(fd, data); | |
866 | |
867 tab_completion_emit_enter_signal(td); | |
868 } | |
869 | |
870 static void tab_completion_select_show(TabCompData *td) | |
871 { | |
872 const gchar *title; | |
873 const gchar *path; | |
874 | |
875 if (td->fd) | |
876 { | |
877 gtk_window_present(GTK_WINDOW(GENERIC_DIALOG(td->fd)->dialog)); | |
878 return; | |
879 } | |
880 | |
881 title = (td->fd_title) ? td->fd_title : _("Select path"); | |
1174
0bea79d87065
Drop useless wmclass stuff. Gtk will take care of it and as said in the documentation using gtk_window_set_wmclass() is sort of pointless.
zas_
parents:
1167
diff
changeset
|
882 td->fd = file_dialog_new(title, "select_path", td->entry, |
9 | 883 tab_completion_select_cancel_cb, td); |
884 file_dialog_add_button(td->fd, GTK_STOCK_OK, NULL, | |
885 tab_completion_select_ok_cb, TRUE); | |
886 | |
887 generic_dialog_add_message(GENERIC_DIALOG(td->fd), NULL, title, NULL); | |
888 | |
889 path = gtk_entry_get_text(GTK_ENTRY(td->entry)); | |
890 if (strlen(path) == 0) path = NULL; | |
891 if (td->fd_folders_only) | |
892 { | |
893 file_dialog_add_path_widgets(td->fd, NULL, path, td->history_key, NULL, NULL); | |
894 } | |
895 else | |
896 { | |
897 file_dialog_add_path_widgets(td->fd, NULL, path, td->history_key, "*", _("All files")); | |
898 } | |
899 | |
900 gtk_widget_show(GENERIC_DIALOG(td->fd)->dialog); | |
901 } | |
902 | |
903 static void tab_completion_select_pressed(GtkWidget *widget, gpointer data) | |
904 { | |
905 TabCompData *td = data; | |
906 | |
907 tab_completion_select_show(td); | |
908 } | |
909 | |
910 void tab_completion_add_select_button(GtkWidget *entry, const gchar *title, gint folders_only) | |
911 { | |
912 TabCompData *td; | |
913 GtkWidget *parent; | |
914 GtkWidget *hbox; | |
915 | |
916 td = g_object_get_data(G_OBJECT(entry), "tab_completion_data"); | |
917 | |
918 if (!td) return; | |
919 | |
920 g_free(td->fd_title); | |
921 td->fd_title = g_strdup(title); | |
922 td->fd_folders_only = folders_only; | |
923 | |
924 if (td->fd_button) return; | |
925 | |
926 parent = (td->combo) ? td->combo : td->entry; | |
927 | |
928 hbox = gtk_widget_get_parent(parent); | |
929 if (!GTK_IS_BOX(hbox)) return; | |
930 | |
931 td->fd_button = gtk_button_new_with_label("..."); | |
932 g_signal_connect(G_OBJECT(td->fd_button), "size_allocate", | |
933 G_CALLBACK(tab_completion_button_size_allocate), parent); | |
934 g_signal_connect(G_OBJECT(td->fd_button), "clicked", | |
935 G_CALLBACK(tab_completion_select_pressed), td); | |
936 | |
937 gtk_box_pack_start(GTK_BOX(hbox), td->fd_button, FALSE, FALSE, 0); | |
938 | |
939 gtk_widget_show(td->fd_button); | |
940 } | |
1055
1646720364cf
Adding a vim modeline to all files - patch by Klaus Ethgen
nadvornik
parents:
1043
diff
changeset
|
941 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ |