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