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