1 /* Bluefish HTML Editor
2 * bookmark.c - bookmarks
3 *
4 * Copyright (C) 2003 Oskar Swida
5 * modifications (C) 2004-2013 Olivier Sessink
6 * modifications (C) 2011-2012 James Hayward
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21 /*#define DEBUG*/
22 /*#define BMARKREF*/
23
24 #include <gtk/gtk.h>
25 #include <fcntl.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 #include "bluefish.h"
33 #include "bookmark.h"
34 #include "bf_lib.h"
35 #include "bfwin_uimanager.h"
36 #include "dialog_utils.h"
37 #include "document.h"
38 #include "gtk_easy.h"
39 #include "stringlist.h"
40
41 #ifdef BMARKREF
42 typedef struct {
43 gint itercount;
44 } Tbmarkref;
45
46 Tbmarkref bmarkref = { 0 };
47
48 #endif
49
50 /*
51 bookmarks will be loaded and saved to an arraylist (see stringlist.c). This
52 is a double linked list (GList *) with pointers to string arrays (gchar **).
53
54 To have the GUI work with them, we convert those arrays (gchar **) into a
55 Tbmark struct. This struct will have a pointer to the array (gchar **) so
56 on change it can directly change the array as well, without any need to
57 look it up in the list.
58
59 For the GUI, we store everything in a Gtktreestore. The treestore will have a
60 pointer to a string with the name, and it will also have a pointer to the
61 Tbmark. When the user clicks in the Gtktreeview widget, we can get
62 immediately a pointer to the Tbmark, and that has the Gtktextmark, so that
63 is very easy, and very fast!
64
65 All normal (non-project) windows do share the same bookmarks list.
66 So they store the same Gtktreestore as well. The project functions
67 create a new gtktreestore when they convert a window (Tbfwin)
68 into a project window.
69 */
70
71 #define BMARK_SHOW_NUM_TEXT_CHARS 40
72 #define BMARK_STORE_TEXT_NUM_CHARS 25
73
74 enum {
75 NAME_COLUMN, /* bookmark name */
76 PTR_COLUMN, /* bookmark pointer */
77 N_COLUMNS
78 };
79
80 enum {
81 BMARK_ADD_PERM_DIALOG,
82 BMARK_RENAME_TEMP_DIALOG,
83 BMARK_RENAME_PERM_DIALOG
84 };
85
86 typedef struct {
87 GtkTextMark *mark;
88 GFile *uri;
89 gint offset;
90 Tdocument *doc;
91 GtkTreeIter iter; /* for tree view */
92 gchar *description;
93 gchar *text;
94 gchar *name;
95 gint len; /* file length for integrity check - perhaps some hash code is needed */
96 gboolean is_temp;
97 gchar **strarr; /* this is a pointer to the location where this bookmark is stored in the sessionlist,
98 so we can immediately change it _in_ the list */
99 } Tbmark;
100 #define BMARK(var) ((Tbmark *)(var))
101
102 typedef struct {
103 GtkTreeStore *bookmarkstore; /* the treestore with the name and the pointer to the Tbmark */
104 GHashTable *bmarkfiles; /* a hash table with the GFile as key, and the iter in the treestore as value */
105 } Tbmarkdata;
106 #define BMARKDATA(var) ((Tbmarkdata *)(var))
107
108 enum {
109 BM_FMODE_FILE,
110 BM_FMODE_PATH,
111 BM_FMODE_URI,
112 BM_FMODE_HOME /* not implemented, defaults to full */
113 };
114
115 enum {
116 BM_SMODE_CONTENT,
117 BM_SMODE_NAME,
118 BM_SMODE_BOTH
119 };
120
121 enum {
122 BM_SEARCH_CONTENT,
123 BM_SEARCH_NAME,
124 BM_SEARCH_BOTH
125 };
126
127 /* Free bookmark structure */
128 static void
bmark_free(gpointer ptr)129 bmark_free(gpointer ptr)
130 {
131 Tbmark *m;
132 if (ptr == NULL)
133 return;
134 m = BMARK(ptr);
135 if (m->doc && m->mark) {
136 DEBUG_MSG("bmark_free, deleting GtkTextMark %p\n", m->mark);
137 gtk_text_buffer_delete_mark(m->doc->buffer, m->mark);
138 m->doc = NULL;
139 }
140 #ifdef DEBUG
141 if (m->strarr) {
142 DEBUG_MSG("bmark_free, NOT GOOD, strarr should be NULL here...\n");
143 }
144 #endif
145 DEBUG_MSG("bmark_free, unref uri %p\n", m->uri);
146 if (m->uri)
147 g_object_unref(m->uri);
148 g_free(m->text);
149 g_free(m->name);
150 g_free(m->description);
151 /*g_print("free bmark %p\n",m); */
152 g_slice_free(Tbmark, m);
153 }
154
155 static gchar *
bmark_showname(Tbfwin * bfwin,Tbmark * b)156 bmark_showname(Tbfwin * bfwin, Tbmark * b)
157 {
158 if (b->name && strlen(b->name) > 0 && bfwin->session->bookmarks_show_mode == BM_SMODE_BOTH) {
159 return g_strconcat(b->name, " - ", b->text, NULL);
160 } else if ((b->name && bfwin->session->bookmarks_show_mode == BM_SMODE_NAME) || !b->text) {
161 return g_strdup(b->name);
162 } else {
163 return g_strdup(b->text);
164 }
165 }
166
167 static gchar *
bmark_filename(Tbfwin * bfwin,GFile * uri)168 bmark_filename(Tbfwin * bfwin, GFile * uri)
169 {
170 gchar *title;
171 if (!uri) {
172 g_warning("Bookmark without uri! Please report this message as a bug!\n");
173 return g_strdup("Bug - please report");
174 }
175 if (g_file_has_uri_scheme(uri, "tmp")) {
176 DEBUG_MSG("bmark_filename, have untitled document with uri scheme %s and path %s\n",
177 g_file_get_uri_scheme(uri), g_file_get_basename(uri));
178 return g_file_get_basename(uri);
179 }
180
181 switch (bfwin->session->bookmarks_filename_mode) {
182 case BM_FMODE_PATH:
183 title = g_file_get_uri(uri);
184 break;
185 case BM_FMODE_FILE:
186 title = g_file_get_basename(uri);
187 break;
188 case BM_FMODE_URI:
189 default:
190 title = g_file_get_uri(uri);
191 break;
192 }
193 return title;
194 }
195
196 static GFile *
bmark_uri_from_doc(Tdocument * doc)197 bmark_uri_from_doc(Tdocument * doc)
198 {
199 GFile *uri;
200 if (!doc->uri) {
201 gchar *tmp = g_strdup_printf("tmp:///%s", gtk_label_get_text(GTK_LABEL(doc->tab_label)));
202 DEBUG_MSG("use uri '%s' for untitled document\n", tmp);
203 uri = g_file_new_for_uri(tmp);
204 g_free(tmp);
205 } else {
206 g_object_ref(doc->uri);
207 uri = doc->uri;
208 }
209 return uri;
210 }
211
212 static void
bmark_update_treestore_name(Tbfwin * bfwin)213 bmark_update_treestore_name(Tbfwin * bfwin)
214 {
215 GtkTreeIter piter, citer;
216 gboolean cont1, cont2;
217 cont1 = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &piter);
218
219 while (cont1) {
220 Tbmark *b = NULL;
221 gchar *name;
222 cont2 =
223 gtk_tree_model_iter_children(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &citer,
224 &piter);
225 /* first handle the filename of the parent */
226 if (cont2) {
227 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &citer, PTR_COLUMN,
228 &b, -1);
229 /* TODO: if the bookmark has a document, use the untitled number from that document */
230 name = bmark_filename(bfwin, b->uri);
231 gtk_tree_store_set(BMARKDATA(bfwin->bmarkdata)->bookmarkstore, &piter, NAME_COLUMN, name, -1);
232 g_free(name);
233 }
234 while (cont2) {
235 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &citer, PTR_COLUMN,
236 &b, -1);
237 name = bmark_showname(bfwin, b);
238 gtk_tree_store_set(BMARKDATA(bfwin->bmarkdata)->bookmarkstore, &citer, NAME_COLUMN, name, -1);
239 g_free(name);
240 cont2 =
241 gtk_tree_model_iter_next(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &citer);
242 }
243 cont1 = gtk_tree_model_iter_next(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &piter);
244 }
245 }
246
247 static void
bmark_update_offset_from_textmark(Tbmark * b)248 bmark_update_offset_from_textmark(Tbmark * b)
249 {
250 if (b->doc && b->mark) {
251 GtkTextIter it, it2;
252 int len;
253 gtk_text_buffer_get_iter_at_mark(b->doc->buffer, &it, b->mark);
254 b->offset = gtk_text_iter_get_offset(&it);
255 len = strlen(b->text);
256 /* to aid future repositioning (if the file has changed) update the text as well */
257 gtk_text_buffer_get_iter_at_offset(b->doc->buffer, &it2, b->offset + len);
258 g_free(b->text);
259 b->text = gtk_text_buffer_get_text(b->doc->buffer, &it, &it2, FALSE);
260 DEBUG_MSG("bmark_update_offset_from_textmark, text=%s\n", b->text);
261 }
262 }
263
264 /*
265 * this function should use a smart sorting algorithm to find
266 * the GtkTreeIter of the bookmark *before* the place where this
267 * bookmark should be added, but the same function can be used to
268 * find the bookmarks we have to check to detect double bookmarks
269 * at the same line.
270 *
271 * returns the bookmark closest before 'offset', or the bookmark exactly at 'offset'
272 *
273 * returns NULL if we have to append this as first child to the parent
274 *
275 */
276 static Tbmark *
bmark_find_bookmark_before_offset(Tbfwin * bfwin,guint offset,GtkTreeIter * parent)277 bmark_find_bookmark_before_offset(Tbfwin * bfwin, guint offset, GtkTreeIter * parent)
278 {
279 gboolean cont;
280 GtkTreeIter iter;
281 Tbmark *b1 = NULL, *b2;
282 cont =
283 gtk_tree_model_iter_children(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &iter,
284 parent);
285 while (cont) {
286 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &iter, PTR_COLUMN, &b2,
287 -1);
288 bmark_update_offset_from_textmark(b2);
289 if (b2->offset > offset)
290 return b1;
291 b1 = b2;
292 cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &iter);
293 }
294 return b1;
295 }
296
297 static void
bmark_rename_uri(Tbfwin * bfwin,Tbmark * b,Tdocument * doc)298 bmark_rename_uri(Tbfwin * bfwin, Tbmark * b, Tdocument * doc)
299 {
300 if (b->uri)
301 g_object_unref(b->uri);
302 b->uri = bmark_uri_from_doc(doc);
303 if (b->strarr != NULL) {
304 g_free(b->strarr[2]);
305 b->strarr[2] = g_file_get_parse_name(b->uri);
306 }
307 }
308
309 void
bmark_doc_renamed(Tbfwin * bfwin,Tdocument * doc)310 bmark_doc_renamed(Tbfwin * bfwin, Tdocument * doc)
311 {
312 if (!doc->bmark_parent) {
313 DEBUG_MSG("bmark_doc_renamed, doc %p with uri %p has no bmark_parent, return\n", doc, doc->uri);
314 return;
315 }
316 GtkTreeIter tmpiter;
317 gboolean cont;
318 gboolean parent_renamed = FALSE;
319
320 cont =
321 gtk_tree_model_iter_children(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &tmpiter,
322 doc->bmark_parent);
323 while (cont) {
324 Tbmark *b;
325 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &tmpiter,
326 PTR_COLUMN, &b, -1);
327 cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &tmpiter);
328 if (!b) {
329 continue;
330 }
331 #ifdef DEVELOPMENT
332 if (b->doc != doc) {
333 g_warning("bmark_doc_renamed, bug, b->doc(%p)!=doc(%p)\n", b->doc, doc);
334 g_assert_not_reached();
335 }
336 #endif
337
338 if (doc->uri == b->uri || (doc->uri && g_file_equal(doc->uri, b->uri))) {
339 return;
340 }
341
342 if (!parent_renamed) {
343 /* first remove the old uri from the hash table, but keep the GtkTreeIter stored in a variable */
344 GtkTreeIter *newiter;
345 newiter = g_slice_new(GtkTreeIter);
346 *newiter = *doc->bmark_parent;
347 g_hash_table_remove(BMARKDATA(bfwin->bmarkdata)->bmarkfiles, b->uri);
348 doc->bmark_parent = newiter;
349 }
350 bmark_rename_uri(bfwin, b, doc);
351 if (!parent_renamed) {
352 /* now use the new b->uri as new name, and insert the new uri in the hash table */
353 gchar *name = bmark_filename(bfwin, b->uri);
354 gtk_tree_store_set(BMARKDATA(bfwin->bmarkdata)->bookmarkstore, doc->bmark_parent, NAME_COLUMN,
355 name, -1);
356 g_object_ref(b->uri);
357 g_hash_table_insert(BMARKDATA(bfwin->bmarkdata)->bmarkfiles, b->uri, doc->bmark_parent);
358 DEBUG_MSG("bmark_doc_renamed, renamed parent to %s\n", name);
359 g_free(name);
360 parent_renamed = TRUE;
361 }
362 }
363 }
364
365 /* removes the bookmark from the session, removed the b->strarr pointer and frees it */
366 static void
bmark_unstore(Tbfwin * bfwin,Tbmark * b)367 bmark_unstore(Tbfwin * bfwin, Tbmark * b)
368 {
369 if (bfwin->session->bmarks == NULL || b->strarr == NULL)
370 return;
371 DEBUG_MSG("bmark_remove, removing bookmark %p from sessionlist\n", b);
372 bfwin->session->bmarks = g_list_remove(bfwin->session->bmarks, b->strarr);
373 g_strfreev(b->strarr);
374 b->strarr = NULL;
375 }
376
377 /* this function re-uses the b->strarr if possible, otherwise it will create a new one and
378 append it to the list */
379 static void
bmark_store(Tbfwin * bfwin,Tbmark * b)380 bmark_store(Tbfwin * bfwin, Tbmark * b)
381 {
382 gchar **strarr;
383 if (b->is_temp) {
384 DEBUG_MSG("bmark_store, called for temp bookmark %p ?!?! weird!!!! returning\n", b);
385 return;
386 }
387 if (!b->uri) {
388 DEBUG_MSG("bmark_store, cannot store bookmark for file without filename\n");
389 return;
390 }
391 if (g_file_has_uri_scheme(b->uri, "tmp")) {
392 DEBUG_MSG("bmark_store, cannot store bookmark for file without filename (tmp:// uri)\n");
393 if (b->strarr) {
394 bmark_unstore(bfwin, b);
395 }
396 return;
397 }
398 /* if there is a strarr already, we only update the fields, else we append a new one */
399 if (b->strarr == NULL) {
400 DEBUG_MSG("bmark_store, creating new strarr for bookmark %p\n", b);
401 strarr = g_malloc0(sizeof(gchar *) * 7);
402 DEBUG_MSG("name=%s, description=%s, text=%s\n", b->name, b->description, b->text);
403 strarr[2] = g_file_get_parse_name(b->uri);
404 strarr[4] = g_strdup(b->text);
405 } else {
406 DEBUG_MSG("bmark_store, bookmark %p has strarr at %p\n", b, b->strarr);
407 strarr = b->strarr;
408 /* free the ones we are going to update */
409 g_free(strarr[0]);
410 g_free(strarr[1]);
411 g_free(strarr[3]);
412 g_free(strarr[5]);
413 }
414 strarr[0] = g_strdup(b->name);
415 strarr[1] = g_strdup(b->description);
416
417 if (b->doc)
418 b->len = gtk_text_buffer_get_char_count(b->doc->buffer);
419
420 strarr[3] = g_strdup_printf("%d", b->offset);
421 DEBUG_MSG("bmark_store, offset string=%s, offset int=%d\n", strarr[3], b->offset);
422 strarr[5] = g_strdup_printf("%d", b->len);
423 DEBUG_MSG("bmark_store, stored size=%d\n", b->len);
424 if (b->strarr == NULL) {
425 bfwin->session->bmarks = g_list_append(bfwin->session->bmarks, strarr);
426 DEBUG_MSG("added new (previously unstored) bookmark to session list, list length=%d\n",
427 g_list_length(bfwin->session->bmarks));
428 b->strarr = strarr;
429 }
430 }
431
432 /* when a users want to save the project, it's good to have updated bookmarks
433 so this function will update all arrays (strarr**)
434 */
435 void
bmark_store_all(Tbfwin * bfwin)436 bmark_store_all(Tbfwin * bfwin)
437 {
438 /* we loop over all filename iters, and only for the ones that are opened
439 we loop over the children (the ones that are not open cannot be changed) */
440 GtkTreeIter fileit;
441 gboolean cont;
442
443 cont =
444 gtk_tree_model_iter_children(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &fileit,
445 NULL);
446 while (cont) {
447 Tdocument *doc = NULL;
448 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &fileit, PTR_COLUMN,
449 &doc, -1);
450 if (doc) {
451 /* the document is open, so the offsets could be changed, store all permanent */
452 GtkTreeIter bmit;
453 gboolean cont2 =
454 gtk_tree_model_iter_children(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore),
455 &bmit, &fileit);
456 DEBUG_MSG("bmark_store_all, storing bookmarks for %s\n",
457 gtk_label_get_text(GTK_LABEL(doc->tab_menu)));
458 while (cont2) {
459 Tbmark *bmark;
460 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &bmit,
461 PTR_COLUMN, &bmark, -1);
462 if (!bmark->is_temp) {
463 bmark_update_offset_from_textmark(bmark);
464 bmark_store(bfwin, bmark);
465 }
466 cont2 =
467 gtk_tree_model_iter_next(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore),
468 &bmit);
469 }
470 } else {
471 DEBUG_MSG("doc not set, so not open...\n");
472 }
473 cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &fileit);
474 } /* cont */
475 }
476
477 static Tbmark *
get_bmark_at_iter(GtkTreeModel * model,GtkTreeIter * iter)478 get_bmark_at_iter(GtkTreeModel * model, GtkTreeIter * iter)
479 {
480 Tbmark *retval = NULL;
481 gtk_tree_model_get(model, iter, PTR_COLUMN, &retval, -1);
482 return retval;
483 }
484
485 /* get value from pointer column */
486 static Tbmark *
get_current_bmark(Tbfwin * bfwin)487 get_current_bmark(Tbfwin * bfwin)
488 {
489 if (bfwin->bmark) {
490 GtkTreePath *path;
491 GtkTreeViewColumn *col;
492 gtk_tree_view_get_cursor(bfwin->bmark, &path, &col);
493 if (path != NULL) {
494 Tbmark *retval = NULL;
495 if (gtk_tree_path_get_depth(path) == 2) {
496 GtkTreeIter iter;
497 GtkTreeModel *model = gtk_tree_view_get_model(bfwin->bmark);
498 gtk_tree_model_get_iter(model, &iter, path);
499 retval = get_bmark_at_iter(model, &iter);
500 } else {
501 DEBUG_MSG("get_current_bmark, error, depth=%d\n", gtk_tree_path_get_depth(path));
502 }
503 gtk_tree_path_free(path);
504 DEBUG_MSG("get_current_bmark, returning %p\n", retval);
505 return retval;
506 }
507 }
508 return NULL;
509 }
510
511 void
bmark_add_rename_dialog(Tbfwin * bfwin,gchar * dialogtitle)512 bmark_add_rename_dialog(Tbfwin * bfwin, gchar * dialogtitle)
513 {
514 #if GTK_CHECK_VERSION(3,0,0)
515 GtkWidget *align, *child, *dlg, *name, *desc, *button, *grid, *istemp;
516 #else
517 GtkWidget *dlg, *name, *desc, *button, *table, *istemp;
518 #endif
519 gint result;
520 Tbmark *m = get_current_bmark(bfwin);
521 if (!m)
522 return;
523
524 dlg =
525 gtk_dialog_new_with_buttons(dialogtitle, GTK_WINDOW(bfwin->main_window), GTK_DIALOG_MODAL,
526 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
527 button = gtk_button_new_from_stock(GTK_STOCK_OK);
528 gtk_widget_set_can_default(button, TRUE);
529 gtk_dialog_add_action_widget(GTK_DIALOG(dlg), button, GTK_RESPONSE_OK);
530
531 #if GTK_CHECK_VERSION(3,0,0)
532 align = gtk_alignment_new(0, 0, 1, 1);
533 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 12, 12, 6, 6);
534 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))), align, FALSE, FALSE, 0);
535
536 grid = gtk_grid_new();
537 gtk_grid_set_column_spacing(GTK_GRID(grid), 12);
538 gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
539 gtk_container_add(GTK_CONTAINER(align), grid);
540
541 name = gtk_entry_new();
542 if (m->name)
543 gtk_entry_set_text(GTK_ENTRY(name), m->name);
544 gtk_widget_set_hexpand(name, TRUE);
545 gtk_entry_set_activates_default(GTK_ENTRY(name), TRUE);
546 gtk_container_add(GTK_CONTAINER(grid), name);
547 gtk_grid_insert_column(GTK_GRID(grid), 0);
548 gtk_grid_attach(GTK_GRID(grid), dialog_mnemonic_label_new(_("_Name:"), name), 0, 0, 1, 1);
549
550 desc = gtk_entry_new();
551 if (m->description)
552 gtk_entry_set_text(GTK_ENTRY(desc), m->description);
553 gtk_entry_set_activates_default(GTK_ENTRY(desc), TRUE);
554 gtk_grid_attach_next_to(GTK_GRID(grid), desc, name, GTK_POS_BOTTOM, 1, 1);
555 gtk_grid_attach_next_to(GTK_GRID(grid), dialog_mnemonic_label_new(_("_Description:"), desc), desc,
556 GTK_POS_LEFT, 1, 1);
557
558 istemp = checkbut_with_value(_("_Temporary"), m->is_temp);
559 child = gtk_grid_get_child_at(GTK_GRID(grid), 0, 1);
560 gtk_grid_attach_next_to(GTK_GRID(grid), istemp, child, GTK_POS_BOTTOM, 2, 1);
561 #else
562 table = gtk_table_new(2, 3, FALSE);
563 gtk_table_set_col_spacings(GTK_TABLE(table), 12);
564 gtk_table_set_row_spacings(GTK_TABLE(table), 6);
565 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))), table, FALSE, FALSE, 12);
566
567 name = dialog_entry_in_table(m->name, table, 1, 2, 0, 1);
568 gtk_entry_set_activates_default(GTK_ENTRY(name), TRUE);
569 dialog_mnemonic_label_in_table(_("_Name:"), name, table, 0, 1, 0, 1);
570
571 desc = dialog_entry_in_table(m->description, table, 1, 2, 1, 2);
572 gtk_entry_set_activates_default(GTK_ENTRY(desc), TRUE);
573 dialog_mnemonic_label_in_table(_("_Description:"), desc, table, 0, 1, 1, 2);
574
575 istemp = checkbut_with_value(_("Temporary"), m->is_temp);
576 gtk_table_attach_defaults(GTK_TABLE(table), istemp, 0, 2, 2, 3);
577 #endif
578
579 gtk_window_set_default(GTK_WINDOW(dlg), button);
580
581 gtk_widget_show_all(dlg);
582 result = gtk_dialog_run(GTK_DIALOG(dlg));
583
584 if (result == GTK_RESPONSE_OK) {
585 gchar *tmpstr;
586 g_free(m->name);
587 m->name = g_strdup(gtk_entry_get_text(GTK_ENTRY(name)));
588 g_free(m->description);
589 m->description = g_strdup(gtk_entry_get_text(GTK_ENTRY(desc)));
590 m->is_temp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(istemp));
591 tmpstr = bmark_showname(bfwin, m);
592 gtk_tree_store_set(BMARKDATA(bfwin->bmarkdata)->bookmarkstore, &m->iter, NAME_COLUMN, tmpstr, -1);
593 g_free(tmpstr);
594 if (m->is_temp) {
595 if (m->strarr) {
596 /* hmm previously this was not a temporary bookmark */
597 bmark_unstore(bfwin, m);
598 }
599 } else {
600 bmark_store(bfwin, m);
601 }
602 }
603 gtk_widget_destroy(dlg);
604 }
605
606 static void
bmark_activate(Tbfwin * bfwin,Tbmark * b,gboolean select_bmark)607 bmark_activate(Tbfwin * bfwin, Tbmark * b, gboolean select_bmark)
608 {
609 GtkTextIter it;
610
611 if (!b)
612 return;
613
614 if (!b->doc && !b->uri)
615 return;
616
617 if (b->doc && b->mark) {
618 /* recalculate offset */
619 gtk_text_buffer_get_iter_at_mark(b->doc->buffer, &it, b->mark);
620 b->offset = gtk_text_iter_get_offset(&it);
621 }
622 DEBUG_MSG("bmark_activate, bmark at %p, uri at %p\n", b, b->uri);
623 DEBUG_MSG("bmark_activate, calling doc_new_from_uri with goto_offset %d\n", b->offset);
624 if (b->doc) {
625 bfwin_switch_to_document_by_pointer(BFWIN(b->doc->bfwin), b->doc);
626 doc_select_line_by_offset(b->doc, b->offset, TRUE, TRUE);
627 } else {
628 doc_new_from_uri(bfwin, b->uri, NULL, FALSE, FALSE, -1, b->offset, -1, TRUE, FALSE);
629 }
630 /* remove selection */
631 if (b->doc) {
632 gtk_text_buffer_get_iter_at_mark(b->doc->buffer, &it, gtk_text_buffer_get_insert(b->doc->buffer));
633 gtk_text_buffer_move_mark_by_name(b->doc->buffer, "selection_bound", &it);
634 gtk_widget_grab_focus(b->doc->view);
635 } else if (bfwin->current_document) {
636 gtk_widget_grab_focus(bfwin->current_document->view);
637 }
638 if (select_bmark) {
639 GtkTreeIter fiter;
640 gtk_tree_model_filter_convert_child_iter_to_iter(bfwin->bmarkfilter, &fiter, &b->iter);
641 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(bfwin->bmark), &fiter);
642 }
643
644 }
645
646 /*static void bmark_goto_selected(Tbfwin *bfwin) {
647 Tbmark *b = get_current_bmark(bfwin);
648 if (b) {
649 bmark_activate(bfwin, b, FALSE);
650 }
651 }*/
652 /*
653 * removes the bookmark from the treestore, and if it is the last remaining bookmark
654 * for the document, it will remove the parent iter (the filename) from the treestore as well
655 *
656 * if the parent is not removed it will return TRUE
657 * if the parent is removed, it will return FALSE
658 */
659 static gboolean
bmark_check_remove(Tbfwin * bfwin,Tbmark * b)660 bmark_check_remove(Tbfwin * bfwin, Tbmark * b)
661 {
662 GtkTreeIter parent;
663 /*GtkTextIter it; */
664
665 if (gtk_tree_model_iter_parent
666 (GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &parent, &b->iter)) {
667 gint numchild =
668 gtk_tree_model_iter_n_children(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore),
669 &parent);
670 DEBUG_MSG("bmark_check_remove, the parent of this bookmark has %d children\n", numchild);
671 gtk_tree_store_remove(BMARKDATA(bfwin->bmarkdata)->bookmarkstore, &(b->iter));
672
673 /* Olivier, 22 may 2013: what does the next line do ?????????? seems it can be removed */
674 /*if (b->doc) {
675 gtk_text_buffer_get_iter_at_mark(b->doc->buffer, &it, b->mark);
676 } */
677
678 if (numchild == 1) {
679 GtkTextIter *tmpiter;
680 DEBUG_MSG("bmark_check_remove, we removed the last child, now remove the parent\n");
681 gtk_tree_store_remove(BMARKDATA(bfwin->bmarkdata)->bookmarkstore, &parent);
682 /* if the document is open, it should be removed from the hastable as well */
683 tmpiter = g_hash_table_lookup(BMARKDATA(bfwin->bmarkdata)->bmarkfiles, b->uri);
684 if (tmpiter) {
685 DEBUG_MSG("bmark_check_remove, removing iter %p from hashtable\n", tmpiter);
686 g_hash_table_remove(BMARKDATA(bfwin->bmarkdata)->bmarkfiles, b->uri);
687 if (b->doc)
688 b->doc->bmark_parent = NULL;
689 }
690
691 if (b->doc && b->doc->view)
692 bluefish_text_view_set_show_symbols_redraw(BLUEFISH_TEXT_VIEW(b->doc->view), FALSE);
693
694 return FALSE;
695 }
696 }
697 DEBUG_MSG("bmark_check_remove, finished\n");
698 return TRUE;
699 }
700
701 /* *parent should be a valid GtkTreeIter pointing to a filename. */
702 static void
bmark_del_children_backend(Tbfwin * bfwin,GtkTreeIter * parent)703 bmark_del_children_backend(Tbfwin * bfwin, GtkTreeIter * parent)
704 {
705 GtkTreeIter tmpiter;
706 gboolean have_parent = TRUE;
707 while (have_parent
708 && gtk_tree_model_iter_children(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore),
709 &tmpiter, parent)) {
710 Tbmark *b;
711 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &tmpiter, PTR_COLUMN,
712 &b, -1);
713 if (b) {
714 DEBUG_MSG("bmark_del_children_backend, found b=%p\n", b);
715 have_parent = bmark_check_remove(bfwin, b);
716 if (!b->is_temp)
717 bmark_unstore(bfwin, b);
718 bmark_free(b);
719 } else {
720 DEBUG_MSG("bmark_del_children_backend, iter without bookmark ??? LOOP WARNING!\n");
721 }
722 }
723 }
724
725 static void
popup_menu_default_permanent(GtkAction * action,gpointer user_data)726 popup_menu_default_permanent(GtkAction * action, gpointer user_data)
727 {
728 main_v->globses.bookmarks_default_store = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
729 }
730
731 static void
popup_menu_delete(GtkAction * action,gpointer user_data)732 popup_menu_delete(GtkAction * action, gpointer user_data)
733 {
734 Tbfwin *bfwin = BFWIN(user_data);
735 Tbmark *bmark;
736 gint retval;
737 gchar *pstr;
738 const gchar *buttons[] = { GTK_STOCK_NO, GTK_STOCK_YES, NULL };
739 DEBUG_MSG("popup_menu_delete\n");
740 bmark = get_current_bmark(bfwin);
741 if (!bmark)
742 return;
743 /* check if it is temp mark */
744 if (bmark->is_temp) {
745 bmark_check_remove(bfwin, bmark); /* check if we should remove a filename too */
746 bmark_free(bmark);
747 } else {
748 pstr = g_strdup_printf(_("Do you really want to delete %s?"), bmark->name);
749 retval = message_dialog_new_multi(bfwin->main_window,
750 GTK_MESSAGE_QUESTION,
751 buttons, _("Delete permanent bookmark."), pstr);
752 g_free(pstr);
753 if (retval == 0)
754 return;
755 bmark_check_remove(bfwin, bmark); /* check if we should remove a filename too */
756 bmark_unstore(bfwin, bmark);
757 bmark_free(bmark);
758 }
759 if (bfwin->current_document)
760 gtk_widget_grab_focus(bfwin->current_document->view);
761 }
762
763 static void
popup_menu_delete_all(GtkAction * action,gpointer user_data)764 popup_menu_delete_all(GtkAction * action, gpointer user_data)
765 {
766 Tbfwin *bfwin = BFWIN(user_data);
767 GtkTreeIter tmpiter;
768 gint retval;
769
770 const gchar *buttons[] = { GTK_STOCK_NO, GTK_STOCK_YES, NULL };
771
772
773 if (bfwin == NULL || !bfwin->current_document)
774 return;
775
776 retval = message_dialog_new_multi(bfwin->main_window,
777 GTK_MESSAGE_QUESTION, buttons, _("Delete all bookmarks."), NULL);
778 if (retval == 0)
779 return;
780
781 DEBUG_MSG("bmark_del_all, deleting all bookmarks!\n");
782 while (gtk_tree_model_iter_children
783 (GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &tmpiter, NULL))
784 bmark_del_children_backend(bfwin, &tmpiter);
785
786 gtk_widget_grab_focus(bfwin->current_document->view);
787 }
788
789 static void
popup_menu_delete_all_doc(GtkAction * action,gpointer user_data)790 popup_menu_delete_all_doc(GtkAction * action, gpointer user_data)
791 {
792 Tbfwin *bfwin = BFWIN(user_data);
793
794 if (bfwin->bmark) {
795 GtkTreePath *path;
796 GtkTreeViewColumn *col;
797 gtk_tree_view_get_cursor(bfwin->bmark, &path, &col);
798 if (path != NULL) {
799 gchar *name;
800 gchar *pstr;
801 const gchar *buttons[] = { GTK_STOCK_NO, GTK_STOCK_YES, NULL };
802 GtkTreeIter iter, realiter;
803 GtkTreeModel *model = gtk_tree_view_get_model(bfwin->bmark);
804 gint depth, retval;
805 depth = gtk_tree_path_get_depth(path);
806 if (depth == 2) {
807 /* go up to parent */
808 gtk_tree_path_up(path);
809 }
810 gtk_tree_model_get_iter(model, &iter, path);
811 /* iter is now an iter in the filter model, not in the real backend model !!!! */
812 gtk_tree_path_free(path);
813 gtk_tree_model_get(model, &iter, NAME_COLUMN, &name, -1);
814
815 pstr = g_strdup_printf(_("Do you really want to delete all bookmarks for %s?"), name);
816 retval = message_dialog_new_multi(bfwin->main_window,
817 GTK_MESSAGE_QUESTION, buttons, _("Delete bookmarks?"), pstr);
818 g_free(pstr);
819 if (retval == 0)
820 return;
821 gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), &realiter, &iter);
822 bmark_del_children_backend(bfwin, &realiter);
823 }
824 }
825 if (bfwin->current_document)
826 gtk_widget_grab_focus(bfwin->current_document->view);
827 }
828
829 static void
popup_menu_edit(GtkAction * action,gpointer user_data)830 popup_menu_edit(GtkAction * action, gpointer user_data)
831 {
832 Tbfwin *bfwin = BFWIN(user_data);
833 Tbmark *bmark = get_current_bmark(bfwin);
834
835 if (!bmark)
836 return;
837
838 bmark_add_rename_dialog(bfwin, _("Edit bookmark"));
839 }
840
841 static void
popup_menu_show_bookmark(GtkRadioAction * action,GtkRadioAction * current,gpointer user_data)842 popup_menu_show_bookmark(GtkRadioAction * action, GtkRadioAction * current, gpointer user_data)
843 {
844 BFWIN(user_data)->session->bookmarks_show_mode = gtk_radio_action_get_current_value(action);
845 bmark_update_treestore_name(BFWIN(user_data));
846 }
847
848 static void
popup_menu_show_file(GtkRadioAction * action,GtkRadioAction * current,gpointer user_data)849 popup_menu_show_file(GtkRadioAction * action, GtkRadioAction * current, gpointer user_data)
850 {
851 BFWIN(user_data)->session->bookmarks_filename_mode = gtk_radio_action_get_current_value(action);
852 bmark_update_treestore_name(BFWIN(user_data));
853 }
854
855 static void
popup_search_mode_changed(GtkRadioAction * action,GtkRadioAction * current,gpointer user_data)856 popup_search_mode_changed(GtkRadioAction * action, GtkRadioAction * current, gpointer user_data)
857 {
858 BFWIN(user_data)->session->bmarksearchmode = gtk_radio_action_get_current_value(action);
859 }
860
861 static const gchar *bookmark_menu_ui =
862 "<ui>"
863 " <popup action='BookmarkMenu'>"
864 " <menuitem action='EditBookmark'/>"
865 " <menuitem action='DeleteBookmark'/>"
866 " <separator/>"
867 " <menuitem action='DeleteAllBookmarkInDoc'/>"
868 " <menuitem action='DeleteAllBookmark'/>"
869 " <separator/>"
870 " <menuitem action='DefaultPermanent'/>"
871 " <separator/>"
872 " <menu action='ShowFileMenu'>"
873 " <menuitem action='BookmarkFileByName'/>"
874 " <menuitem action='BookmarkFileByPath'/>"
875 " <menuitem action='BookmarkFileByURI'/>"
876 " </menu>"
877 " <menu action='ShowBookmarkMenu'>"
878 " <menuitem action='BookmarkContent'/>"
879 " <menuitem action='BookmarkName'/>"
880 " <menuitem action='BookmarkNameContent'/>"
881 " </menu>"
882 " </popup>"
883 "</ui>";
884
885 static const gchar *bookmark_search_menu_ui =
886 "<ui>"
887 " <popup action='BookmarkSearchMenu'>"
888 " <menuitem action='BookmarkSearchContent'/>"
889 " <menuitem action='BookmarkSearchName'/>"
890 " <menuitem action='BookmarkSearchNameContent'/>"
891 " </popup>"
892 "</ui>";
893
894 static const GtkActionEntry bookmark_actions[] = {
895 {"BookmarkMenu", NULL, N_("Bookmark menu")},
896 {"ShowBookmarkMenu", NULL, N_("Show Bookmark")},
897 {"ShowFileMenu", NULL, N_("Show File")},
898 {"BookmarkSearchMenu", NULL, N_("Bookmark search menu")},
899 {"EditBookmark", NULL, N_("_Edit..."), NULL, N_("Edit bookmark"),
900 G_CALLBACK(popup_menu_edit)},
901 {"DeleteBookmark", NULL, N_("_Delete"), NULL, N_("Delete bookmark"), G_CALLBACK(popup_menu_delete)},
902 {"DeleteAllBookmark", NULL, N_("Delete All"), NULL, N_("Delete all bookmarks"),
903 G_CALLBACK(popup_menu_delete_all)},
904 {"DeleteAllBookmarkInDoc", NULL, N_("Delete All in Document"), NULL,
905 N_("Delete all bookmarks in document"),
906 G_CALLBACK(popup_menu_delete_all_doc)}
907 };
908
909 static const GtkToggleActionEntry bookmark_toggle_actions[] = {
910 {"DefaultPermanent", NULL, N_("Permanent by default"), NULL, N_("Permanent by default"),
911 G_CALLBACK(popup_menu_default_permanent)}
912 };
913
914 static const GtkRadioActionEntry bookmark_file_radio_actions[] = {
915 {"BookmarkFileByName", NULL, N_("By Name"), NULL, NULL, 0},
916 {"BookmarkFileByPath", NULL, N_("By Full Path"), NULL, NULL, 1},
917 {"BookmarkFileByURI", NULL, N_("By Full URI"), NULL, NULL, 2},
918 };
919
920 static const GtkRadioActionEntry bookmark_radio_actions[] = {
921 {"BookmarkContent", NULL, N_("Content"), NULL, NULL, 0},
922 {"BookmarkName", NULL, N_("Name"), NULL, NULL, 1},
923 {"BookmarkNameContent", NULL, N_("Name & Content"), NULL, NULL, 2}
924 };
925
926 static const GtkRadioActionEntry bookmark_search_radio_actions[] = {
927 {"BookmarkSearchContent", NULL, N_("Content"), NULL, NULL, 0},
928 {"BookmarkSearchName", NULL, N_("Name"), NULL, NULL, 1},
929 {"BookmarkSearchNameContent", NULL, N_("Name & Content"), NULL, NULL, 2}
930 };
931
932 static void
popup_menu_action_group_init(Tbfwin * bfwin)933 popup_menu_action_group_init(Tbfwin * bfwin)
934 {
935 GError *error = NULL;
936
937 bfwin->bookmarkGroup = gtk_action_group_new("BookmarkActions");
938 gtk_action_group_set_translation_domain(bfwin->bookmarkGroup, GETTEXT_PACKAGE);
939 gtk_action_group_add_actions(bfwin->bookmarkGroup, bookmark_actions, G_N_ELEMENTS(bookmark_actions),
940 bfwin);
941 gtk_action_group_add_toggle_actions(bfwin->bookmarkGroup, bookmark_toggle_actions,
942 G_N_ELEMENTS(bookmark_toggle_actions), bfwin);
943 gtk_action_group_add_radio_actions(bfwin->bookmarkGroup, bookmark_file_radio_actions,
944 G_N_ELEMENTS(bookmark_file_radio_actions),
945 bfwin->session->bookmarks_filename_mode,
946 G_CALLBACK(popup_menu_show_file), bfwin);
947 gtk_action_group_add_radio_actions(bfwin->bookmarkGroup, bookmark_radio_actions,
948 G_N_ELEMENTS(bookmark_radio_actions),
949 bfwin->session->bookmarks_show_mode,
950 G_CALLBACK(popup_menu_show_bookmark), bfwin);
951 gtk_action_group_add_radio_actions(bfwin->bookmarkGroup, bookmark_search_radio_actions,
952 G_N_ELEMENTS(bookmark_search_radio_actions),
953 bfwin->session->bmarksearchmode,
954 G_CALLBACK(popup_search_mode_changed), bfwin);
955 gtk_ui_manager_insert_action_group(bfwin->uimanager, bfwin->bookmarkGroup, 1);
956 g_object_unref(bfwin->bookmarkGroup);
957
958 gtk_ui_manager_add_ui_from_string(bfwin->uimanager, bookmark_menu_ui, -1, &error);
959 if (error != NULL) {
960 g_warning("building bookmark menu failed: %s", error->message);
961 g_error_free(error);
962 }
963
964 gtk_ui_manager_add_ui_from_string(bfwin->uimanager, bookmark_search_menu_ui, -1, &error);
965 if (error != NULL) {
966 g_warning("building bookmark search menu failed: %s", error->message);
967 g_error_free(error);
968 }
969 }
970
971 static void
popup_menu(Tbfwin * bfwin,GdkEventButton * event,gboolean show_bmark_specific,gboolean show_file_specific)972 popup_menu(Tbfwin * bfwin, GdkEventButton * event, gboolean show_bmark_specific, gboolean show_file_specific)
973 {
974 GtkWidget *menu = gtk_ui_manager_get_widget(bfwin->uimanager, "/BookmarkMenu");
975 if (!menu)
976 return;
977
978 bfwin_action_set_sensitive(bfwin->uimanager, "/BookmarkMenu/EditBookmark", show_bmark_specific);
979 bfwin_action_set_sensitive(bfwin->uimanager, "/BookmarkMenu/DeleteBookmark", show_bmark_specific);
980 bfwin_action_set_sensitive(bfwin->uimanager, "/BookmarkMenu/DeleteAllBookmarkInDoc", show_file_specific);
981 bfwin_set_menu_toggle_item_from_path(bfwin->uimanager, "/BookmarkMenu/DefaultPermanent",
982 main_v->globses.bookmarks_default_store);
983 gtk_radio_action_set_current_value((GtkRadioAction *)
984 gtk_ui_manager_get_action(bfwin->uimanager,
985 "/BookmarkMenu/ShowFileMenu/BookmarkFileByName"),
986 bfwin->session->bookmarks_filename_mode);
987 gtk_radio_action_set_current_value((GtkRadioAction *)
988 gtk_ui_manager_get_action(bfwin->uimanager,
989 "/BookmarkMenu/ShowBookmarkMenu/BookmarkName"),
990 bfwin->session->bookmarks_show_mode);
991 gtk_widget_show(menu);
992 #if GTK_CHECK_VERSION(3,22,0)
993 gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
994 #else
995 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
996 #endif
997 }
998
999 static void
popup_search_menu(Tbfwin * bfwin,GdkEventButton * bevent)1000 popup_search_menu(Tbfwin * bfwin, GdkEventButton * bevent)
1001 {
1002 GtkWidget *menu = gtk_ui_manager_get_widget(bfwin->uimanager, "/BookmarkSearchMenu");
1003
1004 if (menu) {
1005 gtk_widget_show(menu);
1006 #if GTK_CHECK_VERSION(3,22,0)
1007 gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
1008 #else
1009 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
1010 #endif
1011 } else
1012 g_warning("showing bookmark search popup menu failed");
1013 }
1014
1015 static void
bmark_search_icon_press(GtkEntry * entry,GtkEntryIconPosition icon_pos,GdkEvent * event,gpointer user_data)1016 bmark_search_icon_press(GtkEntry * entry, GtkEntryIconPosition icon_pos, GdkEvent * event, gpointer user_data)
1017 {
1018 popup_search_menu(user_data, (GdkEventButton *) event);
1019 }
1020
1021 static void
bmark_row_activated(GtkTreeView * tree,GtkTreePath * path,GtkTreeViewColumn * column,Tbfwin * bfwin)1022 bmark_row_activated(GtkTreeView * tree, GtkTreePath * path, GtkTreeViewColumn * column, Tbfwin * bfwin)
1023 {
1024 GtkTreeIter iter;
1025 gtk_tree_model_get_iter(GTK_TREE_MODEL(bfwin->bmarkfilter), &iter, path);
1026 if (gtk_tree_path_get_depth(path) == 2) {
1027 bmark_activate(bfwin, get_bmark_at_iter(GTK_TREE_MODEL(bfwin->bmarkfilter), &iter), FALSE);
1028 }
1029 }
1030
1031 /* mouse click */
1032 static gboolean
bmark_event_mouseclick(GtkWidget * widget,GdkEventButton * event,Tbfwin * bfwin)1033 bmark_event_mouseclick(GtkWidget * widget, GdkEventButton * event, Tbfwin * bfwin)
1034 {
1035 GtkTreePath *path;
1036 gboolean show_bmark_specific = FALSE, show_file_specific = FALSE;
1037 if (gtk_tree_view_get_path_at_pos
1038 (GTK_TREE_VIEW(bfwin->bmark), event->x, event->y, &path, NULL, NULL, NULL)) {
1039 if (path) {
1040 gint depth = gtk_tree_path_get_depth(path);
1041
1042 if (depth == 2) {
1043 show_bmark_specific = TRUE;
1044 show_file_specific = TRUE;
1045 if (event->button == 1) {
1046 GtkTreeIter iter;
1047 gtk_tree_model_get_iter(GTK_TREE_MODEL(bfwin->bmarkfilter), &iter, path);
1048 bmark_activate(bfwin, get_bmark_at_iter(GTK_TREE_MODEL(bfwin->bmarkfilter), &iter),
1049 FALSE);
1050 }
1051 } else if (depth == 1) {
1052 show_file_specific = TRUE;
1053 }
1054 gtk_tree_path_free(path);
1055 }
1056 }
1057 if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { /* right mouse click */
1058 popup_menu(bfwin, event, show_bmark_specific, show_file_specific);
1059 }
1060
1061 return FALSE;
1062 }
1063
1064 /*static void bmark_selection_changed_lcb(GtkTreeSelection *treeselection,Tbfwin * bfwin) {
1065 / * this is not the best way to activate bookmarks. according to the gtk documentation:
1066 Emitted whenever the selection has (possibly) changed. Please note that this signal is
1067 mostly a hint. It may only be emitted once when a range of rows are selected, and it
1068 may occasionally be emitted when nothing has happened.
1069
1070 THUS: we should better use the mouse click event to find the correct bookmark to
1071 activate.
1072 * /
1073 DEBUG_MSG("bmark_selection_changed_lcb, started\n");
1074 bmark_goto_selected(bfwin);
1075 }*/
1076
1077 static void
bmark_first_lcb(GtkWidget * widget,Tbfwin * bfwin)1078 bmark_first_lcb(GtkWidget * widget, Tbfwin * bfwin)
1079 {
1080 GtkTreeModel *model = GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore);
1081 GtkTreeIter iter;
1082
1083 if (!CURDOC(bfwin) || !CURDOC(bfwin)->bmark_parent)
1084 return;
1085 DEBUG_MSG("bmark_first_lcb, started\n");
1086 if (gtk_tree_model_iter_children(model, &iter, CURDOC(bfwin)->bmark_parent))
1087 bmark_activate(bfwin, get_bmark_at_iter(model, &iter), TRUE);
1088 }
1089
1090 static void
bmark_last_lcb(GtkWidget * widget,Tbfwin * bfwin)1091 bmark_last_lcb(GtkWidget * widget, Tbfwin * bfwin)
1092 {
1093 GtkTreeModel *model = GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore);
1094 GtkTreeIter iter;
1095 gint num;
1096
1097 if (!CURDOC(bfwin) || !CURDOC(bfwin)->bmark_parent)
1098 return;
1099 DEBUG_MSG("bmark_last_lcb, started\n");
1100 num = gtk_tree_model_iter_n_children(model, CURDOC(bfwin)->bmark_parent);
1101 if (gtk_tree_model_iter_nth_child(model, &iter, CURDOC(bfwin)->bmark_parent, num - 1))
1102 bmark_activate(bfwin, get_bmark_at_iter(model, &iter), TRUE);
1103 }
1104
1105 static void
bmark_previous_lcb(GtkWidget * widget,Tbfwin * bfwin)1106 bmark_previous_lcb(GtkWidget * widget, Tbfwin * bfwin)
1107 {
1108 GtkTextMark *insert;
1109 GtkTextIter titer;
1110 Tbmark *bmark = NULL;
1111 gint line;
1112
1113 if (!CURDOC(bfwin) || !CURDOC(bfwin)->bmark_parent)
1114 return;
1115 DEBUG_MSG("bmark_previous_lcb, started\n");
1116 insert = gtk_text_buffer_get_insert(CURDOC(bfwin)->buffer);
1117 gtk_text_buffer_get_iter_at_mark(CURDOC(bfwin)->buffer, &titer, insert);
1118 /*gtk_text_iter_set_line_offset(&titer, 0); */
1119 gtk_text_iter_backward_line(&titer);
1120 line = bmark_margin_get_initial_bookmark(CURDOC(bfwin), &titer, (gpointer) & bmark);
1121 DEBUG_MSG("bmark_previous_lcb, got initial bookmark at line %d, cursor is at line %d\n", line,
1122 gtk_text_iter_get_line(&titer));
1123 if (-1 == line)
1124 return;
1125 if (line > gtk_text_iter_get_line(&titer)) {
1126 bmark_last_lcb(widget, bfwin);
1127 return;
1128 }
1129 bmark_activate(bfwin, bmark, TRUE);
1130 }
1131
1132 static void
bmark_next_lcb(GtkWidget * widget,Tbfwin * bfwin)1133 bmark_next_lcb(GtkWidget * widget, Tbfwin * bfwin)
1134 {
1135 GtkTextMark *insert;
1136 GtkTextIter titer;
1137 Tbmark *bmark = NULL;
1138 gint line;
1139
1140 if (!CURDOC(bfwin) || !CURDOC(bfwin)->bmark_parent)
1141 return;
1142 DEBUG_MSG("bmark_next_lcb, started\n");
1143 insert = gtk_text_buffer_get_insert(CURDOC(bfwin)->buffer);
1144 gtk_text_buffer_get_iter_at_mark(CURDOC(bfwin)->buffer, &titer, insert);
1145 gtk_text_iter_forward_to_line_end(&titer);
1146 line = bmark_margin_get_initial_bookmark(CURDOC(bfwin), &titer, (gpointer) & bmark);
1147 if (-1 == line)
1148 return;
1149 if (line <= gtk_text_iter_get_line(&titer)) {
1150 /* get the 'next' bookmark */
1151 if (-1 == bmark_margin_get_next_bookmark(CURDOC(bfwin), (gpointer) & bmark)) {
1152 bmark_first_lcb(widget, bfwin);
1153 return;
1154 }
1155 }
1156 bmark_activate(bfwin, bmark, TRUE);
1157 }
1158
1159 void
bookmark_navigate(Tbfwin * bfwin,guint action)1160 bookmark_navigate(Tbfwin * bfwin, guint action)
1161 {
1162 switch (action) {
1163 case 1:
1164 bmark_first_lcb(NULL, bfwin);
1165 break;
1166 case 2:
1167 bmark_previous_lcb(NULL, bfwin);
1168 break;
1169 case 3:
1170 bmark_next_lcb(NULL, bfwin);
1171 break;
1172 case 4:
1173 bmark_last_lcb(NULL, bfwin);
1174 break;
1175 default:
1176 g_warning("invalid menu action for bookmark menu, please report as bluefish bug\n ");
1177 break;
1178 }
1179 }
1180
1181 static gboolean
all_children_hidden(GtkTreeModel * model,GtkTreeIter * iter,gpointer data,GtkTreeModelFilterVisibleFunc func)1182 all_children_hidden(GtkTreeModel * model, GtkTreeIter * iter, gpointer data,
1183 GtkTreeModelFilterVisibleFunc func)
1184 {
1185 GtkTreeIter citer;
1186 gboolean cont = TRUE;
1187 if (!gtk_tree_model_iter_children(model, &citer, iter)) {
1188 return TRUE; /* there are no children */
1189 }
1190 while (cont) {
1191 if (func(model, &citer, data) == TRUE)
1192 return FALSE;
1193 cont = gtk_tree_model_iter_next(model, &citer);
1194 }
1195 return TRUE;
1196 }
1197
1198 static gboolean
bmark_search_filter_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)1199 bmark_search_filter_func(GtkTreeModel * model, GtkTreeIter * iter, gpointer data)
1200 {
1201 Tbfwin *bfwin = data;
1202 Tbmark *bmark;
1203
1204 if (bfwin->bmark_search_prefix == NULL || bfwin->bmark_search_prefix[0] == '\0')
1205 return TRUE;
1206
1207 if (gtk_tree_model_iter_has_child(model, iter)) {
1208 if (all_children_hidden(model, iter, data, bmark_search_filter_func)) {
1209 return FALSE;
1210 }
1211 return TRUE;
1212 }
1213 gtk_tree_model_get(model, iter, PTR_COLUMN, &bmark, -1);
1214 if (bmark) {
1215 switch ( /*bfwin->session->bmark_search_mode */ BM_SEARCH_BOTH) {
1216 case BM_SEARCH_NAME:
1217 return (bmark->name && strstr(bmark->name, bfwin->bmark_search_prefix));
1218 break;
1219 case BM_SEARCH_CONTENT:
1220 return (bmark->text && strstr(bmark->text, bfwin->bmark_search_prefix));
1221 break;
1222 case BM_SEARCH_BOTH:
1223 return ((bmark->text && strstr(bmark->text, bfwin->bmark_search_prefix))
1224 || (bmark->name && g_str_has_prefix(bmark->name, bfwin->bmark_search_prefix)));
1225 break;
1226 }
1227 }
1228 return FALSE;
1229 }
1230
1231 static void
bmark_search_changed(GtkEditable * editable,gpointer user_data)1232 bmark_search_changed(GtkEditable * editable, gpointer user_data)
1233 {
1234 Tbfwin *bfwin = user_data;
1235 /* call refilter on the bmarkfilter */
1236 g_free(bfwin->bmark_search_prefix);
1237 bfwin->bmark_search_prefix = gtk_editable_get_chars(editable, 0, -1);
1238 gtk_tree_model_filter_refilter(bfwin->bmarkfilter);
1239 }
1240
1241 /* Initialize bookmarks gui for window */
1242 GtkWidget *
bmark_gui(Tbfwin * bfwin)1243 bmark_gui(Tbfwin * bfwin)
1244 {
1245 GtkWidget *vbox, *hbox, *scroll, *entry;
1246 GtkToolItem *but;
1247 GtkCellRenderer *cell;
1248 GtkTreeViewColumn *column;
1249 DEBUG_MSG("bmark_gui, building gui for bfwin=%p\n", bfwin);
1250 /* Tree Store is in BMARKDATA(bfwin->bmarkdata)->bookmarkstore
1251 Tree View is in bfwin->bmark
1252 */
1253 vbox = gtk_vbox_new(FALSE, 1);
1254 entry = gtk_entry_new();
1255 gtk_entry_set_icon_from_stock(GTK_ENTRY(entry), GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
1256 gtk_entry_set_icon_activatable(GTK_ENTRY(entry), GTK_ENTRY_ICON_PRIMARY, TRUE);
1257 g_signal_connect(G_OBJECT(entry), "icon-press", G_CALLBACK(bmark_search_icon_press), bfwin);
1258 g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(bmark_search_changed), bfwin);
1259 #if GTK_CHECK_VERSION(3,2,0)
1260 gtk_entry_set_width_chars(GTK_ENTRY(entry), 1);
1261 #endif
1262 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 0);
1263 hbox = gtk_toolbar_new();
1264 gtk_toolbar_set_icon_size(GTK_TOOLBAR(hbox), GTK_ICON_SIZE_MENU);
1265 gtk_toolbar_set_style(GTK_TOOLBAR(hbox), GTK_TOOLBAR_ICONS);
1266
1267 but = gtk_tool_button_new(gtk_image_new_from_stock(GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU), "");
1268 g_signal_connect(G_OBJECT(but), "clicked", G_CALLBACK(bmark_first_lcb), bfwin);
1269 gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(but), _("First bookmark"));
1270 gtk_toolbar_insert(GTK_TOOLBAR(hbox), but, -1);
1271 but = gtk_tool_button_new(gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU), "");
1272 g_signal_connect(G_OBJECT(but), "clicked", G_CALLBACK(bmark_previous_lcb), bfwin);
1273 gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(but), _("Previous bookmark"));
1274 gtk_toolbar_insert(GTK_TOOLBAR(hbox), but, -1);
1275 but = gtk_tool_button_new(gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU), "");
1276 g_signal_connect(G_OBJECT(but), "clicked", G_CALLBACK(bmark_next_lcb), bfwin);
1277 gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(but), _("Next bookmark"));
1278 gtk_toolbar_insert(GTK_TOOLBAR(hbox), but, -1);
1279 but = gtk_tool_button_new(gtk_image_new_from_stock(GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU), "");
1280 g_signal_connect(G_OBJECT(but), "clicked", G_CALLBACK(bmark_last_lcb), bfwin);
1281 gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(but), _("Last bookmark"));
1282 gtk_toolbar_insert(GTK_TOOLBAR(hbox), but, -1);
1283
1284 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1285
1286 bfwin->bmarkfilter = (GtkTreeModelFilter *)
1287 gtk_tree_model_filter_new(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), NULL);
1288 gtk_tree_model_filter_set_visible_func(bfwin->bmarkfilter, bmark_search_filter_func, bfwin, NULL);
1289 bfwin->bmark = (GtkTreeView *) gtk_tree_view_new_with_model(GTK_TREE_MODEL(bfwin->bmarkfilter));
1290 g_object_unref(bfwin->bmarkfilter);
1291 cell = gtk_cell_renderer_text_new();
1292 column = gtk_tree_view_column_new_with_attributes("", cell, "text", NAME_COLUMN, NULL);
1293 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1294 gtk_tree_view_append_column(GTK_TREE_VIEW(bfwin->bmark), column);
1295 gtk_widget_show_all(GTK_WIDGET(bfwin->bmark));
1296 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(bfwin->bmark), FALSE);
1297 /*gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(bfwin->bmark), TRUE); */
1298 scroll = gtk_scrolled_window_new(NULL, NULL);
1299 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1300 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN);
1301 gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(bfwin->bmark));
1302 gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
1303 g_signal_connect(G_OBJECT(bfwin->bmark), "button-press-event", G_CALLBACK(bmark_event_mouseclick), bfwin);
1304 g_signal_connect(G_OBJECT(bfwin->bmark), "row-activated", G_CALLBACK(bmark_row_activated), bfwin);
1305 gtk_tree_view_expand_all(bfwin->bmark);
1306 /*{
1307 GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(bfwin->bmark));
1308 gtk_tree_selection_set_mode(selection,GTK_SELECTION_BROWSE);
1309 g_signal_connect(G_OBJECT(selection), "changed",G_CALLBACK(bmark_selection_changed_lcb), bfwin);
1310 } */
1311
1312 if (!bfwin->bookmarkGroup)
1313 popup_menu_action_group_init(bfwin);
1314
1315 return vbox;
1316 }
1317
1318 /**
1319 * bmark_get_iter_at_tree_position:
1320 *
1321 * determine bookmark's location in the tree and insert - result GtkTreeIter is stored in m->iter
1322 */
1323 static void
bmark_get_iter_at_tree_position(Tbfwin * bfwin,Tbmark * m)1324 bmark_get_iter_at_tree_position(Tbfwin * bfwin, Tbmark * m)
1325 {
1326 GtkTreeIter *parent;
1327 gpointer ptr;
1328 DEBUG_MSG("bmark_get_iter_at_tree_position, started\n");
1329 ptr = g_hash_table_lookup(BMARKDATA(bfwin->bmarkdata)->bmarkfiles, m->uri);
1330 DEBUG_MSG("bmark_get_iter_at_tree_position, found %p in hashtable %p\n", ptr,
1331 BMARKDATA(bfwin->bmarkdata)->bmarkfiles);
1332 if (ptr == NULL) { /* closed document or bookmarks never set */
1333 gchar *title;
1334 parent = g_slice_new0(GtkTreeIter);
1335 #ifdef BMARKREF
1336 bmarkref.itercount++;
1337 g_print("bmark_get_iter_at_tree_position, itercount=%d\n", bmarkref.itercount);
1338 #endif
1339 /* we should sort the document names in the treestore */
1340 title = bmark_filename(bfwin, m->uri);
1341 DEBUG_MSG("insert parent with name %s and doc=%p in treestore %p\n", title, m->doc,
1342 BMARKDATA(bfwin->bmarkdata)->bookmarkstore);
1343 gtk_tree_store_insert_with_values(BMARKDATA(bfwin->bmarkdata)->bookmarkstore, parent, NULL, 0,
1344 NAME_COLUMN, title, PTR_COLUMN, m->doc, -1);
1345 /* gtk_tree_store_append(BMARKDATA(bfwin->bmarkdata)->bookmarkstore, parent, NULL);
1346 gtk_tree_store_set(BMARKDATA(bfwin->bmarkdata)->bookmarkstore, parent, NAME_COLUMN, title, PTR_COLUMN, m->doc, -1);*/
1347 g_free(title);
1348 if (m->doc != NULL) {
1349 m->doc->bmark_parent = parent;
1350 }
1351 DEBUG_MSG("bmark_get_iter_at_tree_position, appending parent %p in hashtable %p\n", parent,
1352 BMARKDATA(bfwin->bmarkdata)->bmarkfiles);
1353 /* the hash table frees the key, but not the value, on destroy */
1354 g_object_ref(m->uri);
1355 g_hash_table_insert(BMARKDATA(bfwin->bmarkdata)->bmarkfiles, m->uri, parent);
1356 } else {
1357 parent = (GtkTreeIter *) ptr;
1358 }
1359 gtk_tree_store_prepend(BMARKDATA(bfwin->bmarkdata)->bookmarkstore, &m->iter, parent);
1360 }
1361
1362 static gint
bmark_sort_func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)1363 bmark_sort_func(GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer user_data)
1364 {
1365 GtkTreeIter tmp;
1366 if (gtk_tree_model_iter_parent(model, &tmp, a) == FALSE) {
1367 gint retval;
1368 gchar *name_a, *name_b;
1369 gtk_tree_model_get(model, a, NAME_COLUMN, &name_a, -1);
1370 gtk_tree_model_get(model, b, NAME_COLUMN, &name_b, -1);
1371 retval = g_strcmp0(name_a, name_b);
1372 g_free(name_a);
1373 g_free(name_b);
1374 return retval;
1375 } else {
1376 Tbmark *bmark_a, *bmark_b;
1377 gtk_tree_model_get(model, a, PTR_COLUMN, &bmark_a, -1);
1378 gtk_tree_model_get(model, b, PTR_COLUMN, &bmark_b, -1);
1379 if (bmark_a && bmark_b) {
1380 return bmark_a->offset - bmark_b->offset;
1381 } else {
1382 return (gint) (bmark_a - bmark_b);
1383 }
1384 }
1385 }
1386
1387 static void
bmark_hash_value_free(gpointer data)1388 bmark_hash_value_free(gpointer data)
1389 {
1390 DEBUG_MSG("bmark_hash_value_free, free iter %p\n", data);
1391 #ifdef BMARKREF
1392 bmarkref.itercount--;
1393 g_print("bmark_hash_value_free, itercount=%d\n", bmarkref.itercount);
1394 #endif
1395 g_slice_free(GtkTreeIter, data);
1396 }
1397
1398 static void
bmark_hash_key_free(gpointer data)1399 bmark_hash_key_free(gpointer data)
1400 {
1401 if (!data)
1402 return;
1403 DEBUG_MSG("bmark_hash_key_free, unref %p\n", data);
1404 g_object_unref(data);
1405 }
1406
1407 /*
1408 * this function is used to create the global main_v->bookmarkstore
1409 * as well as the project bookmarkstores
1410 */
1411 gpointer
bookmark_data_new(void)1412 bookmark_data_new(void)
1413 {
1414 Tbmarkdata *bmd;
1415 bmd = g_new0(Tbmarkdata, 1);
1416 bmd->bookmarkstore = gtk_tree_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
1417 gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(bmd->bookmarkstore), bmark_sort_func, bmd,
1418 NULL);
1419 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(bmd->bookmarkstore),
1420 GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
1421 bmd->bmarkfiles =
1422 g_hash_table_new_full(g_file_hash, (GEqualFunc) g_file_equal, bmark_hash_key_free,
1423 bmark_hash_value_free);
1424 DEBUG_MSG("bookmark_data_new, created bookmarkstore at %p\n", bmd->bookmarkstore);
1425 return bmd;
1426 }
1427
1428 /* used to clean up the project bookmarkdata */
1429 gpointer
bookmark_data_cleanup(gpointer data)1430 bookmark_data_cleanup(gpointer data)
1431 {
1432 Tbmarkdata *bmd = BMARKDATA(data);
1433 GtkTreeIter fileit;
1434 gboolean cont;
1435 /*walk the treestore and free all Tbmark's in the pointer columns */
1436 DEBUG_MSG("bookmark_data_cleanup bmarkdata %p\n", bmd);
1437 cont = gtk_tree_model_iter_children(GTK_TREE_MODEL(bmd->bookmarkstore), &fileit, NULL);
1438 while (cont) { /* walk the toplevel */
1439 GtkTreeIter bmit;
1440 gboolean cont2 = gtk_tree_model_iter_children(GTK_TREE_MODEL(bmd->bookmarkstore), &bmit, &fileit);
1441 while (cont2) {
1442 Tbmark *bmark;
1443 gtk_tree_model_get(GTK_TREE_MODEL(bmd->bookmarkstore), &bmit, PTR_COLUMN, &bmark, -1);
1444 bmark->strarr = NULL;
1445 if (bmark->doc)
1446 bmark->doc->bmark_parent = NULL;
1447 bmark_free(bmark);
1448 cont2 = gtk_tree_model_iter_next(GTK_TREE_MODEL(bmd->bookmarkstore), &bmit);
1449 }
1450 cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(bmd->bookmarkstore), &fileit);
1451 }
1452 g_object_unref(bmd->bookmarkstore);
1453 g_hash_table_destroy(bmd->bmarkfiles);
1454 g_free(bmd);
1455 return NULL;
1456 }
1457
1458 /* this function will load the bookmarks
1459 * from bfwin->session->bmarks and parse
1460 * them into treestore BMARKDATA(bfwin->bmarkdata)->bookmarkstore
1461 *
1462 * it is called from bluefish.c for the first window (global bookmarks)
1463 * and from project.c during opening a project
1464 *
1465 * this function should ALSO check all douments that are
1466 * opened (bfwin->documentlist) if they have bookmarks !!
1467 */
1468 void
bmark_reload(Tbfwin * bfwin)1469 bmark_reload(Tbfwin * bfwin)
1470 {
1471 GFile *cacheduri = NULL;
1472 GList *tmplist;
1473
1474 DEBUG_MSG("bmark_reload for bfwin %p\n", bfwin);
1475 bmark_store_all(bfwin);
1476
1477 tmplist = g_list_first(bfwin->session->bmarks);
1478 while (tmplist) {
1479 gchar **items = (gchar **) tmplist->data;
1480 if (items && g_strv_length(items) == 6) {
1481 gchar *ptr;
1482 Tbmark *b;
1483 b = g_slice_new0(Tbmark);
1484 /*g_print("bmark_reload, alloc bmark %p\n",b); */
1485 b->name = g_strdup(items[0]);
1486 b->description = g_strdup(items[1]);
1487 /* convert old (Bf 1.0) bookmarks to new bookmarks with uri's */
1488 if (strchr(items[2], ':') == NULL) {
1489 gchar *tmp;
1490 tmp = g_strconcat("file://", items[2], NULL);
1491 b->uri = g_file_parse_name(tmp);
1492 g_free(tmp);
1493 } else {
1494 b->uri = g_file_parse_name(items[2]);
1495 }
1496 /* because the bookmark list is usually sorted, we try to cache the uri's and consume less memory */
1497 if (cacheduri && (cacheduri == b->uri || g_file_equal(cacheduri, b->uri))) {
1498 DEBUG_MSG("bmark_reload, uri %p and %p are identical, unref %p and use %p\n", cacheduri,
1499 b->uri, b->uri, cacheduri);
1500 g_object_unref(b->uri);
1501 b->uri = g_object_ref(cacheduri);;
1502 } else {
1503 DEBUG_MSG("bmark_reload, new uri %p\n", b->uri);
1504 cacheduri = b->uri;
1505 }
1506
1507 b->offset = atoi(items[3]);
1508 b->text = g_strdup(items[4]);
1509 b->len = atoi(items[5]);
1510 b->strarr = items;
1511 DEBUG_MSG("bmark_reload, loaded bookmark %p for uri=%pat offset %d with text %s\n", b,
1512 b->uri, b->offset, b->text);
1513 bmark_get_iter_at_tree_position(bfwin, b);
1514 ptr = bmark_showname(bfwin, b);
1515 gtk_tree_store_set(BMARKDATA(bfwin->bmarkdata)->bookmarkstore, &(b->iter), NAME_COLUMN, ptr,
1516 PTR_COLUMN, b, -1);
1517 g_free(ptr);
1518 }
1519 tmplist = g_list_next(tmplist);
1520 }
1521 #ifdef WALKTREE
1522 /* walk over all bookmarks and print them to stdout */
1523 {
1524 GtkTreeIter iter;
1525 gboolean cont;
1526 cont =
1527 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &iter);
1528 while (cont) {
1529 gchar *name;
1530 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &iter, NAME_COLUMN,
1531 &name, -1);
1532 g_print("walk bookmarks, got name %s\n", name);
1533 g_free(name);
1534 cont =
1535 gtk_tree_model_iter_next(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &iter);
1536 }
1537 }
1538 #endif
1539 }
1540
1541 /*
1542 * this function will simply call
1543 * gtk_tree_view_set_model() to connect the treeview
1544 * to the new treestore, used in unloading and
1545 * loading of projects
1546 */
1547 void
bmark_set_store(Tbfwin * bfwin)1548 bmark_set_store(Tbfwin * bfwin)
1549 {
1550 DEBUG_MSG("bmark_set_store set store %p for bfwin %p\n", BMARKDATA(bfwin->bmarkdata)->bookmarkstore,
1551 bfwin);
1552 if (BMARKDATA(bfwin->bmarkdata)->bookmarkstore && bfwin->bmark) {
1553 bfwin->bmarkfilter = (GtkTreeModelFilter *)
1554 gtk_tree_model_filter_new(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), NULL);
1555 gtk_tree_model_filter_set_visible_func(bfwin->bmarkfilter, bmark_search_filter_func, bfwin, NULL);
1556 gtk_tree_view_set_model(bfwin->bmark, GTK_TREE_MODEL(bfwin->bmarkfilter));
1557 g_object_unref(bfwin->bmarkfilter);
1558 }
1559 }
1560
1561 /* the Tdocument will be closed, but the bookmark should stay in the treestore */
1562 void
bmark_clean_for_doc(Tdocument * doc)1563 bmark_clean_for_doc(Tdocument * doc)
1564 {
1565 GtkTreeIter tmpiter;
1566 GtkTreePath *path;
1567 gboolean cont;
1568
1569 if (doc->bmark_parent == NULL)
1570 return;
1571
1572 if (BFWIN(doc->bfwin)->bmarkdata == NULL)
1573 return;
1574
1575 DEBUG_MSG("bmark_clean_for_doc, doc=%p, bfwin=%p, bmarkdata=%p, getting children for parent_iter=%p\n",
1576 doc, doc->bfwin, BFWIN(doc->bfwin)->bmarkdata, doc->bmark_parent);
1577 /* a segfault is reported here, coming from a document save and close */
1578 cont =
1579 gtk_tree_model_iter_children(GTK_TREE_MODEL(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore),
1580 &tmpiter, doc->bmark_parent);
1581 while (cont) {
1582 Tbmark *b = NULL;
1583 DEBUG_MSG("bmark_clean_for_doc, getting bookmark for first child\n");
1584 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore), &tmpiter,
1585 PTR_COLUMN, &b, -1);
1586 if (b) {
1587 bmark_update_offset_from_textmark(b);
1588 DEBUG_MSG
1589 ("bmark_clean_for_doc, bookmark=%p, new offset=%d, now deleting GtkTextMark %p from TextBuffer\n",
1590 b, b->offset, b->mark);
1591 gtk_text_buffer_delete_mark(doc->buffer, b->mark);
1592 if (doc->fileinfo)
1593 b->len = gtk_text_buffer_get_char_count(doc->buffer);
1594 b->mark = NULL;
1595 b->doc = NULL;
1596 if (!b->is_temp) {
1597 bmark_store(doc->bfwin, b);
1598 }
1599 }
1600 cont =
1601 gtk_tree_model_iter_next(GTK_TREE_MODEL(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore),
1602 &tmpiter);
1603 } /* cont */
1604 /* now unset the Tdocument* in the second column */
1605 DEBUG_MSG("bmark_clean_for_doc, unsetting and freeing parent_iter %p for doc %p\n", doc->bmark_parent,
1606 doc);
1607 gtk_tree_store_set(GTK_TREE_STORE(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore),
1608 doc->bmark_parent, PTR_COLUMN, NULL, -1);
1609 gtk_tree_model_filter_convert_child_iter_to_iter(BFWIN(doc->bfwin)->bmarkfilter, &tmpiter,
1610 doc->bmark_parent);
1611 doc->bmark_parent = NULL;
1612 path = gtk_tree_model_get_path(GTK_TREE_MODEL(BFWIN(doc->bfwin)->bmarkfilter), &tmpiter);
1613 gtk_tree_view_collapse_row(BFWIN(doc->bfwin)->bmark, path);
1614 gtk_tree_path_free(path);
1615 }
1616
1617 static gboolean
bookmark_reposition(Tbmark * mark,gint offset)1618 bookmark_reposition(Tbmark * mark, gint offset)
1619 {
1620 gint doclen = gtk_text_buffer_get_char_count(mark->doc->buffer);
1621 gint bandwidth = offset > 0 ? 2 * offset : -2 * offset;
1622 if (bandwidth < (5 * strlen(mark->text)))
1623 bandwidth = 5 * strlen(mark->text);
1624 /* search for the bookmark near the old positions */
1625
1626 while (TRUE) {
1627 GtkTextIter its, ite, /*starr,end */ itrs, itre; /* resultstart, resultend */
1628 gint startpos;
1629 startpos = mark->offset + offset - bandwidth / 2;
1630 if (startpos < 0)
1631 startpos = 0;
1632 DEBUG_MSG("bookmark_reposition, searching from %d to %d for %s\n", startpos, startpos + bandwidth,
1633 mark->text);
1634 gtk_text_buffer_get_iter_at_offset(mark->doc->buffer, &its, startpos);
1635 gtk_text_buffer_get_iter_at_offset(mark->doc->buffer, &ite, startpos + bandwidth);
1636 if (gtk_text_iter_forward_search(&its, mark->text, GTK_TEXT_SEARCH_TEXT_ONLY, &itrs, &itre, &ite)) {
1637 /* found !!!!!!! */
1638 DEBUG_MSG("bookmark_reposition, found result! resposition from %d to %d\n", mark->offset,
1639 gtk_text_iter_get_offset(&itrs));
1640 mark->offset = gtk_text_iter_get_offset(&itrs);
1641 return TRUE;
1642 }
1643 if (bandwidth > doclen) {
1644 DEBUG_MSG("bookmark_reposition, no result for %s, original offset %d\n", mark->text,
1645 mark->offset);
1646 return FALSE;
1647 }
1648 bandwidth *= 2;
1649 }
1650 }
1651
1652 static gboolean
bookmark_needs_repositioning(Tbmark * mark,GtkTextIter * it)1653 bookmark_needs_repositioning(Tbmark * mark, GtkTextIter * it)
1654 {
1655 GtkTextIter it2;
1656 gboolean retval;
1657 gchar *tmpstr;
1658 /* check the content at the bookmark */
1659 gtk_text_buffer_get_iter_at_offset(mark->doc->buffer, &it2, mark->offset + strlen(mark->text));
1660 tmpstr = gtk_text_buffer_get_text(mark->doc->buffer, it, &it2, FALSE);
1661 DEBUG_MSG("original offset %d, compare %s and %s\n", mark->offset, tmpstr, mark->text);
1662 retval = (strcmp(tmpstr, mark->text) != 0);
1663 DEBUG_MSG("bookmark_needs_repositioning, reposition=%d,text='%s', tmpstr='%s'\n", retval, mark->text,
1664 tmpstr);
1665 g_free(tmpstr);
1666 return retval;
1667 }
1668
1669 /*
1670 * this function will check is this document needs any bookmarks, and set the
1671 * doc->bmark_parent if needed
1672 *
1673 * if there are bookmarks, the bookmark GtkTextMark's will be inserted
1674 *
1675 * if check_position is TRUE, the content of the bookmark will be checked, and if
1676 * changed, the offset will be re-positioned
1677 *
1678 */
1679 void
bmark_set_for_doc(Tdocument * doc,gboolean check_positions)1680 bmark_set_for_doc(Tdocument * doc, gboolean check_positions)
1681 {
1682 gboolean cont2;
1683 GtkTreeIter child;
1684 GtkTreePath *path;
1685
1686 if (!doc->uri) {
1687 DEBUG_MSG("bmark_set_for_doc, document %p does not have a filename, returning\n", doc);
1688 return;
1689 }
1690
1691 DEBUG_MSG("bmark_set_for_doc, doc=%p, filename=%s\n", doc, gtk_label_get_text(GTK_LABEL(doc->tab_menu)));
1692 /* if (!BFWIN(doc->bfwin)->bmark) {
1693 DEBUG_MSG("bmark_set_for_doc, no leftpanel, not implemented yet!!\n");
1694 return;
1695 }*/
1696 if (doc->bmark_parent) {
1697 DEBUG_MSG("this document (%p) already has a bmark_parent (%p) why is this function called?\n", doc,
1698 doc->bmark_parent);
1699 return;
1700 }
1701
1702 doc->bmark_parent = g_hash_table_lookup(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bmarkfiles, doc->uri);
1703 if (!doc->bmark_parent)
1704 return;
1705
1706 /*g_print("bmark_set_for_doc, we found a bookmark for document %s at offset=%d!\n",gtk_label_get_text(GTK_LABEL(doc->tab_menu)),mark->offset); */
1707 gtk_tree_store_set(GTK_TREE_STORE(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore),
1708 doc->bmark_parent, PTR_COLUMN, doc, -1);
1709
1710 cont2 =
1711 gtk_tree_model_iter_children(GTK_TREE_MODEL
1712 (BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore), &child,
1713 doc->bmark_parent);
1714 while (cont2) { /* loop the bookmarks for this document */
1715 Tbmark *mark = NULL;
1716 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore), &child,
1717 PTR_COLUMN, &mark, -1);
1718 if (mark) {
1719 GtkTextIter it;
1720 mark->doc = doc;
1721 DEBUG_MSG("bmark_set_for_doc, next bookmark at offset=%d!\n", mark->offset);
1722 gtk_text_buffer_get_iter_at_offset(doc->buffer, &it, mark->offset);
1723 if (check_positions && bookmark_needs_repositioning(mark, &it)) { /* repositioning required ! */
1724 if (bookmark_reposition(mark, gtk_text_buffer_get_char_count(doc->buffer) - mark->len)) {
1725 gtk_text_buffer_get_iter_at_offset(doc->buffer, &it, mark->offset);
1726 } else {
1727 /* BUG: bookmark not restored, what to do now ???? - just put it where it was ?? */
1728 }
1729 }
1730 mark->mark = gtk_text_buffer_create_mark(doc->buffer, NULL, &it, TRUE);
1731 DEBUG_MSG("bmark_set_for_doc, create GtkTextMark for bmark %p at %p\n", mark, mark->mark);
1732 }
1733 cont2 =
1734 gtk_tree_model_iter_next(GTK_TREE_MODEL
1735 (BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore), &child);
1736 }
1737
1738 /* expand it */
1739 gtk_tree_model_filter_convert_child_iter_to_iter(BFWIN(doc->bfwin)->bmarkfilter, &child,
1740 doc->bmark_parent);
1741 path = gtk_tree_model_get_path(GTK_TREE_MODEL(BFWIN(doc->bfwin)->bmarkfilter), &child);
1742 gtk_tree_view_expand_row(BFWIN(doc->bfwin)->bmark, path, TRUE);
1743 gtk_tree_path_free(path);
1744 bluefish_text_view_set_show_symbols_redraw(BLUEFISH_TEXT_VIEW(doc->view), TRUE);
1745 }
1746
1747 /* this is called by the editor widget to show bookmarks in the left margin.
1748 returns a line number for the Tbmark that bmark points to, or -1 if there is no bmark */
1749 gint
bmark_margin_get_next_bookmark(Tdocument * doc,gpointer * bmark)1750 bmark_margin_get_next_bookmark(Tdocument * doc, gpointer * bmark)
1751 {
1752 gboolean cont;
1753 GtkTextIter textit;
1754 GtkTreeIter treeit = ((Tbmark *) * bmark)->iter;
1755 cont =
1756 gtk_tree_model_iter_next(GTK_TREE_MODEL(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore),
1757 &treeit);
1758 if (!cont) {
1759 return -1;
1760 }
1761 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore), &treeit,
1762 PTR_COLUMN, bmark, -1);
1763 gtk_text_buffer_get_iter_at_mark(doc->buffer, &textit, ((Tbmark *) * bmark)->mark);
1764 return gtk_text_iter_get_line(&textit);
1765 }
1766
1767 /* this is called by the editor widget to show bookmarks in the left margin.
1768 returns a line number for the Tbmark that bmark points to, or -1 if there is no bmark */
1769 gint
bmark_margin_get_initial_bookmark(Tdocument * doc,GtkTextIter * fromit,gpointer * bmark)1770 bmark_margin_get_initial_bookmark(Tdocument * doc, GtkTextIter * fromit, gpointer * bmark)
1771 {
1772 guint offset;
1773 GtkTextIter textit;
1774 if (!doc->bmark_parent) {
1775 return -1;
1776 }
1777 offset = gtk_text_iter_get_offset(fromit);
1778 *bmark = bmark_find_bookmark_before_offset(BFWIN(doc->bfwin), offset, doc->bmark_parent); /* returns NULL if there is no existing bookmark *before* offset */
1779 if (!*bmark) {
1780 GtkTreeIter treeit;
1781 gboolean retval =
1782 gtk_tree_model_iter_children(GTK_TREE_MODEL
1783 (BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore), &treeit,
1784 doc->bmark_parent);
1785 if (!retval) {
1786 return -1;
1787 }
1788 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore), &treeit,
1789 PTR_COLUMN, bmark, -1);
1790 }
1791 gtk_text_buffer_get_iter_at_mark(doc->buffer, &textit, ((Tbmark *) * bmark)->mark);
1792 return gtk_text_iter_get_line(&textit);
1793 }
1794
1795 /* this function will simply add the bookmark as defined in the arguments
1796 *
1797 * will use offset if itoffset is NULL
1798 * will use itoffset if not NULL
1799 */
1800 static Tbmark *
bmark_add_backend(Tdocument * doc,GtkTextIter * itoffset,gint offset,const gchar * name,const gchar * text,gboolean is_temp)1801 bmark_add_backend(Tdocument * doc, GtkTextIter * itoffset, gint offset, const gchar * name,
1802 const gchar * text, gboolean is_temp)
1803 {
1804 Tbmark *m;
1805 gchar *displaytext = NULL;
1806 GtkTextIter it;
1807 m = g_slice_new0(Tbmark);
1808 /*g_print("bmark_add_backend, alloc bmark %p\n",m); */
1809 m->doc = doc;
1810
1811 if (itoffset) {
1812 it = *itoffset;
1813 m->offset = gtk_text_iter_get_offset(&it);
1814 } else {
1815 gtk_text_buffer_get_iter_at_offset(doc->buffer, &it, offset);
1816 m->offset = offset;
1817 }
1818
1819 m->mark = gtk_text_buffer_create_mark(doc->buffer, NULL, &it, TRUE);
1820 DEBUG_MSG("bmark_add_backend, mark=%p, create GtkTextMark %p\n", m, m->mark);
1821 m->uri = bmark_uri_from_doc(doc);
1822 m->is_temp = is_temp;
1823 m->text = g_strdup(text);
1824 m->name = (name) ? g_strdup(name) : g_strdup("");
1825 m->description = g_strdup("");
1826
1827 /* insert into tree */
1828 bmark_get_iter_at_tree_position(doc->bfwin, m);
1829 displaytext = bmark_showname(doc->bfwin, m);
1830 gtk_tree_store_set(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore, &m->iter, NAME_COLUMN,
1831 displaytext, PTR_COLUMN, m, -1);
1832 g_free(displaytext);
1833
1834 /* and store */
1835 if (!m->is_temp) {
1836 bmark_store(BFWIN(doc->bfwin), m);
1837 }
1838 DEBUG_MSG("bmark_add_backend, set show symbols to true and call for redraw\n");
1839 bluefish_text_view_set_show_symbols_redraw(BLUEFISH_TEXT_VIEW(doc->view), TRUE);
1840 return m;
1841 }
1842
1843 /**
1844 * bmark_text_for_offset:
1845 *
1846 * will use offset if itoffset is NULL
1847 * will use itoffset if not NULL
1848 */
1849 static gchar *
bmark_text_for_offset(Tdocument * doc,GtkTextIter * itoffset,gint offset)1850 bmark_text_for_offset(Tdocument * doc, GtkTextIter * itoffset, gint offset)
1851 {
1852 GtkTextIter it, eit, sit;
1853 if (itoffset) {
1854 it = *itoffset;
1855 } else {
1856 gtk_text_buffer_get_iter_at_offset(doc->buffer, &it, offset);
1857 }
1858 sit = eit = it;
1859 gtk_text_iter_forward_to_line_end(&eit);
1860 gtk_text_iter_forward_chars(&sit, BMARK_SHOW_NUM_TEXT_CHARS);
1861 if (!gtk_text_iter_in_range(&sit, &it, &eit))
1862 sit = eit;
1863 #ifdef DEBUG
1864 {
1865 gchar *tmp = gtk_text_iter_get_text(&it, &sit);
1866 DEBUG_MSG("bmark_text_for_offset, text=%s\n", tmp);
1867 g_free(tmp);
1868 }
1869 #endif
1870 return gtk_text_iter_get_text(&it, &sit);
1871 }
1872
1873 /* this function will add a bookmark to the current document at current cursor / selection */
1874 static void
bmark_add_current_doc_backend(Tbfwin * bfwin,const gchar * name,gint offset,gboolean is_temp)1875 bmark_add_current_doc_backend(Tbfwin * bfwin, const gchar * name, gint offset, gboolean is_temp)
1876 {
1877 GtkTextIter it, eit, sit;
1878 Tbmark *m;
1879 if (!bfwin->current_document)
1880 return;
1881 DEBUG_MSG("bmark_add_backend, adding bookmark at offset=%d for bfwin=%p\n", offset, bfwin);
1882 /* create bookmark */
1883 gtk_text_buffer_get_iter_at_offset(DOCUMENT(bfwin->current_document)->buffer, &it, offset);
1884 /* if there is a selection, and the offset is within the selection, we'll use it as text content */
1885 if (gtk_text_buffer_get_selection_bounds(DOCUMENT(bfwin->current_document)->buffer, &sit, &eit)
1886 && gtk_text_iter_in_range(&it, &sit, &eit)) {
1887 gchar *text = gtk_text_iter_get_text(&sit, &eit);
1888 m = bmark_add_backend(DOCUMENT(bfwin->current_document), &sit, offset, name, text, is_temp);
1889 g_free(text);
1890
1891 } else {
1892 gchar *text;
1893 text = bmark_text_for_offset(DOCUMENT(bfwin->current_document), &it, offset);
1894 m = bmark_add_backend(DOCUMENT(bfwin->current_document), &it, offset, name, text, is_temp);
1895 g_free(text);
1896 }
1897 if (bfwin->bmark) { /* only if there is a left panel we should do this */
1898 GtkTreePath *path;
1899 path = gtk_tree_model_get_path(GTK_TREE_MODEL(BMARKDATA(bfwin->bmarkdata)->bookmarkstore), &m->iter);
1900 gtk_tree_view_expand_to_path(bfwin->bmark, path);
1901 gtk_tree_path_free(path);
1902 gtk_widget_grab_focus(bfwin->current_document->view);
1903 }
1904 }
1905
1906 /*
1907 can we make this function faster? when adding bookmarks from a search this function uses
1908 a lot of time, perhaps that can be improved
1909 */
1910 static Tbmark *
bmark_get_bmark_at_iter(Tdocument * doc,GtkTextIter * iter,gint offset)1911 bmark_get_bmark_at_iter(Tdocument * doc, GtkTextIter * iter, gint offset)
1912 {
1913 GtkTextIter sit, eit;
1914 GtkTreeIter tmpiter;
1915 gint linenum;
1916 sit = *iter;
1917 linenum = gtk_text_iter_get_line(&sit);
1918 eit = sit;
1919 gtk_text_iter_set_line_offset(&sit, 0);
1920 gtk_text_iter_forward_to_line_end(&eit);
1921 /* check for existing bookmark in this place */
1922 if (DOCUMENT(doc)->bmark_parent) {
1923 GtkTextIter testit;
1924 Tbmark *m, *m2;
1925 /* the next function is probably the slowest since it jumps through the listmodel
1926 to find the right bookmark */
1927 m = bmark_find_bookmark_before_offset(BFWIN(doc->bfwin), offset, doc->bmark_parent);
1928 if (m == NULL) {
1929 DEBUG_MSG("bmark_get_bmark_at_iter, m=NULL, get first child\n");
1930 if (gtk_tree_model_iter_children
1931 (GTK_TREE_MODEL(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore), &tmpiter,
1932 doc->bmark_parent)) {
1933 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore),
1934 &tmpiter, PTR_COLUMN, &m2, -1);
1935 gtk_text_buffer_get_iter_at_mark(doc->buffer, &testit, m2->mark);
1936 if (gtk_text_iter_get_line(&testit) == linenum) {
1937 return m2;
1938 }
1939 }
1940 } else {
1941 gtk_text_buffer_get_iter_at_mark(doc->buffer, &testit, m->mark);
1942 DEBUG_MSG("bmark_get_bmark_at_iter, m=%p, has linenum=%d\n", m, gtk_text_iter_get_line(&testit));
1943 if (gtk_text_iter_get_line(&testit) == linenum) {
1944 return m;
1945 }
1946 tmpiter = m->iter;
1947 if (gtk_tree_model_iter_next
1948 (GTK_TREE_MODEL(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore), &tmpiter)) {
1949 gtk_tree_model_get(GTK_TREE_MODEL(BMARKDATA(BFWIN(doc->bfwin)->bmarkdata)->bookmarkstore),
1950 &tmpiter, PTR_COLUMN, &m2, -1);
1951 gtk_text_buffer_get_iter_at_mark(doc->buffer, &testit, m2->mark);
1952 if (gtk_text_iter_get_line(&testit) == linenum) {
1953 return m2;
1954 }
1955 }
1956 }
1957 DEBUG_MSG("bmark_get_bmark_at_iter, nothing found at this line, return NULL\n");
1958 return NULL;
1959
1960 }
1961 DEBUG_MSG("bmark_get_bmark_at_iter, no existing bookmark found, return NULL\n");
1962 return NULL;
1963 }
1964
1965 static Tbmark *
bmark_get_bmark_at_line(Tdocument * doc,gint line)1966 bmark_get_bmark_at_line(Tdocument * doc, gint line)
1967 {
1968 GtkTextIter iter;
1969 gtk_text_buffer_get_iter_at_line(doc->buffer, &iter, line);
1970 return bmark_get_bmark_at_iter(doc, &iter, gtk_text_iter_get_offset(&iter));
1971 }
1972
1973 static Tbmark *
bmark_get_bmark_at_offset(Tdocument * doc,gint offset)1974 bmark_get_bmark_at_offset(Tdocument * doc, gint offset)
1975 {
1976 GtkTextIter iter;
1977 gtk_text_buffer_get_iter_at_offset(doc->buffer, &iter, offset);
1978 return bmark_get_bmark_at_iter(doc, &iter, offset);
1979 }
1980
1981 /**
1982 * bmark_add_extern
1983 * @doc: a #Tdocument* with the document
1984 * @offset: the character position where to set the bookmark
1985 * @name: a name to set for the bookmark, or NULL for no name
1986 * @text: the text for the bookmark, or NULL to have it set automatically
1987 *
1988 * Code in bluefish that want to set a bookmark, not related to
1989 * the cursor location or a mouse position should use
1990 * this function.
1991 */
1992 void
bmark_add_extern(Tdocument * doc,gint offset,const gchar * name,const gchar * text,gboolean is_temp)1993 bmark_add_extern(Tdocument * doc, gint offset, const gchar * name, const gchar * text, gboolean is_temp)
1994 {
1995 if (!doc)
1996 return;
1997 DEBUG_MSG("adding bookmark at offset %d with name %s\n", offset, name); /* dummy */
1998 if (!bmark_get_bmark_at_offset(doc, offset)) {
1999 if (text) {
2000 bmark_add_backend(doc, NULL, offset, (name) ? name : "", text, is_temp);
2001 } else {
2002 gchar *tmp = bmark_text_for_offset(doc, NULL, offset);
2003 bmark_add_backend(doc, NULL, offset, (name) ? name : "", tmp, is_temp);
2004 g_free(tmp);
2005 }
2006 }
2007 }
2008
2009 void
bmark_toggle(Tdocument * doc,gint offset,const gchar * name,const gchar * text)2010 bmark_toggle(Tdocument * doc, gint offset, const gchar * name, const gchar * text)
2011 {
2012 Tbmark *bmark;
2013 if (!doc)
2014 return;
2015 bmark = bmark_get_bmark_at_offset(doc, offset);
2016 if (bmark) {
2017 bmark_check_remove(BFWIN(doc->bfwin), bmark); /* check if we should remove a filename too */
2018 bmark_unstore(BFWIN(doc->bfwin), bmark);
2019 bmark_free(bmark);
2020 } else {
2021 bmark_add_extern(doc, offset, name, text, !main_v->globses.bookmarks_default_store);
2022 }
2023 }
2024
2025 void
bmark_toggle_at_cursor(Tbfwin * bfwin)2026 bmark_toggle_at_cursor(Tbfwin * bfwin)
2027 {
2028 GtkTextIter it, it2;
2029 gint offset;
2030 Tbmark *bmark;
2031 /* if the left panel is disabled, we simply should add the bookmark to the list, and do nothing else */
2032 gtk_text_buffer_get_iter_at_mark(DOCUMENT(bfwin->current_document)->buffer, &it,
2033 gtk_text_buffer_get_insert(DOCUMENT(bfwin->current_document)->buffer));
2034 gtk_text_buffer_get_iter_at_mark(DOCUMENT(bfwin->current_document)->buffer, &it2,
2035 gtk_text_buffer_get_selection_bound(DOCUMENT
2036 (bfwin->current_document)->buffer));
2037 gtk_text_iter_order(&it, &it2);
2038 offset = gtk_text_iter_get_offset(&it);
2039 /* check for existing bookmark in this place */
2040 bmark = bmark_get_bmark_at_offset(DOCUMENT(bfwin->current_document), offset);
2041 if (bmark) {
2042 bmark_check_remove(bfwin, bmark); /* check if we should remove a filename too */
2043 bmark_unstore(bfwin, bmark);
2044 bmark_free(bmark);
2045 } else {
2046 bmark_add_current_doc_backend(bfwin, "", offset, !main_v->globses.bookmarks_default_store);
2047 }
2048 }
2049
2050 gboolean
bmark_have_bookmark_at_stored_bevent(Tdocument * doc)2051 bmark_have_bookmark_at_stored_bevent(Tdocument * doc)
2052 {
2053 if (main_v->bevent_doc == doc) {
2054 return (bmark_get_bmark_at_offset(doc, main_v->bevent_charoffset) != NULL);
2055 }
2056 return FALSE;
2057 }
2058
2059 gchar *
bmark_get_tooltip_for_line(Tdocument * doc,gint line)2060 bmark_get_tooltip_for_line(Tdocument * doc, gint line)
2061 {
2062 Tbmark *bmark;
2063 bmark = bmark_get_bmark_at_line(doc, line);
2064 if (!bmark || !bmark->text)
2065 return NULL;
2066 if (bmark->text && bmark->name)
2067 return g_markup_printf_escaped("%s %s", bmark->name, bmark->text);
2068 if (bmark->name)
2069 return g_markup_escape_text(bmark->name, -1);
2070 return g_markup_escape_text(bmark->text, -1);
2071 }
2072
2073
2074 /*gchar *
2075 bmark_get_tooltip_for_line(Tdocument *doc, gint line)
2076 {
2077 Tbmark *bmark;
2078 bmark = bmark_get_bmark_at_line(doc, line);
2079 if (!bmark || !bmark->text)
2080 return NULL;
2081 if (bmark->text && bmark->name)
2082 return g_strconcat(bmark->name," ", bmark->text, NULL);
2083 if (bmark->name)
2084 return g_strdup(bmark->name);
2085 return g_strdup(bmark->text);
2086 }
2087 */
2088 void
bmark_del_at_bevent(Tdocument * doc)2089 bmark_del_at_bevent(Tdocument * doc)
2090 {
2091 if (main_v->bevent_doc == doc) {
2092 Tbmark *b = bmark_get_bmark_at_offset(doc, main_v->bevent_charoffset);
2093 if (b) {
2094 DEBUG_MSG("bmark_del_at_bevent, deleting bookmark %p\n", b);
2095 bmark_check_remove(BFWIN(doc->bfwin), b); /* check if we should remove a filename too */
2096 bmark_unstore(BFWIN(doc->bfwin), b);
2097 bmark_free(b);
2098 }
2099 }
2100 }
2101
2102 void
bmark_add_at_bevent(Tdocument * doc)2103 bmark_add_at_bevent(Tdocument * doc)
2104 {
2105 /* check for unnamed document */
2106 if (main_v->bevent_doc == doc) {
2107 gint offset = main_v->bevent_charoffset;
2108 /* we have the location */
2109 bmark_add_current_doc_backend(doc->bfwin, "", offset, !main_v->globses.bookmarks_default_store);
2110 }
2111 }
2112
2113 /* not used yet
2114 void bmark_del_for_filename(Tbfwin *bfwin, gchar *filename) {
2115 GtkTreeIter *parent = (GtkTreeIter *)g_hash_table_lookup(BMARKDATA(bfwin->bmarkdata)->bmarkfiles,filename);
2116 if (parent) {
2117 bmark_del_children_backend(bfwin, parent);
2118 }
2119 }
2120 */
2121
2122 void
bmark_cleanup(Tbfwin * bfwin)2123 bmark_cleanup(Tbfwin * bfwin)
2124 {
2125 DEBUG_MSG("bmark_cleanup, cleanup for bfwin=%p\n", bfwin);
2126 bfwin->bmark = NULL;
2127 bfwin->bmarkfilter = NULL;
2128 g_free(bfwin->bmark_search_prefix);
2129 bfwin->bmark_search_prefix = NULL;
2130 #ifdef BMARKREF
2131 g_print("bmark_cleanup, itercount=%d\n", bmarkref.itercount);
2132 #endif
2133 }
2134