Mercurial > geeqie
comparison src/ui_tabcomp.c @ 9:d907d608745f
Sync to GQview 1.5.9 release.
########
DO NOT BASE ENHANCEMENTS OR TRANSLATION UPDATES ON CODE IN THIS CVS!
This CVS is never up to date with current development and is provided
solely for reference purposes, please use the latest official release
package when making any changes or translation updates.
########
author | gqview |
---|---|
date | Sat, 26 Feb 2005 00:13:35 +0000 |
parents | |
children | 147f4c4b9025 |
comparison
equal
deleted
inserted
replaced
8:e0d0593d519e | 9:d907d608745f |
---|---|
1 /* | |
2 * (SLIK) SimpLIstic sKin functions | |
3 * (C) 2004 John Ellis | |
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 { | |
118 /* skips removed files */ | |
119 if (dir->d_ino > 0) | |
120 { | |
121 gchar *name = dir->d_name; | |
122 if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0) | |
123 { | |
124 list = g_list_prepend(list, path_to_utf8(name)); | |
125 } | |
126 } | |
127 } | |
128 closedir(dp); | |
129 | |
130 td->dir_path = g_strdup(path); | |
131 td->file_list = list; | |
132 } | |
133 | |
134 static void tab_completion_destroy(GtkWidget *widget, gpointer data) | |
135 { | |
136 TabCompData *td = data; | |
137 | |
138 tab_completion_free_list(td); | |
139 g_free(td->history_key); | |
140 | |
141 if (td->fd) file_dialog_close(td->fd); | |
142 g_free(td->fd_title); | |
143 | |
144 g_free(td); | |
145 } | |
146 | |
147 static gint tab_completion_emit_enter_signal(TabCompData *td) | |
148 { | |
149 gchar *text; | |
150 if (!td->enter_func) return FALSE; | |
151 | |
152 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(td->entry))); | |
153 | |
154 if (text[0] == '~') | |
155 { | |
156 gchar *t = text; | |
157 text = g_strconcat(homedir(), t + 1, NULL); | |
158 g_free(t); | |
159 } | |
160 | |
161 td->enter_func(text, td->enter_data); | |
162 g_free(text); | |
163 | |
164 return TRUE; | |
165 } | |
166 | |
167 static void tab_completion_emit_tab_signal(TabCompData *td) | |
168 { | |
169 gchar *text; | |
170 if (!td->tab_func) return; | |
171 | |
172 text = g_strdup(gtk_entry_get_text(GTK_ENTRY(td->entry))); | |
173 | |
174 if (text[0] == '~') | |
175 { | |
176 gchar *t = text; | |
177 text = g_strconcat(homedir(), t + 1, NULL); | |
178 g_free(t); | |
179 } | |
180 | |
181 td->tab_func(text, td->tab_data); | |
182 g_free(text); | |
183 } | |
184 | |
185 #ifdef TAB_COMPLETION_ENABLE_POPUP_MENU | |
186 | |
187 static gint tab_completion_popup_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) | |
188 { | |
189 TabCompData *td = data; | |
190 | |
191 if (event->keyval == GDK_Tab || | |
192 event->keyval == GDK_BackSpace || | |
193 (event->keyval >= 0x20 && event->keyval <= 0xFF) ) | |
194 { | |
195 if (event->keyval >= 0x20 && event->keyval <= 0xFF) | |
196 { | |
197 gchar buf[2]; | |
198 gint p = -1; | |
199 | |
200 buf[0] = event->keyval; | |
201 buf[1] = '\0'; | |
202 gtk_editable_insert_text(GTK_EDITABLE(td->entry), buf, 1, &p); | |
203 gtk_editable_set_position(GTK_EDITABLE(td->entry), -1); | |
204 } | |
205 | |
206 /*close the menu */ | |
207 gtk_menu_popdown(GTK_MENU(widget)); | |
208 /* doing this does not emit the "selection done" signal, unref it ourselves */ | |
209 gtk_widget_unref(widget); | |
210 | |
211 return TRUE; | |
212 } | |
213 | |
214 return FALSE; | |
215 } | |
216 | |
217 static void tab_completion_popup_cb(GtkWidget *widget, gpointer data) | |
218 { | |
219 gchar *name = data; | |
220 TabCompData *td; | |
221 gchar *buf; | |
222 gchar *ptr; | |
223 | |
224 td = g_object_get_data(G_OBJECT(widget), "tab_completion_data"); | |
225 if (!td) return; | |
226 | |
227 ptr = td->dir_path + strlen(td->dir_path) - 1; | |
228 buf = g_strconcat(td->dir_path, (ptr[0] == '/') ? "" : "/", name, NULL); | |
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; | |
244 | |
245 | |
246 gdk_window_get_origin(td->entry->window, x, y); | |
247 | |
248 height = MIN(td->entry->requisition.height, td->entry->allocation.height); | |
249 *y += height; | |
250 | |
251 length = strlen(gtk_entry_get_text(GTK_ENTRY(td->entry))); | |
252 gtk_entry_get_layout_offsets(GTK_ENTRY(td->entry), &xoffset, &yoffset); | |
253 | |
254 layout = gtk_entry_get_layout(GTK_ENTRY(td->entry)); | |
255 pango_layout_get_cursor_pos(layout, length, &strong_pos, &weak_pos); | |
256 *x += strong_pos.x / PANGO_SCALE + xoffset; | |
257 } | |
258 | |
259 static void tab_completion_popup_list(TabCompData *td, GList *list) | |
260 { | |
261 GtkWidget *menu; | |
262 GList *work; | |
263 GdkEvent *event; | |
264 guint32 etime; | |
265 gint ebutton; | |
266 gint count = 0; | |
267 | |
268 if (!list) return; | |
269 | |
270 #if 0 | |
271 /* | |
272 * well, the menu would be too long anyway... | |
273 * (listing /dev causes gtk+ window allocation errors, -> too big a window) | |
274 * this is why menu popups are disabled, this really should be a popup scrollable listview. | |
275 */ | |
276 if (g_list_length(list) > 200) return; | |
277 #endif | |
278 | |
279 menu = popup_menu_short_lived(); | |
280 | |
281 work = list; | |
282 while (work && count < TAB_COMP_POPUP_MAX) | |
283 { | |
284 gchar *name = work->data; | |
285 GtkWidget *item; | |
286 | |
287 item = menu_item_add_simple(menu, name, G_CALLBACK(tab_completion_popup_cb), name); | |
288 g_object_set_data(G_OBJECT(item), "tab_completion_data", td); | |
289 | |
290 work = work->next; | |
291 count++; | |
292 } | |
293 | |
294 g_signal_connect(G_OBJECT(menu), "key_press_event", | |
295 G_CALLBACK(tab_completion_popup_key_press), td); | |
296 | |
297 /* peek at the current event to get the time, etc. */ | |
298 event = gtk_get_current_event(); | |
299 | |
300 if (event && event->type == GDK_BUTTON_RELEASE) | |
301 { | |
302 ebutton = event->button.button; | |
303 } | |
304 else | |
305 { | |
306 ebutton = 0; | |
307 } | |
308 | |
309 if (event) | |
310 { | |
311 etime = gdk_event_get_time(event); | |
312 gdk_event_free(event); | |
313 } | |
314 else | |
315 { | |
316 etime = 0; | |
317 } | |
318 | |
319 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, | |
320 tab_completion_popup_pos_cb, td, ebutton, etime); | |
321 } | |
322 | |
323 #ifndef CASE_SORT | |
324 #define CASE_SORT strcmp | |
325 #endif | |
326 | |
327 static gint simple_sort(gconstpointer a, gconstpointer b) | |
328 { | |
329 return CASE_SORT((gchar *)a, (gchar *)b); | |
330 } | |
331 | |
332 #endif | |
333 | |
334 static gint tab_completion_do(TabCompData *td) | |
335 { | |
336 const gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(td->entry)); | |
337 const gchar *entry_file; | |
338 gchar *entry_dir; | |
339 gchar *ptr; | |
340 gint home_exp = FALSE; | |
341 | |
342 /* home dir expansion */ | |
343 if (entry_text[0] == '~') | |
344 { | |
345 entry_dir = g_strconcat(homedir(), entry_text + 1, NULL); | |
346 home_exp = TRUE; | |
347 } | |
348 else | |
349 { | |
350 entry_dir = g_strdup(entry_text); | |
351 } | |
352 | |
353 entry_file = filename_from_path(entry_text); | |
354 | |
355 if (isfile(entry_dir)) | |
356 { | |
357 if (home_exp) | |
358 { | |
359 gtk_entry_set_text(GTK_ENTRY(td->entry), entry_dir); | |
360 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(entry_dir)); | |
361 } | |
362 g_free(entry_dir); | |
363 return home_exp; | |
364 } | |
365 if (isdir(entry_dir) && strcmp(entry_file, ".") != 0 && strcmp(entry_file, "..") != 0) | |
366 { | |
367 ptr = entry_dir + strlen(entry_dir) - 1; | |
368 if (ptr[0] == '/') | |
369 { | |
370 if (home_exp) | |
371 { | |
372 gtk_entry_set_text(GTK_ENTRY(td->entry), entry_dir); | |
373 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(entry_dir)); | |
374 } | |
375 | |
376 tab_completion_read_dir(td, entry_dir); | |
377 td->file_list = g_list_sort(td->file_list, simple_sort); | |
378 if (td->file_list && !td->file_list->next) | |
379 { | |
380 gchar *buf; | |
381 const gchar *file; | |
382 | |
383 file = td->file_list->data; | |
384 buf = g_strconcat(entry_dir, file, NULL); | |
385 if (isdir(buf)) | |
386 { | |
387 g_free(buf); | |
388 buf = g_strconcat(entry_dir, file, "/", NULL); | |
389 } | |
390 gtk_entry_set_text(GTK_ENTRY(td->entry), buf); | |
391 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf)); | |
392 g_free(buf); | |
393 } | |
394 | |
395 #ifdef TAB_COMPLETION_ENABLE_POPUP_MENU | |
396 | |
397 else | |
398 { | |
399 tab_completion_popup_list(td, td->file_list); | |
400 } | |
401 #endif | |
402 | |
403 g_free(entry_dir); | |
404 return home_exp; | |
405 } | |
406 else | |
407 { | |
408 gchar *buf = g_strconcat(entry_dir, "/", NULL); | |
409 gtk_entry_set_text(GTK_ENTRY(td->entry), buf); | |
410 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf)); | |
411 g_free(buf); | |
412 g_free(entry_dir); | |
413 return TRUE; | |
414 } | |
415 } | |
416 | |
417 ptr = (gchar *)filename_from_path(entry_dir); | |
418 if (ptr > entry_dir) ptr--; | |
419 ptr[0] = '\0'; | |
420 | |
421 if (strlen(entry_dir) == 0) | |
422 { | |
423 g_free(entry_dir); | |
424 entry_dir = g_strdup("/"); | |
425 } | |
426 | |
427 if (isdir(entry_dir)) | |
428 { | |
429 GList *list; | |
430 GList *poss = NULL; | |
431 gint l = strlen(entry_file); | |
432 | |
433 if (!td->dir_path || !td->file_list || strcmp(td->dir_path, entry_dir) != 0) | |
434 { | |
435 tab_completion_read_dir(td, entry_dir); | |
436 } | |
437 | |
438 if (strcmp(entry_dir, "/") == 0) entry_dir[0] = '\0'; | |
439 | |
440 list = td->file_list; | |
441 while(list) | |
442 { | |
443 gchar *file = list->data; | |
444 if (strncmp(entry_file, file, l) == 0) | |
445 { | |
446 poss = g_list_prepend(poss, file); | |
447 } | |
448 list = list->next; | |
449 } | |
450 | |
451 if (poss) | |
452 { | |
453 if (!poss->next) | |
454 { | |
455 gchar *file = poss->data; | |
456 gchar *buf; | |
457 | |
458 buf = g_strconcat(entry_dir, "/", file, NULL); | |
459 | |
460 if (isdir(buf)) | |
461 { | |
462 g_free(buf); | |
463 buf = g_strconcat(entry_dir, "/", file, "/", NULL); | |
464 } | |
465 gtk_entry_set_text(GTK_ENTRY(td->entry), buf); | |
466 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf)); | |
467 g_free(buf); | |
468 g_list_free(poss); | |
469 g_free(entry_dir); | |
470 return TRUE; | |
471 } | |
472 else | |
473 { | |
474 gint c = strlen(entry_file); | |
475 gint done = FALSE; | |
476 gchar *test_file = poss->data; | |
477 | |
478 while (!done) | |
479 { | |
480 list = poss; | |
481 if (!list) done = TRUE; | |
482 while(list && !done) | |
483 { | |
484 gchar *file = list->data; | |
485 if (strlen(file) < c || strncmp(test_file, file, c) != 0) | |
486 { | |
487 done = TRUE; | |
488 } | |
489 list = list->next; | |
490 } | |
491 c++; | |
492 } | |
493 c -= 2; | |
494 if (c > 0) | |
495 { | |
496 gchar *file; | |
497 gchar *buf; | |
498 file = g_strdup(test_file); | |
499 file[c] = '\0'; | |
500 buf = g_strconcat(entry_dir, "/", file, NULL); | |
501 gtk_entry_set_text(GTK_ENTRY(td->entry), buf); | |
502 gtk_editable_set_position(GTK_EDITABLE(td->entry), strlen(buf)); | |
503 | |
504 #ifdef TAB_COMPLETION_ENABLE_POPUP_MENU | |
505 | |
506 poss = g_list_sort(poss, simple_sort); | |
507 tab_completion_popup_list(td, poss); | |
508 | |
509 #endif | |
510 | |
511 g_free(file); | |
512 g_free(buf); | |
513 g_list_free(poss); | |
514 g_free(entry_dir); | |
515 return TRUE; | |
516 } | |
517 } | |
518 g_list_free(poss); | |
519 } | |
520 } | |
521 | |
522 g_free(entry_dir); | |
523 | |
524 return FALSE; | |
525 } | |
526 | |
527 static gint tab_completion_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data) | |
528 { | |
529 TabCompData *td = data; | |
530 gint stop_signal = FALSE; | |
531 | |
532 switch (event->keyval) | |
533 { | |
534 case GDK_Tab: | |
535 if (!(event->state & GDK_CONTROL_MASK)) | |
536 { | |
537 if (tab_completion_do(td)) | |
538 { | |
539 tab_completion_emit_tab_signal(td); | |
540 } | |
541 stop_signal = TRUE; | |
542 } | |
543 break; | |
544 case GDK_Return: case GDK_KP_Enter: | |
545 if (td->fd_button && | |
546 (event->state & GDK_CONTROL_MASK)) | |
547 { | |
548 tab_completion_select_show(td); | |
549 stop_signal = TRUE; | |
550 } | |
551 else if (tab_completion_emit_enter_signal(td)) | |
552 { | |
553 stop_signal = TRUE; | |
554 } | |
555 break; | |
556 default: | |
557 break; | |
558 } | |
559 | |
560 if (stop_signal) g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event"); | |
561 | |
562 return (stop_signal); | |
563 } | |
564 | |
565 static void tab_completion_button_pressed(GtkWidget *widget, gpointer data) | |
566 { | |
567 TabCompData *td; | |
568 GtkWidget *entry = data; | |
569 | |
570 td = g_object_get_data(G_OBJECT(entry), "tab_completion_data"); | |
571 | |
572 if (!td) return; | |
573 | |
574 if (!GTK_WIDGET_HAS_FOCUS(entry)) | |
575 { | |
576 gtk_widget_grab_focus(entry); | |
577 } | |
578 | |
579 if (tab_completion_do(td)) | |
580 { | |
581 tab_completion_emit_tab_signal(td); | |
582 } | |
583 } | |
584 | |
585 static void tab_completion_button_size_allocate(GtkWidget *button, GtkAllocation *allocation, gpointer data) | |
586 { | |
587 GtkWidget *parent = data; | |
588 | |
589 if (allocation->height > parent->allocation.height) | |
590 { | |
591 GtkAllocation button_allocation; | |
592 | |
593 button_allocation = button->allocation; | |
594 button_allocation.height = parent->allocation.height; | |
595 button_allocation.y = parent->allocation.y + | |
596 (parent->allocation.height - parent->allocation.height) / 2; | |
597 gtk_widget_size_allocate(button, &button_allocation); | |
598 } | |
599 } | |
600 | |
601 static GtkWidget *tab_completion_create_complete_button(GtkWidget *entry, GtkWidget *parent) | |
602 { | |
603 GtkWidget *button; | |
604 GtkWidget *icon; | |
605 GdkPixbuf *pixbuf; | |
606 | |
607 button = gtk_button_new(); | |
608 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS); | |
609 g_signal_connect(G_OBJECT(button), "size_allocate", | |
610 G_CALLBACK(tab_completion_button_size_allocate), parent); | |
611 g_signal_connect(G_OBJECT(button), "clicked", | |
612 G_CALLBACK(tab_completion_button_pressed), entry); | |
613 | |
614 pixbuf = gdk_pixbuf_new_from_inline(-1, icon_tabcomp, FALSE, NULL); | |
615 icon = gtk_image_new_from_pixbuf(pixbuf); | |
616 gdk_pixbuf_unref(pixbuf); | |
617 gtk_container_add(GTK_CONTAINER(button), icon); | |
618 gtk_widget_show(icon); | |
619 | |
620 return button; | |
621 } | |
622 | |
623 /* | |
624 *---------------------------------------------------------------------------- | |
625 * public interface | |
626 *---------------------------------------------------------------------------- | |
627 */ | |
628 | |
629 GtkWidget *tab_completion_new_with_history(GtkWidget **entry, const gchar *text, | |
630 const gchar *history_key, gint max_levels, | |
631 void (*enter_func)(const gchar *, gpointer), gpointer data) | |
632 { | |
633 GtkWidget *box; | |
634 GtkWidget *combo; | |
635 GtkWidget *combo_entry; | |
636 GtkWidget *button; | |
637 GList *work; | |
638 TabCompData *td; | |
639 gint n = 0; | |
640 | |
641 box = gtk_hbox_new(FALSE, 0); | |
642 | |
643 combo = gtk_combo_box_entry_new_text(); | |
644 gtk_box_pack_start(GTK_BOX(box), combo, TRUE, TRUE, 0); | |
645 gtk_widget_show(combo); | |
646 | |
647 combo_entry = GTK_BIN(combo)->child; | |
648 #if 0 | |
649 gtk_combo_set_case_sensitive(GTK_COMBO(combo), TRUE); | |
650 gtk_combo_set_use_arrows(GTK_COMBO(combo), FALSE); | |
651 #endif | |
652 | |
653 button = tab_completion_create_complete_button(combo_entry, combo); | |
654 gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0); | |
655 gtk_widget_show(button); | |
656 | |
657 tab_completion_add_to_entry(combo_entry, enter_func, data); | |
658 | |
659 td = g_object_get_data(G_OBJECT(combo_entry), "tab_completion_data"); | |
660 if (!td) return NULL; /* this should never happen! */ | |
661 | |
662 td->combo = combo; | |
663 td->has_history = TRUE; | |
664 td->history_key = g_strdup(history_key); | |
665 td->history_levels = max_levels; | |
666 | |
667 work = history_list_get_by_key(td->history_key); | |
668 | |
669 work = history_list_get_by_key(history_key); | |
670 while (work) | |
671 { | |
672 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), (gchar *)work->data); | |
673 work = work->next; | |
674 n++; | |
675 } | |
676 | |
677 if (text) | |
678 { | |
679 gtk_entry_set_text(GTK_ENTRY(combo_entry), text); | |
680 } | |
681 else if (n > 0) | |
682 { | |
683 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); | |
684 } | |
685 | |
686 if (entry) *entry = combo_entry; | |
687 return box; | |
688 } | |
689 | |
690 const gchar *tab_completion_set_to_last_history(GtkWidget *entry) | |
691 { | |
692 TabCompData *td = g_object_get_data(G_OBJECT(entry), "tab_completion_data"); | |
693 const gchar *buf; | |
694 | |
695 if (!td || !td->has_history) return NULL; | |
696 | |
697 buf = history_list_find_last_path_by_key(td->history_key); | |
698 if (buf) | |
699 { | |
700 gtk_entry_set_text(GTK_ENTRY(td->entry), buf); | |
701 } | |
702 | |
703 return buf; | |
704 } | |
705 | |
706 void tab_completion_append_to_history(GtkWidget *entry, const gchar *path) | |
707 { | |
708 TabCompData *td; | |
709 GtkTreeModel *store; | |
710 GList *work; | |
711 | |
712 td = g_object_get_data(G_OBJECT(entry), "tab_completion_data"); | |
713 | |
714 if (!path) return; | |
715 | |
716 if (!td || !td->has_history) return; | |
717 | |
718 history_list_add_to_key(td->history_key, path, td->history_levels); | |
719 | |
720 gtk_combo_box_set_active(GTK_COMBO_BOX(td->combo), -1); | |
721 | |
722 store = gtk_combo_box_get_model(GTK_COMBO_BOX(td->combo)); | |
723 gtk_list_store_clear(GTK_LIST_STORE(store)); | |
724 | |
725 work = history_list_get_by_key(td->history_key); | |
726 while (work) | |
727 { | |
728 gtk_combo_box_append_text(GTK_COMBO_BOX(td->combo), (gchar *)work->data); | |
729 work = work->next; | |
730 } | |
731 } | |
732 | |
733 GtkWidget *tab_completion_new(GtkWidget **entry, const gchar *text, | |
734 void (*enter_func)(const gchar *, gpointer), gpointer data) | |
735 { | |
736 GtkWidget *hbox; | |
737 GtkWidget *button; | |
738 GtkWidget *newentry; | |
739 | |
740 hbox = gtk_hbox_new(FALSE, 0); | |
741 | |
742 newentry = gtk_entry_new(); | |
743 if (text) gtk_entry_set_text(GTK_ENTRY(newentry), text); | |
744 gtk_box_pack_start(GTK_BOX(hbox), newentry, TRUE, TRUE, 0); | |
745 gtk_widget_show(newentry); | |
746 | |
747 button = tab_completion_create_complete_button(newentry, newentry); | |
748 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); | |
749 gtk_widget_show(button); | |
750 | |
751 tab_completion_add_to_entry(newentry, enter_func, data); | |
752 | |
753 if (entry) *entry = newentry; | |
754 return hbox; | |
755 } | |
756 | |
757 void tab_completion_add_to_entry(GtkWidget *entry, void (*enter_func)(const gchar *, gpointer), gpointer data) | |
758 { | |
759 TabCompData *td; | |
760 if (!entry) | |
761 { | |
762 printf("Tab completion error: entry != NULL\n"); | |
763 return; | |
764 } | |
765 | |
766 td = g_new0(TabCompData, 1); | |
767 td->entry = entry; | |
768 td->dir_path = NULL; | |
769 td->file_list = NULL; | |
770 td->enter_func = enter_func; | |
771 td->enter_data = data; | |
772 td->tab_func = NULL; | |
773 td->tab_data = NULL; | |
774 | |
775 td->has_history = FALSE; | |
776 td->history_key = NULL; | |
777 td->history_levels = 0; | |
778 | |
779 g_object_set_data(G_OBJECT(td->entry), "tab_completion_data", td); | |
780 | |
781 g_signal_connect(G_OBJECT(entry), "key_press_event", | |
782 G_CALLBACK(tab_completion_key_pressed), td); | |
783 g_signal_connect(G_OBJECT(entry), "destroy", | |
784 G_CALLBACK(tab_completion_destroy), td); | |
785 } | |
786 | |
787 void tab_completion_add_tab_func(GtkWidget *entry, void (*tab_func)(const gchar *, gpointer), gpointer data) | |
788 { | |
789 TabCompData *td = g_object_get_data(G_OBJECT(entry), "tab_completion_data"); | |
790 | |
791 if (!td) return; | |
792 | |
793 td->tab_func = tab_func; | |
794 td->tab_data = data; | |
795 } | |
796 | |
797 gchar *remove_trailing_slash(const gchar *path) | |
798 { | |
799 gchar *ret; | |
800 gint l; | |
801 if (!path) return NULL; | |
802 | |
803 ret = g_strdup(path); | |
804 l = strlen(ret); | |
805 if (l > 1 && ret[l - 1] == '/') ret[l - 1] = '\0'; | |
806 | |
807 return ret; | |
808 } | |
809 | |
810 static void tab_completion_select_cancel_cb(FileDialog *fd, gpointer data) | |
811 { | |
812 TabCompData *td = data; | |
813 | |
814 td->fd = NULL; | |
815 file_dialog_close(fd); | |
816 } | |
817 | |
818 static void tab_completion_select_ok_cb(FileDialog *fd, gpointer data) | |
819 { | |
820 TabCompData *td = data; | |
821 | |
822 gtk_entry_set_text(GTK_ENTRY(td->entry), gtk_entry_get_text(GTK_ENTRY(fd->entry))); | |
823 | |
824 tab_completion_select_cancel_cb(fd, data); | |
825 | |
826 tab_completion_emit_enter_signal(td); | |
827 } | |
828 | |
829 static void tab_completion_select_show(TabCompData *td) | |
830 { | |
831 const gchar *title; | |
832 const gchar *path; | |
833 | |
834 if (td->fd) | |
835 { | |
836 gtk_window_present(GTK_WINDOW(GENERIC_DIALOG(td->fd)->dialog)); | |
837 return; | |
838 } | |
839 | |
840 title = (td->fd_title) ? td->fd_title : _("Select path"); | |
841 td->fd = file_dialog_new(title, PACKAGE, "select_path", td->entry, | |
842 tab_completion_select_cancel_cb, td); | |
843 file_dialog_add_button(td->fd, GTK_STOCK_OK, NULL, | |
844 tab_completion_select_ok_cb, TRUE); | |
845 | |
846 generic_dialog_add_message(GENERIC_DIALOG(td->fd), NULL, title, NULL); | |
847 | |
848 path = gtk_entry_get_text(GTK_ENTRY(td->entry)); | |
849 if (strlen(path) == 0) path = NULL; | |
850 if (td->fd_folders_only) | |
851 { | |
852 file_dialog_add_path_widgets(td->fd, NULL, path, td->history_key, NULL, NULL); | |
853 } | |
854 else | |
855 { | |
856 file_dialog_add_path_widgets(td->fd, NULL, path, td->history_key, "*", _("All files")); | |
857 } | |
858 | |
859 gtk_widget_show(GENERIC_DIALOG(td->fd)->dialog); | |
860 } | |
861 | |
862 static void tab_completion_select_pressed(GtkWidget *widget, gpointer data) | |
863 { | |
864 TabCompData *td = data; | |
865 | |
866 tab_completion_select_show(td); | |
867 } | |
868 | |
869 void tab_completion_add_select_button(GtkWidget *entry, const gchar *title, gint folders_only) | |
870 { | |
871 TabCompData *td; | |
872 GtkWidget *parent; | |
873 GtkWidget *hbox; | |
874 | |
875 td = g_object_get_data(G_OBJECT(entry), "tab_completion_data"); | |
876 | |
877 if (!td) return; | |
878 | |
879 g_free(td->fd_title); | |
880 td->fd_title = g_strdup(title); | |
881 td->fd_folders_only = folders_only; | |
882 | |
883 if (td->fd_button) return; | |
884 | |
885 parent = (td->combo) ? td->combo : td->entry; | |
886 | |
887 hbox = gtk_widget_get_parent(parent); | |
888 if (!GTK_IS_BOX(hbox)) return; | |
889 | |
890 td->fd_button = gtk_button_new_with_label("..."); | |
891 g_signal_connect(G_OBJECT(td->fd_button), "size_allocate", | |
892 G_CALLBACK(tab_completion_button_size_allocate), parent); | |
893 g_signal_connect(G_OBJECT(td->fd_button), "clicked", | |
894 G_CALLBACK(tab_completion_select_pressed), td); | |
895 | |
896 gtk_box_pack_start(GTK_BOX(hbox), td->fd_button, FALSE, FALSE, 0); | |
897 | |
898 gtk_widget_show(td->fd_button); | |
899 } | |
900 |