Mercurial > geeqie
comparison src/ui_bookmark.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 | 04ff0df3ad2f |
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 | |
21 #include <gtk/gtk.h> | |
22 | |
23 #include <gdk/gdkkeysyms.h> /* for key values */ | |
24 | |
25 #include "ui_bookmark.h" | |
26 | |
27 #include "ui_fileops.h" | |
28 #include "ui_menu.h" | |
29 #include "ui_misc.h" | |
30 #include "ui_utildlg.h" | |
31 #include "ui_tabcomp.h" | |
32 | |
33 | |
34 /* | |
35 *----------------------------------------------------------------------------- | |
36 * history lists | |
37 *----------------------------------------------------------------------------- | |
38 */ | |
39 | |
40 #define HISTORY_DEFAULT_KEY_COUNT 16 | |
41 | |
42 | |
43 typedef struct _HistoryData HistoryData; | |
44 struct _HistoryData | |
45 { | |
46 gchar *key; | |
47 GList *list; | |
48 }; | |
49 | |
50 static GList *history_list = NULL; | |
51 | |
52 | |
53 static gchar *quoted_from_text(const gchar *text) | |
54 { | |
55 const gchar *ptr; | |
56 gint c = 0; | |
57 gint l = strlen(text); | |
58 | |
59 if (l == 0) return NULL; | |
60 | |
61 while (c < l && text[c] !='"') c++; | |
62 if (text[c] == '"') | |
63 { | |
64 gint e; | |
65 c++; | |
66 ptr = text + c; | |
67 e = c; | |
68 while (e < l && text[e] !='"') e++; | |
69 if (text[e] == '"') | |
70 { | |
71 if (e - c > 0) | |
72 { | |
73 return g_strndup(ptr, e - c); | |
74 } | |
75 } | |
76 } | |
77 return NULL; | |
78 } | |
79 | |
80 gint history_list_load(const gchar *path) | |
81 { | |
82 FILE *f; | |
83 gchar *key = NULL; | |
84 gchar s_buf[1024]; | |
85 gchar *pathl; | |
86 | |
87 pathl = path_from_utf8(path); | |
88 f = fopen(pathl, "r"); | |
89 g_free(pathl); | |
90 if (!f) return FALSE; | |
91 | |
92 /* first line must start with History comment */ | |
93 if (!fgets(s_buf,1024,f) || | |
94 strncmp(s_buf, "#History", 8) != 0) | |
95 { | |
96 fclose(f); | |
97 return FALSE; | |
98 } | |
99 | |
100 while (fgets(s_buf,1024,f)) | |
101 { | |
102 if (s_buf[0]=='#') continue; | |
103 if (s_buf[0]=='[') | |
104 { | |
105 gint c; | |
106 gchar *ptr; | |
107 | |
108 ptr = s_buf + 1; | |
109 c = 0; | |
110 while(ptr[c] != ']' && ptr[c] != '\n' && ptr[c] != '\0') c++; | |
111 | |
112 g_free(key); | |
113 key = g_strndup(ptr, c); | |
114 } | |
115 else | |
116 { | |
117 gchar *value; | |
118 | |
119 value = quoted_from_text(s_buf); | |
120 if (value && key) | |
121 { | |
122 history_list_add_to_key(key, value, 0); | |
123 } | |
124 g_free(value); | |
125 } | |
126 } | |
127 | |
128 fclose(f); | |
129 | |
130 g_free(key); | |
131 | |
132 return TRUE; | |
133 } | |
134 | |
135 gint history_list_save(const gchar *path) | |
136 { | |
137 FILE *f; | |
138 GList *list; | |
139 gchar *pathl; | |
140 | |
141 pathl = path_from_utf8(path); | |
142 f = fopen(pathl, "w"); | |
143 g_free(pathl); | |
144 if (!f) | |
145 { | |
146 gchar *buf; | |
147 | |
148 buf = g_strdup_printf(_("Unable to write history lists to: %s\n"), path); | |
149 print_term(buf); | |
150 g_free(buf); | |
151 | |
152 return FALSE; | |
153 } | |
154 | |
155 fprintf(f, "#History lists\n"); | |
156 fprintf(f, "\n"); | |
157 | |
158 list = g_list_last(history_list); | |
159 while(list) | |
160 { | |
161 HistoryData *hd; | |
162 GList *work; | |
163 | |
164 hd = list->data; | |
165 list = list->prev; | |
166 | |
167 fprintf(f, "[%s]\n", hd->key); | |
168 | |
169 /* save them inverted (oldest to newest) | |
170 * so that when reading they are added correctly | |
171 */ | |
172 work = g_list_last(hd->list); | |
173 while(work) | |
174 { | |
175 fprintf(f, "\"%s\"\n", (gchar *)work->data); | |
176 work = work->prev; | |
177 } | |
178 fprintf(f, "\n"); | |
179 } | |
180 | |
181 fprintf(f, "#end\n"); | |
182 | |
183 fclose(f); | |
184 | |
185 return TRUE; | |
186 } | |
187 | |
188 static void history_list_free(HistoryData *hd) | |
189 { | |
190 GList *work; | |
191 | |
192 if (!hd) return; | |
193 | |
194 work = hd->list; | |
195 while(work) | |
196 { | |
197 g_free(work->data); | |
198 work = work->next; | |
199 } | |
200 | |
201 g_free(hd->key); | |
202 g_free(hd); | |
203 } | |
204 | |
205 static HistoryData *history_list_find_by_key(const gchar* key) | |
206 { | |
207 GList *work = history_list; | |
208 | |
209 if (!key) return NULL; | |
210 | |
211 while (work) | |
212 { | |
213 HistoryData *hd = work->data; | |
214 if (strcmp(hd->key, key) == 0) return hd; | |
215 work = work->next; | |
216 } | |
217 return NULL; | |
218 } | |
219 | |
220 const gchar *history_list_find_last_path_by_key(const gchar* key) | |
221 { | |
222 HistoryData *hd; | |
223 | |
224 hd = history_list_find_by_key(key); | |
225 if (!hd || !hd->list) return NULL; | |
226 | |
227 return hd->list->data; | |
228 } | |
229 | |
230 void history_list_free_key(const gchar *key) | |
231 { | |
232 HistoryData *hd; | |
233 hd = history_list_find_by_key(key); | |
234 if (!hd) return; | |
235 | |
236 history_list = g_list_remove(history_list, hd); | |
237 history_list_free(hd); | |
238 } | |
239 | |
240 void history_list_add_to_key(const gchar *key, const gchar *path, gint max) | |
241 { | |
242 HistoryData *hd; | |
243 GList *work; | |
244 | |
245 if (!key || !path) return; | |
246 | |
247 hd = history_list_find_by_key(key); | |
248 if (!hd) | |
249 { | |
250 hd = g_new(HistoryData, 1); | |
251 hd->key = g_strdup(key); | |
252 hd->list = NULL; | |
253 history_list = g_list_prepend(history_list, hd); | |
254 } | |
255 | |
256 /* if already in the list, simply move it to the top */ | |
257 work = hd->list; | |
258 while(work) | |
259 { | |
260 gchar *buf = work->data; | |
261 work = work->next; | |
262 if (strcmp(buf, path) == 0) | |
263 { | |
264 hd->list = g_list_remove(hd->list, buf); | |
265 hd->list = g_list_prepend(hd->list, buf); | |
266 return; | |
267 } | |
268 } | |
269 | |
270 hd->list = g_list_prepend(hd->list, g_strdup(path)); | |
271 | |
272 if (max == -1) max = HISTORY_DEFAULT_KEY_COUNT; | |
273 if (max > 0) | |
274 { | |
275 while(hd->list && g_list_length(hd->list) > max) | |
276 { | |
277 GList *work = g_list_last(hd->list); | |
278 gchar *buf = work->data; | |
279 hd->list = g_list_remove(hd->list, buf); | |
280 g_free(buf); | |
281 } | |
282 } | |
283 } | |
284 | |
285 void history_list_item_change(const gchar *key, const gchar *oldpath, const gchar *newpath) | |
286 { | |
287 HistoryData *hd; | |
288 GList *work; | |
289 | |
290 if (!oldpath) return; | |
291 hd = history_list_find_by_key(key); | |
292 if (!hd) return; | |
293 | |
294 work = hd->list; | |
295 while(work) | |
296 { | |
297 gchar *buf = work->data; | |
298 if (strcmp(buf, oldpath) == 0) | |
299 { | |
300 if (newpath) | |
301 { | |
302 work->data = g_strdup(newpath); | |
303 } | |
304 else | |
305 { | |
306 hd->list = g_list_remove(hd->list, buf); | |
307 } | |
308 g_free(buf); | |
309 return; | |
310 } | |
311 work = work->next; | |
312 } | |
313 } | |
314 | |
315 void history_list_item_move(const gchar *key, const gchar *path, gint direction) | |
316 { | |
317 HistoryData *hd; | |
318 GList *work; | |
319 gint p = 0; | |
320 | |
321 if (!path) return; | |
322 hd = history_list_find_by_key(key); | |
323 if (!hd) return; | |
324 | |
325 work = hd->list; | |
326 while (work) | |
327 { | |
328 gchar *buf = work->data; | |
329 if (strcmp(buf, path) == 0) | |
330 { | |
331 p += direction; | |
332 if (p < 0) return; | |
333 hd->list = g_list_remove(hd->list, buf); | |
334 hd->list = g_list_insert(hd->list, buf, p); | |
335 return; | |
336 } | |
337 work = work->next; | |
338 p++; | |
339 } | |
340 } | |
341 | |
342 void history_list_item_remove(const gchar *key, const gchar *path) | |
343 { | |
344 history_list_item_change(key, path, NULL); | |
345 } | |
346 | |
347 GList *history_list_get_by_key(const gchar *key) | |
348 { | |
349 HistoryData *hd; | |
350 | |
351 hd = history_list_find_by_key(key); | |
352 if (!hd) return NULL; | |
353 | |
354 return hd->list; | |
355 } | |
356 | |
357 /* | |
358 *----------------------------------------------------------------------------- | |
359 * bookmarks | |
360 *----------------------------------------------------------------------------- | |
361 */ | |
362 | |
363 #define BOOKMARK_DATA_KEY "bookmarkdata" | |
364 #define MARKER_PATH "[path]" | |
365 #define MARKER_ICON "[icon]" | |
366 | |
367 typedef struct _BookMarkData BookMarkData; | |
368 typedef struct _BookButtonData BookButtonData; | |
369 typedef struct _BookPropData BookPropData; | |
370 | |
371 struct _BookMarkData | |
372 { | |
373 GtkWidget *widget; | |
374 GtkWidget *box; | |
375 gchar *key; | |
376 | |
377 void (*select_func)(const gchar *path, gpointer data); | |
378 gpointer select_data; | |
379 | |
380 gint no_defaults; | |
381 gint editable; | |
382 | |
383 BookButtonData *active_button; | |
384 }; | |
385 | |
386 struct _BookButtonData | |
387 { | |
388 GtkWidget *button; | |
389 GtkWidget *image; | |
390 GtkWidget *label; | |
391 | |
392 gchar *key; | |
393 gchar *name; | |
394 gchar *path; | |
395 gchar *icon; | |
396 gchar *parent; | |
397 }; | |
398 | |
399 struct _BookPropData | |
400 { | |
401 GtkWidget *name_entry; | |
402 GtkWidget *path_entry; | |
403 GtkWidget *icon_entry; | |
404 | |
405 BookButtonData *bb; | |
406 }; | |
407 | |
408 enum { | |
409 TARGET_URI_LIST, | |
410 TARGET_X_URL, | |
411 TARGET_TEXT_PLAIN | |
412 }; | |
413 | |
414 static GtkTargetEntry bookmark_drop_types[] = { | |
415 { "text/uri-list", 0, TARGET_URI_LIST }, | |
416 { "x-url/http", 0, TARGET_X_URL }, | |
417 { "_NETSCAPE_URL", 0, TARGET_X_URL } | |
418 }; | |
419 #define bookmark_drop_types_n 3 | |
420 | |
421 static GtkTargetEntry bookmark_drag_types[] = { | |
422 { "text/uri-list", 0, TARGET_URI_LIST }, | |
423 { "text/plain", 0, TARGET_TEXT_PLAIN } | |
424 }; | |
425 #define bookmark_drag_types_n 2 | |
426 | |
427 | |
428 static GList *bookmark_widget_list = NULL; | |
429 static GList *bookmark_default_list = NULL; | |
430 | |
431 | |
432 static void bookmark_populate_all(const gchar *key); | |
433 | |
434 | |
435 static BookButtonData *bookmark_from_string(const gchar *text) | |
436 { | |
437 BookButtonData *b; | |
438 const gchar *path_ptr; | |
439 const gchar *icon_ptr; | |
440 | |
441 b = g_new0(BookButtonData, 1); | |
442 | |
443 if (!text) | |
444 { | |
445 b->name = g_strdup(_("New Bookmark")); | |
446 b->path = g_strdup(homedir()); | |
447 b->key = NULL; | |
448 return b; | |
449 } | |
450 | |
451 b->key = g_strdup(text); | |
452 | |
453 path_ptr = strstr(text, MARKER_PATH); | |
454 icon_ptr = strstr(text, MARKER_ICON); | |
455 | |
456 if (path_ptr && icon_ptr && icon_ptr < path_ptr) | |
457 { | |
458 printf("warning, bookmark icon must be after path\n"); | |
459 return NULL; | |
460 } | |
461 | |
462 if (path_ptr) | |
463 { | |
464 gint l; | |
465 | |
466 l = path_ptr - text; | |
467 b->name = g_strndup(text, l); | |
468 path_ptr += strlen(MARKER_PATH); | |
469 if (icon_ptr) | |
470 { | |
471 l = icon_ptr - path_ptr; | |
472 b->path = g_strndup(path_ptr, l); | |
473 } | |
474 else | |
475 { | |
476 b->path = g_strdup(path_ptr); | |
477 } | |
478 } | |
479 else | |
480 { | |
481 b->name = g_strdup(text); | |
482 b->path = g_strdup(""); | |
483 } | |
484 | |
485 if (icon_ptr) | |
486 { | |
487 icon_ptr += strlen(MARKER_ICON); | |
488 b->icon = g_strdup(icon_ptr); | |
489 } | |
490 | |
491 return b; | |
492 } | |
493 | |
494 static void bookmark_free(BookButtonData *b) | |
495 { | |
496 if (!b) return; | |
497 | |
498 g_free(b->name); | |
499 g_free(b->path); | |
500 g_free(b->icon); | |
501 g_free(b->key); | |
502 g_free(b->parent); | |
503 g_free(b); | |
504 } | |
505 | |
506 static gchar *bookmark_string(const gchar *name, const gchar *path, const gchar *icon) | |
507 { | |
508 if (!name) name = _("New Bookmark"); | |
509 if (icon && strncmp(icon, "/", 1) != 0) icon = NULL; | |
510 | |
511 if (icon) | |
512 { | |
513 return g_strdup_printf("%s"MARKER_PATH"%s"MARKER_ICON"%s", name, path, icon); | |
514 } | |
515 | |
516 return g_strdup_printf("%s"MARKER_PATH"%s", name, path); | |
517 } | |
518 | |
519 static void bookmark_select_cb(GtkWidget *button, gpointer data) | |
520 { | |
521 BookMarkData *bm = data; | |
522 BookButtonData *b; | |
523 | |
524 b = g_object_get_data(G_OBJECT(button), "bookbuttondata"); | |
525 if (!b) return; | |
526 | |
527 if (bm->select_func) bm->select_func(b->path, bm->select_data); | |
528 } | |
529 | |
530 static void bookmark_edit_destroy_cb(GtkWidget *widget, gpointer data) | |
531 { | |
532 BookPropData *p = data; | |
533 | |
534 bookmark_free(p->bb); | |
535 g_free(p); | |
536 } | |
537 | |
538 static void bookmark_edit_cancel_cb(GenericDialog *gd, gpointer data) | |
539 { | |
540 } | |
541 | |
542 static void bookmark_edit_ok_cb(GenericDialog *gd, gpointer data) | |
543 { | |
544 BookPropData *p = data; | |
545 const gchar *name; | |
546 gchar *path; | |
547 const gchar *icon; | |
548 gchar *new; | |
549 | |
550 name = gtk_entry_get_text(GTK_ENTRY(p->name_entry)); | |
551 path = remove_trailing_slash(gtk_entry_get_text(GTK_ENTRY(p->path_entry))); | |
552 icon = gtk_entry_get_text(GTK_ENTRY(p->icon_entry)); | |
553 | |
554 new = bookmark_string(name, path, icon); | |
555 | |
556 if (p->bb->key) | |
557 { | |
558 history_list_item_change(p->bb->parent, p->bb->key, new); | |
559 } | |
560 else | |
561 { | |
562 history_list_add_to_key(p->bb->parent, new, 0); | |
563 } | |
564 | |
565 if (path && strlen(path) > 0) tab_completion_append_to_history(p->path_entry, path); | |
566 if (icon && strlen(icon) > 0) tab_completion_append_to_history(p->icon_entry, icon); | |
567 | |
568 g_free(path); | |
569 g_free(new); | |
570 | |
571 bookmark_populate_all(p->bb->parent); | |
572 } | |
573 | |
574 /* simply pass NULL for text to turn this into a 'new bookmark' dialog */ | |
575 | |
576 static void bookmark_edit(const gchar *key, const gchar *text, GtkWidget *parent) | |
577 { | |
578 BookPropData *p; | |
579 GenericDialog *gd; | |
580 GtkWidget *table; | |
581 GtkWidget *label; | |
582 const gchar *icon; | |
583 | |
584 if (!key) key = "bookmarks"; | |
585 | |
586 p = g_new0(BookPropData, 1); | |
587 | |
588 p->bb = bookmark_from_string(text); | |
589 p->bb->parent = g_strdup(key); | |
590 | |
591 gd = generic_dialog_new(_("Edit Bookmark"), PACKAGE, "bookmark_edit", | |
592 parent, TRUE, | |
593 bookmark_edit_cancel_cb, p); | |
594 g_signal_connect(G_OBJECT(gd->dialog), "destroy", | |
595 G_CALLBACK(bookmark_edit_destroy_cb), p); | |
596 | |
597 generic_dialog_add_message(gd, NULL, _("Edit Bookmark"), NULL); | |
598 | |
599 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, | |
600 bookmark_edit_ok_cb, TRUE); | |
601 | |
602 table = pref_table_new(gd->vbox, 3, 2, FALSE, TRUE); | |
603 pref_table_label(table, 0, 0, _("Name:"), 1.0); | |
604 | |
605 p->name_entry = gtk_entry_new(); | |
606 gtk_widget_set_size_request(p->name_entry, 300, -1); | |
607 if (p->bb->name) gtk_entry_set_text(GTK_ENTRY(p->name_entry), p->bb->name); | |
608 gtk_table_attach_defaults(GTK_TABLE(table), p->name_entry, 1, 2, 0, 1); | |
609 generic_dialog_attach_default(gd, p->name_entry); | |
610 gtk_widget_show(p->name_entry); | |
611 | |
612 pref_table_label(table, 0, 1, _("Path:"), 1.0); | |
613 | |
614 label = tab_completion_new_with_history(&p->path_entry, p->bb->path, | |
615 "bookmark_path", -1, NULL, NULL); | |
616 tab_completion_add_select_button(p->path_entry, NULL, TRUE); | |
617 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2); | |
618 generic_dialog_attach_default(gd, p->path_entry); | |
619 gtk_widget_show(label); | |
620 | |
621 pref_table_label(table, 0, 2, _("Icon:"), 1.0); | |
622 | |
623 icon = p->bb->icon; | |
624 if (!icon) icon = ""; | |
625 label = tab_completion_new_with_history(&p->icon_entry, icon, | |
626 "bookmark_icons", -1, NULL, NULL); | |
627 tab_completion_add_select_button(p->icon_entry, _("Select icon"), FALSE); | |
628 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 2, 3); | |
629 generic_dialog_attach_default(gd, p->icon_entry); | |
630 gtk_widget_show(label); | |
631 | |
632 gtk_widget_show(gd->dialog); | |
633 } | |
634 | |
635 static void bookmark_move(BookMarkData *bm, GtkWidget *button, gint direction) | |
636 { | |
637 BookButtonData *b; | |
638 gint p; | |
639 GList *list; | |
640 gchar *key_holder; | |
641 | |
642 if (!bm->editable) return; | |
643 | |
644 b = g_object_get_data(G_OBJECT(button), "bookbuttondata"); | |
645 if (!b) return; | |
646 | |
647 list = gtk_container_get_children(GTK_CONTAINER(bm->box)); | |
648 p = g_list_index(list, button); | |
649 g_list_free(list); | |
650 | |
651 if (p < 0 || p + direction < 0) return; | |
652 | |
653 key_holder = bm->key; | |
654 bm->key = "_TEMPHOLDER"; | |
655 history_list_item_move(key_holder, b->key, -direction); | |
656 bookmark_populate_all(key_holder); | |
657 bm->key = key_holder; | |
658 | |
659 gtk_box_reorder_child(GTK_BOX(bm->box), button, p + direction); | |
660 } | |
661 | |
662 static void bookmark_menu_prop_cb(GtkWidget *widget, gpointer data) | |
663 { | |
664 BookMarkData *bm = data; | |
665 | |
666 if (!bm->active_button) return; | |
667 | |
668 bookmark_edit(bm->key, bm->active_button->key, widget); | |
669 } | |
670 | |
671 static void bookmark_menu_move(BookMarkData *bm, gint direction) | |
672 { | |
673 if (!bm->active_button) return; | |
674 | |
675 bookmark_move(bm, bm->active_button->button, direction); | |
676 } | |
677 | |
678 static void bookmark_menu_up_cb(GtkWidget *widget, gpointer data) | |
679 { | |
680 bookmark_menu_move(data, -1); | |
681 } | |
682 | |
683 static void bookmark_menu_down_cb(GtkWidget *widget, gpointer data) | |
684 { | |
685 bookmark_menu_move(data, 1); | |
686 } | |
687 | |
688 static void bookmark_menu_remove_cb(GtkWidget *widget, gpointer data) | |
689 { | |
690 BookMarkData *bm = data; | |
691 | |
692 if (!bm->active_button) return; | |
693 | |
694 history_list_item_remove(bm->key, bm->active_button->key); | |
695 bookmark_populate_all(bm->key); | |
696 } | |
697 | |
698 static void bookmark_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gint *pushed_in, gpointer data) | |
699 { | |
700 GtkWidget *button = data; | |
701 | |
702 gdk_window_get_origin(button->window, x, y); | |
703 *y += button->allocation.y + button->allocation.height; | |
704 } | |
705 | |
706 static void bookmark_menu_popup(BookMarkData *bm, GtkWidget *button, | |
707 gint button_n, guint32 time, gint local) | |
708 { | |
709 GtkWidget *menu; | |
710 BookButtonData *b; | |
711 | |
712 b = g_object_get_data(G_OBJECT(button), "bookbuttondata"); | |
713 if (!b) return; | |
714 | |
715 bm->active_button = b; | |
716 | |
717 menu = popup_menu_short_lived(); | |
718 menu_item_add_stock_sensitive(menu, _("_Properties..."), GTK_STOCK_PROPERTIES, bm->editable, | |
719 G_CALLBACK(bookmark_menu_prop_cb), bm); | |
720 menu_item_add_stock_sensitive(menu, _("Move _up"), GTK_STOCK_GO_UP, bm->editable, | |
721 G_CALLBACK(bookmark_menu_up_cb), bm); | |
722 menu_item_add_stock_sensitive(menu, _("Move _down"), GTK_STOCK_GO_DOWN, bm->editable, | |
723 G_CALLBACK(bookmark_menu_down_cb), bm); | |
724 menu_item_add_stock_sensitive(menu, _("_Remove"), GTK_STOCK_REMOVE, bm->editable, | |
725 G_CALLBACK(bookmark_menu_remove_cb), bm); | |
726 | |
727 if (local) | |
728 { | |
729 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, | |
730 bookmark_menu_position_cb, button, button_n, time); | |
731 } | |
732 else | |
733 { | |
734 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button_n, time); | |
735 } | |
736 } | |
737 | |
738 static gint bookmark_press_cb(GtkWidget *button, GdkEventButton *event, gpointer data) | |
739 { | |
740 BookMarkData *bm = data; | |
741 | |
742 if (event->button != 3) return FALSE; | |
743 | |
744 bookmark_menu_popup(bm, button, event->button, event->time, FALSE); | |
745 | |
746 return TRUE; | |
747 } | |
748 | |
749 static gint bookmark_keypress_cb(GtkWidget *button, GdkEventKey *event, gpointer data) | |
750 { | |
751 BookMarkData *bm = data; | |
752 | |
753 switch (event->keyval) | |
754 { | |
755 case GDK_F10: | |
756 if (!(event->state & GDK_CONTROL_MASK)) return FALSE; | |
757 case GDK_Menu: | |
758 bookmark_menu_popup(bm, button, 0, event->time, TRUE); | |
759 return TRUE; | |
760 break; | |
761 case GDK_Up: | |
762 if (event->state & GDK_SHIFT_MASK) | |
763 { | |
764 bookmark_move(bm, button, -1); | |
765 return TRUE; | |
766 } | |
767 break; | |
768 case GDK_Down: | |
769 if (event->state & GDK_SHIFT_MASK) | |
770 { | |
771 bookmark_move(bm, button, 1); | |
772 return TRUE; | |
773 } | |
774 break; | |
775 } | |
776 | |
777 return FALSE; | |
778 } | |
779 | |
780 static void bookmark_drag_set_data(GtkWidget *button, | |
781 GdkDragContext *context, GtkSelectionData *selection_data, | |
782 guint info, guint time, gpointer data) | |
783 { | |
784 BookMarkData *bm = data; | |
785 BookButtonData *b; | |
786 gchar *uri_text = NULL; | |
787 gint length = 0; | |
788 GList *list = NULL; | |
789 | |
790 if (context->dest_window == bm->widget->window) return; | |
791 | |
792 b = g_object_get_data(G_OBJECT(button), "bookbuttondata"); | |
793 if (!b) return; | |
794 | |
795 list = g_list_append(list, b->path); | |
796 | |
797 switch (info) | |
798 { | |
799 case TARGET_URI_LIST: | |
800 uri_text = uri_text_from_list(list, &length, FALSE); | |
801 break; | |
802 case TARGET_TEXT_PLAIN: | |
803 uri_text = uri_text_from_list(list, &length, TRUE); | |
804 break; | |
805 } | |
806 | |
807 g_list_free(list); | |
808 | |
809 if (!uri_text) return; | |
810 | |
811 gtk_selection_data_set(selection_data, selection_data->target, | |
812 8, uri_text, length); | |
813 g_free(uri_text); | |
814 } | |
815 | |
816 static void bookmark_drag_begin(GtkWidget *button, GdkDragContext *context, gpointer data) | |
817 { | |
818 GdkPixbuf *pixbuf; | |
819 GdkModifierType mask; | |
820 gint x, y; | |
821 | |
822 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, | |
823 button->allocation.width, button->allocation.height); | |
824 gdk_pixbuf_get_from_drawable(pixbuf, button->window, NULL, | |
825 button->allocation.x, button->allocation.y, | |
826 0, 0, button->allocation.width, button->allocation.height); | |
827 | |
828 gdk_window_get_pointer(button->window, &x, &y, &mask); | |
829 | |
830 gtk_drag_set_icon_pixbuf(context, pixbuf, | |
831 x - button->allocation.x, y - button->allocation.y); | |
832 g_object_unref(pixbuf); | |
833 } | |
834 | |
835 static void bookmark_populate(BookMarkData *bm) | |
836 { | |
837 GtkBox *box; | |
838 GList *work; | |
839 GList *children; | |
840 | |
841 box = GTK_BOX(bm->box); | |
842 children = gtk_container_get_children(GTK_CONTAINER(box)); | |
843 work = children; | |
844 while (work) | |
845 { | |
846 GtkWidget *widget = GTK_WIDGET(work->data); | |
847 work = work->next; | |
848 gtk_widget_destroy(widget); | |
849 } | |
850 | |
851 if (!bm->no_defaults && !history_list_get_by_key(bm->key)) | |
852 { | |
853 gchar *buf; | |
854 gchar *path; | |
855 | |
856 if (!bookmark_default_list) | |
857 { | |
858 buf = bookmark_string(_("Home"), homedir(), NULL); | |
859 history_list_add_to_key(bm->key, buf, 0); | |
860 g_free(buf); | |
861 | |
862 path = concat_dir_and_file(homedir(), "Desktop"); | |
863 if (isname(path)) | |
864 { | |
865 buf = bookmark_string(_("Desktop"), path, NULL); | |
866 history_list_add_to_key(bm->key, buf, 0); | |
867 g_free(buf); | |
868 } | |
869 g_free(path); | |
870 } | |
871 | |
872 work = bookmark_default_list; | |
873 while (work && work->next) | |
874 { | |
875 gchar *name; | |
876 | |
877 name = work->data; | |
878 work = work->next; | |
879 path = work->data; | |
880 work = work->next; | |
881 | |
882 buf = bookmark_string(name, path, NULL); | |
883 history_list_add_to_key(bm->key, buf, 0); | |
884 g_free(buf); | |
885 } | |
886 } | |
887 | |
888 work = history_list_get_by_key(bm->key); | |
889 work = g_list_last(work); | |
890 while (work) | |
891 { | |
892 BookButtonData *b; | |
893 | |
894 b = bookmark_from_string(work->data); | |
895 if (b) | |
896 { | |
897 GtkWidget *box; | |
898 | |
899 b->button = gtk_button_new(); | |
900 gtk_button_set_relief(GTK_BUTTON(b->button), GTK_RELIEF_NONE); | |
901 gtk_box_pack_start(GTK_BOX(bm->box), b->button, FALSE, FALSE, 0); | |
902 gtk_widget_show(b->button); | |
903 | |
904 g_object_set_data_full(G_OBJECT(b->button), "bookbuttondata", | |
905 b, (GDestroyNotify)bookmark_free); | |
906 | |
907 box = gtk_hbox_new(FALSE, PREF_PAD_BUTTON_GAP); | |
908 gtk_container_add(GTK_CONTAINER(b->button), box); | |
909 gtk_widget_show(box); | |
910 | |
911 if (b->icon) | |
912 { | |
913 GdkPixbuf *pixbuf; | |
914 gchar *iconl; | |
915 | |
916 iconl = path_from_utf8(b->icon); | |
917 pixbuf = gdk_pixbuf_new_from_file(iconl, NULL); | |
918 g_free(iconl); | |
919 if (pixbuf) | |
920 { | |
921 GdkPixbuf *scaled; | |
922 gint w, h; | |
923 | |
924 w = h = 16; | |
925 gtk_icon_size_lookup(GTK_ICON_SIZE_BUTTON, &w, &h); | |
926 | |
927 scaled = gdk_pixbuf_scale_simple(pixbuf, w, h, | |
928 GDK_INTERP_BILINEAR); | |
929 b->image = gtk_image_new_from_pixbuf(scaled); | |
930 g_object_unref(scaled); | |
931 g_object_unref(pixbuf); | |
932 } | |
933 else | |
934 { | |
935 b->image = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, | |
936 GTK_ICON_SIZE_BUTTON); | |
937 } | |
938 } | |
939 else | |
940 { | |
941 b->image = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_BUTTON); | |
942 } | |
943 gtk_box_pack_start(GTK_BOX(box), b->image, FALSE, FALSE, 0); | |
944 gtk_widget_show(b->image); | |
945 | |
946 b->label = gtk_label_new(b->name); | |
947 gtk_box_pack_start(GTK_BOX(box), b->label, FALSE, FALSE, 0); | |
948 gtk_widget_show(b->label); | |
949 | |
950 g_signal_connect(G_OBJECT(b->button), "clicked", | |
951 G_CALLBACK(bookmark_select_cb), bm); | |
952 g_signal_connect(G_OBJECT(b->button), "button_press_event", | |
953 G_CALLBACK(bookmark_press_cb), bm); | |
954 g_signal_connect(G_OBJECT(b->button), "key_press_event", | |
955 G_CALLBACK(bookmark_keypress_cb), bm); | |
956 | |
957 gtk_drag_source_set(b->button, GDK_BUTTON1_MASK, | |
958 bookmark_drag_types, bookmark_drag_types_n, | |
959 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK); | |
960 g_signal_connect(G_OBJECT(b->button), "drag_data_get", | |
961 G_CALLBACK(bookmark_drag_set_data), bm); | |
962 g_signal_connect(G_OBJECT(b->button), "drag_begin", | |
963 G_CALLBACK(bookmark_drag_begin), bm); | |
964 } | |
965 | |
966 work = work->prev; | |
967 } | |
968 } | |
969 | |
970 static void bookmark_populate_all(const gchar *key) | |
971 { | |
972 GList *work; | |
973 | |
974 if (!key) return; | |
975 | |
976 work = bookmark_widget_list; | |
977 while (work) | |
978 { | |
979 BookMarkData *bm; | |
980 | |
981 bm = work->data; | |
982 work = work->next; | |
983 | |
984 if (strcmp(bm->key, key) == 0) | |
985 { | |
986 bookmark_populate(bm); | |
987 } | |
988 } | |
989 } | |
990 | |
991 static void bookmark_dnd_get_data(GtkWidget *widget, | |
992 GdkDragContext *context, gint x, gint y, | |
993 GtkSelectionData *selection_data, guint info, | |
994 guint time, gpointer data) | |
995 { | |
996 BookMarkData *bm = data; | |
997 GList *list = NULL; | |
998 GList *work; | |
999 | |
1000 if (!bm->editable) return; | |
1001 | |
1002 switch (info) | |
1003 { | |
1004 case TARGET_URI_LIST: | |
1005 case TARGET_X_URL: | |
1006 list = uri_list_from_text(selection_data->data, FALSE); | |
1007 break; | |
1008 } | |
1009 | |
1010 work = list; | |
1011 while (work) | |
1012 { | |
1013 gchar *path = work->data; | |
1014 gchar *buf; | |
1015 | |
1016 buf = bookmark_string(filename_from_path(path), path, NULL); | |
1017 history_list_add_to_key(bm->key, buf, 0); | |
1018 g_free(buf); | |
1019 | |
1020 work = work->next; | |
1021 } | |
1022 | |
1023 path_list_free(list); | |
1024 | |
1025 bookmark_populate_all(bm->key); | |
1026 } | |
1027 | |
1028 static void bookmark_list_destroy(GtkWidget *widget, gpointer data) | |
1029 { | |
1030 BookMarkData *bm = data; | |
1031 | |
1032 bookmark_widget_list = g_list_remove(bookmark_widget_list, bm); | |
1033 | |
1034 g_free(bm->key); | |
1035 g_free(bm); | |
1036 } | |
1037 | |
1038 GtkWidget *bookmark_list_new(const gchar *key, | |
1039 void (*select_func)(const gchar *path, gpointer data), gpointer select_data) | |
1040 { | |
1041 GtkWidget *scrolled; | |
1042 BookMarkData *bm; | |
1043 | |
1044 if (!key) key = "bookmarks"; | |
1045 | |
1046 bm = g_new0(BookMarkData, 1); | |
1047 bm->key = g_strdup(key); | |
1048 | |
1049 bm->select_func = select_func; | |
1050 bm->select_data = select_data; | |
1051 | |
1052 bm->no_defaults = FALSE; | |
1053 bm->editable = TRUE; | |
1054 | |
1055 scrolled = gtk_scrolled_window_new(NULL, NULL); | |
1056 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), | |
1057 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); | |
1058 | |
1059 bm->box = gtk_vbox_new(FALSE, 0); | |
1060 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), bm->box); | |
1061 gtk_widget_show(bm->box); | |
1062 | |
1063 bookmark_populate(bm); | |
1064 | |
1065 g_signal_connect(G_OBJECT(bm->box), "destroy", | |
1066 G_CALLBACK(bookmark_list_destroy), bm); | |
1067 g_object_set_data(G_OBJECT(bm->box), BOOKMARK_DATA_KEY, bm); | |
1068 g_object_set_data(G_OBJECT(scrolled), BOOKMARK_DATA_KEY, bm); | |
1069 bm->widget = scrolled; | |
1070 | |
1071 gtk_drag_dest_set(scrolled, | |
1072 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, | |
1073 bookmark_drop_types, bookmark_drop_types_n, | |
1074 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK); | |
1075 g_signal_connect(G_OBJECT(scrolled), "drag_data_received", | |
1076 G_CALLBACK(bookmark_dnd_get_data), bm); | |
1077 | |
1078 bookmark_widget_list = g_list_append(bookmark_widget_list, bm); | |
1079 | |
1080 return scrolled; | |
1081 } | |
1082 | |
1083 void bookmark_list_set_key(GtkWidget *list, const gchar *key) | |
1084 { | |
1085 BookMarkData *bm; | |
1086 | |
1087 if (!list || !key) return; | |
1088 | |
1089 bm = g_object_get_data(G_OBJECT(list), BOOKMARK_DATA_KEY); | |
1090 if (!bm) return; | |
1091 | |
1092 if (bm->key && strcmp(bm->key, key) == 0) return; | |
1093 | |
1094 g_free(bm->key); | |
1095 bm->key = g_strdup(key); | |
1096 | |
1097 bookmark_populate(bm); | |
1098 } | |
1099 | |
1100 void bookmark_list_set_no_defaults(GtkWidget *list, gint no_defaults) | |
1101 { | |
1102 BookMarkData *bm; | |
1103 | |
1104 bm = g_object_get_data(G_OBJECT(list), BOOKMARK_DATA_KEY); | |
1105 if (!bm) return; | |
1106 | |
1107 bm->no_defaults = no_defaults; | |
1108 } | |
1109 | |
1110 void bookmark_list_set_editable(GtkWidget *list, gint editable) | |
1111 { | |
1112 BookMarkData *bm; | |
1113 | |
1114 bm = g_object_get_data(G_OBJECT(list), BOOKMARK_DATA_KEY); | |
1115 if (!bm) return; | |
1116 | |
1117 bm->editable = editable; | |
1118 } | |
1119 | |
1120 void bookmark_list_add(GtkWidget *list, const gchar *name, const gchar *path) | |
1121 { | |
1122 BookMarkData *bm; | |
1123 gchar *buf; | |
1124 | |
1125 bm = g_object_get_data(G_OBJECT(list), BOOKMARK_DATA_KEY); | |
1126 if (!bm) return; | |
1127 | |
1128 buf = bookmark_string(name, path, NULL); | |
1129 history_list_add_to_key(bm->key, buf, 0); | |
1130 g_free(buf); | |
1131 | |
1132 bookmark_populate_all(bm->key); | |
1133 } | |
1134 | |
1135 void bookmark_add_default(const gchar *name, const gchar *path) | |
1136 { | |
1137 if (!name || !path) return; | |
1138 bookmark_default_list = g_list_append(bookmark_default_list, g_strdup(name)); | |
1139 bookmark_default_list = g_list_append(bookmark_default_list, g_strdup(path)); | |
1140 } | |
1141 | |
1142 /* | |
1143 *----------------------------------------------------------------------------- | |
1144 * combo with history key | |
1145 *----------------------------------------------------------------------------- | |
1146 */ | |
1147 | |
1148 typedef struct _HistoryComboData HistoryComboData; | |
1149 struct _HistoryComboData | |
1150 { | |
1151 GtkWidget *combo; | |
1152 GtkWidget *entry; | |
1153 gchar *history_key; | |
1154 gint history_levels; | |
1155 }; | |
1156 | |
1157 static void history_combo_destroy(GtkWidget *widget, gpointer data) | |
1158 { | |
1159 HistoryComboData *hc = data; | |
1160 | |
1161 g_free(hc->history_key); | |
1162 g_free(data); | |
1163 } | |
1164 | |
1165 /* if text is NULL, entry is set to the most recent item */ | |
1166 GtkWidget *history_combo_new(GtkWidget **entry, const gchar *text, | |
1167 const gchar *history_key, gint max_levels) | |
1168 { | |
1169 HistoryComboData *hc; | |
1170 GList *work; | |
1171 gint n = 0; | |
1172 | |
1173 hc = g_new0(HistoryComboData, 1); | |
1174 hc->history_key = g_strdup(history_key); | |
1175 hc->history_levels = max_levels; | |
1176 | |
1177 hc->combo = gtk_combo_box_entry_new_text(); | |
1178 #if 0 | |
1179 gtk_combo_set_case_sensitive(GTK_COMBO(hc->combo), TRUE); | |
1180 gtk_combo_set_use_arrows(GTK_COMBO(hc->combo), FALSE); | |
1181 #endif | |
1182 | |
1183 hc->entry = GTK_BIN(hc->combo)->child; | |
1184 | |
1185 g_object_set_data(G_OBJECT(hc->combo), "history_combo_data", hc); | |
1186 g_object_set_data(G_OBJECT(hc->entry), "history_combo_data", hc); | |
1187 g_signal_connect(G_OBJECT(hc->combo), "destroy", | |
1188 G_CALLBACK(history_combo_destroy), hc); | |
1189 | |
1190 work = history_list_get_by_key(hc->history_key); | |
1191 while (work) | |
1192 { | |
1193 gtk_combo_box_append_text(GTK_COMBO_BOX(hc->combo), (gchar *)work->data); | |
1194 work = work->next; | |
1195 n++; | |
1196 } | |
1197 | |
1198 if (text) | |
1199 { | |
1200 gtk_entry_set_text(GTK_ENTRY(hc->entry), text); | |
1201 } | |
1202 else if (n > 0) | |
1203 { | |
1204 gtk_combo_box_set_active(GTK_COMBO_BOX(hc->combo), 0); | |
1205 } | |
1206 | |
1207 if (entry) *entry = hc->entry; | |
1208 return hc->combo; | |
1209 } | |
1210 | |
1211 /* if text is NULL, current entry text is used | |
1212 * widget can be the combo or entry widget | |
1213 */ | |
1214 void history_combo_append_history(GtkWidget *widget, const gchar *text) | |
1215 { | |
1216 HistoryComboData *hc; | |
1217 gchar *new_text; | |
1218 | |
1219 hc = g_object_get_data(G_OBJECT(widget), "history_combo_data"); | |
1220 if (!hc) | |
1221 { | |
1222 printf("widget is not a history combo\n"); | |
1223 return; | |
1224 } | |
1225 | |
1226 if (text) | |
1227 { | |
1228 new_text = g_strdup(text); | |
1229 } | |
1230 else | |
1231 { | |
1232 new_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(hc->entry))); | |
1233 } | |
1234 | |
1235 if (new_text && strlen(new_text) > 0) | |
1236 { | |
1237 GtkTreeModel *store; | |
1238 GList *work; | |
1239 | |
1240 history_list_add_to_key(hc->history_key, new_text, hc->history_levels); | |
1241 | |
1242 gtk_combo_box_set_active(GTK_COMBO_BOX(hc->combo), -1); | |
1243 | |
1244 store = gtk_combo_box_get_model(GTK_COMBO_BOX(hc->combo)); | |
1245 gtk_list_store_clear(GTK_LIST_STORE(store)); | |
1246 | |
1247 work = history_list_get_by_key(hc->history_key); | |
1248 while (work) | |
1249 { | |
1250 gtk_combo_box_append_text(GTK_COMBO_BOX(hc->combo), (gchar *)work->data); | |
1251 work = work->next; | |
1252 } | |
1253 } | |
1254 | |
1255 g_free(new_text); | |
1256 } | |
1257 | |
1258 /* | |
1259 *----------------------------------------------------------------------------- | |
1260 * drag and drop uri utils | |
1261 *----------------------------------------------------------------------------- | |
1262 */ | |
1263 | |
1264 /* the following characters are allowed to be unencoded for pathnames: | |
1265 * $ & + , / : = @ | |
1266 */ | |
1267 static gint escape_char_list[] = { | |
1268 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0 */ | |
1269 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 */ | |
1270 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 */ | |
1271 /* spc ! " # $ % & ' */ | |
1272 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, /* 30 */ | |
1273 /* ( ) * + , - . / 0 1 */ | |
1274 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 */ | |
1275 /* 2 3 4 5 6 7 8 9 : ; */ | |
1276 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 50 */ | |
1277 /* < = > ? @ A B C D E */ | |
1278 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, /* 60 */ | |
1279 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 */ | |
1280 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 */ | |
1281 /* Z [ \ ] ^ _ ` a b c */ | |
1282 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, /* 90 */ | |
1283 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 100 */ | |
1284 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110 */ | |
1285 /* x y z { | } ~ del */ | |
1286 0, 0, 0, 1, 1, 1, 0, 0 /* 120, 127 is end */ | |
1287 }; | |
1288 | |
1289 static gchar *hex_char = "0123456789ABCDEF"; | |
1290 | |
1291 static gint escape_test(guchar c) | |
1292 { | |
1293 if (c < 32 || c > 127) return TRUE; | |
1294 return (escape_char_list[c] != 0); | |
1295 } | |
1296 | |
1297 static const gchar *escape_code(guchar c) | |
1298 { | |
1299 static gchar text[4]; | |
1300 | |
1301 text[0] = '%'; | |
1302 text[1] = hex_char[c>>4]; | |
1303 text[2] = hex_char[c%16]; | |
1304 text[3] = '\0'; | |
1305 | |
1306 return text; | |
1307 } | |
1308 | |
1309 gchar *uri_text_escape(const gchar *text) | |
1310 { | |
1311 GString *string; | |
1312 gchar *result; | |
1313 const gchar *p; | |
1314 | |
1315 if (!text) return NULL; | |
1316 | |
1317 string = g_string_new(""); | |
1318 | |
1319 p = text; | |
1320 while (*p != '\0') | |
1321 { | |
1322 if (escape_test(*p)) | |
1323 { | |
1324 g_string_append(string, escape_code(*p)); | |
1325 } | |
1326 else | |
1327 { | |
1328 g_string_append_c(string, *p); | |
1329 } | |
1330 p++; | |
1331 } | |
1332 | |
1333 result = string->str; | |
1334 g_string_free(string, FALSE); | |
1335 | |
1336 /* dropped filenames are expected to be utf-8 compatible */ | |
1337 if (!g_utf8_validate(result, -1, NULL)) | |
1338 { | |
1339 gchar *tmp; | |
1340 | |
1341 tmp = g_locale_to_utf8(result, -1, NULL, NULL, NULL); | |
1342 if (tmp) | |
1343 { | |
1344 g_free(result); | |
1345 result = tmp; | |
1346 } | |
1347 } | |
1348 | |
1349 return result; | |
1350 } | |
1351 | |
1352 /* this operates on the passed string, decoding escaped characters */ | |
1353 void uri_text_decode(gchar *text) | |
1354 { | |
1355 if (strchr(text, '%')) | |
1356 { | |
1357 gchar *w; | |
1358 gchar *r; | |
1359 | |
1360 w = r = text; | |
1361 | |
1362 while(*r != '\0') | |
1363 { | |
1364 if (*r == '%' && *(r + 1) != '\0' && *(r + 2) != '\0') | |
1365 { | |
1366 gchar t[3]; | |
1367 gint n; | |
1368 | |
1369 r++; | |
1370 t[0] = *r; | |
1371 r++; | |
1372 t[1] = *r; | |
1373 t[2] = '\0'; | |
1374 n = (gint)strtol(t, NULL, 16); | |
1375 if (n > 0 && n < 256) | |
1376 { | |
1377 *w = (gchar)n; | |
1378 } | |
1379 else | |
1380 { | |
1381 /* invalid number, rewind and ignore this escape */ | |
1382 r -= 2; | |
1383 *w = *r; | |
1384 } | |
1385 } | |
1386 else if (w != r) | |
1387 { | |
1388 *w = *r; | |
1389 } | |
1390 r++; | |
1391 w++; | |
1392 } | |
1393 if (*w != '\0') *w = '\0'; | |
1394 } | |
1395 } | |
1396 | |
1397 static void uri_list_parse_encoded_chars(GList *list) | |
1398 { | |
1399 GList *work = list; | |
1400 | |
1401 while (work) | |
1402 { | |
1403 gchar *text = work->data; | |
1404 | |
1405 uri_text_decode(text); | |
1406 | |
1407 work = work->next; | |
1408 } | |
1409 } | |
1410 | |
1411 GList *uri_list_from_text(gchar *data, gint files_only) | |
1412 { | |
1413 GList *list = NULL; | |
1414 gint b, e; | |
1415 | |
1416 b = e = 0; | |
1417 | |
1418 while (data[b] != '\0') | |
1419 { | |
1420 while (data[e] != '\r' && data[e] != '\n' && data[e] != '\0') e++; | |
1421 if (strncmp(data + b, "file:", 5) == 0) | |
1422 { | |
1423 gchar *path; | |
1424 b += 5; | |
1425 while (data[b] == '/' && data[b+1] == '/') b++; | |
1426 path = g_strndup(data + b, e - b); | |
1427 list = g_list_append(list, path_to_utf8(path)); | |
1428 g_free(path); | |
1429 } | |
1430 else if (!files_only && strncmp(data + b, "http:", 5) == 0) | |
1431 { | |
1432 list = g_list_append(list, g_strndup(data + b, e - b)); | |
1433 } | |
1434 else if (!files_only && strncmp(data + b, "ftp:", 3) == 0) | |
1435 { | |
1436 list = g_list_append(list, g_strndup(data + b, e - b)); | |
1437 } | |
1438 while (data[e] == '\r' || data[e] == '\n') e++; | |
1439 b = e; | |
1440 } | |
1441 | |
1442 uri_list_parse_encoded_chars(list); | |
1443 | |
1444 return list; | |
1445 } | |
1446 | |
1447 gchar *uri_text_from_list(GList *list, gint *len, gint plain_text) | |
1448 { | |
1449 gchar *uri_text = NULL; | |
1450 GString *string; | |
1451 GList *work; | |
1452 | |
1453 if (!list) | |
1454 { | |
1455 if (len) *len = 0; | |
1456 return NULL; | |
1457 } | |
1458 | |
1459 string = g_string_new(""); | |
1460 | |
1461 work = list; | |
1462 while (work) | |
1463 { | |
1464 const gchar *name8; /* dnd filenames are in utf-8 */ | |
1465 | |
1466 name8 = work->data; | |
1467 | |
1468 if (!plain_text) | |
1469 { | |
1470 gchar *escaped; | |
1471 | |
1472 escaped = uri_text_escape(name8); | |
1473 g_string_append(string, "file:"); | |
1474 g_string_append(string, escaped); | |
1475 g_free(escaped); | |
1476 | |
1477 g_string_append(string, "\r\n"); | |
1478 } | |
1479 else | |
1480 { | |
1481 g_string_append(string, name8); | |
1482 if (work->next) g_string_append(string, "\n"); | |
1483 } | |
1484 | |
1485 work = work->next; | |
1486 } | |
1487 | |
1488 uri_text = string->str; | |
1489 if (len) *len = string->len; | |
1490 g_string_free(string, FALSE); | |
1491 | |
1492 return uri_text; | |
1493 } | |
1494 |