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