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
|
|
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
|