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