1 /* Bluefish HTML Editor
2 * document.c - the document
3 *
4 * Copyright (C) 1998-2018 Olivier Sessink
5 * Copyright (C) 1998 Chris Mazuc
6 * some additions Copyright (C) 2004 Eugene Morenko(More)
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
22 #include <gtk/gtk.h>
23
24 #include <string.h> /* strchr() */
25 #include <stdlib.h> /* system() */
26
27 /*#define DEBUG*/
28
29 #include "bluefish.h"
30
31 #ifdef DEBUGPROFILING
32 #ifdef WIN32
33 #undef DEBUGPROFILING
34 #else
35 #include <sys/times.h>
36 #endif
37 #endif
38
39 #ifdef MAC_INTEGRATION
40 /*#include <ige-mac-integration.h>*/
41 #include <gtkosxapplication.h>
42 #endif
43
44 #include "bf_lib.h"
45 #include "bftextview2.h"
46 #include "bftextview2_langmgr.h"
47 #include "bftextview2_identifier.h"
48 #include "bfwin.h"
49 #include "bfwin_uimanager.h"
50 #include "bookmark.h"
51 #include "dialog_utils.h"
52 #include "document.h"
53 #include "file.h"
54 #include "filebrowser2.h"
55 #include "file_dialogs.h"
56 #include "gtk_easy.h" /* *_dialog() */
57 #include "pixmap.h"
58 #include "stringlist.h" /* free_stringlist() */
59 #include "undo_redo.h" /* doc_unre_init() */
60 #include "file_autosave.h"
61 #ifdef HAVE_LIBENCHANT
62 #include "bftextview2_spell.h"
63 #endif
64
65 typedef struct {
66 GtkWidget *textview;
67 GtkWidget *window;
68 } Tfloatingview;
69 #define FLOATINGVIEW(var) ((Tfloatingview *)(var))
70
71 Tselectionsave *
doc_save_selection(Tdocument * doc)72 doc_save_selection(Tdocument * doc)
73 {
74 Tselectionsave *selsave;
75 GtkTextIter start, end;
76 selsave = g_slice_new(Tselectionsave);
77 gtk_text_buffer_get_selection_bounds(doc->buffer, &start, &end);
78 /*g_print("saving selection %d:%d\n",gtk_text_iter_get_offset(&start),gtk_text_iter_get_offset(&end)); */
79 selsave->start = gtk_text_buffer_create_mark(doc->buffer, NULL, &start, FALSE);
80 selsave->end = gtk_text_buffer_create_mark(doc->buffer, NULL, &end, TRUE);
81 selsave->doc = doc;
82 return selsave;
83 }
84
85 void
doc_restore_selection(Tselectionsave * selsave,gboolean only_if_no_selection)86 doc_restore_selection(Tselectionsave * selsave, gboolean only_if_no_selection)
87 {
88 if (!only_if_no_selection || !gtk_text_buffer_get_has_selection(selsave->doc->buffer)) {
89 GtkTextIter start, end;
90 gtk_text_buffer_get_iter_at_mark(selsave->doc->buffer, &start, selsave->start);
91 gtk_text_buffer_get_iter_at_mark(selsave->doc->buffer, &end, selsave->end);
92 /*g_print("restoring selection %d:%d\n",gtk_text_iter_get_offset(&start),gtk_text_iter_get_offset(&end)); */
93 gtk_text_buffer_select_range(selsave->doc->buffer, &start, &end);
94 }
95 gtk_text_buffer_delete_mark(selsave->doc->buffer, selsave->start);
96 gtk_text_buffer_delete_mark(selsave->doc->buffer, selsave->end);
97 g_slice_free(Tselectionsave, selsave);
98 }
99
100 static void
session_set_opendir(Tbfwin * bfwin,gchar * curi)101 session_set_opendir(Tbfwin * bfwin, gchar * curi)
102 {
103 if (curi) {
104 gchar *pos = strrchr(curi, '/');
105 if (pos != NULL) {
106 if (bfwin->session->opendir)
107 g_free(bfwin->session->opendir);
108 bfwin->session->opendir = g_strndup(curi, pos - curi);
109 DEBUG_MSG("session_set_opendir, opendir=%s\n", bfwin->session->opendir);
110 }
111 }
112 }
113
114 /**
115 * return_allwindows_documentlist:
116 *
117 * returns a documentlist with all documents in all windows, the list should be freed, the Tdocuments obviously not
118 *
119 * Return value: #GList* with all documents
120 */
121 GList *
return_allwindows_documentlist()122 return_allwindows_documentlist()
123 {
124 GList *newdoclist = NULL, *bflist, *tmplist = NULL;
125 bflist = g_list_first(main_v->bfwinlist);
126 DEBUG_MSG("return_allwindows_documentlist, bfwinlist length=%d\n", g_list_length(main_v->bfwinlist));
127 while (bflist) {
128 DEBUG_MSG("return_allwindows_documentlist, current bfwin doclist length=%d\n",
129 g_list_length(BFWIN(bflist->data)->documentlist));
130 tmplist = g_list_first(BFWIN(bflist->data)->documentlist);
131 while (tmplist) {
132 newdoclist = g_list_prepend(newdoclist, tmplist->data);
133 tmplist = g_list_next(tmplist);
134 }
135 bflist = g_list_next(bflist);
136 }
137 DEBUG_MSG("return_allwindows_documentlist, returning list length %d\n", g_list_length(newdoclist));
138 return newdoclist;
139 }
140
141 void
alldocs_foreach(foreachdocfunc func,gpointer data)142 alldocs_foreach(foreachdocfunc func, gpointer data)
143 {
144 GList *tmplist, *doclist = return_allwindows_documentlist();
145 tmplist = g_list_first(doclist);
146 while (tmplist) {
147 func(tmplist->data, data);
148 tmplist = g_list_next(tmplist);
149 }
150 g_list_free(doclist);
151 }
152
153 gint
have_modified_documents(GList * doclist)154 have_modified_documents(GList * doclist)
155 {
156 GList *tmplist = g_list_first(doclist);
157 gint count = 0;
158 while (tmplist) {
159 if (DOCUMENT(tmplist->data)->modified) {
160 count++;
161 if (count > 2)
162 return 2;
163 }
164 tmplist = g_list_next(tmplist);
165 }
166 return count;
167 }
168
169 /**
170 * return_urilist_from_doclist:
171 * @doclist: #GList*
172 *
173 * Returns a stringlist with filenames given a
174 * list with documents (#Tdocument*)
175 *
176 * Return value: #GList* stringlist with filenames
177 */
178 GList *
return_urilist_from_doclist(GList * doclist)179 return_urilist_from_doclist(GList * doclist)
180 {
181 GList *newlist = NULL, *tmplist;
182 DEBUG_MSG("return_filenamestringlist_from_doclist, started for doclist %p, len=%d\n", doclist,
183 g_list_length(doclist));
184 tmplist = g_list_first(doclist);
185 while (tmplist) {
186 if (DOCUMENT(tmplist->data)->uri) {
187 g_object_ref(DOCUMENT(tmplist->data)->uri);
188 newlist = g_list_prepend(newlist, DOCUMENT(tmplist->data)->uri);
189 }
190 tmplist = g_list_next(tmplist);
191 }
192 return newlist;
193 }
194
195 /**
196 * return_arraylist_from_doclist:
197 * @doclist: #GList*
198 *
199 * Returns a list of gchar arrays with filenames and document status information given a
200 * list with documents (#Tdocument*)
201 *
202 * Return value: #GList* arraylist with each array containing following data:
203 * [0] - filename
204 * [1] - cursor offset
205 * [2] - topleft corner of visible area offset (used to scroll textview to the same position)
206 * [3] - 1 if document is current
207 * [4] - NULL
208 */
209 GList *
return_arraylist_from_doclist(GList * doclist)210 return_arraylist_from_doclist(GList * doclist)
211 {
212 GList *newlist = NULL, *tmplist;
213 Tdocument *tmpdoc;
214 gchar **tmparr;
215 DEBUG_MSG("return_array_list_from_doclist, started for doclist %p, len=%d\n", doclist,
216 g_list_length(doclist));
217 tmplist = g_list_last(doclist);
218 gint active_doc =
219 g_list_length(doclist) -
220 gtk_notebook_get_current_page(GTK_NOTEBOOK(BFWIN(DOCUMENT(tmplist->data)->bfwin)->notebook)) - 1;
221 gint i = 0;
222 while (tmplist) {
223 GtkTextIter iter;
224 GdkRectangle visible_area;
225 tmpdoc = DOCUMENT(tmplist->data);
226 if (tmpdoc->uri) {
227 gint cursor_offset = doc_get_cursor_position(tmpdoc);
228 gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(tmpdoc->view), &visible_area);
229 gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(tmpdoc->view), &iter, visible_area.x,
230 visible_area.y);
231 gint visible_area_offset = gtk_text_iter_get_offset(&iter);
232 tmparr = g_malloc0(sizeof(gchar *) * 5);
233 tmparr[0] = g_file_get_parse_name(tmpdoc->uri);
234 tmparr[1] = g_strdup_printf("%d", cursor_offset);
235 tmparr[2] = g_strdup_printf("%d", visible_area_offset);
236 if (i == active_doc) {
237 tmparr[3] = g_strdup_printf("%d", 1);
238 } else {
239 tmparr[3] = g_strdup_printf("%d", 0);
240 }
241 newlist = g_list_prepend(newlist, tmparr);
242 }
243 i++;
244 tmplist = g_list_previous(tmplist);
245 }
246 return newlist;
247 }
248
249
250 /*
251 * return_num_untitled_documents:
252 * @doclist: #GList* with documents
253 *
254 * returns the number of untitled documents
255 * opened in Bluefish
256 *
257 * Return value: #gint with number
258 *
259 gint return_num_untitled_documents(GList *doclist) {
260 gint retval = 0;
261 GList *tmplist = g_list_first(doclist);
262 while (tmplist) {
263 if (DOCUMENT(tmplist->data)->filename == NULL) retval++;
264 tmplist = g_list_next(tmplist);
265 }
266 return retval;
267 }*/
268
269 /**
270 * add_filename_to_recentlist:
271 * @bfwin: #Tbfwin*
272 * @uri: a #GFile*
273 *
274 * adds a filename to the recently opened files list
275 * will not add it to the menu, only to the list and the file
276 **/
277 void
add_filename_to_recentlist(Tbfwin * bfwin,GFile * uri)278 add_filename_to_recentlist(Tbfwin * bfwin, GFile * uri)
279 {
280 gchar *curi = g_file_get_uri(uri);
281 bfwin->session->recent_files = add_to_history_stringlist(bfwin->session->recent_files, curi, TRUE);
282 if (main_v->props.recent_means_recently_closed) {
283 bfwin_recent_menu_remove(bfwin, FALSE, curi, uri);
284 } else {
285 bfwin_recent_menu_add(bfwin, FALSE, curi, uri);
286 }
287 if (main_v->props.register_recent_mode == 1) {
288 gtk_recent_manager_add_item(main_v->recentm, curi);
289 }
290 g_free(curi);
291 }
292
293 void
remove_filename_from_recentlist(Tbfwin * bfwin,gboolean project,GFile * uri)294 remove_filename_from_recentlist(Tbfwin * bfwin, gboolean project, GFile * uri)
295 {
296 gchar *curi = g_file_get_uri(uri);
297 if (!project) {
298 bfwin->session->recent_files = remove_from_stringlist(bfwin->session->recent_files, curi);
299 bfwin_recent_menu_remove(bfwin, FALSE, curi, uri);
300 if (main_v->props.register_recent_mode == 1) {
301 GError *gerror = NULL;
302 gtk_recent_manager_remove_item(main_v->recentm, curi, &gerror);
303 }
304 } else {
305 main_v->globses.recent_projects = remove_from_stringlist(main_v->globses.recent_projects, curi);
306 bfwin_recent_menu_remove(bfwin, TRUE, curi, uri);
307 if (main_v->props.register_recent_mode != 0) {
308 GError *gerror = NULL;
309 gtk_recent_manager_remove_item(main_v->recentm, curi, &gerror);
310 }
311 }
312 g_free(curi);
313 }
314
315 /**
316 * documentlist_return_index_from_uri:
317 * @doclist: #GList* with the documents to search in
318 * @filename: a #gchar
319 *
320 * if the file is open, it returns the index in the documentlist
321 * which is also the index in the notebook
322 * if the file is not open it returns -1
323 *
324 * Return value: the index number on success, -1 if the file is not open
325 **/
326 gint
documentlist_return_index_from_uri(GList * doclist,GFile * uri)327 documentlist_return_index_from_uri(GList * doclist, GFile * uri)
328 {
329 GList *tmplist;
330 gint count = 0;
331
332 if (!uri) {
333 return -1;
334 }
335
336 tmplist = g_list_first(doclist);
337 while (tmplist) {
338 Tdocument *doc = tmplist->data;
339 if (doc->uri && (doc->uri == uri || g_file_equal(doc->uri, uri))) {
340 return count;
341 }
342 count++;
343 tmplist = g_list_next(tmplist);
344 }
345 return -1;
346 }
347
348 void
doc_set_uri(Tdocument * doc,GFile * uri,gboolean on_destroy)349 doc_set_uri(Tdocument * doc, GFile * uri, gboolean on_destroy)
350 {
351 if (uri == doc->uri)
352 return;
353
354 if (doc->uri) {
355 g_hash_table_remove(main_v->alldochash, doc->uri);
356 fb2_file_is_closed(doc->uri);
357 g_object_unref(doc->uri);
358 }
359 doc->uri = uri;
360 if (doc->uri) {
361 const gchar *mime = NULL;
362 g_object_ref(doc->uri);
363 g_hash_table_insert(main_v->alldochash, doc->uri, doc);
364 if (doc->fileinfo) {
365 mime = g_file_info_get_attribute_string(doc->fileinfo, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
366 if (!mime)
367 mime =
368 g_file_info_get_attribute_string(doc->fileinfo,
369 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
370 }
371 fb2_file_is_opened(doc->uri, mime);
372 }
373 if (!on_destroy) {
374 DEBUG_MSG("doc_set_uri, call bmark_doc_renamed for doc %p (new uri %p)\n", doc, uri);
375 bmark_doc_renamed(BFWIN(doc->bfwin), doc);
376 }
377 }
378
379
380 /**
381 * documentlist_return_document_from_uri:
382 * @doclist: #GList* with the documents to search in
383 * @filename: a #gchar
384 *
385 * if the file is open, it returns the Tdocument* in the documentlist
386 * if the file is not open it returns NULL
387 *
388 * Return value: #Tdocument* or NULL if not open
389 **/
390 Tdocument *
documentlist_return_document_from_uri(GList * doclist,GFile * uri)391 documentlist_return_document_from_uri(GList * doclist, GFile * uri)
392 {
393 #ifdef DEVELOPMENT
394 GList *tmplist;
395 if (!uri) {
396 DEBUG_MSG("documentlist_return_document_from_filename, no filename! returning\n");
397 return NULL;
398 }
399 tmplist = g_list_first(doclist);
400 while (tmplist) {
401 if (DOCUMENT(tmplist->data)->uri
402 && (DOCUMENT(tmplist->data)->uri == uri || g_file_equal(DOCUMENT(tmplist->data)->uri, uri))) {
403 DEBUG_MSG("documentlist_return_document_from_filename, found, returning %p\n", tmplist->data);
404 if (tmplist->data != g_hash_table_lookup(main_v->alldochash, uri)) {
405 g_warning("document uri hash table is corrupt!!!!!!!!!!!!!!\n");
406 } else {
407 DEBUG_MSG("alldochash is correct\n");
408 }
409 return DOCUMENT(tmplist->data);
410 }
411 tmplist = g_list_next(tmplist);
412 }
413 DEBUG_MSG("documentlist_return_document_from_filename, not found, returning NULL\n");
414 return NULL;
415 #else
416 return g_hash_table_lookup(main_v->alldochash, uri);
417 #endif
418 }
419
420 /**
421 * documentlist_return_document_from_index:
422 * @doclist: #GList* with the documents to search in
423 * @index: a #gint, index in the documentlist.
424 *
425 * If the index is valid, it returns the appropriate Tdocument.
426 *
427 * Return value: Pointer to Tdocument on success, NULL on invalid index.
428 **/
429 Tdocument *
documentlist_return_document_from_index(GList * doclist,gint index)430 documentlist_return_document_from_index(GList * doclist, gint index)
431 {
432 return (Tdocument *) g_list_nth_data(doclist, index);
433 }
434
435 /**
436 * document_return_num_loading:
437 * @doclist: a list of Tdocument* to count
438 *
439 * Return value: number of documents that are not 'complete'
440 */
441 gint
document_return_num_notcomplete(GList * doclist)442 document_return_num_notcomplete(GList * doclist)
443 {
444 GList *tmplist;
445 gint count = 0;
446 for (tmplist = g_list_first(doclist); tmplist != NULL; tmplist = tmplist->next) {
447 if (DOCUMENT(tmplist->data)->status != DOC_STATUS_COMPLETE)
448 count++;
449 }
450 return count;
451 }
452
453 /**
454 * doc_update_highlighting:
455 * @bfwin: #Tbfwin* with the window
456 * @callback_action: #guint ignored
457 * @widget: a #GtkWidget* ignored
458 *
459 * this function works on the current document
460 * if highlighting is disabled, this enables the highlighting
461 * the highlighting is also refreshed for the full document
462 *
463 * Return value: void
464 **/
465 void
doc_update_highlighting(Tbfwin * bfwin,guint callback_action,GtkWidget * widget)466 doc_update_highlighting(Tbfwin * bfwin, guint callback_action, GtkWidget * widget)
467 {
468 if (!bfwin->current_document)
469 return;
470 if (!BLUEFISH_TEXT_VIEW(bfwin->current_document->view)->enable_scanner) {
471 DEBUG_MSG("doc_update_highlighting, set enable_scanner to TRUE\n");
472 BLUEFISH_TEXT_VIEW(bfwin->current_document->view)->enable_scanner = TRUE;
473 }
474 bluefish_text_view_rescan(BLUEFISH_TEXT_VIEW(bfwin->current_document->view));
475 }
476
477 /**
478 * doc_set_wrap:
479 * @doc: a #Tdocument
480 *
481 * this function will synchronise doc->wrapstate with the textview widget
482 * if doc->wrapstate TRUE it will set the textview to GTK_WRAP_WORD
483 * else (FALSE) it will set the textview to GTK_WRAP_NONE
484 *
485 * Return value: void
486 **/
487 void
doc_set_wrap(Tdocument * doc,gboolean enabled)488 doc_set_wrap(Tdocument * doc, gboolean enabled)
489 {
490 GtkWrapMode wmode;
491 if (enabled) {
492 wmode = main_v->props.wrap_on_right_margin ? GTK_WRAP_CHAR : GTK_WRAP_WORD;
493 } else {
494 wmode = GTK_WRAP_NONE;
495 }
496 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(doc->view), wmode);
497 if (doc->slave)
498 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(doc->slave), wmode);
499 doc_recalculate_right_margin(doc);
500 }
501
502 /**
503 * doc_set_tooltip:
504 * @doc: #Tdocument*
505 *
506 * will set the tooltip on the notebook tab eventbox
507 *
508 * Return value: void
509 */
510 void
doc_set_tooltip(Tdocument * doc)511 doc_set_tooltip(Tdocument * doc)
512 {
513 GString *retstr;
514 gchar *tmp;
515 gchar *mtimestr = NULL;
516 gchar *sizestr = NULL;
517
518 retstr = g_string_new(_("Name: "));
519
520 #ifdef PLATFORM_DARWIN
521 /* For specific UI configuration (narrow filebrowser window, document tabs on top, html toolbar hidden,
522 * very long file path >60 symbols) tooltips are not shown for several document tabs on the left, most likely
523 * due some bug in gtk+3.6.4 that determines position and size of the tooltip. Below we split file path to two segments that
524 * results in smaller tooltip window. This hack does not solve issue completely, but reduces probabability of its oscurrence.
525 * There is high probability that this issue affects MacOSX builds only */
526
527 gchar *seg1, *seg2;
528 const gchar *tmptext;
529 tmptext = gtk_label_get_text(GTK_LABEL(doc->tab_menu));
530 if (!g_utf8_validate(tmptext, -1, NULL) || (g_utf8_strlen(tmptext, -1) < 55)) {
531 retstr = g_string_append(retstr, tmptext);
532 } else {
533 seg1 = g_utf8_substring(tmptext, 0, 45);
534 seg2 = g_utf8_substring(tmptext, 45, g_utf8_strlen(tmptext, -1));
535 retstr = g_string_append(retstr, seg1);
536 retstr = g_string_append(retstr, "\n");
537 retstr = g_string_append(retstr, seg2);
538 g_free(seg1);
539 g_free(seg2);
540 }
541 #else
542 retstr = g_string_append(retstr, gtk_label_get_text(GTK_LABEL(doc->tab_menu)));
543 #endif
544 if (BLUEFISH_TEXT_VIEW(doc->view)->bflang) {
545 retstr = g_string_append(retstr, _("\nLanguage mode: "));
546 retstr = g_string_append(retstr, BLUEFISH_TEXT_VIEW(doc->view)->bflang->name);
547 }
548 if (doc->encoding) {
549 retstr = g_string_append(retstr, _("\nEncoding: "));
550 retstr = g_string_append(retstr, doc->encoding);
551 }
552 DEBUG_MSG("doc_set_tooltip, fileinfo=%p for doc %s\n", doc->fileinfo,
553 gtk_label_get_text(GTK_LABEL(doc->tab_menu)));
554 if (doc->fileinfo) {
555 const gchar *mime =
556 g_file_info_get_attribute_string(doc->fileinfo, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
557 if (!mime)
558 mime =
559 g_file_info_get_attribute_string(doc->fileinfo, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
560 if (mime) {
561 retstr = g_string_append(retstr, _("\nMime type: "));
562 retstr = g_string_append(retstr, mime);
563 }
564
565 if (g_file_info_has_attribute(doc->fileinfo, G_FILE_ATTRIBUTE_UNIX_MODE)) {
566 tmp =
567 filemode_to_string(g_file_info_get_attribute_uint32
568 (doc->fileinfo, G_FILE_ATTRIBUTE_UNIX_MODE));
569 g_string_append_printf(retstr, _("\nPermissions: %s\nUid: %u Gid: %u"), tmp,
570 g_file_info_get_attribute_uint32(doc->fileinfo, G_FILE_ATTRIBUTE_UNIX_UID),
571 g_file_info_get_attribute_uint32(doc->fileinfo,
572 G_FILE_ATTRIBUTE_UNIX_GID));
573 g_free(tmp);
574 }
575 if (g_file_info_has_attribute(doc->fileinfo, G_FILE_ATTRIBUTE_STANDARD_SIZE)) {
576 DEBUG_MSG("doc_set_tooltip: size for %s is %" G_GOFFSET_FORMAT "\n",
577 gtk_label_get_text(GTK_LABEL(doc->tab_menu)), g_file_info_get_size(doc->fileinfo));
578 #if (GLIB_CHECK_VERSION(2,30,0))
579 guint64 size;
580
581 size = g_file_info_get_attribute_uint64(doc->fileinfo, G_FILE_ATTRIBUTE_STANDARD_SIZE);
582 sizestr = g_format_size(size);
583 #else
584 sizestr = g_format_size_for_display(g_file_info_get_size(doc->fileinfo));
585 #endif
586 retstr = g_string_append(retstr, _("\nSize (on disk): "));
587 retstr = g_string_append(retstr, sizestr);
588 g_free(sizestr);
589 }
590 if (g_file_info_has_attribute(doc->fileinfo, G_FILE_ATTRIBUTE_TIME_MODIFIED)) {
591 /* this function always appends a newline to the string */
592 time_t modtime =
593 (time_t) g_file_info_get_attribute_uint64(doc->fileinfo, G_FILE_ATTRIBUTE_TIME_MODIFIED);
594 mtimestr = bf_portable_time(&modtime);
595 retstr = g_string_append(retstr, _("\nLast modified: "));
596 retstr = g_string_append(retstr, mtimestr);
597 g_free(mtimestr);
598 }
599 }
600 tmp = g_string_free(retstr, FALSE);
601 gtk_widget_set_tooltip_text(doc->tab_eventbox, tmp);
602 g_free(tmp);
603 }
604
605 static void
tab_label_set_string(Tdocument * doc,const gchar * string)606 tab_label_set_string(Tdocument * doc, const gchar * string)
607 {
608 gtk_label_set_text(GTK_LABEL(doc->tab_label), string);
609 if (main_v->props.max_shown_filename_len > 5) {
610 gint len = g_utf8_strlen(string, -1);
611 if (len > main_v->props.max_shown_filename_len) {
612 gtk_label_set_width_chars(GTK_LABEL(doc->tab_label),
613 MIN(main_v->props.max_shown_filename_len, len));
614 gtk_label_set_ellipsize(GTK_LABEL(doc->tab_label), PANGO_ELLIPSIZE_MIDDLE);
615 return;
616 }
617 }
618 gtk_label_set_ellipsize(GTK_LABEL(doc->tab_label), PANGO_ELLIPSIZE_NONE);
619 }
620
621 /**
622 * doc_set_title:
623 * @doc: #Tdocument*
624 *
625 * will set the notebook tab label and the notebook tab menu label
626 * and if this document->bfwin == document->bfwin->current_document
627 * it will update the bfwin title
628 * it will also call doc_set_tooltip() to reflect the changes in the tooltip
629 *
630 * Return value: void
631 */
632 void
doc_set_title(Tdocument * doc,const gchar * override_label_string)633 doc_set_title(Tdocument * doc, const gchar * override_label_string)
634 {
635 gchar *label_string, *tabmenu_string;
636 if (override_label_string) {
637 label_string = g_strdup(override_label_string);
638 tabmenu_string = g_strdup(override_label_string);
639 } else if (doc->uri) {
640 gchar *parsename, *basename;
641
642 if (g_file_is_native(doc->uri)) {
643 gchar * encodedfilename;
644 gsize bytes_written;
645 GError *gerror=NULL;
646 encodedfilename = g_file_get_path(doc->uri);
647 tabmenu_string = g_filename_to_utf8(encodedfilename,-1,NULL,&bytes_written,&gerror);
648 if (gerror) {
649 g_print("got corrupted filename from disk: %s\n",gerror->message);
650 g_error_free(gerror);
651 }
652 g_free(encodedfilename);
653 } else {
654 tabmenu_string = g_file_get_uri(doc->uri);
655 }
656 parsename = g_file_get_parse_name(doc->uri);
657 basename = g_path_get_basename(parsename);
658 label_string = g_strdup(basename);
659
660 g_free(parsename);
661 g_free(basename);
662 } else {
663 label_string = g_strdup_printf(_("Untitled %d"), main_v->num_untitled_documents);
664 tabmenu_string = g_strdup(label_string);
665 main_v->num_untitled_documents++;
666 }
667 gtk_label_set_text(GTK_LABEL(doc->tab_menu), tabmenu_string);
668 tab_label_set_string(doc, label_string);
669 DEBUG_MSG("doc_set_title, tabmenu_string=%s,label_string=%s\n", tabmenu_string, label_string);
670 doc_set_tooltip(doc);
671 g_free(label_string);
672 g_free(tabmenu_string);
673 if (doc == BFWIN(doc->bfwin)->current_document) {
674 bfwin_set_title(doc->bfwin, doc, 0);
675 }
676 }
677
678 void
doc_set_mimetype(Tdocument * doc,const gchar * mimetype,const gchar * filename)679 doc_set_mimetype(Tdocument * doc, const gchar * mimetype, const gchar * filename)
680 {
681 DEBUG_MSG("doc_set_mimetype(%p, %s)\n", doc, mimetype);
682 if (doc->newdoc_autodetect_lang_id) {
683 /*g_print("disable newdoc_autodetect_lang_id for doc %p\n", doc); */
684 g_source_remove(doc->newdoc_autodetect_lang_id);
685 doc->newdoc_autodetect_lang_id = 0;
686 }
687 bluefish_text_view_select_language(BLUEFISH_TEXT_VIEW(doc->view), mimetype, filename);
688 if (!doc->fileinfo) {
689 doc->fileinfo = g_file_info_new();
690 }
691 g_file_info_set_content_type(doc->fileinfo, mimetype);
692 doc_set_statusbar_lang_encoding(doc);
693 }
694
695 /**
696 * doc_reset_filetype:
697 * @doc: #Tdocument to reset
698 * @newfilename: a #gchar* with the new filename
699 * @buf: the contents of the file
700 * @buflen: the size of the contents
701 *
702 * sets the new filetype based on newfilename and content, updates the widgets and highlighting
703 * (using doc_set_filetype())
704 *
705 * Return value: void
706 **/
707 void
doc_reset_filetype(Tdocument * doc,GFile * newuri,gconstpointer buf,gssize buflen)708 doc_reset_filetype(Tdocument * doc, GFile * newuri, gconstpointer buf, gssize buflen)
709 {
710 gboolean uncertain = FALSE;
711 gchar *filename = NULL, *conttype;
712 #ifdef WIN32
713 gchar *tmp;
714 #endif
715
716 if (newuri)
717 filename = g_file_get_basename(newuri);
718 conttype = g_content_type_guess(filename, buf, buflen, &uncertain);
719
720 #ifdef WIN32
721 /* on WIN32 conttype does not contain a MIME type, that has to be converted */
722 tmp = g_content_type_get_mime_type(conttype);
723 g_free(conttype);
724 conttype = tmp;
725 #endif
726 if (strcmp(conttype, "text/html") == 0 && buf) {
727 const gchar *newtype = NULL;
728 if (strstr(buf, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML") != NULL) {
729 newtype = "application/xhtml+xml";
730 } else if (strstr(buf, "<!DOCTYPE html>") != NULL) {
731 newtype = "text/x-html5";
732 }
733 if (newtype) {
734 g_free(conttype);
735 conttype = g_strdup(newtype);
736 }
737 }
738 DEBUG_MSG("doc_reset_filetype, call doc_set_mimetype for doc=%p\n", doc);
739 doc_set_mimetype(doc, conttype, filename);
740 g_free(filename);
741 g_free(conttype);
742 }
743
744 /**
745 * This function is taken from gtksourceview
746 * Copyright (C) 2001
747 * Mikael Hermansson <tyan@linux.se>
748 * Chris Phelps <chicane@reninet.com>
749 */
750 static gint
textview_calculate_real_tab_width(GtkWidget * textview,gint tab_size)751 textview_calculate_real_tab_width(GtkWidget * textview, gint tab_size)
752 {
753 gchar *tab_string;
754 gint tab_width = 0;
755
756 if (tab_size <= 0)
757 return 0;
758
759 tab_string = g_strnfill(tab_size, ' ');
760 tab_width = widget_get_string_size(textview, tab_string);
761 DEBUG_MSG("textview_calculate_real_tab_width, got %d for '%s' on widget %p\n", tab_width, tab_string,
762 textview);
763 g_free(tab_string);
764 /* if (tab_width < 0) tab_width = 0;*/
765 return tab_width;
766 }
767
768 /**
769 * doc_set_tabsize:
770 * @doc: a #Tdocument
771 * @tabsize: a #gint with the tab size
772 *
773 * this function will set the textview from doc to use the tabsize
774 * described by tabsize
775 *
776 * Return value: void
777 **/
778 void
doc_set_tabsize(Tdocument * doc,gint tabsize)779 doc_set_tabsize(Tdocument * doc, gint tabsize)
780 {
781 PangoTabArray *tab_array;
782 gint pixels = textview_calculate_real_tab_width(GTK_WIDGET(doc->view), tabsize);
783 DEBUG_MSG("doc_set_tabsize, tabsize=%d, pixels=%d\n", tabsize, pixels);
784 tab_array = pango_tab_array_new(1, TRUE);
785 pango_tab_array_set_tab(tab_array, 0, PANGO_TAB_LEFT, pixels);
786 gtk_text_view_set_tabs(GTK_TEXT_VIEW(doc->view), tab_array);
787 if (doc->slave)
788 gtk_text_view_set_tabs(GTK_TEXT_VIEW(doc->slave), tab_array);
789 pango_tab_array_free(tab_array);
790 }
791
792 gint
doc_get_tabsize(Tdocument * doc)793 doc_get_tabsize(Tdocument * doc)
794 {
795 PangoTabArray *tab_array;
796 PangoTabAlign align;
797 gint setsize;
798
799 tab_array = gtk_text_view_get_tabs(GTK_TEXT_VIEW(doc->view));
800 if (tab_array) {
801 gint singlesize;
802
803 singlesize = textview_calculate_real_tab_width(doc->view, 1);
804 pango_tab_array_get_tab(tab_array, 0, &align, &setsize);
805 pango_tab_array_free(tab_array);
806 DEBUG_MSG("doc_get_tabsize, return %d/%d=%d\n", setsize, singlesize, setsize / singlesize);
807 return setsize / singlesize;
808 }
809 return 8;
810 }
811
812 /**
813 * gui_change_tabsize:
814 * @bfwin: #Tbfwin* with the window
815 * @action: a #guint, if 1 increase the tabsize, if 0 decrease
816 * @widget: a #GtkWidget, ignored
817 *
818 * this function is the callback for the menu, based on action
819 * it will increase or decrease the tabsize by one
820 *
821 * Return value: void
822 **/
823 void
doc_change_tabsize(Tdocument * doc,gint direction)824 doc_change_tabsize(Tdocument * doc, gint direction)
825 {
826 PangoTabArray *tab_array;
827 PangoTabAlign align;
828 gint setsize, singlesize;
829 gchar *message;
830 singlesize = textview_calculate_real_tab_width(doc->view, 1);
831 tab_array = gtk_text_view_get_tabs(GTK_TEXT_VIEW(doc->view));
832 if (tab_array) {
833 pango_tab_array_get_tab(tab_array, 0, &align, &setsize);
834 /*g_print("doc_change_tabsize, got setsize=%d\n",setsize); */
835 } else {
836 tab_array = pango_tab_array_new(1, TRUE);
837 setsize = 8;
838 }
839 if (direction == 0) { /* 0 means reset to default */
840 setsize = BFWIN(doc->bfwin)->session->editor_tab_width * singlesize;
841 } else if (direction < 0) {
842 setsize -= singlesize;
843 } else {
844 setsize += singlesize;
845 }
846 message = g_strdup_printf(_("Changed tab width to %d"), setsize / singlesize);
847 bfwin_statusbar_message(BFWIN(doc->bfwin), message, 2);
848 g_free(message);
849 /*g_print("doc_change_tabsize, set setsize=%d\n",setsize); */
850 pango_tab_array_set_tab(tab_array, 0, PANGO_TAB_LEFT, setsize);
851 gtk_text_view_set_tabs(GTK_TEXT_VIEW(doc->view), tab_array);
852 if (doc->slave)
853 gtk_text_view_set_tabs(GTK_TEXT_VIEW(doc->slave), tab_array);
854 pango_tab_array_free(tab_array);
855 }
856
857 void
doc_font_size(Tdocument * doc,gint direction)858 doc_font_size(Tdocument * doc, gint direction)
859 {
860 PangoFontDescription *font_desc;
861 if (direction == 0) {
862 font_desc = pango_font_description_from_string(main_v->props.editor_font_string);
863 } else {
864 PangoContext *pc;
865 gint size, oldsize;
866
867 pc = gtk_widget_get_pango_context(doc->view);
868 font_desc = pango_font_description_copy(pango_context_get_font_description(pc));
869 oldsize = size = pango_font_description_get_size(font_desc);
870 if (direction > 0) {
871 size = MAX(12.0 * size / 10.0, oldsize+PANGO_SCALE);
872 } else {
873 size = MAX(MIN(10.0 * size / 12.0, oldsize-PANGO_SCALE),PANGO_SCALE);
874 }
875 if (pango_font_description_get_size_is_absolute(font_desc)) {
876 pango_font_description_set_absolute_size(font_desc, size);
877 } else {
878 pango_font_description_set_size(font_desc, size);
879 }
880 /* do we have to unref the pangocontext, bluefish crashes if we do so, so I guess not ? */
881 }
882 bluefish_text_view_set_font(BLUEFISH_TEXT_VIEW(doc->view), font_desc);
883 pango_font_description_free(font_desc);
884 }
885
886 /**
887 * doc_is_empty_non_modified_and_nameless:
888 * @doc: a #Tdocument
889 *
890 * this function returns TRUE if the document pointer to by doc
891 * is an empty, nameless and non-modified document
892 *
893 * Return value: gboolean, TRUE if doc is empty, non-modified and nameless
894 **/
895 gboolean
doc_is_empty_non_modified_and_nameless(Tdocument * doc)896 doc_is_empty_non_modified_and_nameless(Tdocument * doc)
897 {
898 if (!doc) {
899 return FALSE;
900 }
901 if (doc->modified || doc->uri || doc->autosave_uri || doc->status != DOC_STATUS_COMPLETE) {
902 return FALSE;
903 }
904 if (gtk_text_buffer_get_char_count(doc->buffer) > 0) {
905 return FALSE;
906 }
907 return TRUE;
908 }
909
910 /**
911 * test_only_empty_doc_left:
912 * @doclist: #GList* with all documents to test in
913 *
914 * returns TRUE if there is only 1 document open, and that document
915 * is not modified and 0 bytes long and without filename
916 * returns FALSE if there are multiple documents open, or
917 * a modified document is open, or a > 0 bytes document is open
918 * or a document with filename is open
919 *
920 * Return value: void
921 **/
922 gboolean
test_only_empty_doc_left(GList * doclist)923 test_only_empty_doc_left(GList * doclist)
924 {
925 if (g_list_length(doclist) > 1) {
926 return FALSE;
927 } else {
928 Tdocument *tmpdoc;
929 GList *tmplist = g_list_first(doclist);
930 if (tmplist) {
931 #ifdef DEBUG
932 g_assert(tmplist->data);
933 #endif
934 tmpdoc = tmplist->data;
935 if (!doc_is_empty_non_modified_and_nameless(tmpdoc)) {
936 return FALSE;
937 }
938 }
939 }
940 return TRUE;
941 }
942
943 /**
944 * doc_move_to_window:
945 * @doc: #Tdocument*
946 * @newwin: #Tbfwin*
947 *
948 * detaches the document from it's old window (doc->bfwin) and attaches
949 * it to the window newwin
950 *
951 * Return value: void, ignored
952 */
953 void
doc_move_to_window(Tdocument * doc,Tbfwin * oldwin,Tbfwin * newwin)954 doc_move_to_window(Tdocument * doc, Tbfwin * oldwin, Tbfwin * newwin)
955 {
956 GtkWidget *tab_widget;
957 DEBUG_MSG("doc_move_to_window, oldwin=%p, newwin=%p, doc=%p\n", oldwin, newwin, doc);
958 /* first test if the document still exists in the old window */
959 if (g_list_index(oldwin->documentlist, doc) == -1 || !bfwin_exists(oldwin) || !bfwin_exists(newwin)) {
960 DEBUG_MSG("doc_move_to_window, the document no longer exists in oldwin %p\n", oldwin);
961 return;
962 }
963 tab_widget = gtk_widget_get_parent(doc->tab_eventbox);
964 g_object_ref(G_OBJECT(doc->vsplit));
965 g_object_ref(G_OBJECT(tab_widget));
966 g_object_ref(G_OBJECT(doc->tab_menu));
967 DEBUG_MSG("doc_move_to_window, tab_label=%p, tab_widget=%p\n", doc->tab_label, tab_widget);
968 /* gtk_container_remove(GTK_CONTAINER(oldwin->notebook), doc->view);*/
969 gtk_notebook_remove_page(GTK_NOTEBOOK(oldwin->notebook), g_list_index(oldwin->documentlist, doc));
970 oldwin->documentlist = g_list_remove(oldwin->documentlist, doc);
971 DEBUG_MSG("doc_move_to_window, removed doc=%p from oldwin %p\n", doc, oldwin);
972 doc->bfwin = newwin;
973 newwin->documentlist = g_list_append(newwin->documentlist, doc);
974 gtk_notebook_append_page_menu(GTK_NOTEBOOK(newwin->notebook), doc->vsplit, tab_widget, doc->tab_menu);
975 DEBUG_MSG("doc_move_to_window, appended doc=%p to newwin %p\n", doc, newwin);
976
977 g_object_unref(G_OBJECT(doc->vsplit));
978 g_object_unref(G_OBJECT(tab_widget));
979 g_object_unref(G_OBJECT(doc->tab_menu));
980
981 gtk_widget_show_all(doc->vsplit);
982 gtk_widget_show_all(tab_widget);
983 gtk_widget_show(doc->tab_menu);
984
985 if (NULL == oldwin->documentlist) {
986 file_new_cb(NULL, oldwin);
987 }
988 }
989
990 typedef struct {
991 Tdocument *doc;
992 Tbfwin *newwin;
993 Tbfwin *oldwin;
994 } Tdmwd;
995
996 static void
doc_move_to_window_dialog_response_lcb(GtkDialog * dialog,gint response,gpointer user_data)997 doc_move_to_window_dialog_response_lcb(GtkDialog * dialog, gint response, gpointer user_data)
998 {
999 Tdmwd *dmwd = (Tdmwd *) user_data;
1000 if (response == 1) {
1001 doc_move_to_window(dmwd->doc, dmwd->oldwin, dmwd->newwin);
1002 } else if (response == 2) {
1003 /* TODO: open readonly */
1004 file_doc_from_uri(dmwd->newwin, dmwd->doc->uri, NULL, NULL, -1, -1, TRUE, -1, TRUE, FALSE);
1005 } else {
1006 /* TODO: do not open */
1007 }
1008 gtk_widget_destroy(GTK_WIDGET(dialog));
1009 g_free(dmwd);
1010 }
1011
1012 void
doc_move_to_window_dialog(Tdocument * doc,Tbfwin * newwin)1013 doc_move_to_window_dialog(Tdocument * doc, Tbfwin * newwin)
1014 {
1015 GtkWidget *dialog;
1016 Tdmwd *dmwd;
1017 dmwd = g_new(Tdmwd, 1);
1018 dmwd->doc = doc;
1019 dmwd->newwin = newwin;
1020 dmwd->oldwin = doc->bfwin;
1021 dialog =
1022 gtk_message_dialog_new(GTK_WINDOW(newwin->main_window), GTK_DIALOG_DESTROY_WITH_PARENT,
1023 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1024 _("Document %s is already open in another Bluefish window.")
1025 , gtk_label_get_text(GTK_LABEL(doc->tab_label)));
1026 gtk_dialog_add_buttons(GTK_DIALOG(dialog), _("Move to this window"), 1, _("Open readonly"), 2,
1027 _("Do not open"), 3, NULL);
1028 g_signal_connect(dialog, "response", G_CALLBACK(doc_move_to_window_dialog_response_lcb), dmwd);
1029 gtk_widget_show_all(dialog);
1030 }
1031
1032
1033 /**
1034 * doc_set_label_color:
1035 * @doc: a #Tdocument
1036 * @color: a parsable string or NULL
1037 *
1038 * sets the text of the notebook tab and tab menu to color
1039 * if color is NULL, the color is reset to the gtk-theme setting
1040 *
1041 * Return value: void
1042 **/
1043 static void
doc_set_label_color(Tdocument * doc,const gchar * color)1044 doc_set_label_color(Tdocument * doc, const gchar * color)
1045 {
1046 GdkColor labelcolor;
1047 GdkColor *color_p = NULL;
1048 if (color != NULL) {
1049 gdk_color_parse(color, &labelcolor);
1050 color_p = &labelcolor;
1051 }
1052 gtk_widget_modify_fg(doc->tab_menu, GTK_STATE_NORMAL, color_p);
1053 gtk_widget_modify_fg(doc->tab_menu, GTK_STATE_PRELIGHT, color_p);
1054 gtk_widget_modify_fg(doc->tab_label, GTK_STATE_NORMAL, color_p);
1055 gtk_widget_modify_fg(doc->tab_label, GTK_STATE_PRELIGHT, color_p);
1056 gtk_widget_modify_fg(doc->tab_label, GTK_STATE_ACTIVE, color_p);
1057 }
1058
1059 static void
doc_update_label_color(Tdocument * doc,gboolean isactive)1060 doc_update_label_color(Tdocument *doc, gboolean isactive)
1061 {
1062 if (!doc) return;
1063
1064 if (doc->readonly || doc->status == DOC_STATUS_LOADING) {
1065 doc_set_label_color(doc, main_v->props.tab_color_loading);
1066 } else if (doc->status == DOC_STATUS_ERROR) {
1067 doc_set_label_color(doc, main_v->props.tab_color_error);
1068 } else if (doc->modified) {
1069 doc_set_label_color(doc, main_v->props.tab_color_modified);
1070 } else if (isactive) {
1071 doc_set_label_color(doc, main_v->props.tab_color_active);
1072 } else {
1073 doc_set_label_color(doc, NULL);
1074 }
1075 }
1076
1077 void
doc_set_status(Tdocument * doc,gint status)1078 doc_set_status(Tdocument * doc, gint status)
1079 {
1080 doc->status = status;
1081 if (status == DOC_STATUS_COMPLETE) {
1082 g_object_set(G_OBJECT(doc->view), "editable", !doc->readonly, NULL);
1083 doc->modified = FALSE;
1084 } else if (status == DOC_STATUS_ERROR) {
1085 g_object_set(G_OBJECT(doc->view), "editable", !doc->readonly, NULL);
1086 } else if (status == DOC_STATUS_LOADING) {
1087 g_object_set(G_OBJECT(doc->view), "editable", FALSE, NULL);
1088 }
1089 doc_update_label_color(doc, (doc == BFWIN(doc->bfwin)->last_activated_doc));
1090 }
1091
1092 /**
1093 * doc_set_modified:
1094 * @doc: a #Tdocument
1095 * @value: a gint TRUE or FALSE
1096 *
1097 * sets the doc->modified to value
1098 * if it already has this value, do nothing
1099 * if it does not have this value, it will do some action
1100 *
1101 * if the document pointed to by doc == the current document
1102 * it will update the toolbar and menu undo/redo items
1103 *
1104 * if value is TRUE, it will make the notebook and notebook-menu
1105 * label red, if value is FALSE it will set them to normal
1106 *
1107 * Return value: void
1108 **/
1109 void
doc_set_modified(Tdocument * doc,gboolean value)1110 doc_set_modified(Tdocument * doc, gboolean value)
1111 {
1112 DEBUG_MSG("doc_set_modified, started, doc=%p, value=%d\n", doc, value);
1113 if (value) {
1114 need_autosave(doc);
1115 } else {
1116 remove_autosave(doc);
1117 }
1118 if (doc->modified != value) {
1119 gchar *color = NULL;
1120 gint change = (value - doc->modified);
1121 doc->modified = value;
1122 if (doc->modified)
1123 color = main_v->props.tab_color_modified;
1124 doc_set_label_color(doc, color);
1125 gtk_label_set_text(GTK_LABEL(doc->tab_modlabel),value?"*":"");
1126 bfwin_set_title(BFWIN(doc->bfwin), BFWIN(doc->bfwin)->current_document, change);
1127 }
1128 #ifdef DEBUG
1129 else {
1130 DEBUG_MSG("doc_set_modified, doc %p did have value %d already\n", doc, value);
1131 }
1132 #endif
1133 /* only when this is the current document we have to change these */
1134 DEBUG_MSG("doc=%p, doc->bfwin=%p\n", doc, doc->bfwin);
1135 if (doc == BFWIN(doc->bfwin)->current_document) {
1136 bfwin_set_undo_redo_actions(BFWIN(doc->bfwin), doc_has_undo_list(doc), doc_has_redo_list(doc));
1137 }
1138 #ifdef DEBUG
1139 else {
1140 DEBUG_MSG("doc_set_modified, doc != current_document, so we do not update the gui widgets\n");
1141 }
1142 #endif
1143 }
1144
1145 /**
1146 * doc_scroll_to_cursor:
1147 * @doc: a #Tdocument
1148 *
1149 * scolls the document pointer to by doc to its cursor position,
1150 * making the cursor visible
1151 *
1152 * Return value: void
1153 **/
1154 void
doc_scroll_to_cursor(Tdocument * doc)1155 doc_scroll_to_cursor(Tdocument * doc)
1156 {
1157 GtkTextMark *mark = gtk_text_buffer_get_insert(doc->buffer);
1158 DEBUG_MSG("doc_scroll_to_cursor, \n");
1159 gtk_text_view_scroll_to_mark(doc_get_active_view(doc), mark, 0.25, FALSE, 0.5, 0.5);
1160 }
1161
1162 /**
1163 * doc_get_chars:
1164 * @doc: a #Tdocument
1165 * @start: a #gint, the start position
1166 * @end: a #gint, the end position
1167 *
1168 * returns all characters (NOT BYTES!!) from start to end from the document
1169 * pointer to by doc. end may be -1 to point to the end of the document
1170 *
1171 * Return value: gchar * with the requested characters
1172 **/
1173 gchar *
doc_get_chars(Tdocument * doc,gint start,gint end)1174 doc_get_chars(Tdocument * doc, gint start, gint end)
1175 {
1176 GtkTextIter itstart, itend;
1177 gchar *string;
1178
1179 gtk_text_buffer_get_iter_at_offset(doc->buffer, &itstart, start);
1180 if (end >= 0) {
1181 gtk_text_buffer_get_iter_at_offset(doc->buffer, &itend, end);
1182 } else if (end == -1) {
1183 gtk_text_buffer_get_end_iter(doc->buffer, &itend);
1184 } else {
1185 g_warning("invalid call to doc_get_chars, end < -1, returning NULL\n");
1186 return NULL;
1187 }
1188 DEBUG_MSG("doc_get_chars, retrieving string, start=%d, end=%d\n", start, end);
1189 string = gtk_text_buffer_get_text(doc->buffer, &itstart, &itend, TRUE);
1190 DEBUG_MSG("doc_get_chars, retrieved string (%p)\n", string);
1191 return string;
1192 }
1193
1194 void
doc_scroll(Tdocument * doc,gint goto_line,gint goto_offset,gint cursor_offset)1195 doc_scroll(Tdocument *doc, gint goto_line, gint goto_offset, gint cursor_offset)
1196 {
1197 GtkTextIter it;
1198
1199 if (goto_line >= 0) {
1200 gtk_text_buffer_get_iter_at_line(doc->buffer, &it, goto_line - 1);
1201 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(doc->view), &it, 0.1, TRUE, 0.5, 0.0);
1202 } else {
1203 if (goto_offset >= 0) {
1204 gtk_text_buffer_get_iter_at_offset(doc->buffer, &it, goto_offset);
1205 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(doc->view), &it, 0.1, TRUE, 0.5, 0.0);
1206 }
1207 }
1208 if (cursor_offset >= 0) {
1209 gtk_text_buffer_get_iter_at_offset(doc->buffer, &it, cursor_offset);
1210 gtk_text_buffer_place_cursor(doc->buffer, &it);
1211 }
1212 }
1213
1214 static void
doc_select_grab_and_scroll(Tdocument * doc,GtkTextIter * it1,GtkTextIter * it2,gboolean select_it1_line,gboolean do_scroll,gboolean align_center)1215 doc_select_grab_and_scroll(Tdocument * doc, GtkTextIter * it1,
1216 GtkTextIter * it2, gboolean select_it1_line, gboolean do_scroll, gboolean align_center)
1217 {
1218 GtkTextIter sit1 = *it1, sit2 = *it2;
1219 DEBUG_MSG("doc_select_grab_and_scroll, do_scroll=%d\n",do_scroll);
1220 if (select_it1_line) {
1221 sit2 = sit1;
1222 gtk_text_iter_set_line_offset(&sit1, 0);
1223 gtk_text_iter_forward_to_line_end(&sit2);
1224 }
1225 gtk_text_buffer_select_range(doc->buffer, &sit1, &sit2);
1226 if (do_scroll) {
1227 /* in doc reload this works strange, there is no scrolling to the correct position...
1228 perhaps this should be done in an idle callback so that the iter positions can be calculated?? */
1229 if (align_center) {
1230 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(doc->view), &sit1, 0.25, FALSE, 0.5, 0.95);
1231 } else {
1232 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(doc->view), &sit1, 0.0, TRUE, 0.0, 0.0);
1233 }
1234 gtk_widget_grab_focus(doc->view);
1235 }
1236 }
1237
1238 /**
1239 * doc_select_region:
1240 * @doc: a #Tdocument
1241 * @start: a #gint with the start of selection
1242 * @end: a #gint with the end of the selection
1243 * @do_scroll: a #gboolean, if we should scroll to the selection
1244 *
1245 * selects from start to end in the doc, and if do_scroll is set it will make
1246 * sure the selection is visible to the user
1247 *
1248 * Return value: void
1249 **/
1250 /*void doc_select_region(Tdocument *doc, gint start, gint end, gboolean do_scroll) {
1251 GtkTextIter itstart, itend;
1252 gtk_text_buffer_get_iter_at_offset(doc->buffer, &itstart,start);
1253 gtk_text_buffer_get_iter_at_offset(doc->buffer, &itend,end);
1254 doc_select_and_scroll(doc, &itstart, &itend,FALSE, do_scroll);
1255 }*/
1256
1257 /**
1258 * doc_select_line:
1259 * @doc: a #Tdocument
1260 * @line: a #gint with the line number to select
1261 * @do_scroll: a #gboolean, if we should scroll to the selection
1262 *
1263 * selects the line in doc, and if do_scroll is set it will make
1264 * sure the selection is visible to the user
1265 * the line number starts at line 1, not at line 0!!
1266 *
1267 * Return value: void
1268 **/
1269 void
doc_select_line(Tdocument * doc,gint line,gboolean do_scroll)1270 doc_select_line(Tdocument * doc, gint line, gboolean do_scroll)
1271 {
1272 GtkTextIter itstart;
1273 gtk_text_buffer_get_iter_at_line(doc->buffer, &itstart, line - 1);
1274 doc_select_grab_and_scroll(doc, &itstart, &itstart, TRUE, do_scroll, TRUE);
1275 }
1276
1277 /**
1278 * doc_select_line_by_offset:
1279 * @doc: a #Tdocument
1280 * @offset: a #gint with the offset of the line number to select
1281 * @do_scroll: a #gboolean, if we should scroll to the selection
1282 *
1283 * selects the line in doc, and if do_scroll is set it will make
1284 * sure the selection is visible to the user
1285 *
1286 * Return value: void
1287 **/
1288 void
doc_select_line_by_offset(Tdocument * doc,gint offset,gboolean do_scroll,gboolean align_center)1289 doc_select_line_by_offset(Tdocument * doc, gint offset, gboolean do_scroll, gboolean align_center)
1290 {
1291 GtkTextIter itstart;
1292 gtk_text_buffer_get_iter_at_offset(doc->buffer, &itstart, offset);
1293 doc_select_grab_and_scroll(doc, &itstart, &itstart, TRUE, do_scroll, align_center);
1294 }
1295
1296 /**
1297 * doc_get_selection:
1298 * @doc: a #Tdocument
1299 * @start: a #gint * to store the start
1300 * @end: a #gint * to store the end
1301 *
1302 * returns FALSE if there is no selection
1303 * returns TRUE if there is a selection, and start and end will be set
1304 * to the current selection
1305 *
1306 * Return value: gboolean if there is a selection
1307 **/
1308 gboolean
doc_get_selection(Tdocument * doc,gint * start,gint * end)1309 doc_get_selection(Tdocument * doc, gint * start, gint * end)
1310 {
1311 GtkTextIter itstart, itend;
1312 GtkTextMark *mark = gtk_text_buffer_get_insert(doc->buffer);
1313 gtk_text_buffer_get_iter_at_mark(doc->buffer, &itstart, mark);
1314 mark = gtk_text_buffer_get_selection_bound(doc->buffer);
1315 gtk_text_buffer_get_iter_at_mark(doc->buffer, &itend, mark);
1316 if (gtk_text_iter_equal(&itstart, &itend))
1317 return FALSE;
1318
1319 *start = gtk_text_iter_get_offset(&itstart);
1320 *end = gtk_text_iter_get_offset(&itend);
1321 DEBUG_MSG("doc_get_selection, start=%d, end=%d\n", *start, *end);
1322 if (*start > *end) {
1323 gint tmp = *start;
1324 *start = *end;
1325 *end = tmp;
1326 }
1327 return TRUE;
1328 }
1329
1330 /**
1331 * doc_get_cursor_position:
1332 * @doc: a #Tdocument
1333 *
1334 * returns the cursor position in doc as character offset
1335 *
1336 * Return value: gint with the character offset of the cursor
1337 **/
1338 gint
doc_get_cursor_position(Tdocument * doc)1339 doc_get_cursor_position(Tdocument * doc)
1340 {
1341 GtkTextIter iter;
1342 GtkTextMark *mark = gtk_text_buffer_get_insert(doc->buffer);
1343 gtk_text_buffer_get_iter_at_mark(doc->buffer, &iter, mark);
1344 return gtk_text_iter_get_offset(&iter);
1345 }
1346
1347 void
doc_set_cursor_position(Tdocument * doc,gint cursor_offset)1348 doc_set_cursor_position(Tdocument * doc, gint cursor_offset)
1349 {
1350 GtkTextIter iter;
1351 if (doc->cursor_offset >= 0) {
1352 DEBUG_MSG("doc_set_cursor_position, cursor_offset=%d\n",cursor_offset);
1353 gtk_text_buffer_get_iter_at_offset(doc->buffer, &iter, cursor_offset);
1354 gtk_text_buffer_place_cursor(doc->buffer, &iter);
1355 }
1356 }
1357
1358 /**
1359 * doc_set_statusbar_lncol:
1360 * @doc: a #Tdocument
1361 *
1362 * Return value: void
1363 **/
1364 static void
doc_set_statusbar_lncol(Tdocument * doc)1365 doc_set_statusbar_lncol(Tdocument * doc)
1366 {
1367 gchar *msg;
1368 gint line;
1369 gint col = 0;
1370 GtkTextIter iter, start;
1371
1372 gtk_text_buffer_get_iter_at_mark(doc->buffer, &iter, gtk_text_buffer_get_insert(doc->buffer));
1373
1374 line = gtk_text_iter_get_line(&iter);
1375
1376 start = iter;
1377 gtk_text_iter_set_line_offset(&start, 0);
1378
1379 while (!gtk_text_iter_equal(&start, &iter)) {
1380 if (gtk_text_iter_get_char(&start) == '\t') {
1381 col +=
1382 (BFWIN(doc->bfwin)->session->editor_tab_width -
1383 (col % BFWIN(doc->bfwin)->session->editor_tab_width));
1384 } else
1385 ++col;
1386 gtk_text_iter_forward_char(&start);
1387 }
1388 if (1) {
1389 msg =
1390 g_strdup_printf(_(" Ln: %d, Col: %d, Char: %d"), line + 1, col + 1,
1391 gtk_text_iter_get_offset(&iter));
1392 } else {
1393 msg = g_strdup_printf(_(" Ln: %d, Col: %d"), line + 1, col + 1);
1394 }
1395
1396 gtk_statusbar_pop(GTK_STATUSBAR(BFWIN(doc->bfwin)->statusbar_lncol), 0);
1397 gtk_statusbar_push(GTK_STATUSBAR(BFWIN(doc->bfwin)->statusbar_lncol), 0, msg);
1398
1399 g_free(msg);
1400 }
1401
1402 /**
1403 * doc_set_statusbar_insovr:
1404 * @doc: a #Tdocument
1405 *
1406 *
1407 *
1408 * Return value: void
1409 **/
1410 static void
doc_set_statusbar_insovr(Tdocument * doc)1411 doc_set_statusbar_insovr(Tdocument * doc)
1412 {
1413 gtk_statusbar_pop(GTK_STATUSBAR(BFWIN(doc->bfwin)->statusbar_insovr), 0);
1414 if (!doc->readonly)
1415 gtk_statusbar_push(GTK_STATUSBAR(BFWIN(doc->bfwin)->statusbar_insovr), 0,
1416 (gtk_text_view_get_overwrite(GTK_TEXT_VIEW(doc->view)) ? _(" OVR") : _(" INS")));
1417 else
1418 gtk_statusbar_push(GTK_STATUSBAR(BFWIN(doc->bfwin)->statusbar_insovr), 0, _(" RO"));
1419 }
1420
1421 /**
1422 * doc_set_statusbar_mimetype_encoding:
1423 * @doc: a #Tdocument
1424 *
1425 * fills the statusbar for encoding and mimetype
1426 *
1427 * Return value: void
1428 **/
1429 void
doc_set_statusbar_lang_encoding(Tdocument * doc)1430 doc_set_statusbar_lang_encoding(Tdocument * doc)
1431 {
1432 gchar *msg = NULL;
1433
1434 if (BLUEFISH_TEXT_VIEW(doc->view)->bflang) {
1435 msg = g_strdup_printf(" %s, %s", BLUEFISH_TEXT_VIEW(doc->view)->bflang->name, doc->encoding);
1436 } else {
1437 msg = g_strdup_printf(" %s, %s", _("Unknown"), doc->encoding);
1438 }
1439 gtk_statusbar_pop(GTK_STATUSBAR(BFWIN(doc->bfwin)->statusbar_editmode), 0);
1440 gtk_statusbar_push(GTK_STATUSBAR(BFWIN(doc->bfwin)->statusbar_editmode), 0, msg);
1441 g_free(msg);
1442 }
1443
1444
1445 void
doc_insert_text_backend(Tdocument * doc,const gchar * newstring,gint position)1446 doc_insert_text_backend(Tdocument * doc, const gchar * newstring, gint position)
1447 {
1448 GtkTextIter iter;
1449 doc_block_undo_reg(doc);
1450 gtk_text_buffer_get_iter_at_offset(doc->buffer, &iter, position);
1451 gtk_text_buffer_insert(doc->buffer, &iter, newstring, -1);
1452 doc_unre_add(doc, newstring, position, position + g_utf8_strlen(newstring, -1), UndoInsert);
1453 doc_unblock_undo_reg(doc);
1454 g_print("doc_insert_text_backend, about to call doc_set_modified(%p,1)\n",doc);
1455 doc_set_modified(doc, 1);
1456 }
1457
1458 /**
1459 * doc_replace_text_backend:
1460 * @doc: a #Tdocument
1461 * @newstring: a #const char * with the new string
1462 * @start: a gint with the start character position
1463 * @end: a gint with the end character position
1464 *
1465 * unbinds all signals so there will be no call to a highlighting
1466 * update or anything else
1467 * deletes the text in the region between start and end
1468 * registers that text to the undo/redo functionality
1469 * inserts newstring at that same position
1470 * registers this to the undo/redo functionality
1471 * marks the document as modified and marks it as needing highlighting
1472 * binds the signals again to their callbacks
1473 *
1474 * multiple calls to doc_replace_text_backend will all be in the same undo/redo group
1475 *
1476 * Return value: void
1477 **/
1478 void
doc_replace_text_backend(Tdocument * doc,const gchar * newstring,gint start,gint end)1479 doc_replace_text_backend(Tdocument * doc, const gchar * newstring, gint start, gint end)
1480 {
1481 doc_block_undo_reg(doc);
1482 /* delete region, and add that to undo/redo list */
1483 if (end == -1 || end > start) {
1484 gchar *buf;
1485 GtkTextIter itstart, itend;
1486 DEBUG_MSG("doc_replace_text_backend, get iters at start %d and end %d\n", start, end);
1487 gtk_text_buffer_get_iter_at_offset(doc->buffer, &itstart, start);
1488 if (end == -1) {
1489 gtk_text_buffer_get_end_iter(doc->buffer, &itend);
1490 } else {
1491 gtk_text_buffer_get_iter_at_offset(doc->buffer, &itend, end);
1492 }
1493 buf = gtk_text_buffer_get_text(doc->buffer, &itstart, &itend, TRUE);
1494 gtk_text_buffer_delete(doc->buffer, &itstart, &itend);
1495 DEBUG_MSG("doc_replace_text_backend, calling doc_unre_add for buf=%s, start=%d and end=%d\n", buf,
1496 start, end);
1497 doc_unre_add(doc, buf, start, end, UndoDelete);
1498 g_free(buf);
1499 DEBUG_MSG("doc_replace_text_backend, text deleted from %d to %d\n", start, end);
1500 }
1501
1502 /* add new text to this region, the buffer is changed so re-calculate itstart */
1503 if (newstring && newstring[0] != '\0') {
1504 GtkTextIter itstart;
1505 gint insert = (end > start) ? start : end;
1506 DEBUG_MSG("doc_replace_text_backend, set insert pos to %d\n", insert);
1507 gtk_text_buffer_get_iter_at_offset(doc->buffer, &itstart, insert);
1508 gtk_text_buffer_insert(doc->buffer, &itstart, newstring, -1);
1509 doc_unre_add(doc, newstring, insert, insert + g_utf8_strlen(newstring, -1), UndoInsert);
1510 }
1511 doc_unblock_undo_reg(doc);
1512 DEBUG_MSG("doc_replace_text_backend, about to call doc_set_modified(%p,1)\n",doc);
1513 doc_set_modified(doc, 1);
1514 }
1515
1516 /**
1517 * doc_replace_text:
1518 * @doc: a #Tdocument
1519 * @newstring: a #const char * with the new string
1520 * @start: a gint with the start character position
1521 * @end: a gint with the end character position
1522 *
1523 * identical to doc_replace_text_backend, with one difference, multiple calls to
1524 * doc_replace_text will be all be in a different undo/redo group
1525 *
1526 * Return value: void
1527 **/
1528 void
doc_replace_text(Tdocument * doc,const gchar * newstring,gint start,gint end)1529 doc_replace_text(Tdocument * doc, const gchar * newstring, gint start, gint end)
1530 {
1531 doc_unre_new_group(doc);
1532 doc_replace_text_backend(doc, newstring, start, end);
1533 doc_unre_new_group(doc);
1534 }
1535
1536 /*
1537 static void doc_convert_case_in_selection(Tdocument *doc, gboolean toUpper) {
1538 gint start, end;
1539 if (doc_get_selection(doc, &start, &end)) {
1540 gchar *string = doc_get_chars(doc, start, end);
1541 if (string) {
1542 gchar *newstring = (toUpper) ? g_utf8_strup(string,-1) : g_utf8_strdown(string,-1);
1543 g_free(string);
1544 if (newstring) {
1545 doc_replace_text(doc, newstring, start, end);
1546 g_free(newstring);
1547 }
1548 }
1549 }
1550 }
1551 */
1552
1553 /**
1554 * doc_insert_two_strings:
1555 * @doc: a #Tdocument
1556 * @before_str: a #const char * with the first string
1557 * @after_str: a #const char * with the second string
1558 *
1559 * if the marks 'diag_ins' and 'diag_sel' exist, they will be used
1560 * as pos1 and pos2
1561 * if a selection exists, the selection start and end will be pos1 and pos2
1562 * if both not exist the cursor position will be both pos1 and pos2
1563 *
1564 * inserts the first string at pos1 and the second at pos2 in doc
1565 * it does not unbind any signal, so the insert callback will have to do
1566 * do the undo/redo, modified and highlighting stuff
1567 *
1568 * multiple calls to this function will be in separate undo/redo groups
1569 *
1570 * Return value: void
1571 **/
1572 void
doc_insert_two_strings(Tdocument * doc,const gchar * before_str,const gchar * after_str)1573 doc_insert_two_strings(Tdocument * doc, const gchar * before_str, const gchar * after_str)
1574 {
1575 GtkTextIter itinsert, itselect;
1576 GtkTextMark *insert, *select;
1577 gboolean have_diag_marks = FALSE;
1578
1579 doc_unre_new_group(doc);
1580 doc_block_undo_reg(doc);
1581 insert = gtk_text_buffer_get_mark(doc->buffer, "diag_ins");
1582 select = gtk_text_buffer_get_mark(doc->buffer, "diag_sel");
1583 if (insert && select) {
1584 have_diag_marks = TRUE;
1585 DEBUG_MSG("doc_insert_two_strings have_diag_marks, diag_ins=%p, diag_sel=%p\n", insert, select);
1586 } else {
1587 insert = gtk_text_buffer_get_insert(doc->buffer);
1588 select = gtk_text_buffer_get_selection_bound(doc->buffer);
1589 DEBUG_MSG("doc_insert_two_strings no diag_marks, insert=%p, select=%p\n", insert, select);
1590 }
1591 gtk_text_buffer_get_iter_at_mark(doc->buffer, &itinsert, insert);
1592 gtk_text_buffer_get_iter_at_mark(doc->buffer, &itselect, select);
1593 DEBUG_MSG("doc_insert_two_strings, current marks: itinsert=%d, itselect=%d\n",
1594 gtk_text_iter_get_offset(&itinsert), gtk_text_iter_get_offset(&itselect));
1595 if (gtk_text_iter_equal(&itinsert, &itselect)) {
1596 /* no selection */
1597 gint pos;
1598 gchar *double_str = g_strconcat(before_str, after_str, NULL);
1599 pos = gtk_text_iter_get_offset(&itinsert);
1600 DEBUG_MSG("doc_insert_two_strings, no selection, insert strings together as one string\n");
1601 doc_unre_add(doc, double_str, pos, pos + g_utf8_strlen(double_str, -1), UndoInsert);
1602 gtk_text_buffer_insert(doc->buffer, &itinsert, double_str, -1);
1603 g_free(double_str);
1604 if (after_str && strlen(after_str)) {
1605 /* the buffer has changed, but gtk_text_buffer_insert makes sure */
1606 /* that itinsert points to the end of the inserted text. */
1607 /* thus, no need to get a new one. */
1608 gtk_text_iter_backward_chars(&itinsert, g_utf8_strlen(after_str, -1));
1609 gtk_text_buffer_place_cursor(doc->buffer, &itinsert);
1610 gtk_widget_grab_focus(doc->view);
1611 }
1612 } else { /* there is a selection */
1613 GtkTextMark *secondat;
1614 GtkTextIter *firstiter, seconditer;
1615 gint pos;
1616 if (gtk_text_iter_compare(&itinsert, &itselect) < 0) {
1617 DEBUG_MSG("doc_insert_two_strings, selection, with diag_marks=%d, first iter is itinsert at %d\n",
1618 have_diag_marks, gtk_text_iter_get_offset(&itinsert));
1619 firstiter = &itinsert;
1620 secondat = select;
1621 } else {
1622 DEBUG_MSG("doc_insert_two_strings, selection with diag_marks=%d, first iter is itselect at %d\n",
1623 have_diag_marks, gtk_text_iter_get_offset(&itselect));
1624 firstiter = &itselect;
1625 secondat = insert;
1626 }
1627 /* there is a selection */
1628 pos = gtk_text_iter_get_offset(firstiter);
1629 DEBUG_MSG("doc_insert_two_strings, insert first string at %d\n", pos);
1630 doc_unre_add(doc, before_str, pos, pos + g_utf8_strlen(before_str, -1), UndoInsert);
1631 gtk_text_buffer_insert(doc->buffer, firstiter, before_str, -1);
1632 if (after_str && strlen(after_str)) {
1633 /* the buffer is changed, reset the select iterator */
1634 gtk_text_buffer_get_iter_at_mark(doc->buffer, &seconditer, secondat);
1635 pos = gtk_text_iter_get_offset(&seconditer);
1636 DEBUG_MSG("doc_insert_two_strings, insert second string at mark %p at %d\n", secondat, pos);
1637 doc_unre_add(doc, after_str, pos, pos + g_utf8_strlen(after_str, -1), UndoInsert);
1638 gtk_text_buffer_insert(doc->buffer, &seconditer, after_str, -1);
1639 /* now the only thing left is to move the selection and insert mark back to their correct places
1640 to preserve the users selection */
1641 if (have_diag_marks) {
1642 DEBUG_MSG("doc_insert_two_strings, reset selection with diag_marks\n");
1643 gtk_text_buffer_get_iter_at_mark(doc->buffer, &itinsert, insert);
1644 gtk_text_buffer_get_iter_at_mark(doc->buffer, &itselect, select);
1645 gtk_text_iter_forward_chars(firstiter, g_utf8_strlen(before_str, -1));
1646 gtk_text_buffer_select_range(doc->buffer, &itinsert, &itselect);
1647 } else {
1648 gtk_text_buffer_get_iter_at_mark(doc->buffer, &seconditer, secondat);
1649 gtk_text_iter_backward_chars(&seconditer, g_utf8_strlen(after_str, -1));
1650 gtk_text_buffer_move_mark(doc->buffer, secondat, &seconditer);
1651 }
1652 }
1653
1654 }
1655 if (have_diag_marks) {
1656 gtk_text_buffer_delete_mark(doc->buffer, insert);
1657 gtk_text_buffer_delete_mark(doc->buffer, select);
1658 }
1659 doc_unre_new_group(doc);
1660 doc_unblock_undo_reg(doc);
1661 g_print("doc_insert_two_strings, about to call doc_set_modified(%p,1)\n",doc);
1662 doc_set_modified(doc, 1);
1663 DEBUG_MSG("doc_insert_two_strings, finished\n");
1664 }
1665
1666 static void
add_encoding_to_list(gchar * encoding)1667 add_encoding_to_list(gchar * encoding)
1668 {
1669 gboolean found = FALSE, changed = FALSE;
1670 GList *tmplist = g_list_first(main_v->globses.encodings);
1671
1672 while (tmplist) {
1673 gchar **tmparr = tmplist->data;
1674 if (g_strv_length(tmparr) == 3 && g_ascii_strcasecmp(tmparr[1], encoding) == 0) {
1675 if (tmparr[2][0] != '1') { /* enable this encoding */
1676 DEBUG_MSG("enable encoding %s\n", tmparr[0]);
1677 g_free(tmparr[2]);
1678 tmparr[2] = g_strdup("1");
1679 changed = TRUE;
1680 }
1681 found = TRUE;
1682 }
1683 tmplist = g_list_next(tmplist);
1684 }
1685 if (!found) {
1686 gchar *temp = g_ascii_strup(encoding, -1);
1687 main_v->globses.encodings =
1688 g_list_insert(main_v->globses.encodings, array_from_arglist(temp, temp, "1", NULL), 1);
1689 g_free(temp);
1690 changed = TRUE;
1691 }
1692 if (changed) {
1693 tmplist = g_list_first(main_v->bfwinlist);
1694 while (tmplist) {
1695 bfwin_encodings_menu_create(BFWIN(tmplist->data));
1696 tmplist = g_list_next(tmplist);
1697 }
1698 }
1699 }
1700
1701 gchar *
encoding_by_regex(const gchar * buffer,const gchar * pattern,guint subpat)1702 encoding_by_regex(const gchar * buffer, const gchar * pattern, guint subpat)
1703 {
1704 GRegex *reg1;
1705 GError *gerror = NULL;
1706 GMatchInfo *matchinfo = NULL;
1707 gchar *retstring = NULL;
1708 gboolean retval;
1709 DEBUG_ENCODING("encoding_by_regex, started with pattern %s\n", pattern);
1710 reg1 = g_regex_new(pattern, G_REGEX_CASELESS, 0, &gerror);
1711 if (gerror) {
1712 g_warning("error while compiling regex pattern to search for encoding %d: %s\n", gerror->code,
1713 gerror->message);
1714 g_error_free(gerror);
1715 return NULL;
1716 } else {
1717 retval = g_regex_match(reg1, buffer, 0, &matchinfo);
1718 if (retval && g_match_info_get_match_count(matchinfo) >= subpat) {
1719 retstring = g_match_info_fetch(matchinfo, subpat);
1720 DEBUG_ENCODING("encoding_by_regex, got match for encoding %s\n", retstring);
1721 } else {
1722 DEBUG_ENCODING("encoding_by_regex, no match\n");
1723 }
1724 g_match_info_free(matchinfo);
1725 }
1726 g_regex_unref(reg1);
1727 return retstring;
1728 }
1729
1730 /*
1731 * Fallback convertor of corrupted string. Theoreticaly we can convert even binary input.
1732 * General idea taken from g_strescape()- see glib source code for details.
1733 * WIndows-1252 charset is taken since in has lowest non-printable charecter count.
1734 * Non-printable characters are replaced by space 0x20 character.
1735 * Following non-printable characters are not touched:
1736 * \r - carriage return 0x0D
1737 * \n - newline 0x0A
1738 * \f - formfeed 0x0C
1739 * \v - vertical tab 0x0B
1740 * \t - tab 0x09
1741 * These non-printable characters will be replaced by space:
1742 * 0-8, 14-31, 127, 129, 141, 143, 144,157.
1743 * @exceptions: a string of characters not to escape in @source - one can disable replacement of above characters
1744 * @len: lenght of the string to convert
1745 * @count: number of characters replaced
1746 */
1747 gchar *
buffer_convert_fallback(const gchar * source,const gchar * exceptions,gsize len,gsize * count)1748 buffer_convert_fallback(const gchar * source, const gchar * exceptions, gsize len, gsize * count)
1749 {
1750 const guchar *p;
1751 gchar *dest;
1752 gchar *q;
1753 guchar excmap[256];
1754 gsize repl;
1755 gsize i;
1756
1757 g_return_val_if_fail(source != NULL, NULL);
1758
1759 p = (guchar *) source;
1760 q = dest = g_malloc(len + 1); /* we assume lenght of the string will not change */
1761 repl = 0;
1762 i = 0;
1763 memset(excmap, 0, 256);
1764 if (exceptions) {
1765 guchar *e = (guchar *) exceptions;
1766
1767 while (*e) {
1768 excmap[*e] = 1;
1769 e++;
1770 }
1771 }
1772
1773 while (i < len) {
1774 if (excmap[*p]) {
1775 *q++ = *p;
1776 } else {
1777 if ((*p < 9) || ((*p > 13) && (*p < 32)) || (*p == 127) || (*p == 129) || (*p == 141)
1778 || (*p == 143) || (*p == 144) || (*p == 157)) {
1779 DEBUG_MSG("buffer_convert_fallback, replacing symbol=%d\n", *p);
1780 *q++ = ' ';
1781 repl++;
1782 } else {
1783 *q++ = *p;
1784 }
1785 }
1786 p++;
1787 i++;
1788 }
1789 *q = 0;
1790 if (count) {
1791 *count = repl;
1792 }
1793 return dest;
1794 }
1795
1796 /*gboolean
1797 utf8_validate_accept_trailing_nul(gchar *buffer, gsize buflen)
1798 {
1799 gboolean ret;
1800 const gchar *end=NULL;
1801 gint i;
1802 ret = g_utf8_validate(buffer, buflen, &end);
1803 if (ret)
1804 return TRUE;
1805
1806 if (end<=buffer)
1807 return FALSE; */
1808
1809 /* if all characters that are not valid are NUL characters, we accept the conversion */
1810 /*for (i=(end-buffer);i<buflen;i++) {
1811 if (buffer[i]!=0)
1812 return FALSE;
1813 }
1814 return TRUE;
1815 } */
1816
1817 /**
1818 * buffer_find_encoding:
1819 * @buffer: gchar* with \- terminated string
1820 * @encoding: gchar**, if found a newly allocated encoding string will be here
1821 *
1822 * Return value: newly allocated buffer in UTF-8
1823 */
1824 gchar *
buffer_find_encoding(gchar * buffer,gsize buflen,gchar ** encoding,const gchar * sessionencoding)1825 buffer_find_encoding(gchar * buffer, gsize buflen, gchar ** encoding, const gchar * sessionencoding)
1826 {
1827 gchar *newbuf = NULL;
1828 gsize wsize, rsize;
1829 GError *error = NULL;
1830 const gchar *end = NULL;
1831 gchar *tmpencoding = NULL;
1832 GList *tmplist;
1833 gchar endingbyte = '\0';
1834 /* the first try is if the encoding is set in the file
1835 TODO right now only HTML is supported, but xml files should
1836 use a different regex pattern to find the encoding */
1837 if (buflen > main_v->props.encoding_search_Nbytes) {
1838 /* we do a nasty trick to make regexec search only in the first N bytes */
1839 endingbyte = buffer[main_v->props.encoding_search_Nbytes];
1840 buffer[main_v->props.encoding_search_Nbytes] = '\0';
1841 }
1842
1843 /* <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
1844 OR <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1" />
1845 or in html5 <meta charset="iso-8859-1" />
1846 or in python # -*- coding: UTF-8 -*-
1847 */
1848 tmpencoding = encoding_by_regex(buffer, "<meta[ \t\n\r\f]*charset[ \t\n\r\f]*=[ \t\n\r\f]*\"([a-z0-9_-]+)\"", 1);
1849 if (!tmpencoding) {
1850 tmpencoding =
1851 encoding_by_regex(buffer,
1852 "<meta[ \t\n\r\f]http-equiv[ \t\n\r\f]*=[ \t\n\r\f]*\"content-type\"[ \t\n\r\f]+content[ \t\n\r\f]*=[ \t\n\r\f]*\"[^;\"]+;[ \t\n\r\f]*charset=([a-z0-9_-]+)\"[ \t\n\r\f]*/?>",
1853 1);
1854 }
1855 if (!tmpencoding) {
1856 tmpencoding = encoding_by_regex(buffer, "(en)?coding(=|:)(\"| )?([a-z0-9_-]+)(\"| )", 4);
1857 }
1858 if (buflen > main_v->props.encoding_search_Nbytes) {
1859 buffer[main_v->props.encoding_search_Nbytes] = endingbyte;
1860 }
1861
1862 if (tmpencoding) {
1863 DEBUG_ENCODING("buffer_find_encoding, try encoding %s from <meta>\n", tmpencoding);
1864 newbuf = g_convert(buffer, buflen, "UTF-8", tmpencoding, &rsize, &wsize, &error);
1865 if (!newbuf || error) {
1866 DEBUG_ENCODING("buffer_find_encoding, cound not convert %s to UTF-8, %" G_GSIZE_FORMAT
1867 " bytes read until error\n", tmpencoding, rsize);
1868 g_free(tmpencoding);
1869 tmpencoding = NULL;
1870 if (error) {
1871 g_error_free(error);
1872 error = NULL;
1873 }
1874 } else if (g_utf8_validate(newbuf, wsize, NULL)) {
1875 *encoding = tmpencoding;
1876 return newbuf;
1877 }
1878 g_free(newbuf);
1879 }
1880
1881 /* because UTF-8 validation is very critical (very little texts in other encodings actually validate as UTF-8)
1882 we do this early in the detection */
1883 DEBUG_ENCODING("buffer_find_encoding, file NOT is converted yet, trying UTF-8 encoding\n");
1884 if (g_utf8_validate(buffer, buflen, &end) /*utf8_validate_accept_trailing_nul(buffer, buflen) */ ) {
1885 *encoding = g_strdup("UTF-8");
1886 return g_strdup(buffer);
1887 } else {
1888 DEBUG_ENCODING("buffer_find_encoding, failed to validate as UTF-8\n");
1889 end = NULL;
1890 }
1891
1892 if (sessionencoding) {
1893 DEBUG_ENCODING
1894 ("buffer_find_encoding, file does not have <meta> encoding, or could not convert and is not UTF8, trying session default encoding %s\n",
1895 sessionencoding);
1896 newbuf = g_convert(buffer, buflen, "UTF-8", sessionencoding, &rsize, &wsize, &error);
1897 if (error) {
1898 DEBUG_ENCODING("buffer_find_encoding, failed to convert from sessionencoding %s, read %" G_GSIZE_FORMAT
1899 " bytes until error %s\n", sessionencoding, rsize, error->message);
1900 g_error_free(error);
1901 error = NULL;
1902 }
1903 if (newbuf
1904 && g_utf8_validate(newbuf, wsize, NULL) /*utf8_validate_accept_trailing_nul(newbuf, wsize) */ ) {
1905 DEBUG_ENCODING("buffer_find_encoding, file is in default encoding: %s, newbuf=%p, wsize=%"
1906 G_GSIZE_FORMAT ", strlen(newbuf)=%zd\n", sessionencoding, newbuf, wsize,
1907 strlen(newbuf));
1908 *encoding = g_strdup(sessionencoding);
1909 return newbuf;
1910 }
1911 g_free(newbuf);
1912 }
1913 DEBUG_ENCODING
1914 ("buffer_find_encoding, file does not have <meta> encoding, or could not convert, not session encoding, not UTF8, trying newfile default encoding %s\n",
1915 main_v->props.newfile_default_encoding);
1916 newbuf = g_convert(buffer, buflen, "UTF-8", main_v->props.newfile_default_encoding, NULL, &wsize, NULL);
1917 if (newbuf && g_utf8_validate(newbuf, wsize, NULL) /*utf8_validate_accept_trailing_nul(newbuf, wsize) */ ) {
1918 DEBUG_ENCODING("buffer_find_encoding, file is in default encoding: %s\n",
1919 main_v->props.newfile_default_encoding);
1920 *encoding = g_strdup(main_v->props.newfile_default_encoding);
1921 return newbuf;
1922 }
1923 g_free(newbuf);
1924
1925 DEBUG_ENCODING("buffer_find_encoding, file is not in UTF-8, trying encoding from locale\n");
1926 newbuf = g_locale_to_utf8(buffer, buflen, NULL, &wsize, NULL);
1927 if (newbuf && g_utf8_validate(newbuf, wsize, NULL) /*utf8_validate_accept_trailing_nul(newbuf, wsize) */ ) {
1928 const gchar *tmpencoding = NULL;
1929 g_get_charset(&tmpencoding);
1930 DEBUG_ENCODING("buffer_find_encoding, file is in locale encoding: %s\n", tmpencoding);
1931 *encoding = g_strdup(tmpencoding);
1932 return newbuf;
1933 }
1934 g_free(newbuf);
1935
1936 DEBUG_ENCODING("buffer_find_encoding, tried the most obvious encodings, nothing found.. go trough list\n");
1937 tmplist = g_list_first(main_v->globses.encodings);
1938 while (tmplist) {
1939 gchar **enc = tmplist->data;
1940 if (enc[2] && enc[2][0] == '1') {
1941 DEBUG_ENCODING("buffer_find_encoding, trying user set encoding %s\n", enc[1]);
1942 newbuf = g_convert(buffer, buflen, "UTF-8", enc[1], &rsize, &wsize, &error);
1943 if (error) {
1944 DEBUG_ENCODING("buffer_find_encoding, trying %s, error: %s\n", enc[1], error->message);
1945 g_error_free(error);
1946 error = NULL;
1947 }
1948 if (newbuf
1949 && g_utf8_validate(newbuf, wsize,
1950 NULL) /*utf8_validate_accept_trailing_nul(newbuf, wsize) */ ) {
1951 *encoding = g_strdup(enc[1]);
1952 return newbuf;
1953 }
1954 g_free(newbuf);
1955 }
1956 tmplist = g_list_next(tmplist);
1957 }
1958 tmplist = g_list_first(main_v->globses.encodings);
1959 while (tmplist) {
1960 gchar **enc = tmplist->data;
1961 if (enc[2] || enc[2][0] != '1') {
1962 newbuf = g_convert(buffer, buflen, "UTF-8", enc[1], &rsize, &wsize, &error);
1963 if (error) {
1964 DEBUG_ENCODING("buffer_find_encoding, trying %s, error: %s\n", enc[1], error->message);
1965 g_error_free(error);
1966 error = NULL;
1967 }
1968 if (newbuf
1969 && g_utf8_validate(newbuf, wsize,
1970 NULL) /*utf8_validate_accept_trailing_nul(newbuf, wsize) */ ) {
1971 *encoding = g_strdup(enc[1]);
1972 return newbuf;
1973 }
1974 g_free(newbuf);
1975 }
1976 tmplist = g_list_next(tmplist);
1977 }
1978 return NULL;
1979 }
1980
1981 gchar *
buffer_use_fallback_encoding(gchar * buffer,gsize buflen,gsize * replaced)1982 buffer_use_fallback_encoding(gchar * buffer, gsize buflen, gsize * replaced)
1983 {
1984 /* Fallback converter that theoretically should never fail */
1985 gchar *fallback_string, *newbuf;
1986 gsize repl;
1987 gsize wsize, rsize;
1988
1989 fallback_string = buffer_convert_fallback(buffer, NULL, buflen, &repl);
1990 DEBUG_MSG
1991 ("buffer_use_fallback_encoding, executed fallback converter, lenght og new string=%zd , replacements=%lu\n",
1992 strlen(fallback_string), repl);
1993 newbuf = g_convert(fallback_string, buflen, "UTF-8", "WINDOWS-1252", &rsize, &wsize, NULL);
1994 g_free(fallback_string);
1995 if (newbuf && g_utf8_validate(newbuf, wsize, NULL)) {
1996 if (replaced) {
1997 *replaced = repl;
1998 }
1999 return newbuf;
2000 }
2001 g_free(newbuf);
2002 return NULL;
2003 }
2004
2005 #define MAX_TOO_LONG_LINE 10000
2006 #define MIN_TOO_LONG_LINE 9500
2007
2008 static gchar *
check_very_long_line(Tdocument * doc,gchar * buffer)2009 check_very_long_line(Tdocument * doc, gchar * buffer)
2010 {
2011 gsize i = 0, buflen;
2012 guint curline = 0, maxline = 0, numtoolong = 0;
2013
2014 for (i = 0; buffer[i] != '\0'; i++) {
2015 if (buffer[i] == '\n' || buffer[i] == '\r') {
2016 if (curline > maxline)
2017 maxline = curline;
2018 if (curline > MIN_TOO_LONG_LINE)
2019 numtoolong++;
2020 curline = 0;
2021 }
2022 curline++;
2023 }
2024 if (curline > maxline)
2025 maxline = curline;
2026 if (curline > MIN_TOO_LONG_LINE)
2027 numtoolong++;
2028 buflen = i;
2029 DEBUG_MSG("check_very_long_line, maxline=%d, buflen=%ld\n", maxline, (long int) buflen);
2030 if (maxline > MAX_TOO_LONG_LINE) {
2031 gint response;
2032 const gchar *buttons[] = { _("_No"), _("_Split"), NULL };
2033 response = message_dialog_new_multi(BFWIN(doc->bfwin)->main_window,
2034 GTK_MESSAGE_WARNING, buttons,
2035 _("File contains very long lines. Split these lines?"),
2036 _
2037 ("The lines in this file are longer than Bluefish can handle with reasonable performance. This split function, however, is unaware of any language syntax, and may replace spaces or tabs with newlines in any location, or insert newlines if no spaces or tabs are found."));
2038 if (response == 1) {
2039 gint inserted = 0;
2040 buffer = g_realloc(buffer, buflen + numtoolong + (maxline / MIN_TOO_LONG_LINE) + 1);
2041 curline = 0;
2042 for (i = 0; buffer[i] != '\0'; i++) {
2043 if (curline > MIN_TOO_LONG_LINE && (buffer[i] == ' ' || buffer[i] == '\t')) {
2044 DEBUG_MSG("check_very_long_line, replace space or tab at position %lu with newline\n", i);
2045 buffer[i] = '\n';
2046 curline = 0;
2047 } else if (curline > MAX_TOO_LONG_LINE && (buffer[i] == ';' ||
2048 buffer[i] == ',' ||
2049 buffer[i] == '=' ||
2050 buffer[i] == '}' ||
2051 buffer[i] == '>' ||
2052 buffer[i] == ')' ||
2053 buffer[i] == '+' || buffer[i] == '-')) {
2054 DEBUG_MSG
2055 ("check_very_long_line, insert newline at %lu, move buffer %p to %p, %lu bytes\n", i,
2056 buffer + i + 1, buffer + i, buflen - i + inserted);
2057 memmove(buffer + i + 1, buffer + i, buflen - i + inserted);
2058 buffer[i] = '\n';
2059 inserted++;
2060 curline = 0;
2061 } else if (buffer[i] == '\n' || buffer[i] == '\r') {
2062 curline = 0;
2063 }
2064 curline++;
2065 }
2066 }
2067 }
2068 return buffer;
2069 }
2070
2071 /**
2072 * doc_buffer_to_textbox:
2073 * @doc: #Tdocument*
2074 * @buffer: #gchar*
2075 * @buflen: #gsize
2076 * @enable_undo: #gboolean, if the buffer insert should be undo-able
2077 * @delay: #gboolean
2078 *
2079 * inserts buffer at the current cursor position, tries to find the encoding of the document
2080 * using the contents of the buffer (<meta encoding)
2081 * and places the cursor back at this position
2082 *
2083 */
2084 gboolean
doc_buffer_to_textbox(Tdocument * doc,gchar * buffer,gsize buflen,gboolean enable_undo,gboolean delay)2085 doc_buffer_to_textbox(Tdocument * doc, gchar * buffer, gsize buflen, gboolean enable_undo, gboolean delay)
2086 {
2087 gint cursor_offset;
2088 gchar *encoding = NULL, *newbuf;
2089 GtkTextMark *insert;
2090 GtkTextIter iter;
2091
2092 if (!buffer) {
2093 DEBUG_MSG("doc_buffer_to_textbox, buffer==NULL, returning\n");
2094 return FALSE;
2095 }
2096
2097 if (!enable_undo) {
2098 doc_block_undo_reg(doc);
2099 }
2100 /* now get the current cursor position */
2101 insert = gtk_text_buffer_get_insert(doc->buffer);
2102 gtk_text_buffer_get_iter_at_mark(doc->buffer, &iter, insert);
2103 cursor_offset = gtk_text_iter_get_offset(&iter);
2104
2105 /* This opens the contents of a file to a textbox */
2106
2107 newbuf = buffer_find_encoding(buffer, buflen, &encoding, BFWIN(doc->bfwin)->session->encoding);
2108
2109 if (!newbuf) {
2110 gint response;
2111 gchar *utf8name, *tmpstr;
2112 gsize replaced;
2113 const gchar *buttons[] = { _("_Close"), _("_Convert"), NULL };
2114 utf8name = gfile_display_name(doc->uri, NULL);
2115 tmpstr =
2116 g_strdup_printf(_
2117 ("File '%s' is not a valid text file.\nThe file might be corrupted, truncated, or in an unexpected encoding."),
2118 utf8name);
2119 response =
2120 message_dialog_new_multi(BFWIN(doc->bfwin)->main_window, GTK_MESSAGE_WARNING, buttons, tmpstr,
2121 _
2122 ("Bluefish can convert this file into a valid one by replacing illegal symbols with spaces. Convert?"));
2123 g_free(tmpstr);
2124 g_free(utf8name);
2125 if (response == 0) {
2126 return FALSE;
2127 }
2128 replaced = 0;
2129 newbuf = buffer_use_fallback_encoding(buffer, buflen, &replaced);
2130 encoding = g_strdup("WINDOWS-1252");
2131 if (replaced != 0) {
2132 tmpstr =
2133 g_strdup_printf(_
2134 ("File is opened in read-only mode since it differs from original one.\n%lu characters were replaced by space"),
2135 replaced);
2136 message_dialog_new(BFWIN(doc->bfwin)->main_window, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, tmpstr,
2137 _("Use 'Save As' to save it with different name and make editable"));
2138
2139 doc_set_readonly(doc, TRUE);
2140 g_free(tmpstr);
2141 }
2142 }
2143 DEBUG_MSG("doc_buffer_to_textbox, will set encoding to %s\n", encoding);
2144 if (doc->encoding)
2145 g_free(doc->encoding);
2146 doc->encoding = encoding;
2147 add_encoding_to_list(encoding);
2148 if (main_v->props.show_long_line_warning) {
2149 newbuf = check_very_long_line(doc, newbuf);
2150 }
2151
2152 gtk_text_buffer_insert_at_cursor(doc->buffer, newbuf, -1);
2153
2154 g_free(newbuf);
2155 if (!enable_undo) {
2156 doc_unblock_undo_reg(doc);
2157 }
2158
2159 {
2160 /* set the cursor position back */
2161 GtkTextIter iter;
2162 gtk_text_buffer_get_iter_at_offset(doc->buffer, &iter, cursor_offset);
2163 gtk_text_buffer_place_cursor(doc->buffer, &iter);
2164 if (!delay) {
2165 gtk_text_view_place_cursor_onscreen(GTK_TEXT_VIEW(doc->view));
2166 }
2167 }
2168 return TRUE;
2169 }
2170
2171 static void
doc_buffer_insert_text_lcb(GtkTextBuffer * textbuffer,GtkTextIter * iter,gchar * string,gint len,Tdocument * doc)2172 doc_buffer_insert_text_lcb(GtkTextBuffer * textbuffer, GtkTextIter * iter, gchar * string, gint len,
2173 Tdocument * doc)
2174 {
2175 GSList *tmpslist;
2176 gint pos = gtk_text_iter_get_offset(iter);
2177 gint clen = g_utf8_strlen(string, len);
2178 /*DEBUG_MSG("doc_buffer_insert_text_lcb, started, string='%s', len=%d, clen=%d\n", string, len, clen); */
2179 /* 'len' is the number of bytes and not the number of characters.. */
2180 if (!doc->block_undo_reg) {
2181 if (!doc->in_paste_operation && (!doc_unre_test_last_entry(doc, UndoInsert, -1, pos)
2182 || string[0] == ' '
2183 || string[0] == '\n' || string[0] == '\t' || string[0] == '\r')) {
2184 DEBUG_MSG("doc_buffer_insert_text_lcb, create a new undogroup\n");
2185 doc_unre_new_group(doc);
2186 }
2187 doc_unre_add(doc, string, pos, pos + clen, UndoInsert);
2188 DEBUG_MSG("doc_buffer_insert_text_lcb, about to call doc_set_modified(%p,1)\n",doc);
2189 if (!last_undo_is_spacingtoclick(BLUEFISH_TEXT_VIEW(doc->view))) {
2190 doc_set_modified(doc, 1);
2191 }
2192 }
2193 /* see if any other code wants to see document changes */
2194 for (tmpslist = BFWIN(doc->bfwin)->doc_insert_text; tmpslist; tmpslist = g_slist_next(tmpslist)) {
2195 Tcallback *cb = tmpslist->data;
2196 ((DocInsertTextCallback) cb->func) (doc, string, iter, pos, len, clen, cb->data);
2197 }
2198 DEBUG_MSG("doc_buffer_insert_text_lcb, done\n");
2199 }
2200
2201 static void
doc_buffer_delete_range_lcb(GtkTextBuffer * textbuffer,GtkTextIter * itstart,GtkTextIter * itend,Tdocument * doc)2202 doc_buffer_delete_range_lcb(GtkTextBuffer * textbuffer, GtkTextIter * itstart, GtkTextIter * itend,
2203 Tdocument * doc)
2204 {
2205 GSList *tmpslist;
2206 gchar *string;
2207 gint start, end, len;
2208
2209 start = gtk_text_iter_get_offset(itstart);
2210 end = gtk_text_iter_get_offset(itend);
2211 len = end - start;
2212 string = gtk_text_buffer_get_text(doc->buffer, itstart, itend, TRUE);
2213
2214 DEBUG_MSG("doc_buffer_delete_range_lcb, string='%s'\n", string);
2215 if (!doc->block_undo_reg) {
2216 if (string) {
2217 /* undo_redo stuff */
2218 DEBUG_MSG("doc_buffer_delete_range_lcb, start=%d, end=%d, len=%d, string='%s'\n", start, end, len,
2219 string);
2220 if (!doc->in_paste_operation) {
2221 if (len == 1) {
2222 if ((!doc_unre_test_last_entry(doc, UndoDelete, start, -1) /* delete */
2223 &&!doc_unre_test_last_entry(doc, UndoDelete, end, -1)) /* backspace */
2224 ||string[0] == ' ' || string[0] == '\n' || string[0] == '\t' || string[0] == '\r') {
2225 DEBUG_MSG("doc_buffer_delete_range_lcb, need a new undogroup\n");
2226 doc_unre_new_group(doc);
2227 }
2228 } else {
2229 doc_unre_new_group(doc);
2230 }
2231 }
2232 doc_unre_add(doc, string, start, end, UndoDelete);
2233 }
2234 DEBUG_MSG("doc_buffer_delete_range_lcb, about to call doc_set_modified(%p,1)\n",doc);
2235 doc_set_modified(doc, 1);
2236 }
2237 /* see if any other code wants to see document changes */
2238 for (tmpslist = BFWIN(doc->bfwin)->doc_delete_range; tmpslist; tmpslist = g_slist_next(tmpslist)) {
2239 Tcallback *cb = tmpslist->data;
2240 ((DocDeleteRangeCallback) cb->func) (doc, itstart, start, itend, end, string, cb->data);
2241 }
2242 g_free(string);
2243 }
2244
2245 void
doc_get_iter_location(Tdocument * doc,GtkTextIter * iter,GdkRectangle * rectangle)2246 doc_get_iter_location(Tdocument * doc, GtkTextIter * iter, GdkRectangle * rectangle)
2247 {
2248 GdkRectangle rect;
2249 gint itx, ity, px, py;
2250
2251 gtk_text_view_get_iter_location(GTK_TEXT_VIEW(doc->view), iter, &rect);
2252
2253 /* the following function will return the position relative to the text area of the widget
2254 but we also have margins!! */
2255 gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(doc->view)
2256 , GTK_TEXT_WINDOW_TEXT, rect.x, rect.y, &itx, &ity);
2257 /* the following function will return the position of the total text widget */
2258 gdk_window_get_origin(gtk_widget_get_window(doc->view), &px, &py);
2259
2260 DEBUG_MSG("doc_get_iter_location, px=%d, itx=%d,border=%d\n", px, itx,
2261 gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(doc->view), GTK_TEXT_WINDOW_LEFT));
2262 DEBUG_MSG("doc_get_iter_location, py=%d, ity=%d,border=%d\n", py, ity,
2263 gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(doc->view), GTK_TEXT_WINDOW_TOP));
2264 rectangle->x =
2265 px + itx + gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(doc->view), GTK_TEXT_WINDOW_LEFT);
2266 rectangle->y =
2267 py + ity + gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(doc->view), GTK_TEXT_WINDOW_TOP);
2268 rectangle->width = rect.width;
2269 rectangle->height = rect.height;
2270 }
2271
2272 /*
2273 void doc_get_cursor_location(Tdocument *doc, gint *x, gint *y) {
2274 GtkTextIter iter;
2275 gtk_text_buffer_get_iter_at_mark(doc->buffer,&iter,gtk_text_buffer_get_insert(doc->buffer));
2276 doc_get_iter_location(doc, &iter, x, y);
2277 }
2278 */
2279
2280 static void
exit_fullscreen_lcb(GtkWidget * widget,Tbfwin * bfwin)2281 exit_fullscreen_lcb(GtkWidget * widget, Tbfwin * bfwin)
2282 {
2283 bfwin_fullscreen_toggle(bfwin, FALSE);
2284 }
2285
2286 static void
rpopup_add_bookmark_lcb(GtkWidget * widget,Tdocument * doc)2287 rpopup_add_bookmark_lcb(GtkWidget * widget, Tdocument * doc)
2288 {
2289 bmark_add_at_bevent(doc);
2290 }
2291
2292 static void
rpopup_del_bookmark_lcb(GtkWidget * widget,Tdocument * doc)2293 rpopup_del_bookmark_lcb(GtkWidget * widget, Tdocument * doc)
2294 {
2295 bmark_del_at_bevent(doc);
2296 }
2297
2298
2299 static void
doc_view_populate_popup_lcb(GtkTextView * textview,GtkMenu * menu,Tdocument * doc)2300 doc_view_populate_popup_lcb(GtkTextView * textview, GtkMenu * menu, Tdocument * doc)
2301 {
2302 GtkWidget *menuitem;
2303 /* I found no way to connect an item-factory to this menu widget, so we have to do it in the manual way... */
2304
2305 /*#ifdef HAVE_LIBENCHANT
2306 bftextview2_populate_preferences_popup(menu, doc);
2307 #endif*/
2308
2309 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(gtk_menu_item_new()));
2310 /* menuitem = gtk_image_menu_item_new_with_label(_("Replace"));
2311 g_signal_connect(menuitem, "activate", G_CALLBACK(replace_cb), doc->bfwin);
2312 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem),
2313 gtk_image_new_from_stock(GTK_STOCK_FIND_AND_REPLACE, GTK_ICON_SIZE_MENU));
2314 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
2315
2316 menuitem = gtk_image_menu_item_new_with_label(_("Find"));
2317 g_signal_connect(menuitem, "activate", G_CALLBACK(search_cb), doc->bfwin);
2318 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem),
2319 gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU));
2320 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
2321
2322 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(gtk_menu_item_new()));
2323 */
2324 if (bmark_have_bookmark_at_stored_bevent(doc)) {
2325 menuitem = gtk_menu_item_new_with_label(_("Delete bookmark"));
2326 g_signal_connect(menuitem, "activate", G_CALLBACK(rpopup_del_bookmark_lcb), doc);
2327 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
2328 } else {
2329 menuitem = gtk_menu_item_new_with_label(_("Add bookmark"));
2330 g_signal_connect(menuitem, "activate", G_CALLBACK(rpopup_add_bookmark_lcb), doc);
2331 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
2332 }
2333 if (!gtk_widget_get_visible(GTK_WIDGET(BFWIN(doc->bfwin)->toolbarbox))) {
2334 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(gtk_menu_item_new()));
2335 menuitem = gtk_menu_item_new_with_label(_("Exit fullscreen"));
2336 g_signal_connect(menuitem, "activate", G_CALLBACK(exit_fullscreen_lcb), doc->bfwin);
2337 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
2338 }
2339 /*
2340 menuitem = gtk_menu_item_new_with_label(_("Add permanent bookmark"));
2341 g_signal_connect(menuitem, "activate", G_CALLBACK(rpopup_permanent_bookmark_lcb), doc->bfwin);
2342 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
2343
2344 menuitem = gtk_menu_item_new_with_label(_("Add temporary bookmark"));
2345 g_signal_connect(menuitem, "activate", G_CALLBACK(rpopup_temporary_bookmark_lcb), doc->bfwin);
2346 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem)); */
2347
2348 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(gtk_menu_item_new()));
2349
2350 /* here we ask any plugins to add there menu item */
2351 if (main_v->doc_view_populate_popup_cbs) {
2352 GSList *tmplist = main_v->doc_view_populate_popup_cbs;
2353 while (tmplist) {
2354 void *(*func) () = tmplist->data;
2355 func(textview, menu, doc);
2356 tmplist = g_slist_next(tmplist);
2357 }
2358 }
2359 #ifdef HAVE_LIBENCHANT
2360 bftextview2_populate_suggestions_popup(menu, doc);
2361 #endif /*HAVE_LIBENCHANT */
2362
2363 gtk_widget_show_all(GTK_WIDGET(menu));
2364 }
2365
2366 static void
doc_buffer_mark_set_lcb(GtkTextBuffer * buffer,GtkTextIter * iter,GtkTextMark * set_mark,Tdocument * doc)2367 doc_buffer_mark_set_lcb(GtkTextBuffer * buffer, GtkTextIter * iter, GtkTextMark * set_mark, Tdocument * doc)
2368 {
2369 /*DEBUG_MSG("doc_buffer_mark_set_lcb, set_mark=%p, insert_mark=%p\n",set_mark,gtk_text_buffer_get_insert(buffer)); */
2370 if (set_mark == gtk_text_buffer_get_insert(buffer)) {
2371 doc_set_statusbar_lncol(doc);
2372 }
2373 }
2374
2375 static void
doc_buffer_changed_lcb(GtkTextBuffer * textbuffer,Tdocument * doc)2376 doc_buffer_changed_lcb(GtkTextBuffer * textbuffer, Tdocument * doc)
2377 {
2378 DEBUG_MSG("doc_buffer_changed_lcb()\n");
2379 doc_set_statusbar_lncol(doc);
2380 }
2381
2382 static void
doc_view_toggle_overwrite_lcb(GtkTextView * view,Tdocument * doc)2383 doc_view_toggle_overwrite_lcb(GtkTextView * view, Tdocument * doc)
2384 {
2385 /* if there is a slave bview, toggle that too! */
2386 if (doc->slave) {
2387 if (view == (GtkTextView *) doc->view)
2388 gtk_text_view_set_overwrite(GTK_TEXT_VIEW(doc->slave),
2389 gtk_text_view_get_overwrite(GTK_TEXT_VIEW(view)));
2390 else
2391 gtk_text_view_set_overwrite(GTK_TEXT_VIEW(doc->view),
2392 gtk_text_view_get_overwrite(GTK_TEXT_VIEW(view)));
2393 }
2394
2395 doc_set_statusbar_insovr(doc);
2396 }
2397
2398 /**
2399 * update_encoding_meta_in_file:
2400 * @doc: a #Tdocument*
2401 * @encoding: #gchar*, The documents character encoding
2402 *
2403 * Update the HTML meta encoding tags for the supplied document.
2404 *
2405 * Return value: void
2406 **/
2407 void
update_encoding_meta_in_file(Tdocument * doc,gchar * encoding)2408 update_encoding_meta_in_file(Tdocument * doc, gchar * encoding)
2409 {
2410 if (!encoding)
2411 return;
2412 GRegex *regex1=NULL, *regex2=NULL, *foundregex=NULL;
2413 GMatchInfo *match_info;
2414 gchar *type, *xhtmlend, *fulltext, *replacestring = NULL;
2415 gint so, eo, cso, ceo;
2416 const gchar *langname, *userval;
2417 gboolean is_xhtml = 0;
2418 /* first find if there is an encoding in the document already, if so, make sure it points to the right encoding
2419 if not, detect if we should do it html4, xhtml or html5 style, and insert the correct encoding
2420 */
2421
2422 langname = bluefish_text_view_get_lang_name(BLUEFISH_TEXT_VIEW(doc->view));
2423 userval = lookup_user_option(langname, "is_XHTML");
2424 if (userval && userval[0] != '\0') {
2425 is_xhtml = (userval[0] == '1');
2426 }
2427
2428 fulltext = doc_get_chars(doc, 0, -1);
2429 /*
2430 encoding_by_regex(buffer, "<meta[ \t\n\r\f]*charset[ \t\n\r\f]*=[ \t\n\r\f]*\"([a-z0-9_-]+)\"", 1);
2431
2432 */
2433 regex1 =
2434 g_regex_new
2435 ("<meta[ \t\n\r\f]http-equiv[ \t\n\r\f]*=[ \t\n\r\f]*\"content-type\"[ \t\n\r\f]+content[ \t\n\r\f]*=[ \t\n\r\f]*\"([^;]*);[ \t\n\r\f]*charset=[a-z0-9-]*\"[ \t\n\r\f]*(/?)>",
2436 G_REGEX_MULTILINE | G_REGEX_CASELESS, 0, NULL);
2437 if (g_regex_match(regex1, fulltext, 0, &match_info)) {
2438 foundregex = regex1;
2439 } else {
2440 /* no html4 or xhtml <meta encoding tag, try html5, re-use match_info variable */
2441 g_match_info_free(match_info);
2442 regex2 = g_regex_new
2443 ("<meta[ \t\n\r\f]*charset[ \t\n\r\f]*=[ \t\n\r\f]*\"([a-z0-9_-]+)\"[ \t\n\r\f]*/?>" ,
2444 G_REGEX_MULTILINE | G_REGEX_CASELESS, 0, NULL);
2445 if (g_regex_match(regex2, fulltext, 0, &match_info)) {
2446 foundregex = regex2;
2447 }
2448 }
2449 if (foundregex) {
2450 DEBUG_MSG("we have a match, see if the encoding needs change, and if so replace the encoding\n");
2451 if (g_match_info_get_match_count(match_info) > 2) {
2452 type = g_match_info_fetch(match_info, 1);
2453 xhtmlend = g_match_info_fetch(match_info, 2);
2454 } else {
2455 type = g_strdup("text/html");
2456 xhtmlend = g_strdup(is_xhtml ? "/" : "");
2457 }
2458 if (foundregex == regex1) {
2459 replacestring =
2460 g_strconcat("<meta http-equiv=\"content-type\" content=\"", type, "; charset=", encoding,
2461 "\" ", xhtmlend, ">", NULL);
2462 } else {
2463 replacestring =
2464 g_strconcat("<meta charset=\"", encoding,"\">", NULL);
2465 }
2466 g_free(type);
2467 g_free(xhtmlend);
2468
2469 } else {
2470 GRegex *regex3;
2471 DEBUG_MSG("no match, add the encoding\n");
2472 /* no <meta encoding tag yet, re-use match_info variable */
2473 g_match_info_free(match_info);
2474 /* check if we need html5 or html4 */
2475 regex3 = g_regex_new("<head>", G_REGEX_CASELESS, 0, NULL);
2476 g_regex_match(regex3, fulltext, 0, &match_info);
2477 if (g_match_info_matches(match_info)) {
2478 if (g_regex_match_simple ("<!DOCTYPE[ \t\n\r\f]*html>",fulltext,G_REGEX_CASELESS,0)) {
2479 replacestring =
2480 g_strconcat("<head>\n<meta charset=\"", encoding, "\">", NULL);
2481 } else {
2482 replacestring =
2483 g_strconcat("<head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=",
2484 encoding, "\" ", (is_xhtml ? "/>" : ">"), NULL);
2485 }
2486 }
2487 g_regex_unref(regex3);
2488 }
2489
2490 if (replacestring) {
2491 g_match_info_fetch_pos(match_info, 0, &so, &eo);
2492 cso = utf8_byteoffset_to_charsoffset(fulltext, so);
2493 ceo = utf8_byteoffset_to_charsoffset(fulltext, eo);
2494 DEBUG_MSG("update_encoding_meta_in_file, update from %d to %d\n", cso, ceo);
2495 doc_replace_text(doc, replacestring, cso, ceo);
2496 g_free(replacestring);
2497 g_match_info_free(match_info);
2498 }
2499 g_regex_unref(regex1);
2500 if (regex2) {
2501 g_regex_unref(regex2);
2502 }
2503 g_free(fulltext);
2504 }
2505
2506 /*
2507 returns a buffer in the encoding stored in doc->encoding, or NULL if that fails
2508 and the user aborted conversion to UTF-8
2509 */
2510 gchar *
doc_get_buffer_in_encoding(Tdocument * doc,gsize * newbuflen)2511 doc_get_buffer_in_encoding(Tdocument * doc, gsize * newbuflen)
2512 {
2513 GtkTextIter itstart, itend;
2514 gchar *buffer;
2515
2516 gtk_text_buffer_get_bounds(doc->buffer, &itstart, &itend);
2517 buffer = gtk_text_buffer_get_text(doc->buffer, &itstart, &itend, TRUE);
2518
2519 if (doc->encoding) {
2520 gchar *newbuf;
2521 GError *gerror = NULL;
2522 gsize bytes_written = 0, bytes_read = 0;
2523 DEBUG_MSG("doc_get_buffer_in_encoding, converting from UTF-8 to %s\n", doc->encoding);
2524 newbuf = g_convert(buffer, -1, doc->encoding, "UTF-8", &bytes_read, &bytes_written, &gerror);
2525 if (gerror) {
2526 g_print("doc_get_buffer_in_encoding in encoding %s, got error %d: %s, newbuf=%p\n", doc->encoding,
2527 gerror->code, gerror->message, newbuf);
2528 g_error_free(gerror);
2529 }
2530 if (newbuf) {
2531 g_free(buffer);
2532 *newbuflen = bytes_written;
2533 buffer = newbuf;
2534 } else {
2535 const gchar *buttons[] = { _("_Abort save"), _("_Continue save in UTF-8"), NULL };
2536 gint retval, line, column;
2537 glong position;
2538 gchar *tmpstr, failed[6];
2539 GtkTextIter iter;
2540 position = g_utf8_pointer_to_offset(buffer, buffer + bytes_read);
2541 gtk_text_buffer_get_iter_at_offset(doc->buffer, &iter, position);
2542 line = gtk_text_iter_get_line(&iter);
2543 column = gtk_text_iter_get_line_offset(&iter);
2544 failed[0] = '\0';
2545 g_utf8_strncpy(failed, buffer + bytes_read, 1);
2546 tmpstr =
2547 g_strdup_printf(_
2548 ("Failed to convert %s to character encoding %s. Encoding failed on character '%s' at line %d column %d\n\nContinue saving in UTF-8 encoding?"),
2549 gtk_label_get_text(GTK_LABEL(doc->tab_menu)), doc->encoding, failed, line + 1,
2550 column + 1);
2551 retval =
2552 message_dialog_new_multi(BFWIN(doc->bfwin)->main_window, GTK_MESSAGE_WARNING, buttons,
2553 _("File encoding conversion failure"), tmpstr);
2554 g_free(tmpstr);
2555 if (retval == 0) {
2556 DEBUG_MSG("doc_get_buffer_in_encoding, character set conversion failed, user aborted!\n");
2557 *newbuflen = 0;
2558 return NULL;
2559 } else {
2560 /* continue in UTF-8 */
2561 update_encoding_meta_in_file(doc, "UTF-8");
2562 g_free(buffer);
2563 gtk_text_buffer_get_bounds(doc->buffer, &itstart, &itend);
2564 buffer = gtk_text_buffer_get_text(doc->buffer, &itstart, &itend, TRUE);
2565 *newbuflen = strlen(buffer);
2566 DEBUG_MSG("doc_get_buffer_in_encoding, using UTF8 encoding, have %d bytes\n", *newbuflen);
2567 }
2568 }
2569 } else {
2570 *newbuflen = strlen(buffer);
2571 }
2572 DEBUG_MSG("doc_get_buffer_in_encoding, returning %p with %d bytes\n", buffer, *newbuflen);
2573 return buffer;
2574 }
2575
2576 void
doc_set_readonly(Tdocument * doc,gboolean readonly)2577 doc_set_readonly(Tdocument * doc, gboolean readonly)
2578 {
2579 gchar *color = NULL;
2580 doc->readonly = readonly;
2581 if (doc->view) {
2582 g_object_set(G_OBJECT(doc->view), "editable", !readonly, NULL);
2583 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(doc->view), !readonly);
2584 if (readonly) {
2585 color = main_v->props.tab_color_loading;
2586 }
2587 doc_set_label_color(doc, color);
2588 }
2589
2590 }
2591
2592 static void
delete_backupfile_lcb(gpointer data)2593 delete_backupfile_lcb(gpointer data)
2594 {
2595 GFile *uri = (GFile *) data;
2596 g_object_unref(uri);
2597 }
2598
2599 /**
2600 * doc_destroy:
2601 * @doc: a #Tdocument
2602 * @delay_activation: #gboolean whether to delay gui-updates.
2603 *
2604 * Performs all actions neccessary to remove an open document from the fish:
2605 * Adds filename to recent-list,
2606 * removes the document from the documentlist and notebook,
2607 * change notebook-focus (if !delay_activation),
2608 * delete backupfile if required by pref,
2609 * free all related memory.
2610 *
2611 * Return value: void
2612 **/
2613
2614 void
doc_destroy(Tdocument * doc,gboolean delay_activation)2615 doc_destroy(Tdocument * doc, gboolean delay_activation)
2616 {
2617 Tbfwin *bfwin = BFWIN(doc->bfwin);
2618 Tdocument *switch_to_doc = NULL;
2619 GSList *tmpslist;
2620 GList *tmplist;
2621 DEBUG_MSG("doc_destroy(%p,%d), doc->status=%d\n", doc, delay_activation, doc->status);
2622
2623 tmplist = g_list_next(doc->recentpos);
2624 if (tmplist)
2625 switch_to_doc = tmplist->data;
2626 if (doc->status == DOC_STATUS_LOADING) {
2627 bfwin_docs_not_complete(doc->bfwin, FALSE);
2628 DEBUG_MSG("doc_destroy, called bfwin_docs_not_complete(), bfwin->num_docs_not_completed=%d\n",
2629 BFWIN(doc->bfwin)->num_docs_not_completed);
2630 } else if (doc->status == DOC_STATUS_ERROR) {
2631 if (doc->uri && bfwin->session)
2632 remove_filename_from_recentlist(bfwin, FALSE, doc->uri); /* Remove inaccesible files from OpenRecent list */
2633 }
2634
2635 if (bfwin->last_activated_doc == doc) {
2636 bfwin->last_activated_doc = NULL;
2637 }
2638
2639 for (tmpslist = bfwin->doc_destroy; tmpslist; tmpslist = g_slist_next(tmpslist)) {
2640 Tcallback *cb = tmpslist->data;
2641 ((DocDestroyCallback) cb->func) (doc, cb->data);
2642 }
2643
2644 DEBUG_MSG("doc_destroy, calling bmark_clean_for_doc(%p)\n", doc);
2645 bmark_clean_for_doc(doc);
2646 if (doc->uri && bfwin->session && doc->status != DOC_STATUS_ERROR && main_v->props.recent_means_recently_closed) { /* in a special situation the bfwin does not have a session: if a project window is closing; documents with errors should not be added to the menu */
2647 gchar *curi = g_file_get_uri(doc->uri);
2648 bfwin_recent_menu_add(doc->bfwin, FALSE, curi, doc->uri);
2649 g_free(curi);
2650 }
2651 bfwin_notebook_block_signals(BFWIN(doc->bfwin));
2652 if (doc->newdoc_autodetect_lang_id) {
2653 g_source_remove(doc->newdoc_autodetect_lang_id);
2654 doc->newdoc_autodetect_lang_id = 0;
2655 }
2656
2657 if (doc->floatingview) {
2658 gtk_widget_destroy(FLOATINGVIEW(doc->floatingview)->window);
2659 doc->floatingview = NULL;
2660 }
2661 if (doc->slave) {
2662 gtk_widget_destroy(doc->slave);
2663 doc->slave = NULL;
2664 }
2665
2666 /* now we remove the document from the document list */
2667 bfwin->documentlist = g_list_remove(bfwin->documentlist, doc);
2668 bfwin->recentdoclist = g_list_delete_link(bfwin->recentdoclist, doc->recentpos);
2669 doc->recentpos = NULL;
2670 DEBUG_MSG("removed %p from documentlist, list %p length=%d\n", doc, bfwin->documentlist,
2671 g_list_length(bfwin->documentlist));
2672 /* then we remove the page from the notebook */
2673 DEBUG_MSG("about to remove widget from notebook (doc=%p, current_document=%p)\n", doc,
2674 bfwin->current_document);
2675 gtk_notebook_remove_page(GTK_NOTEBOOK(bfwin->notebook),
2676 gtk_notebook_page_num(GTK_NOTEBOOK(bfwin->notebook), doc->vsplit));
2677 DEBUG_MSG("doc_destroy, removed widget from notebook (doc=%p), delay_activation=%d\n", doc,
2678 delay_activation);
2679 DEBUG_MSG("doc_destroy, (doc=%p) about to bind notebook signals...\n", doc);
2680 bfwin_notebook_unblock_signals(BFWIN(doc->bfwin));
2681 bftextview2_identifier_hash_remove_doc(doc->bfwin, doc);
2682 if (bfwin->current_document == doc) {
2683 bfwin_gotoline_search_bar_close(doc->bfwin, TRUE); /* Do not close search bar, just clean goto line entry */
2684 bfwin->current_document = NULL;
2685 /* We have to switch to another tab only if we destroying current_document */
2686 if (!delay_activation) {
2687 gint newpage = -1;
2688 if (switch_to_doc)
2689 bfwin_switch_to_document_by_pointer(bfwin, switch_to_doc);
2690 bfwin_notebook_changed(BFWIN(doc->bfwin), newpage);
2691 }
2692 }
2693 DEBUG_MSG("doc_destroy, (doc=%p) after calling notebook_changed(), vsplit=%p\n", doc, doc->vsplit);
2694 remove_autosave(doc);
2695 if (doc->uri) {
2696 if (main_v->props.backup_cleanuponclose) {
2697 gchar *tmp, *tmp2;
2698 GFile *backupuri;
2699 tmp = g_file_get_uri(doc->uri);
2700 tmp2 = g_strconcat(tmp, "~", NULL);
2701 backupuri = g_file_new_for_uri(tmp2);
2702 g_free(tmp);
2703 g_free(tmp2);
2704 file_delete_async(backupuri, FALSE, delete_backupfile_lcb, backupuri);
2705 g_object_unref(backupuri);
2706 }
2707 DEBUG_MSG("doc_destroy, unref doc->uri %p\n", doc->uri);
2708 doc_set_uri(doc, NULL, TRUE);
2709 }
2710
2711 if (doc->encoding)
2712 g_free(doc->encoding);
2713
2714 if (doc->fileinfo) {
2715 DEBUG_MSG("doc_destroy, unref doc->fileinfo %p\n", doc->fileinfo);
2716 g_object_unref(doc->fileinfo);
2717 }
2718
2719 g_object_unref(doc->buffer);
2720 doc_unre_destroy(doc);
2721 DEBUG_MSG("doc_destroy, finished for %p\n", doc);
2722 g_free(doc);
2723 }
2724
2725 /**
2726 * document_unset_filename:
2727 * @document: #Tdocument*
2728 *
2729 * this function is called if some other document is saved with a filename
2730 * equal to this files filename, or when this file is deleted in the filebrowser
2731 *
2732 * return value: void, ignored
2733 */
2734 void
document_unset_filename(Tdocument * doc)2735 document_unset_filename(Tdocument * doc)
2736 {
2737 if (doc->uri) {
2738 gchar *tmpstr;
2739 tmpstr = g_strconcat(_("Previously: "), gtk_label_get_text(GTK_LABEL(doc->tab_label)), NULL);
2740 /* doc_set_uri calls bmark_renamed which uses the tab_label for the name, so first set the tab label */
2741 doc_set_title(doc, tmpstr);
2742 doc_set_uri(doc, NULL, FALSE);
2743
2744 if (doc->fileinfo) {
2745 g_object_unref(doc->fileinfo);
2746 doc->fileinfo = NULL;
2747 }
2748
2749 doc_set_modified(doc, TRUE);
2750 g_free(tmpstr);
2751 }
2752 }
2753
2754 /**
2755 * doc_close:
2756 * @doc: The #Tdocument to clase.
2757 * @warn_only: a #gint set to 1 if the document shouldn't actually be destroyed.
2758 *
2759 * Get confirmation when closing an unsaved file, save it if neccessary,
2760 * and destroy the file unless aborted by user.
2761 *
2762 * Return value: #gint set to 0 (when cancelled/aborted) or 1 (when closed or saved&closed)
2763 ** /
2764 gint doc_close(Tdocument * doc, gint warn_only)
2765 {
2766 gchar *text;
2767 gint retval;
2768 #ifdef DEBUG
2769 if (!doc) {
2770 DEBUG_MSG("doc_close, returning because doc=NULL\n");
2771 return 0;
2772 }
2773 #endif
2774
2775 if (doc_is_empty_non_modified_and_nameless(doc) && g_list_length(BFWIN(doc->bfwin)->documentlist) ==1) {
2776 / * no need to close this doc, it's an Untitled empty document * /
2777 DEBUG_MSG("doc_close, 1 untitled empty non-modified document, returning\n");
2778 return 0;
2779 }
2780
2781 if (doc->modified) {
2782 / *if (doc->tab_label) {* /
2783 text = g_strdup_printf(_("Save changes to \"%s\" before closing?."),
2784 gtk_label_get_text (GTK_LABEL (doc->tab_label)));
2785 / *} else {
2786 text = g_strdup(_("Save changes to this untitled file before closing?"));
2787 }* /
2788
2789 {
2790 gchar *buttons[] = {_("Do_n't save"), GTK_STOCK_CANCEL, GTK_STOCK_SAVE, NULL};
2791 retval = multi_query_dialog(BFWIN(doc->bfwin)->main_window, text,
2792 _("If you don't save your changes they will be lost."), 2, 1, buttons);
2793 }
2794 g_free(text);
2795
2796 switch (retval) {
2797 case 1:
2798 DEBUG_MSG("doc_close, retval=2 (cancel) , returning\n");
2799 return 2;
2800 break;
2801 case 2:
2802 doc_save(doc, FALSE, FALSE, FALSE);
2803 if (doc->modified == 1) {
2804 / * something went wrong it's still not saved * /
2805 return 0;
2806 }
2807 if (!warn_only) {
2808 doc_destroy(doc, FALSE);
2809 }
2810 break;
2811 case 0:
2812 if (!warn_only) {
2813 doc_destroy(doc, FALSE);
2814 }
2815 break;
2816 default:
2817 return 0; / * something went wrong * /
2818 break;
2819 }
2820 } else {
2821 if (!warn_only) {
2822 DEBUG_MSG("doc_close, starting doc_destroy for doc=%p\n", doc);
2823 doc_destroy(doc, FALSE);
2824 }
2825 }
2826 DEBUG_MSG("doc_close, finished\n");
2827 / * notebook_changed();* /
2828 return 1;
2829 }*/
2830
2831 static void
doc_close_but_clicked_lcb(GtkWidget * wid,gpointer data)2832 doc_close_but_clicked_lcb(GtkWidget * wid, gpointer data)
2833 {
2834 /*doc_close(data, 0); */
2835 doc_close_single_backend(data, FALSE, FALSE);
2836 }
2837
2838 static gboolean
doc_scroll_event_lcb(GtkWidget * widget,GdkEventScroll * event,gpointer user_data)2839 doc_scroll_event_lcb(GtkWidget * widget, GdkEventScroll * event, gpointer user_data)
2840 {
2841 gint font_zoom = 0;
2842 if (event->state & GDK_CONTROL_MASK) {
2843 if (event->direction == GDK_SCROLL_UP) {
2844 font_zoom = 1;
2845 } else if (event->direction == GDK_SCROLL_DOWN) {
2846 font_zoom = -1;
2847 }
2848 #if GTK_CHECK_VERSION(3,4,0)
2849 else if (event->direction == GDK_SCROLL_SMOOTH) {
2850 /* Fixes bug #1020925 for mice that outputs
2851 smooth scroll events for standard scrolling..
2852 delta_y values for scroll wheels:
2853 -1 = up
2854 1 = down */
2855 if (event->delta_y < 0) {
2856 font_zoom = 1;
2857 } else if (event->delta_y > 0) {
2858 font_zoom = -1;
2859 }
2860 }
2861 #endif /* GTK_CHECK_VERSION(3,4,0) */
2862 }
2863 if (font_zoom) {
2864 doc_font_size(DOCUMENT(user_data), font_zoom);
2865 }
2866 return (font_zoom != 0);
2867 }
2868
2869 static gboolean
doc_view_button_press_lcb(GtkWidget * widget,GdkEventButton * bevent,Tdocument * doc)2870 doc_view_button_press_lcb(GtkWidget * widget, GdkEventButton * bevent, Tdocument * doc)
2871 {
2872 if (bevent->button == 2) {
2873 doc->in_paste_operation = TRUE;
2874 doc_unre_new_group(doc);
2875 }
2876 return FALSE;
2877 }
2878
2879 static gboolean
doc_view_button_release_lcb(GtkWidget * widget,GdkEventButton * bevent,Tdocument * doc)2880 doc_view_button_release_lcb(GtkWidget * widget, GdkEventButton * bevent, Tdocument * doc)
2881 {
2882 if (bevent->button == 2) {
2883 doc->in_paste_operation = FALSE;
2884 doc_unre_new_group(doc);
2885 }
2886 return FALSE;
2887 }
2888
2889 static gboolean
doc_focus_in_lcb(GtkWidget * widget,GdkEvent * event,Tdocument * doc)2890 doc_focus_in_lcb(GtkWidget * widget, GdkEvent * event, Tdocument * doc)
2891 {
2892 bfwin_set_cutcopypaste_actions(doc->bfwin, TRUE);
2893 return FALSE;
2894 }
2895
2896 static gboolean
doc_focus_out_lcb(GtkWidget * widget,GdkEvent * event,Tdocument * doc)2897 doc_focus_out_lcb(GtkWidget * widget, GdkEvent * event, Tdocument * doc)
2898 {
2899 if (gtk_window_get_focus(GTK_WINDOW(BFWIN(doc->bfwin)->main_window)) != widget) {
2900 bfwin_set_cutcopypaste_actions(doc->bfwin, FALSE);
2901 }
2902 return FALSE;
2903 }
2904
2905 #if GTK_CHECK_VERSION(3,0,0)
2906 static void
doc_view_style_updated_lcb(GtkWidget * widget,gpointer user_data)2907 doc_view_style_updated_lcb(GtkWidget * widget, gpointer user_data)
2908 {
2909 Tdocument *doc = user_data;
2910 DEBUG_MSG("doc_view_style_updated_lcb\n");
2911 doc_set_tabsize(doc, BFWIN(doc->bfwin)->session->editor_tab_width);
2912 }
2913 #endif
2914
2915 /* called from g_list_foreach() in bfwin.c on a window configure event */
2916 void
doc_recalculate_right_margin(Tdocument * doc)2917 doc_recalculate_right_margin(Tdocument * doc)
2918 {
2919 GdkWindow *gwin;
2920 gint width = -1;
2921
2922 if (main_v->props.wrap_on_right_margin) {
2923 gwin = gtk_text_view_get_window(GTK_TEXT_VIEW(doc->view), GTK_TEXT_WINDOW_TEXT);
2924 if (gwin) {
2925 #if GTK_CHECK_VERSION(2,24,0)
2926 width = gdk_window_get_width(gwin);
2927 #else
2928 gint height;
2929 gdk_drawable_get_size((GdkDrawable *) gwin, &width, &height);
2930 #endif
2931 }
2932 }
2933
2934 if (width >= 0 && gtk_text_view_get_wrap_mode(GTK_TEXT_VIEW(doc->view)) != GTK_WRAP_NONE) {
2935 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(doc->view),
2936 width -
2937 BLUEFISH_TEXT_VIEW(doc->view)->margin_pixels_per_char *
2938 main_v->props.right_margin_pos);
2939 if (doc->slave)
2940 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(doc->slave),
2941 width -
2942 BLUEFISH_TEXT_VIEW(doc->view)->margin_pixels_per_char *
2943 main_v->props.right_margin_pos);
2944 } else {
2945 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(doc->view), main_v->props.adv_textview_right_margin);
2946 if (doc->slave)
2947 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(doc->slave),
2948 main_v->props.adv_textview_right_margin);
2949 }
2950 }
2951
2952 void
bfwin_alldoc_recalc_right_margin(Tbfwin * bfwin)2953 bfwin_alldoc_recalc_right_margin(Tbfwin * bfwin)
2954 {
2955 GList *tmplist = g_list_first(bfwin->documentlist);
2956 while (tmplist) {
2957 doc_recalculate_right_margin(tmplist->data);
2958 tmplist = g_list_next(tmplist);
2959 }
2960 }
2961
2962 Tdocument *
doc_new_backend(Tbfwin * bfwin,gboolean force_new,gboolean readonly,gboolean init_fileinfo)2963 doc_new_backend(Tbfwin * bfwin, gboolean force_new, gboolean readonly, gboolean init_fileinfo)
2964 {
2965 GtkWidget *scroll;
2966 Tdocument *newdoc;
2967 GtkWidget *hbox, *button;
2968 GList *tmplist;
2969
2970 #ifdef MAC_INTEGRATION
2971 if (main_v->osx_status == 2 && g_list_length(main_v->bfwinlist) == 1) {
2972 gtk_widget_show(bfwin->main_window);
2973 bfwin_action_groups_set_sensitive(bfwin, TRUE);
2974 force_new = FALSE;
2975 main_v->osx_status = 0;
2976 }
2977 #endif
2978 /* test if the current document is empty and nameless, if so we return that */
2979 if (!force_new && bfwin->current_document && g_list_length(bfwin->documentlist) == 1
2980 && doc_is_empty_non_modified_and_nameless(bfwin->current_document)) {
2981 newdoc = bfwin->current_document;
2982 DEBUG_MSG("doc_new_backend, returning existing doc %p\n", newdoc);
2983 return newdoc;
2984 }
2985
2986 newdoc = g_new0(Tdocument, 1);
2987 DEBUG_MSG("doc_new_backend, main_v is at %p, bfwin at %p, newdoc at %p\n", main_v, bfwin, newdoc);
2988 newdoc->readonly = readonly;
2989 newdoc->bfwin = (gpointer) bfwin;
2990 newdoc->status = DOC_STATUS_COMPLETE; /* if we don't set this default we will get problems for new empty files */
2991 newdoc->buffer = gtk_text_buffer_new(langmgr_get_tagtable());
2992 newdoc->view = bftextview2_new_with_buffer(newdoc->buffer);
2993 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(newdoc->view), main_v->props.adv_textview_left_margin);
2994 doc_recalculate_right_margin(newdoc);
2995
2996 bluefish_text_view_multiset(BLUEFISH_TEXT_VIEW(newdoc->view), newdoc,
2997 BFWIN(bfwin)->session->view_line_numbers, BFWIN(bfwin)->session->view_blocks,
2998 BFWIN(bfwin)->session->autoindent, BFWIN(bfwin)->session->autocomplete,
2999 BFWIN(bfwin)->session->show_mbhl, FALSE);
3000 bluefish_text_view_set_show_right_margin(BLUEFISH_TEXT_VIEW(newdoc->view),
3001 bfwin->session->display_right_margin);
3002 bluefish_text_view_set_show_visible_spacing(BLUEFISH_TEXT_VIEW(newdoc->view),
3003 bfwin->session->show_visible_spacing);
3004 #ifdef HAVE_LIBENCHANT
3005 BLUEFISH_TEXT_VIEW(newdoc->view)->spell_check = BFWIN(bfwin)->session->spell_check_default;
3006 #endif
3007 g_object_set(G_OBJECT(newdoc->view), "editable", !readonly, NULL);
3008 bluefish_text_view_select_language(BLUEFISH_TEXT_VIEW(newdoc->view), bfwin->session->default_mime_type,
3009 NULL);
3010 gtk_widget_show(newdoc->view);
3011 if (init_fileinfo) {
3012 newdoc->fileinfo = g_file_info_new();
3013 g_file_info_set_content_type(newdoc->fileinfo, bfwin->session->default_mime_type);
3014 }
3015 scroll = gtk_scrolled_window_new(NULL, NULL);
3016 g_signal_connect(scroll, "scroll-event", G_CALLBACK(doc_scroll_event_lcb), newdoc);
3017 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3018 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN);
3019 gtk_container_add(GTK_CONTAINER(scroll), newdoc->view);
3020 newdoc->highlightstate = bfwin->session->enable_syntax_scan;
3021 newdoc->tab_label = gtk_label_new(NULL);
3022 gtk_widget_set_can_focus(newdoc->tab_label, FALSE);
3023 if (!main_v->props.use_system_tab_font)
3024 apply_font_style(newdoc->tab_label, main_v->props.tab_font_string);
3025 newdoc->tab_menu = gtk_label_new(NULL);
3026 newdoc->tab_eventbox = gtk_event_box_new();
3027 #if GTK_CHECK_VERSION(3,0,0) /* Restores tab scrolling feature for gtk+3 builds */
3028 gtk_widget_add_events(newdoc->tab_eventbox, GDK_SCROLL_MASK);
3029 #endif
3030 gtk_event_box_set_visible_window(GTK_EVENT_BOX(newdoc->tab_eventbox), FALSE);
3031 gtk_misc_set_alignment(GTK_MISC(newdoc->tab_menu), 0, 0);
3032
3033 doc_unre_init(newdoc);
3034 apply_font_style(newdoc->view, main_v->props.editor_font_string);
3035 doc_set_wrap(newdoc, bfwin->session->wrap_text_default);
3036
3037 /* we initialize already with 0 , so we don't need these:
3038 newdoc->uri = NULL;
3039 newdoc->modified = 0;
3040 newdoc->fileinfo = NULL; */
3041 newdoc->encoding = g_strdup(main_v->props.newfile_default_encoding);
3042 DEBUG_MSG("doc_new_backend, encoding is %s\n", newdoc->encoding);
3043
3044 doc_set_title(newdoc, NULL);
3045
3046 g_signal_connect(G_OBJECT(newdoc->buffer), "insert-text", G_CALLBACK(doc_buffer_insert_text_lcb), newdoc);
3047 g_signal_connect(G_OBJECT(newdoc->buffer), "delete-range", G_CALLBACK(doc_buffer_delete_range_lcb),
3048 newdoc);
3049 g_signal_connect(G_OBJECT(newdoc->buffer), "changed", G_CALLBACK(doc_buffer_changed_lcb), newdoc);
3050 g_signal_connect(G_OBJECT(newdoc->buffer), "mark-set", G_CALLBACK(doc_buffer_mark_set_lcb), newdoc);
3051 g_signal_connect_after(G_OBJECT(newdoc->view), "toggle-overwrite",
3052 G_CALLBACK(doc_view_toggle_overwrite_lcb), newdoc);
3053 g_signal_connect_after(G_OBJECT(newdoc->view), "populate-popup",
3054 G_CALLBACK(doc_view_populate_popup_lcb), newdoc);
3055 g_signal_connect(G_OBJECT(newdoc->view), "button-release-event",
3056 G_CALLBACK(doc_view_button_release_lcb), newdoc);
3057 g_signal_connect(G_OBJECT(newdoc->view), "button-press-event",
3058 G_CALLBACK(doc_view_button_press_lcb), newdoc);
3059 g_signal_connect(G_OBJECT(newdoc->view), "focus-in-event", G_CALLBACK(doc_focus_in_lcb), newdoc);
3060 g_signal_connect(G_OBJECT(newdoc->view), "focus-out-event", G_CALLBACK(doc_focus_out_lcb), newdoc);
3061 bfwin->documentlist = g_list_append(bfwin->documentlist, newdoc);
3062 tmplist = g_list_last(bfwin->recentdoclist);
3063 tmplist = g_list_append(tmplist, newdoc);
3064 newdoc->recentpos = g_list_last(tmplist);
3065 if (!bfwin->recentdoclist)
3066 bfwin->recentdoclist = tmplist;
3067 /*g_print("doc_new_backend, doc=%p, recentpos=%p, recentdoclist=%p\n",newdoc, newdoc->recentpos, bfwin->recentdoclist); */
3068
3069 gtk_widget_show(newdoc->tab_label);
3070 gtk_widget_show(scroll);
3071
3072 DEBUG_MSG("doc_new_backend, appending doc to notebook\n");
3073
3074 hbox = gtk_hbox_new(FALSE, 0);
3075 newdoc->tab_modlabel = gtk_label_new("");
3076 gtk_box_pack_start(GTK_BOX(hbox),newdoc->tab_modlabel, FALSE, FALSE, 0);
3077 button = bluefish_small_close_button_new();
3078 #if GTK_CHECK_VERSION(3,0,0) /* Restores tab scrolling feature for gtk+3 builds */
3079 gtk_widget_add_events(hbox, GDK_SCROLL_MASK);
3080 gtk_widget_add_events(button, GDK_SCROLL_MASK);
3081 #endif
3082 g_signal_connect(button, "clicked", G_CALLBACK(doc_close_but_clicked_lcb), newdoc);
3083 gtk_container_add(GTK_CONTAINER(newdoc->tab_eventbox), newdoc->tab_label);
3084 gtk_box_pack_start(GTK_BOX(hbox), newdoc->tab_eventbox, TRUE, TRUE, 0);
3085 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3086 gtk_widget_show_all(hbox);
3087
3088 #if GTK_CHECK_VERSION(3,0,0)
3089 newdoc->vsplit = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
3090 #else
3091 newdoc->vsplit = gtk_vpaned_new();
3092 #endif
3093 gtk_paned_add1(GTK_PANED(newdoc->vsplit), scroll);
3094 gtk_widget_show(newdoc->vsplit);
3095 DEBUG_MSG("doc_new_backend, vsplit at %p\n", newdoc->vsplit);
3096
3097 gtk_notebook_append_page_menu(GTK_NOTEBOOK(bfwin->notebook), newdoc->vsplit, hbox, newdoc->tab_menu);
3098 gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(bfwin->notebook), newdoc->vsplit, TRUE);
3099 /* for some reason it only works after the document is appended to the notebook */
3100 doc_set_tabsize(newdoc, BFWIN(bfwin)->session->editor_tab_width);
3101 #if GTK_CHECK_VERSION(3,0,0)
3102 g_signal_connect(G_OBJECT(newdoc->view), "style-updated", G_CALLBACK(doc_view_style_updated_lcb), newdoc);
3103 #endif
3104 return newdoc;
3105 }
3106
3107 /**
3108 * doc_new_loading_in_background:
3109 * @bfwin: the #Tbfwin* in which window to load this document
3110 *
3111 * creates a new document, which will be loaded in the background
3112 */
3113 Tdocument *
doc_new_loading_in_background(Tbfwin * bfwin,GFile * uri,GFileInfo * finfo,gboolean readonly)3114 doc_new_loading_in_background(Tbfwin * bfwin, GFile * uri, GFileInfo * finfo, gboolean readonly)
3115 {
3116 Tdocument *doc = doc_new_backend(bfwin, FALSE, readonly, FALSE);
3117 DEBUG_MSG("doc_new_loading_in_background, bfwin=%p, doc=%p, for uri %p\n", bfwin, doc, uri);
3118 if (finfo) {
3119 doc->fileinfo = g_object_ref(finfo);
3120 } else {
3121 doc->fileinfo = NULL;
3122 }
3123 doc_set_uri(doc, uri, FALSE);
3124 doc_set_title(doc, NULL);
3125 doc_set_status(doc, DOC_STATUS_LOADING);
3126 bfwin_docs_not_complete(bfwin, TRUE);
3127 return doc;
3128 }
3129
3130 static gboolean
doc_auto_detect_lang_lcb(gpointer data)3131 doc_auto_detect_lang_lcb(gpointer data)
3132 {
3133 Tdocument *doc = data;
3134 gchar *conttype, *buf;
3135 #ifdef WIN32
3136 gchar *mimetype;
3137 #endif
3138 gint buflen;
3139 gboolean uncertain = FALSE;
3140 DEBUG_MSG("doc_auto_detect_lang_lcb, started, doc=%p\n", doc);
3141 buf = doc_get_chars(doc, 0, -1);
3142 buflen = strlen(buf);
3143 conttype = g_content_type_guess(NULL, (guchar *) buf, buflen, &uncertain);
3144 #ifdef WIN32
3145 mimetype = g_content_type_get_mime_type(conttype);
3146 #endif
3147 DEBUG_MSG("doc_auto_detect_lang_lcb, buflen=%d\n", buflen);
3148 g_free(buf);
3149 if (!uncertain && conttype && (strcmp(conttype, "text/plain") != 0 || buflen > 50)) {
3150 DEBUG_MSG("doc_auto_detect_lang_lcb, found %s for certain\n", conttype);
3151 #ifdef WIN32
3152 doc_set_mimetype(doc, mimetype, NULL);
3153 g_free(mimetype);
3154 #else
3155 doc_set_mimetype(doc, conttype, NULL);
3156 #endif
3157 g_free(conttype);
3158 return FALSE;
3159 }
3160 g_free(conttype);
3161 if (buflen > 50) {
3162 doc->newdoc_autodetect_lang_id = 0;
3163 DEBUG_MSG("doc_auto_detect_lang_lcb, filesize>50, stop detection\n");
3164 return FALSE;
3165 }
3166 return TRUE;
3167 }
3168
3169 /**
3170 * doc_new:
3171 * @bfwin: #Tbfwin* with the window to open the document in
3172 * @delay_activate: Whether to perform GUI-calls and flush_queue(). Set to TRUE when loading several documents at once.
3173 *
3174 * Create a new document, related structures and a nice little textview to display the document in.
3175 * Finally, add a new tab to the notebook.
3176 * The GtkTextView is not actually gtk_widget_shown() if delay_activate == TRUE. This is done by doc_activate() instead.
3177 *
3178 * Return value: a #Tdocument* pointer to the just created document.
3179 **/
3180 Tdocument *
doc_new(Tbfwin * bfwin,gboolean delay_activate)3181 doc_new(Tbfwin * bfwin, gboolean delay_activate)
3182 {
3183 Tdocument *doc = doc_new_backend(bfwin, TRUE, FALSE, TRUE);
3184 doc_set_status(doc, DOC_STATUS_COMPLETE);
3185 DEBUG_MSG("doc_new, doc=%p, status=%d\n", doc, doc->status);
3186 if (!doc->newdoc_autodetect_lang_id) {
3187 doc->newdoc_autodetect_lang_id =
3188 g_timeout_add_seconds_full(G_PRIORITY_DEFAULT_IDLE, 10, doc_auto_detect_lang_lcb, doc, NULL);
3189 }
3190 if (!delay_activate)
3191 gtk_widget_show(doc->view); /* Delay _show() if neccessary */
3192 return doc;
3193 }
3194
3195 Tdocument *
doc_new_with_template(Tbfwin * bfwin,GFile * uri,gboolean force_new)3196 doc_new_with_template(Tbfwin * bfwin, GFile * uri, gboolean force_new)
3197 {
3198 Tdocument *doc;
3199 if (!uri)
3200 return doc_new(bfwin, FALSE);
3201 doc = doc_new_backend(bfwin, force_new, FALSE, FALSE);
3202 file_into_doc(doc, uri, TRUE, FALSE);
3203 gtk_widget_show(doc->view);
3204 return doc;
3205 }
3206
3207 /**
3208 * doc_new_with_new_file:
3209 * @bfwin: #Tbfwin*
3210 * @new_curi: #gchar* character uri to give document.
3211 *
3212 * Create a new document, name it by new_curi, and create the file.
3213 *
3214 * Return value: void
3215 **/
3216 /*void doc_new_with_new_file(Tbfwin *bfwin, gchar *new_curi) {
3217 Tdocument *doc;
3218 Tfiletype *ft;
3219 GFile *new_uri;
3220 if (new_curi == NULL) {
3221 statusbar_message(bfwin,_("No filename"), 2);
3222 return;
3223 }
3224 new_uri = gnome_vfs_uri_new(new_curi);
3225 if (!main_v->props.allow_multi_instances) {
3226 gboolean res;
3227 res = switch_to_document_by_uri(bfwin,new_uri);
3228 if (res){
3229 return;
3230 }
3231 }
3232 DEBUG_MSG("doc_new_with_new_file, new_curi=%s\n", new_curi);
3233 add_filename_to_history(bfwin,new_curi);
3234 doc = doc_new(bfwin, FALSE);
3235 doc->uri = new_uri;
3236 if (bfwin->project && bfwin->project->template && strlen(bfwin->project->template) > 2) {
3237 GFile *uri;
3238 uri = gnome_vfs_uri_new(bfwin->project->template);
3239 if (uri) {
3240 file_into_doc(bfwin->current_document, uri, TRUE);
3241 g_object_unref(uri);
3242 }
3243 }
3244 ft = get_filetype_by_filename_and_content(new_curi, NULL);
3245 if (ft) doc->hl = ft;
3246 / * doc->modified = 1;* /
3247 doc_set_title(doc);
3248 / * doc_save(doc, FALSE, FALSE, FALSE); * /
3249 / * doc_set_stat_info(doc); also sets mtime field * /
3250 switch_to_document_by_pointer(bfwin,doc);
3251 doc_activate(doc);
3252 }*/
3253
3254 /**
3255 * doc_new_from_uri:
3256 * @bfwin: #Tbfwin* with the window to open the document in
3257 * @opturi: uri of the document, uri should be set !
3258 * @finfo: finfo may be NULL
3259 * @delay_activate: Whether to perform GUI-calls and flush_queue(). Set to TRUE when loading several documents at once.
3260 * @goto_line: scroll document to specified line. goto_offset and align_center values will be ignored.
3261 * @cursor_offset: place cursor at specified posiion.
3262 * @goto_offset: scrolls texview to offset. Alignment depends on align_center parameter
3263 * @align_center: If set to True places specified offset in the center of the screen, if False- offset will be placed in topleft corner.
3264 * @load_first: passed on to file_doc_from_uri
3265 */
3266 void
doc_new_from_uri(Tbfwin * bfwin,GFile * opturi,GFileInfo * finfo,gboolean delay_activate,gboolean move_to_this_win,gint goto_line,gint goto_offset,gint cursor_offset,gboolean align_center,gboolean load_first)3267 doc_new_from_uri(Tbfwin * bfwin, GFile * opturi, GFileInfo * finfo, gboolean delay_activate,
3268 gboolean move_to_this_win, gint goto_line, gint goto_offset, gint cursor_offset,
3269 gboolean align_center, gboolean load_first)
3270 {
3271 GList *alldocs;
3272 Tdocument *tmpdoc;
3273 gchar *tmpcuri;
3274 GFile *uri;
3275 gboolean open_readonly = FALSE;
3276 if (!bfwin || !opturi) {
3277 return;
3278 }
3279 uri = opturi;
3280 tmpcuri = g_file_get_uri(opturi);
3281 DEBUG_MSG("doc_new_from_uri, started for uri(%p)=%s\n", uri, tmpcuri);
3282
3283 /* check if the document already is opened */
3284 alldocs = return_allwindows_documentlist();
3285 tmpdoc = documentlist_return_document_from_uri(alldocs, uri);
3286 g_list_free(alldocs);
3287 if (tmpdoc) { /* document is already open */
3288 DEBUG_MSG
3289 ("doc_new_from_uri, doc %s is already open, delay_activate=%d, move_to_window=%d, cursor_offset=%d, goto_offset=%d\n",
3290 tmpcuri, delay_activate, move_to_this_win, cursor_offset, goto_offset);
3291 if (tmpdoc->bfwin != bfwin && move_to_this_win) {
3292 /* we should aks the user if it is OK to move the document */
3293 if (!delay_activate)
3294 bfwin->focus_next_new_doc = TRUE;
3295 doc_move_to_window_dialog(tmpdoc, bfwin);
3296 /* TODO: or open the document readonly */
3297 } else if (!delay_activate) { /* switch to window, only if we should */
3298 DEBUG_MSG("doc_new_from_uri, switch to window\n");
3299 bfwin_switch_to_document_by_pointer(BFWIN(tmpdoc->bfwin), tmpdoc);
3300 if (bfwin != tmpdoc->bfwin)
3301 gtk_window_present(GTK_WINDOW(BFWIN(tmpdoc->bfwin)->main_window));
3302 }
3303 if (goto_line >= 0) {
3304 doc_select_line(tmpdoc, goto_line, TRUE);
3305 } else {
3306 if (goto_offset >= 0)
3307 doc_select_line_by_offset(tmpdoc, goto_offset, TRUE, align_center);
3308 if (cursor_offset >= 0)
3309 doc_set_cursor_position(tmpdoc, cursor_offset);
3310 }
3311 } else { /* document is not yet opened */
3312 if (!delay_activate)
3313 bfwin->focus_next_new_doc = TRUE;
3314 DEBUG_MSG("doc_new_from_uri, uri=%p, delay_activate=%d, focus_next_new_doc=%d, goto_offset=%d, cursor_offset=%d, align_center=%d\n",
3315 uri, delay_activate, bfwin->focus_next_new_doc, goto_offset, cursor_offset, align_center);
3316 file_doc_from_uri(bfwin, uri, NULL, finfo, goto_line, goto_offset, open_readonly, cursor_offset,
3317 align_center, load_first);
3318 }
3319 session_set_opendir(bfwin, tmpcuri);
3320 g_free(tmpcuri);
3321 }
3322
3323 void
doc_new_from_input(Tbfwin * bfwin,gchar * input,gboolean delay_activate,gboolean move_to_this_win,gint goto_line)3324 doc_new_from_input(Tbfwin * bfwin, gchar * input, gboolean delay_activate, gboolean move_to_this_win,
3325 gint goto_line)
3326 {
3327 GFile *uri = NULL;
3328 if (!input) {
3329 return;
3330 }
3331 DEBUG_MSG("doc_new_from_input, input=%s, delay_activate=%d\n", input, delay_activate);
3332 if (strchr(input, '/') == NULL) { /* no slashes in the path, relative ? */
3333 if (bfwin->current_document && bfwin->current_document->uri) {
3334 GFile *parent = g_file_get_parent(bfwin->current_document->uri);
3335 uri = g_file_resolve_relative_path(parent, input);
3336 g_object_unref(parent);
3337 } else {
3338 /* relative path to what ?!?!?! */
3339 /* home dir? current dir? */
3340 }
3341 } else {
3342 uri = g_file_new_for_commandline_arg(input);
3343 }
3344 if (uri) {
3345 doc_new_from_uri(bfwin, uri, NULL, delay_activate, move_to_this_win, goto_line, -1, -1, TRUE, FALSE);
3346 g_object_unref(uri);
3347 }
3348 }
3349
3350 static GtkWidget *
doc_create_slave_view(Tdocument * doc)3351 doc_create_slave_view(Tdocument * doc)
3352 {
3353 GtkWidget *scroll;
3354 DEBUG_MSG("doc_create_slave_view, create slave view for %p\n", doc->view);
3355 doc->slave = bftextview2_new_slave(BLUEFISH_TEXT_VIEW(doc->view));
3356 gtk_text_view_set_overwrite(GTK_TEXT_VIEW(doc->slave),
3357 gtk_text_view_get_overwrite(GTK_TEXT_VIEW(doc->view)));
3358 g_signal_connect_after(G_OBJECT(doc->slave), "toggle-overwrite",
3359 G_CALLBACK(doc_view_toggle_overwrite_lcb), doc);
3360 g_signal_connect_after(G_OBJECT(doc->slave), "populate-popup", G_CALLBACK(doc_view_populate_popup_lcb),
3361 doc);
3362 g_signal_connect(G_OBJECT(doc->slave), "button-release-event", G_CALLBACK(doc_view_button_release_lcb),
3363 doc);
3364 g_signal_connect(G_OBJECT(doc->slave), "button-press-event", G_CALLBACK(doc_view_button_press_lcb), doc);
3365
3366 apply_font_style(doc->slave, main_v->props.editor_font_string);
3367 scroll = gtk_scrolled_window_new(NULL, NULL);
3368 g_signal_connect(scroll, "scroll-event", G_CALLBACK(doc_scroll_event_lcb), doc);
3369 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3370 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN);
3371 gtk_container_add(GTK_CONTAINER(scroll), doc->slave);
3372 doc_set_tabsize(doc, BFWIN(doc->bfwin)->session->editor_tab_width);
3373 g_signal_connect(G_OBJECT(doc->slave), "focus-in-event", G_CALLBACK(doc_focus_in_lcb), doc);
3374 g_signal_connect(G_OBJECT(doc->slave), "focus-out-event", G_CALLBACK(doc_focus_out_lcb), doc);
3375 return scroll;
3376 }
3377
3378 static gboolean
doc_split_scroll(gpointer data)3379 doc_split_scroll(gpointer data)
3380 {
3381 Tdocument *doc = data;
3382 GtkAdjustment *prim_adjust = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gtk_widget_get_parent(doc->view)));
3383 GtkAdjustment *adjust =
3384 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gtk_widget_get_parent(doc->slave)));
3385 /* get the scrollbar for the primary textview, and set this one lower */
3386
3387 gtk_adjustment_set_value(adjust, gtk_adjustment_get_value(prim_adjust) + gtk_adjustment_get_page_size(adjust));
3388 return FALSE;
3389 }
3390
3391 static void floatingview_destroy_lcb(GtkWidget * widget, Tdocument * doc);
3392
3393 void
doc_split_view(Tdocument * doc,gboolean enable)3394 doc_split_view(Tdocument * doc, gboolean enable)
3395 {
3396 GtkWidget *botscrol;
3397
3398 if (enable == (doc->slave != NULL))
3399 return;
3400
3401 if (enable) {
3402 GdkRectangle rect;
3403
3404 if (doc->floatingview)
3405 floatingview_destroy_lcb(NULL, doc);
3406
3407 gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(doc->view), &rect);
3408 botscrol = doc_create_slave_view(doc);
3409 DEBUG_MSG("doc_split_view, add %p to pane2\n", botscrol);
3410 gtk_paned_pack2(GTK_PANED(doc->vsplit), botscrol, TRUE, FALSE);
3411 gtk_widget_show(doc->slave);
3412 gtk_widget_show(botscrol);
3413 gtk_paned_set_position(GTK_PANED(doc->vsplit), rect.height / 2);
3414 DEBUG_MSG("doc_split_view, rect.height=%d, set split at %d\n", rect.height, rect.height / 2);
3415 g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, doc_split_scroll, doc, NULL);
3416 } else {
3417 botscrol = gtk_widget_get_parent(doc->slave);
3418 DEBUG_MSG("doc_split_view, destroy %p\n", botscrol);
3419 gtk_widget_destroy(botscrol);
3420 doc->slave = NULL;
3421 }
3422 DEBUG_MSG("doc_split_view, done\n");
3423 }
3424
3425 /**
3426 * doc_reload:
3427 * @doc: a #Tdocument
3428 * @finfo: if the reload is because the file is modified, pass here the new fileinfo, use NULL if unknown
3429 *
3430 * Revert to file on disk.
3431 *
3432 * Return value: void
3433 **/
3434 void
doc_reload(Tdocument * doc,GFileInfo * newfinfo,gboolean warn_user)3435 doc_reload(Tdocument * doc, GFileInfo * newfinfo, gboolean warn_user)
3436 {
3437 GtkTextIter itstart, itend, cursor;
3438 gint cursorpos = -1;
3439
3440 DEBUG_MSG("starting reload for %p\n", doc);
3441 if (doc->uri == NULL) {
3442 bfwin_statusbar_message(BFWIN(doc->bfwin), _("Unable to open file"), 2);
3443 return;
3444 }
3445
3446 if (warn_user) {
3447 gint retval;
3448 const gchar *buttons[] = { GTK_STOCK_CANCEL, GTK_STOCK_REVERT_TO_SAVED, NULL };
3449 gchar *msgstr, *basename;
3450
3451 basename = g_file_get_basename(doc->uri);
3452 msgstr =
3453 g_strdup_printf(_
3454 ("If you revert \"%s\" to your last saved copy, your current changes will be permanently lost."),
3455 basename);
3456 retval =
3457 message_dialog_new_multi(BFWIN(doc->bfwin)->main_window, GTK_MESSAGE_WARNING, buttons,
3458 _("Revert changes to last saved copy?"), msgstr);
3459
3460 g_free(basename);
3461 g_free(msgstr);
3462
3463 if (retval == 0)
3464 return;
3465 }
3466
3467 /* store all bookmark positions, reload them later */
3468 bmark_clean_for_doc(doc);
3469 bluefish_text_view_scan_cleanup(BLUEFISH_TEXT_VIEW(doc->view));
3470 gtk_text_buffer_get_iter_at_mark(doc->buffer, &cursor, gtk_text_buffer_get_insert(doc->buffer));
3471 cursorpos = gtk_text_iter_get_line(&cursor);
3472 gtk_text_buffer_get_bounds(doc->buffer, &itstart, &itend);
3473 gtk_text_buffer_delete(doc->buffer, &itstart, &itend);
3474 doc_set_status(doc, DOC_STATUS_LOADING);
3475 bfwin_docs_not_complete(doc->bfwin, TRUE);
3476 doc_set_modified(doc, FALSE);
3477 if (doc->fileinfo)
3478 g_object_unref(doc->fileinfo);
3479 doc->fileinfo = newfinfo;
3480 if (newfinfo)
3481 g_object_ref(doc->fileinfo);
3482 file_doc_fill_from_uri(doc, doc->uri, doc->fileinfo, cursorpos);
3483 }
3484
3485 static gboolean
doc_close_from_activate(gpointer data)3486 doc_close_from_activate(gpointer data)
3487 {
3488 doc_close_single_backend(DOCUMENT(data), FALSE, FALSE);
3489 return FALSE;
3490 }
3491
3492 /**
3493 * doc_activate:
3494 * @doc: a #Tdocument
3495 *
3496 * Perform actions neccessary when a document is focused. I.e. called from the notebook.
3497 *
3498 * Show textview, warn if the file on disk has been changed,
3499 * update line-numbers etc and highlighting.
3500 *
3501 * Return value: void
3502 **/
3503 void
doc_activate(Tdocument * doc)3504 doc_activate(Tdocument * doc)
3505 {
3506 Tdocument *last_activated_doc;
3507 #ifdef DEBUG
3508 if (!doc) {
3509 DEBUG_MSG("doc_activate, doc=NULL!!! ABORTING!!\n");
3510 exit(44);
3511 }
3512 #endif
3513 if (doc == NULL)
3514 return;
3515 DEBUG_MSG("doc_activate, started for %p\n", doc);
3516 if (doc == BFWIN(doc->bfwin)->last_activated_doc || doc->close_doc) {
3517 /* DO enable the scanner, because it is disabled in notebook_changed(), but if the last document is also the new document it needs to be re-enabled again */
3518 DEBUG_MSG("doc_activate, ONLY enable the scanner for doc %p\n", doc);
3519 BLUEFISH_TEXT_VIEW(doc->view)->enable_scanner = TRUE;
3520 DEBUG_MSG("doc_activate, not doing anything, doc=%p, last_avtivated_doc=%p, close_doc=%d\n", doc,
3521 BFWIN(doc->bfwin)->last_activated_doc, doc->close_doc);
3522 return;
3523 }
3524 DEBUG_MSG("doc_activate for doc with view %p..\n", doc->view);
3525 if (doc->status == DOC_STATUS_ERROR) {
3526 const gchar *buttons[] =
3527 { _("_Retry"), _("Retry _all failed"), _("_Close"), _("Close all _failed"), NULL };
3528 gchar *tmpstr;
3529 gint retval;
3530 DEBUG_MSG("doc_activate, DOC_STATUS_ERROR, retry???\n");
3531 tmpstr =
3532 g_strconcat(_("File "), gtk_label_get_text(GTK_LABEL(doc->tab_menu)), _(" failed to load."),
3533 NULL);
3534 retval =
3535 message_dialog_new_multi(BFWIN(doc->bfwin)->main_window, GTK_MESSAGE_WARNING, buttons,
3536 _("File failed to load\n"), tmpstr);
3537 g_free(tmpstr);
3538 switch (retval) {
3539 case 0:
3540 file_doc_retry_uri(doc);
3541 break;
3542 case 1:{
3543 GList *tmplist;
3544 /* retry all failed documents */
3545 for (tmplist = g_list_first(BFWIN(doc->bfwin)->documentlist); tmplist != NULL;
3546 tmplist = tmplist->next) {
3547 if (DOCUMENT(tmplist->data)->status == DOC_STATUS_ERROR)
3548 file_doc_retry_uri(DOCUMENT(tmplist->data));
3549 }
3550 }
3551 break;
3552 case 2:
3553 g_idle_add(doc_close_from_activate, doc);
3554 break;
3555 case 3:{
3556 GList *tmplist;
3557 /* retry all failed documents */
3558 for (tmplist = g_list_first(BFWIN(doc->bfwin)->documentlist); tmplist != NULL;
3559 tmplist = tmplist->next) {
3560 if (DOCUMENT(tmplist->data)->status == DOC_STATUS_ERROR)
3561 g_idle_add(doc_close_from_activate, DOCUMENT(tmplist->data));
3562 }
3563 }
3564 break;
3565 #ifdef DEVELOPMENT
3566 default:
3567 g_print("doc_activate, retval=%d does not exist\n", retval);
3568 exit(123);
3569 break;
3570 #endif
3571 }
3572 DEBUG_MSG("doc_activate, returning\n");
3573 return;
3574 } else if (doc->status == DOC_STATUS_LOADING) {
3575 DEBUG_MSG("doc_activate, STILL LOADING! returning\n");
3576 return;
3577 } else {
3578 if (doc->highlightstate && !BLUEFISH_TEXT_VIEW(doc->view)->enable_scanner) {
3579 DEBUG_MSG("doc_activate, enable scanner for %p\n", doc);
3580 BLUEFISH_TEXT_VIEW(doc->view)->enable_scanner = TRUE;
3581 bftextview2_schedule_scanning(BLUEFISH_TEXT_VIEW(doc->view));
3582 }
3583 DEBUG_MSG("doc_activate, call gtk_widget_show(doc->view) for doc %p\n",doc);
3584 gtk_widget_show(doc->view); /* This might be the first time this document is activated. */
3585 }
3586 last_activated_doc = BFWIN(doc->bfwin)->last_activated_doc;
3587 BFWIN(doc->bfwin)->last_activated_doc = doc;
3588 /* BUG: hmm if this document previously was in error state, it will now be set to normal !?!?! */
3589 if (last_activated_doc) {
3590 doc_update_label_color(last_activated_doc, FALSE);
3591 }
3592 doc_update_label_color(doc, TRUE);
3593 if (BFWIN(doc->bfwin)->recentdoclist != doc->recentpos) {
3594 /* put this document on top of the recentlist */
3595 DEBUG_MSG("put this document %p with recentpos %p on top of the recentlist %p\n", doc, doc->recentpos,
3596 BFWIN(doc->bfwin)->recentdoclist);
3597 GList *dummy = g_list_remove_link(BFWIN(doc->bfwin)->recentdoclist, doc->recentpos);
3598 BFWIN(doc->bfwin)->recentdoclist = g_list_concat(doc->recentpos, BFWIN(doc->bfwin)->recentdoclist);
3599 DEBUG_MSG("recentlist now starts at %p\n", BFWIN(doc->bfwin)->recentdoclist);
3600 }
3601
3602 doc_start_modified_check(doc);
3603
3604 DEBUG_MSG("doc_activate, calling bfwin_set_document_menu_items()\n");
3605 bfwin_set_document_menu_items(doc);
3606 bfwin_set_title(BFWIN(doc->bfwin), doc, 0);
3607 doc_set_statusbar_lncol(doc);
3608 doc_set_statusbar_insovr(doc);
3609 doc_set_statusbar_lang_encoding(doc);
3610
3611 #ifdef MAC_INTEGRATION
3612 /* ige_mac_menu_sync(GTK_MENU_SHELL(BFWIN(doc->bfwin)->menubar));*/
3613 gtkosx_application_sync_menubar(g_object_new(GTKOSX_TYPE_APPLICATION, NULL));
3614 #endif
3615
3616 /* doc_scroll_to_cursor(doc);*/
3617 if (doc->uri) {
3618 /* gchar *dir1 = g_path_get_dirname(doc->uri);
3619 gchar *dir2 = ending_slash(dir1);
3620 if (dir2[0] == '/') {
3621 chdir(dir2);
3622 }*/
3623 if (BFWIN(doc->bfwin)->session->filebrowser_focus_follow) {
3624 /* DEBUG_MSG("doc_activate, call filebrowser_open_dir() for %s\n",dir2);
3625 filebrowser_open_dir(BFWIN(doc->bfwin),dir2);*/
3626 fb2_focus_document(BFWIN(doc->bfwin), doc);
3627 }
3628 /* g_free(dir1);
3629 g_free(dir2);*/
3630 }
3631 if (doc->highlightstate)
3632 BLUEFISH_TEXT_VIEW(doc->view)->enable_scanner = TRUE;
3633 gtk_widget_grab_focus(GTK_WIDGET(doc->view));
3634
3635 DEBUG_MSG("doc_activate, doc=%p, finished\n", doc);
3636 }
3637
3638 void
doc_force_activate(Tdocument * doc)3639 doc_force_activate(Tdocument * doc)
3640 {
3641 DEBUG_MSG("doc_force_activate, called for %p\n",doc);
3642 BFWIN(doc->bfwin)->last_activated_doc = NULL;
3643 doc_activate(doc);
3644 }
3645
3646 void
file_open_from_selection(Tbfwin * bfwin)3647 file_open_from_selection(Tbfwin * bfwin)
3648 {
3649 gchar *string;
3650 GtkClipboard *cb;
3651
3652 cb = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
3653 string = gtk_clipboard_wait_for_text(cb);
3654 if (string) {
3655 DEBUG_MSG("file_open_from_selection, opening %s\n", string);
3656 doc_new_from_input(bfwin, string, FALSE, FALSE, -1);
3657
3658 g_free(string);
3659 }
3660 }
3661
3662 void
file_insert_doc(Tbfwin * bfwin)3663 file_insert_doc(Tbfwin * bfwin)
3664 {
3665 gchar *tmpfilename = NULL;
3666
3667 {
3668 GtkWidget *dialog;
3669 dialog =
3670 file_chooser_dialog(bfwin, _("Select file to insert"), GTK_FILE_CHOOSER_ACTION_OPEN, NULL, FALSE,
3671 FALSE, NULL, TRUE);
3672 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
3673 tmpfilename = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
3674 }
3675 /* BUG: get the encoding from the dialog !!! */
3676 gtk_widget_destroy(dialog);
3677 }
3678 if (tmpfilename == NULL) {
3679 bfwin_statusbar_message(bfwin, _("No file to insert"), 2);
3680 return;
3681 } else {
3682 GFile *uri;
3683
3684 doc_unre_new_group(bfwin->current_document);
3685 uri = g_file_new_for_uri(tmpfilename);
3686 file_into_doc(bfwin->current_document, uri, FALSE, FALSE);
3687 g_object_unref(uri);
3688 g_free(tmpfilename);
3689 }
3690 }
3691
doc_get_active_view(Tdocument * doc)3692 GtkTextView *doc_get_active_view(Tdocument *doc) {
3693 if (doc->slave && gtk_window_get_focus(GTK_WINDOW(BFWIN(doc->bfwin)->main_window)) == doc->slave) {
3694 return GTK_TEXT_VIEW(doc->slave);
3695 }
3696 return GTK_TEXT_VIEW(doc->view);
3697 }
3698
3699 void
doc_copy(Tbfwin * bfwin)3700 doc_copy(Tbfwin * bfwin)
3701 {
3702 gtk_text_buffer_copy_clipboard(bfwin->current_document->buffer,
3703 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
3704 }
3705
3706 void
doc_cut(Tbfwin * bfwin)3707 doc_cut(Tbfwin * bfwin)
3708 {
3709 doc_unre_new_group(bfwin->current_document);
3710 gtk_text_buffer_cut_clipboard(bfwin->current_document->buffer, gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
3711 TRUE);
3712 doc_unre_new_group(bfwin->current_document);
3713 }
3714
3715 void
doc_paste(Tbfwin * bfwin)3716 doc_paste(Tbfwin * bfwin)
3717 {
3718 GtkTextMark *mark;
3719 Tdocument *doc = bfwin->current_document;
3720 GtkTextView *activeview;
3721
3722 DEBUG_MSG("doc_paste, create new undo group\n");
3723 doc_unre_new_group(doc);
3724 doc->in_paste_operation = TRUE;
3725 DEBUG_MSG("doc_paste, pasting clipboard\n");
3726 gtk_text_buffer_paste_clipboard(doc->buffer, gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), NULL, TRUE);
3727 doc->in_paste_operation = FALSE;
3728 DEBUG_MSG("doc_paste, finished, create new undo group\n");
3729 doc_unre_new_group(doc);
3730 mark = gtk_text_buffer_get_insert(bfwin->current_document->buffer);
3731 /* in split view we have to find the current active view before we scroll the cursor onscreen */
3732 DEBUG_MSG("doc_paste, active widget=%p, view=%p,slave=%p\n",gtk_window_get_focus(GTK_WINDOW(bfwin->main_window)),doc->view,doc->slave);
3733 activeview = doc_get_active_view(doc);
3734 gtk_text_view_scroll_mark_onscreen(activeview, mark);
3735 DEBUG_MSG("doc_paste, scrolled to mark in view %p, now finished\n",activeview);
3736 }
3737
3738 /*************************** paste special code ***************************/
3739
3740 static TcheckNsave_return
paste_image_save_lcb(TcheckNsave_status status,GError * gerror,gpointer callback_data)3741 paste_image_save_lcb(TcheckNsave_status status, GError * gerror, gpointer callback_data)
3742 {
3743 /* TODO: handle error */
3744 if (gerror) {
3745 g_warning("paste_image_save_lcb, failed to save thumbnail: %s\n", gerror->message);
3746 }
3747 return CHECKNSAVE_CONT;
3748 }
3749
3750 static void
image_received(GtkClipboard * clipboard,GdkPixbuf * pixbuf,gpointer data)3751 image_received(GtkClipboard * clipboard, GdkPixbuf * pixbuf, gpointer data)
3752 {
3753 DEBUG_MSG("image_received, started, got %p\n", pixbuf);
3754 if (!pixbuf) {
3755 g_print("no pixbuf received\n");
3756 return;
3757 }
3758 /* ask for the filename */
3759 gchar *filename, *insertname = NULL, *str;
3760 gint width, height;
3761 GFile *uri;
3762 gchar *buffer;
3763 gsize buflen;
3764 GError *gerror = NULL;
3765 Trefcpointer *refbuf;
3766
3767 width = gdk_pixbuf_get_width(pixbuf);
3768 height = gdk_pixbuf_get_height(pixbuf);
3769
3770 filename = ask_new_filename(BFWIN(data), NULL, _("Save pasted image as"));
3771
3772 if (!filename)
3773 return;
3774
3775 uri = g_file_new_for_uri(filename);
3776 gdk_pixbuf_save_to_buffer(pixbuf, &buffer, &buflen, "jpeg", &gerror, "quality", "95", NULL);
3777 refbuf = refcpointer_new(buffer);
3778 /* save the file and insert the image tag */
3779 file_checkNsave_uri_async(uri, NULL, refbuf, buflen, FALSE, FALSE,
3780 (CheckNsaveAsyncCallback) paste_image_save_lcb, NULL, BFWIN(data));
3781
3782
3783 if (BFWIN(data)->current_document->uri) {
3784 gchar *curi = g_file_get_uri(BFWIN(data)->current_document->uri);
3785 insertname = create_relative_link_to(curi, filename);
3786 g_free(curi);
3787 }
3788 str =
3789 g_strdup_printf("<img src=\"%s\" alt=\"\" width=\"%d\" height=\"%d\"/>",
3790 insertname ? insertname : filename, width, height);
3791
3792 doc_insert_two_strings(DOCUMENT(BFWIN(data)->current_document), str, NULL);
3793
3794 refcpointer_unref(refbuf);
3795 g_object_unref(uri);
3796 g_free(filename);
3797 g_free(insertname);
3798 g_free(str);
3799 }
3800
3801 static void
html_received(GtkClipboard * clipboard,GtkSelectionData * seldat,gpointer data)3802 html_received(GtkClipboard * clipboard, GtkSelectionData * seldat, gpointer data)
3803 {
3804 if (!gtk_selection_data_get_data(seldat)) {
3805 g_print("html_received, no text received\n");
3806 return;
3807 }
3808 /* strip headers and footer from the html */
3809 GRegex *reg;
3810 GError *gerror = NULL;
3811 GMatchInfo *match_info;
3812 DEBUG_MSG("got %d bytes of data\n", gtk_selection_data_get_length(seldat));
3813 DEBUG_MSG("got data '%s'\n", gtk_selection_data_get_data(seldat));
3814
3815 reg =
3816 g_regex_new("<body[^>]*>(.*)</body>", G_REGEX_CASELESS | G_REGEX_MULTILINE | G_REGEX_DOTALL, 0,
3817 &gerror);
3818 if (!reg) {
3819 g_warning("paste special, html_received, internal regex error\n");
3820 }
3821 if (g_regex_match(reg, (gchar *) gtk_selection_data_get_data(seldat), 0, &match_info)) {
3822 gchar *str;
3823 str = g_match_info_fetch(match_info, 1);
3824 doc_insert_two_strings(DOCUMENT(BFWIN(data)->current_document), str, NULL);
3825 g_free(str);
3826 }
3827 g_match_info_free(match_info);
3828 g_regex_unref(reg);
3829 }
3830
3831 static void
text_received(GtkClipboard * clipboard,const gchar * text,gpointer data)3832 text_received(GtkClipboard * clipboard, const gchar * text, gpointer data)
3833 {
3834 if (!text) {
3835 return;
3836 }
3837 doc_insert_two_strings(DOCUMENT(BFWIN(data)->current_document), text, NULL);
3838 }
3839
3840 void
doc_paste_special(Tbfwin * bfwin)3841 doc_paste_special(Tbfwin * bfwin)
3842 {
3843 gint result;
3844 GtkWidget *win, *content_area, *rbut0 = NULL, *rbut1 = NULL, /**rbut2=NULL,*/ *rbut3 = NULL;
3845 gboolean have_html = FALSE, have_image = FALSE, have_plain = FALSE;
3846 GSList *rgroup = NULL;
3847 GdkAtom *targets;
3848 gint numtargets;
3849 GtkClipboard *cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
3850
3851 if (!gtk_clipboard_wait_for_targets(cb, &targets, &numtargets)) {
3852 DEBUG_MSG("no clipbord data available\n");
3853 return;
3854 }
3855
3856 while (numtargets > 0 && (have_html == FALSE || have_image == FALSE)) {
3857 gchar *name;
3858 numtargets--;
3859 name = gdk_atom_name(targets[numtargets]);
3860 DEBUG_MSG("%d: got target %s\n", numtargets, name);
3861 if (strcmp(name, "text/html") == 0) {
3862 have_html = TRUE;
3863 } else if (strncmp(name, "image/", 6) == 0) {
3864 have_image = TRUE;
3865 } else if (strncmp(name, "text/plain", 10) == 0 || strcmp(name, "STRING") == 0
3866 || strcmp(name, "TEXT") == 0) {
3867 have_plain = TRUE;
3868 }
3869 g_free(name);
3870 }
3871
3872 win = gtk_dialog_new_with_buttons(_("Paste special")
3873 , GTK_WINDOW(bfwin->main_window),
3874 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_STOCK_OK,
3875 GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
3876 content_area = gtk_dialog_get_content_area(GTK_DIALOG(win));
3877 if (!have_html && !have_image && !have_plain) {
3878 gtk_box_pack_start(GTK_BOX(content_area), gtk_label_new(_("No compatible clipboard data found")),
3879 TRUE, TRUE, 4);
3880 }
3881
3882 if (have_html) {
3883 rbut0 = gtk_radio_button_new_with_mnemonic(NULL, _("Paste as _HTML"));
3884 gtk_box_pack_start(GTK_BOX(content_area), rbut0, TRUE, TRUE, 4);
3885 rgroup = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbut0));
3886 }
3887 if (have_image) {
3888 rbut1 = gtk_radio_button_new_with_mnemonic(rgroup, _("Paste as HTML with _JPG"));
3889 gtk_box_pack_start(GTK_BOX(content_area), rbut1, TRUE, TRUE, 4);
3890 /*rbut2 = gtk_radio_button_new_with_mnemonic(rgroup, _("Paste as HTML _PNG"));
3891 gtk_box_pack_start(GTK_BOX(content_area), rbut2, TRUE, TRUE, 4); */
3892 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rbut1), TRUE);
3893 if (!rgroup) {
3894 rgroup = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbut1));
3895 }
3896 }
3897 if (have_plain) {
3898 rbut3 = gtk_radio_button_new_with_mnemonic(rgroup, _("Paste as plain text"));
3899 gtk_box_pack_start(GTK_BOX(content_area), rbut3, TRUE, TRUE, 4);
3900 }
3901 gtk_widget_show_all(win);
3902 result = gtk_dialog_run(GTK_DIALOG(win));
3903 DEBUG_MSG("gtk_dialog_run, got result %d\n", result);
3904 if (result == GTK_RESPONSE_ACCEPT) {
3905 GtkClipboard *cb;
3906 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
3907
3908 if (have_html && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rbut0))) {
3909 GdkAtom target;
3910 target = gdk_atom_intern_static_string("text/html");
3911 gtk_clipboard_request_contents(cb, target, html_received, bfwin);
3912 } else if (have_plain && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rbut3))) {
3913 gtk_clipboard_request_text(cb, text_received, bfwin);
3914 } else if (have_image) {
3915 gtk_clipboard_request_image(cb, image_received, bfwin);
3916 /*paste_special_image(bfwin, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rbut1))); */
3917 }
3918 }
3919 DEBUG_MSG("destroy dialog %p\n", win);
3920 gtk_widget_destroy(win);
3921 g_free(targets);
3922 DEBUG_MSG("destroy dialog %p, done\n", win);
3923 }
3924
3925 /*************************** end of paste special code ***************************/
3926
3927 void
doc_select_all(Tbfwin * bfwin)3928 doc_select_all(Tbfwin * bfwin)
3929 {
3930 GtkTextIter itstart, itend;
3931
3932 gtk_text_buffer_get_bounds(bfwin->current_document->buffer, &itstart, &itend);
3933 gtk_text_buffer_move_mark_by_name(bfwin->current_document->buffer, "insert", &itstart);
3934 gtk_text_buffer_move_mark_by_name(bfwin->current_document->buffer, "selection_bound", &itend);
3935 }
3936
3937 void
doc_toggle_highlighting(Tbfwin * bfwin,gboolean active)3938 doc_toggle_highlighting(Tbfwin * bfwin, gboolean active)
3939 {
3940 bfwin->current_document->highlightstate = active;
3941 DEBUG_MSG("doc_toggle_highlighting_cb, set enable_scanner=%d\n", bfwin->current_document->highlightstate);
3942 BLUEFISH_TEXT_VIEW(bfwin->current_document->view)->enable_scanner =
3943 bfwin->current_document->highlightstate;
3944 if (active) {
3945 bluefish_text_view_rescan(BLUEFISH_TEXT_VIEW(bfwin->current_document->view));
3946 } else {
3947 bluefish_text_view_scan_cleanup(BLUEFISH_TEXT_VIEW(bfwin->current_document->view));
3948 }
3949 }
3950
3951 /**
3952 * all_documents_apply_settings:
3953 *
3954 * applies changes from the preferences to all documents
3955 *
3956 * Return value: void
3957 */
3958 void
all_documents_apply_settings()3959 all_documents_apply_settings()
3960 {
3961 GList *tmplist = g_list_first(return_allwindows_documentlist());
3962 PangoFontDescription *font_desc;
3963 font_desc = pango_font_description_from_string(main_v->props.editor_font_string);
3964 while (tmplist) {
3965 Tdocument *doc = tmplist->data;
3966 const gchar *tmpstr;
3967 bluefish_text_view_set_font(BLUEFISH_TEXT_VIEW(doc->view), font_desc);
3968 bluefish_text_view_set_colors(BLUEFISH_TEXT_VIEW(doc->view), main_v->props.btv_color_str);
3969 doc_recalculate_right_margin(doc);
3970 if (gtk_text_view_get_wrap_mode(GTK_TEXT_VIEW(doc->view)) != GTK_WRAP_NONE) {
3971 doc_set_wrap(doc, TRUE);
3972 }
3973 tmpstr = gtk_label_get_text(GTK_LABEL(doc->tab_label));
3974 tab_label_set_string(doc, tmpstr);
3975 tmplist = g_list_next(tmplist);
3976 }
3977 pango_font_description_free(font_desc);
3978 }
3979
3980 void
doc_word_count(Tbfwin * bfwin)3981 doc_word_count(Tbfwin * bfwin)
3982 {
3983 gint start = 0, end = -1;
3984 gboolean has_selection;
3985 guint chars = 0, lines = 0, words = 0;
3986 gchar *allchars, *wc_message, *tmp1, *tmp2, *tmp3;
3987
3988 has_selection = doc_get_selection(CURDOC(bfwin), &start, &end);
3989 allchars = doc_get_chars(bfwin->current_document, start, end);
3990
3991 wordcount(allchars, &chars, &lines, &words);
3992 g_free(allchars);
3993 tmp1 = g_strdup_printf(ngettext("%d line", "%d lines", lines), lines);
3994 tmp2 = g_strdup_printf(ngettext("%d word", "%d words", words), words);
3995 tmp3 = g_strdup_printf(ngettext("%d character", "%d characters", chars), chars);
3996 wc_message =
3997 g_strconcat(has_selection ? _("Selection statistics: ") : _("Statistics: "), tmp1, ", ", tmp2, ", ",
3998 tmp3, NULL);
3999 bfwin_statusbar_message(bfwin, wc_message, 7);
4000 g_free(wc_message);
4001 g_free(tmp1);
4002 g_free(tmp2);
4003 g_free(tmp3);
4004 }
4005
4006 /**
4007 * doc_indent_selection:
4008 * @doc: a #Tdocument*
4009 * @unindent: #gboolean
4010 *
4011 * Indent the selected block in current document.
4012 * Set unindent to TRUE to unindent.
4013 *
4014 * Return value: void
4015 **/
4016 void
doc_indent_selection(Tdocument * doc,gboolean unindent)4017 doc_indent_selection(Tdocument * doc, gboolean unindent)
4018 {
4019 GtkTextIter itstart, itend;
4020 if (gtk_text_buffer_get_selection_bounds(doc->buffer, &itstart, &itend)) {
4021 GtkTextMark *end;
4022 /* gboolean firstrun=TRUE;*/
4023
4024 doc_block_undo_reg(doc);
4025 doc_unre_new_group(doc);
4026 /* we have a selection, now we loop trough the characters, and for every newline
4027 we add or remove a tab, we set the end with a mark */
4028 end = gtk_text_buffer_create_mark(doc->buffer, NULL, &itend, TRUE);
4029 if (gtk_text_iter_get_line_offset(&itstart) > 0) {
4030 gtk_text_iter_set_line_index(&itstart, 0);
4031 }
4032 while (gtk_text_iter_compare(&itstart, &itend) < 0) {
4033 GtkTextMark *cur;
4034 /* if (firstrun && !gtk_text_iter_starts_line(&itstart)) {
4035 gtk_text_iter_forward_line(&itstart);
4036 }
4037 firstrun = FALSE;*/
4038 cur = gtk_text_buffer_create_mark(doc->buffer, NULL, &itstart, TRUE);
4039 if (unindent) {
4040 /* when unindenting we try to set itend to the end of the indenting step
4041 which might be a tab or 'tabsize' spaces, then we delete that part */
4042 gboolean cont = TRUE;
4043 gchar *buf = NULL;
4044 gunichar cchar = gtk_text_iter_get_char(&itstart);
4045 if (cchar == 9) { /* 9 is ascii for tab */
4046 itend = itstart;
4047 cont = gtk_text_iter_forward_char(&itend);
4048 buf = g_strdup("\t");
4049 } else if (cchar == 32) { /* 32 is ascii for space */
4050 gint i = 0;
4051 itend = itstart;
4052 gtk_text_iter_forward_chars(&itend, BFWIN(doc->bfwin)->session->editor_tab_width);
4053 buf = gtk_text_buffer_get_text(doc->buffer, &itstart, &itend, TRUE);
4054 DEBUG_MSG("tab_width=%d, strlen(buf)=%zd, buf='%s'\n",
4055 BFWIN(doc->bfwin)->session->editor_tab_width, strlen(buf), buf);
4056 while (cont && buf[i] != '\0') {
4057 cont = (buf[i] == ' ');
4058 DEBUG_MSG("doc_indent_selection, buf[%d]='%c'\n", i, buf[i]);
4059 i++;
4060 }
4061 if (!cont) {
4062 g_free(buf);
4063 }
4064 } else {
4065 cont = FALSE;
4066 }
4067 if (cont) {
4068 gint offsetstart, offsetend;
4069 offsetstart = gtk_text_iter_get_offset(&itstart);
4070 offsetend = gtk_text_iter_get_offset(&itend);
4071 gtk_text_buffer_delete(doc->buffer, &itstart, &itend);
4072 doc_unre_add(doc, buf, offsetstart, offsetend, UndoDelete);
4073 g_free(buf);
4074 }
4075 #ifdef DEBUG
4076 else {
4077 DEBUG_MSG("doc_indent_selection, NOT continue!!\n");
4078 }
4079 #endif
4080 } else { /* indent */
4081 gint offsetstart = gtk_text_iter_get_offset(&itstart);
4082 gchar *indentstring;
4083 gint indentlen;
4084 if (BFWIN(doc->bfwin)->session->editor_indent_wspaces) {
4085 indentstring = bf_str_repeat(" ", BFWIN(doc->bfwin)->session->editor_tab_width);
4086 indentlen = BFWIN(doc->bfwin)->session->editor_tab_width;
4087 } else {
4088 indentstring = g_strdup("\t");
4089 indentlen = 1;
4090 }
4091 gtk_text_buffer_insert(doc->buffer, &itstart, indentstring, indentlen);
4092 doc_unre_add(doc, indentstring, offsetstart, offsetstart + indentlen, UndoInsert);
4093 g_free(indentstring);
4094 }
4095 gtk_text_buffer_get_iter_at_mark(doc->buffer, &itstart, cur);
4096 gtk_text_buffer_get_iter_at_mark(doc->buffer, &itend, end);
4097 gtk_text_buffer_delete_mark(doc->buffer, cur);
4098 gtk_text_iter_forward_line(&itstart);
4099 DEBUG_MSG("doc_indent_selection, itstart at %d, itend at %d\n",
4100 gtk_text_iter_get_offset(&itstart), gtk_text_iter_get_offset(&itend));
4101 }
4102 gtk_text_buffer_delete_mark(doc->buffer, end);
4103 doc_unblock_undo_reg(doc);
4104 doc_set_modified(doc, 1);
4105 } else {
4106 /* there is no selection, work on the current line */
4107 GtkTextIter iter;
4108 gtk_text_buffer_get_iter_at_mark(doc->buffer, &iter, gtk_text_buffer_get_insert(doc->buffer));
4109 gtk_text_iter_set_line_offset(&iter, 0);
4110 if (unindent) {
4111 gint deletelen = 0;
4112 gchar *tmpstr, *tmp2str;
4113 GtkTextIter itend = iter;
4114 gtk_text_iter_forward_chars(&itend, BFWIN(doc->bfwin)->session->editor_tab_width);
4115 tmpstr = gtk_text_buffer_get_text(doc->buffer, &iter, &itend, TRUE);
4116 tmp2str = bf_str_repeat(" ", BFWIN(doc->bfwin)->session->editor_tab_width);
4117 if (tmpstr[0] == '\t') {
4118 deletelen = 1;
4119 } else if (tmpstr && strncmp(tmpstr, tmp2str, BFWIN(doc->bfwin)->session->editor_tab_width) == 0) {
4120 deletelen = BFWIN(doc->bfwin)->session->editor_tab_width;
4121 }
4122 g_free(tmpstr);
4123 g_free(tmp2str);
4124 if (deletelen) {
4125 itend = iter;
4126 gtk_text_iter_forward_chars(&itend, deletelen);
4127 gtk_text_buffer_delete(doc->buffer, &iter, &itend);
4128 }
4129 } else { /* indent */
4130 gchar *indentstring;
4131 gint indentlen;
4132 if (BFWIN(doc->bfwin)->session->editor_indent_wspaces) {
4133 indentstring = bf_str_repeat(" ", BFWIN(doc->bfwin)->session->editor_tab_width);
4134 indentlen = BFWIN(doc->bfwin)->session->editor_tab_width;
4135 } else {
4136 indentstring = g_strdup("\t");
4137 indentlen = 1;
4138 }
4139 gtk_text_buffer_insert(doc->buffer, &iter, indentstring, indentlen);
4140 g_free(indentstring);
4141 }
4142 }
4143 }
4144
4145 /**
4146 * list_relative_document_filenames:
4147 * @curdoc: #Tdocument: the current document
4148 *
4149 * this function will generate a stringlist with a relative links to
4150 * all other open documents. This list should be freed using free_stringlist()
4151 *
4152 * Return value: #GList with strings
4153 */
4154 GList *
list_relative_document_filenames(Tdocument * curdoc)4155 list_relative_document_filenames(Tdocument * curdoc)
4156 {
4157 GList *tmplist, *retlist = NULL;
4158 gchar *curi;
4159 if (curdoc->uri == NULL) {
4160 return NULL;
4161 }
4162 curi = g_file_get_uri(curdoc->uri);
4163
4164 tmplist = g_list_first(BFWIN(curdoc->bfwin)->documentlist);
4165 while (tmplist) {
4166 Tdocument *tmpdoc = tmplist->data;
4167 if (tmpdoc != curdoc && tmpdoc->uri != NULL) {
4168 gchar *tmp = g_file_get_uri(tmpdoc->uri);
4169 retlist = g_list_prepend(retlist, create_relative_link_to(curi, tmp));
4170 g_free(tmp);
4171 }
4172 tmplist = g_list_next(tmplist);
4173 }
4174 g_free(curi);
4175 return retlist;
4176 }
4177
4178 static gchar *
doc_text_under_cursor(Tdocument * doc,gint * context)4179 doc_text_under_cursor(Tdocument * doc, gint * context)
4180 {
4181 GtkTextIter iter;
4182 /* GSList *taglist, *tmplist; */
4183 gchar *retval = NULL;
4184 gint len;
4185 /* GtkTextIter so,eo; */
4186 gtk_text_buffer_get_iter_at_mark(doc->buffer, &iter, gtk_text_buffer_get_insert(doc->buffer));
4187
4188 /* taglist = gtk_text_iter_get_tags(&iter);
4189 for (tmplist=taglist;tmplist;tmplist=tmplist->next) {
4190 GtkTextTag *tag=tmplist->data;
4191 / * avoid tags like needscanning, folded, blockheader and such * /
4192 if (!langmgr_in_highlight_tags(tag))
4193 continue;
4194 so=eo=iter;
4195 if (!gtk_text_iter_begins_tag(&so, tag))
4196 gtk_text_iter_backward_to_tag_toggle(&so, tag);
4197 if (!gtk_text_iter_ends_tag(&eo, tag))
4198 gtk_text_iter_forward_to_tag_toggle(&eo, tag);
4199 / * use the smallest string * /
4200 if (retval && g_utf8_strlen(retval,-1) > (gtk_text_iter_get_offset(&eo)-gtk_text_iter_get_offset(&so))) {
4201 g_free(retval);
4202 retval=NULL;
4203 }
4204 if (!retval)
4205 retval = gtk_text_buffer_get_text(doc->buffer, &so,&eo,TRUE);
4206 }*/
4207
4208 if (!retval)
4209 retval = bf_get_identifier_at_iter(BLUEFISH_TEXT_VIEW(doc->view), &iter, context);
4210
4211 if (!retval)
4212 return NULL;
4213
4214 /* remove any surrounding quotes */
4215 len = strlen(retval);
4216 if (retval[0] == '"' && retval[len - 1] == '"') {
4217 memmove(retval, retval + 1, len - 2);
4218 retval[len - 2] = '\0';
4219 } else if (retval[0] == '\'' && retval[len - 1] == '\'') {
4220 memmove(retval, retval + 1, len - 2);
4221 retval[len - 2] = '\0';
4222 }
4223 return retval;
4224 }
4225
4226 typedef struct {
4227 GFile *uri;
4228 Tdocument *doc;
4229 } Tjumpcheckfile;
4230
4231 static void
doc_jump_query_exists_lcb(GObject * source_object,GAsyncResult * res,gpointer user_data)4232 doc_jump_query_exists_lcb(GObject * source_object, GAsyncResult * res, gpointer user_data)
4233 {
4234 GFileInfo *finfo;
4235 Tjumpcheckfile *jcf = user_data;
4236 GError *gerror = NULL;
4237
4238 finfo = g_file_query_info_finish(jcf->uri, res, &gerror);
4239 if (gerror) {
4240 DEBUG_MSG("%s\n", gerror->message);
4241 g_error_free(gerror);
4242 }
4243 if (finfo) {
4244 doc_new_from_uri(jcf->doc->bfwin, jcf->uri, finfo, FALSE, FALSE, -1, -1, -1, TRUE, FALSE);
4245 g_object_unref(finfo);
4246 }
4247 g_object_unref(jcf->uri);
4248 g_slice_free(Tjumpcheckfile, jcf);
4249 }
4250
4251 static void
doc_jump_check_file(Tdocument * doc,const gchar * filename)4252 doc_jump_check_file(Tdocument * doc, const gchar * filename)
4253 {
4254 Tjumpcheckfile *jcf;
4255 jcf = g_slice_new(Tjumpcheckfile);
4256 jcf->doc = doc;
4257 if (!doc->uri || (filename[0] == '/' || strncmp(filename, "file://", 7) == 0)) {
4258 jcf->uri = g_file_new_for_commandline_arg(filename);
4259 } else {
4260 gchar *tmp;
4261 GFile *parent = g_file_get_parent(doc->uri);
4262 tmp = g_uri_unescape_string(filename, NULL);
4263 jcf->uri = g_file_resolve_relative_path(parent, tmp);
4264 g_free(tmp);
4265 g_object_unref(parent);
4266 }
4267 /* as for BF_FILEINFO, if the file is not yet open, we can re-use the finfo for the to open document */
4268 g_file_query_info_async(jcf->uri, BF_FILEINFO, 0, G_PRIORITY_HIGH, NULL, doc_jump_query_exists_lcb, jcf);
4269 }
4270
4271 void
doc_jump(Tdocument * doc)4272 doc_jump(Tdocument * doc)
4273 {
4274 gchar *string;
4275 gint context = -1;
4276 /* see what's under the cursor */
4277 string = doc_text_under_cursor(doc, &context);
4278 if (!string)
4279 return;
4280 DEBUG_MSG("doc_jump, got string %s\n", string);
4281 /* check if this is an existing file */
4282 doc_jump_check_file(doc, string);
4283
4284 DEBUG_MSG("context=%d\n", context);
4285 if (context != -1) {
4286 Tjumpdata *ijd =
4287 bftextview2_lookup_identifier(doc->bfwin, BLUEFISH_TEXT_VIEW(doc->view), context, string);
4288 if (ijd) {
4289 DEBUG_MSG("got doc=%p (index=%d), line=%d\n", ijd->doc,
4290 g_list_index(BFWIN(doc->bfwin)->documentlist, ijd->doc), ijd->line);
4291 if (ijd->doc && g_list_index(BFWIN(doc->bfwin)->documentlist, ijd->doc) != -1) {
4292 DEBUG_MSG("jump! doc=%p, line=%d\n", ijd->doc, ijd->line);
4293 bfwin_switch_to_document_by_pointer(doc->bfwin, ijd->doc);
4294 doc_select_line(ijd->doc, ijd->line, TRUE);
4295 }
4296 }
4297 }
4298 g_free(string);
4299 }
4300
4301 void
doc_jump_matching_block_boundary(Tdocument * doc)4302 doc_jump_matching_block_boundary(Tdocument * doc)
4303 {
4304 GtkTextIter it1, it2, it3, it4, location;
4305 guint offset;
4306 gpointer haveblock;
4307 gtk_text_buffer_get_iter_at_mark(doc->buffer, &location, gtk_text_buffer_get_insert(doc->buffer));
4308 offset = gtk_text_iter_get_offset(&location);
4309 haveblock =
4310 bftextview2_get_block_at_boundary_location(BLUEFISH_TEXT_VIEW(doc->view), offset, &it1, &it2, &it3,
4311 &it4);
4312 if (!haveblock)
4313 return;
4314 if (gtk_text_iter_equal(&location, &it1)) {
4315 gtk_text_buffer_place_cursor(doc->buffer, &it4);
4316 } else if (gtk_text_iter_equal(&location, &it2)) {
4317 gtk_text_buffer_place_cursor(doc->buffer, &it3);
4318 } else if (gtk_text_iter_equal(&location, &it3)) {
4319 gtk_text_buffer_place_cursor(doc->buffer, &it2);
4320 } else if (gtk_text_iter_equal(&location, &it4)) {
4321 gtk_text_buffer_place_cursor(doc->buffer, &it1);
4322 }
4323 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(doc->view), gtk_text_buffer_get_insert(doc->buffer));
4324 }
4325
4326 static void
floatingview_destroy_lcb(GtkWidget * widget,Tdocument * doc)4327 floatingview_destroy_lcb(GtkWidget * widget, Tdocument * doc)
4328 {
4329 DEBUG_MSG("floatingview_destroy_lcb, called for doc=%p, doc->floatingview=%p\n", doc, doc->floatingview);
4330 if (doc->floatingview) {
4331 gtk_widget_destroy(FLOATINGVIEW(doc->floatingview)->window);
4332 g_free(doc->floatingview);
4333 doc->floatingview = NULL;
4334 }
4335 }
4336
4337 void
doc_floating_view_new(Tbfwin * bfwin)4338 doc_floating_view_new(Tbfwin * bfwin)
4339 {
4340 Tdocument *doc = bfwin->current_document;
4341 Tfloatingview *fv;
4342 gchar *title;
4343 GtkWidget *scrolwin;
4344
4345 if (doc->slave)
4346 doc_split_view(doc, FALSE);
4347
4348 if (doc->floatingview) {
4349 fv = FLOATINGVIEW(doc->floatingview);
4350 gtk_window_present(GTK_WINDOW(fv->window));
4351 return;
4352 }
4353 fv = g_new(Tfloatingview, 1);
4354 doc->floatingview = fv;
4355 DEBUG_MSG("new_floatingview for doc=%p is at %p\n", doc, doc->floatingview);
4356 title = (doc->uri) ? g_file_get_uri(doc->uri) : g_strdup("Untitled");
4357 fv->window =
4358 window_full2(title, GTK_WIN_POS_NONE, 5, G_CALLBACK(floatingview_destroy_lcb), doc, TRUE, NULL);
4359 g_free(title);
4360 gtk_window_set_role(GTK_WINDOW(fv->window), "floatingview");
4361 fv->textview = bftextview2_new_slave(BLUEFISH_TEXT_VIEW(doc->view));
4362 apply_font_style(fv->textview, main_v->props.editor_font_string);
4363 scrolwin = gtk_scrolled_window_new(NULL, NULL);
4364 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
4365 gtk_container_add(GTK_CONTAINER(scrolwin), fv->textview);
4366 gtk_container_add(GTK_CONTAINER(fv->window), scrolwin);
4367 gtk_window_set_default_size(GTK_WINDOW(fv->window), 600, 600);
4368 gtk_widget_show_all(fv->window);
4369 }
4370