1 /* Bluefish HTML Editor
2  * doc_text_tools.c - text tools
3  *
4  * Copyright (C) 2008-2017 Olivier Sessink
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 /*#define DEBUG*/
20 
21 #include "bluefish.h"
22 #include "bf_lib.h"
23 #include "gtk_easy.h"
24 #include "dialog_utils.h"
25 #include "document.h"
26 #include "undo_redo.h"
27 #include "stringlist.h"
28 
29 /* special replace: strip trailing spaces */
30 /* use a very simple loop, one that knows whitespace, non-whitespace and a newline */
31 void
strip_trailing_spaces(Tdocument * doc)32 strip_trailing_spaces(Tdocument * doc)
33 {
34 	gint i = 0, wstart = 0, coffset = 0;
35 	gint start, end;
36 	gchar *buf;
37 
38 	if (!doc_get_selection(doc, &start, &end)) {
39 		start = 0;
40 		end = -1;
41 	}
42 	buf = doc_get_chars(doc, start, end);
43 	coffset = start;
44 
45 	doc_unre_new_group(doc);
46 	while (buf[i] != '\0') {
47 		switch (buf[i]) {
48 		case ' ':
49 		case '\t':
50 			/* do nothing */
51 			break;
52 		case '\n':
53 			if (wstart + 1 < i) {
54 				gint cstart, cend;
55 				cstart = utf8_byteoffset_to_charsoffset_cached(buf, wstart + 1);
56 				cend = utf8_byteoffset_to_charsoffset_cached(buf, i);
57 				doc_replace_text_backend(doc, "", cstart + coffset, cend + coffset);
58 				coffset -= (cend - cstart);
59 			}
60 			/* no break, fall trough */
61 		default:
62 			wstart = i;
63 			break;
64 		}
65 		i++;
66 	}
67 	g_free(buf);
68 	doc_unre_new_group(doc);
69 }
70 
71 static gint
join_lines_backend(Tdocument * doc,gint start,gint end)72 join_lines_backend(Tdocument * doc, gint start, gint end)
73 {
74 	gint i = 0, cstart, cend, coffset;
75 	gchar *buf;
76 	gboolean in_split = FALSE;
77 	gint so_line_split = 0, eo_line_split = 0;
78 
79 	coffset = start; /* the offset in 'buf' compared to the gtktextbuffer */
80 	buf = doc_get_chars(doc, start, end);
81 	utf8_offset_cache_reset();
82 	DEBUG_MSG("join_lines_backend, from %d:%d\n",start,end);
83 	while (buf[i] != '\0') {
84 		if (in_split) {
85 			if (buf[i] == '\n' || buf[i] == '\r') {
86 				in_split = FALSE;
87 				DEBUG_MSG("join_lines, don't join empty line at byte %d\n", i);
88 			} else if (buf[i] != '\t' && buf[i] != ' ') {
89 				eo_line_split = i;
90 				in_split = FALSE;
91 				cstart = utf8_byteoffset_to_charsoffset_cached(buf, so_line_split);
92 				cend = utf8_byteoffset_to_charsoffset_cached(buf, eo_line_split);
93 				DEBUG_MSG("join_lines, replace from %d to %d, coffset=%d\n", cstart + coffset, cend + coffset,
94 						  coffset);
95 				doc_replace_text_backend(doc, " ", cstart + coffset, cend + coffset);
96 				coffset += (1 - (cend - cstart));
97 			}
98 		} else {
99 			if (buf[i] == '\n') {
100 				so_line_split = i;
101 				in_split = TRUE;
102 			} else if (buf[i] == '\r') {
103 				so_line_split = i;
104 				in_split = TRUE;
105 				if (buf[i + 1] == '\n')
106 					i++;
107 			}
108 		}
109 		i++;
110 	}
111 	g_free(buf);
112 	DEBUG_MSG("join_lines_backend, return offset %d\n",coffset-start);
113 	return coffset - start;
114 }
115 
116 void
join_lines(Tdocument * doc)117 join_lines(Tdocument * doc)
118 {
119 	gint start, end;
120 	if (!doc_get_selection(doc, &start, &end)) {
121 		start = 0;
122 		end = -1;
123 	}
124 	doc_unre_new_group(doc);
125 	join_lines_backend(doc, start, end);
126 	doc_unre_new_group(doc);
127 }
128 
129 #define WS_POS_UNDEFINED G_MAXINT
130 static void
split_lines_backend(Tdocument * doc,gint start,gint end)131 split_lines_backend(Tdocument * doc, gint start, gint end)
132 {
133 	gint coffset = 0; /* the offset that we have introduced since the start of this function call */
134 	gint count = 0, tabsize;
135 	gint startws = 0, endws = 0, starti = start, endi = -1, requested_size;
136 	/* ws= whitespace, i=indenting, these are character positions in the GtkTextBufferr !!!!!!!! */
137 	gint charpos;
138 	gchar *buf, *p;
139 	gunichar c;
140 
141 	tabsize = doc_get_tabsize(doc);
142 	p = buf = doc_get_chars(doc, start, end);
143 	utf8_offset_cache_reset();
144 	requested_size = main_v->props.right_margin_pos;
145 	coffset = 0;
146 	charpos = start;
147 	DEBUG_MSG("split_lines_backend, from %d:%d on right margin %d\n",start,end, requested_size);
148 	c = g_utf8_get_char(p);
149 	while (c != '\0') {
150 #ifdef DEBUGSPLIT
151 		g_print("%d '%c'\n",charpos,c<128?c:'Q');
152 #endif
153 		if (count > requested_size && startws!=WS_POS_UNDEFINED) {
154 			gchar *new_indenting, *tmp1, *tmp2;
155 			if (startws >= endws)
156 				endws = charpos;
157 			DEBUG_MSG("split_lines, count=%d(>%d), startws=%d, endws=%d, coffset=%d c='%c'\n", count,requested_size, startws,
158 					  endws, coffset, c);
159 			if (starti == endi || endi==-1) {
160 				new_indenting = g_strdup("\n");
161 			} else {
162 				tmp1 = buf+utf8_byteoffset_to_charsoffset_cached(buf, starti-start);
163 				tmp2 = buf+utf8_byteoffset_to_charsoffset_cached(buf, endi-start);
164 				/*tmp1 = g_utf8_offset_to_pointer(buf, starti-start);
165 				tmp2 = g_utf8_offset_to_pointer(buf, endi-start);*/
166 				new_indenting = g_strndup(tmp1, (tmp2 - tmp1));
167 				DEBUG_MSG("split_lines_backend, starti=%d,endi=%d, len=%d, bytes=%d, new_indenting='%s'\n", starti, endi, endi-starti, (gint) (tmp2 - tmp1),
168 						  new_indenting);
169 			}
170 			DEBUG_MSG("split_lines_backend, replace from startws=%d to endws=%d with offset %d with new indenting\n", startws, endws, coffset);
171 			count = charpos - endws;
172 #ifdef DEBUGSPLIT
173 			tmp1 = doc_get_chars(doc, startws + coffset, endws + coffset);
174 			g_print("replace '%s' with newline + identing\n",tmp1);
175 			g_free(tmp1);
176 #endif
177 			doc_replace_text_backend(doc, new_indenting, startws + coffset, endws + coffset);
178 			coffset += (g_utf8_strlen(new_indenting, -1) - (endws - startws));
179 			DEBUG_MSG("split_lines_backend, new coffset=%d, new count=%d, set startws=%d and endws=%d\n", coffset, count, 0,charpos);
180 			startws = WS_POS_UNDEFINED;
181 			endws = WS_POS_UNDEFINED;
182 			g_free(new_indenting);
183 		}
184 		if (c == '\t') {
185 			count += tabsize;
186 			if (startws < endws || startws==WS_POS_UNDEFINED) {
187 				startws = charpos;
188 				endws = charpos;
189 				DEBUG_MSG("split_lines_backend, tab, set startws to %d\n", startws);
190 			}
191 		} else if (c == ' ') {
192 			count++;
193 			if (startws < endws || startws==WS_POS_UNDEFINED) {
194 				startws = charpos;
195 				endws = charpos;
196 				DEBUG_MSG("split_lines_backend, space, set startws to %d\n", startws);
197 			}
198 		} else if (c == '\n') {
199 			count = 0;
200 			starti = charpos;
201 			endws = startws = charpos + 1;
202 			DEBUG_MSG("split_lines_backend, newline, set starti=%d, endws=startws=%d\n", starti, endws);
203 		} else {
204 			count++;
205 			if (starti > endi) {
206 				endi = charpos;
207 				DEBUG_MSG("split_lines_backend, non-whitespace (%c) and no valid endi, set endi to %d, starti=%d\n", c<128?c:'Q',endi, starti);
208 			} else if (startws >= endws && startws!=WS_POS_UNDEFINED) {
209 				endws = charpos;
210 				DEBUG_MSG("split_lines_backend, non-whitespace (%c) and no valid endws, set endws to %d\n", c<128?c:'Q', endws);
211 			}
212 		}
213 		p = g_utf8_next_char(p);
214 		charpos++;
215 		c = g_utf8_get_char(p);
216 	}
217 	g_free(buf);
218 }
219 
220 void
split_lines(Tdocument * doc)221 split_lines(Tdocument * doc)
222 {
223 	gint start, end;
224 	if (!doc_get_selection(doc, &start, &end)) {
225 		start = 0;
226 		end = -1;
227 	}
228 	doc_unre_new_group(doc);
229 	split_lines_backend(doc, start, end);
230 	doc_unre_new_group(doc);
231 }
232 
233 
234 void
rewrap_lines(Tdocument * doc)235 rewrap_lines(Tdocument * doc)
236 {
237 	gint start, end, offset;
238 	if (!doc_get_selection(doc, &start, &end)) {
239 		start = 0;
240 		end = -1;
241 	}
242 	doc_unre_new_group(doc);
243 	offset = join_lines_backend(doc, start, end);
244 	DEBUG_MSG("rewrap_lines, offset=%d\n",offset);
245 	split_lines_backend(doc, start, end == -1 ? end : end+offset);
246 	doc_unre_new_group(doc);
247 }
248 
249 /* from spaces to tabs or from tabs to spaces */
250 void
convert_identing(Tdocument * doc,gboolean to_tabs)251 convert_identing(Tdocument * doc, gboolean to_tabs)
252 {
253 	gint i = 0, wstart = 0, coffset = 0, indenting = 0, tabsize;
254 	gchar *buf = doc_get_chars(doc, 0, -1);
255 
256 	utf8_offset_cache_reset();
257 	tabsize = doc_get_tabsize(doc);
258 	/*g_print("got tabsize %d\n",tabsize); */
259 	doc_unre_new_group(doc);
260 	while (buf[i] != '\0') {
261 		switch (buf[i]) {
262 		case '\n':
263 			wstart = i;
264 			indenting = 0;
265 			break;
266 		case '\t':
267 			/* a tab increases to the next tab stop */
268 			indenting = ((indenting / tabsize) + 1) * tabsize;
269 			break;
270 		case ' ':
271 			indenting += 1;
272 			break;
273 		default:
274 			if (wstart != -1 && indenting > 0 && (wstart + 1 != i)) {
275 				gchar *newindent;
276 				gint cstart, cend;
277 				if (to_tabs) {
278 					newindent = bf_str_repeat("\t", (indenting / tabsize));
279 				} else {
280 					newindent = bf_str_repeat(" ", indenting);
281 				}
282 				cstart = utf8_byteoffset_to_charsoffset_cached(buf, wstart + 1);
283 				cend = utf8_byteoffset_to_charsoffset_cached(buf, i);
284 				doc_replace_text_backend(doc, newindent, cstart + coffset, cend + coffset);
285 				coffset += strlen(newindent) - (cend - cstart);
286 				g_free(newindent);
287 			}
288 			wstart = -1;
289 			break;
290 		}
291 		i++;
292 	}
293 	g_free(buf);
294 	doc_unre_new_group(doc);
295 }
296 
297 static void
convert_to_columns_backend(Tdocument * doc,gint so,gint eo,gint numcolumns,gboolean spread_horiz,const gchar * separator,const gchar * fillempty)298 convert_to_columns_backend(Tdocument * doc, gint so, gint eo, gint numcolumns, gboolean spread_horiz,
299 						   const gchar * separator, const gchar * fillempty)
300 {
301 	gint numlines, numnewlines, i = 0, j = 0;
302 	gchar *buf;
303 	GList *buflist;
304 	guint offset = 0, separatorlen;
305 	/* get buffer */
306 	buf = doc_get_chars(doc, so, eo);
307 	utf8_offset_cache_reset();
308 	/* buffer to list */
309 	buflist = get_list_from_buffer(buf, NULL, FALSE);
310 	g_free(buf);
311 	/* get the number of lines */
312 	numlines = g_list_length(buflist);
313 	numnewlines = (0.99999999 + 1.0 * numlines / numcolumns);
314 /*	g_print("float=%f, int=%d\n",0.9999+1.0*numlines/numcolumns, (int)(0.9999+1.0*numlines/numcolumns));*/
315 	/*g_print("numlines=%d, numcolumns=%d, numnewlines=%d\n",numlines,numcolumns,numnewlines); */
316 	separatorlen = g_utf8_strlen(separator, -1);
317 	doc_unre_new_group(doc);
318 	doc_replace_text_backend(doc, NULL, so, eo);
319 	for (i = 0; i < numnewlines; i++) {
320 		for (j = 0; j < numcolumns; j++) {
321 			gchar *tmp;
322 			if (spread_horiz) {
323 				/*g_print("i=%d,j=%d,numcolumns=%d, insert string i*numcolumns+j %d\n",i,j,numcolumns,i*numcolumns+j); */
324 				tmp = g_list_nth_data(buflist, i * numcolumns + j);
325 			} else {
326 				/*g_print("i=%d,j=%d,numnewlines=%d, insert string i+j*numnewlines %d\n",i,j,numnewlines,i+j*numnewlines); */
327 				tmp = g_list_nth_data(buflist, i + j * numnewlines);
328 			}
329 			if (tmp) {
330 				doc_replace_text_backend(doc, tmp, so + offset, so + offset);
331 				offset += g_utf8_strlen(tmp, -1);
332 			} else {
333 				doc_replace_text_backend(doc, fillempty, so + offset, so + offset);
334 				offset += g_utf8_strlen(fillempty, -1);
335 			}
336 			if (j + 1 == numcolumns) {
337 				/*g_print("j=%d, numcolumns=%d, j+1==numcolumns, newline!\n",j,numcolumns); */
338 				doc_replace_text_backend(doc, "\n", so + offset, so + offset);
339 				offset += 1;
340 			} else {
341 				doc_replace_text_backend(doc, separator, so + offset, so + offset);
342 				offset += separatorlen;
343 			}
344 		}
345 
346 	}
347 	doc_unre_new_group(doc);
348 	free_stringlist(buflist);
349 }
350 
351 typedef struct {
352 	Tdocument *doc;
353 	GtkWidget *dialog;
354 	GtkWidget *horizontally;
355 	GtkWidget *separator;
356 	GtkWidget *numcolumns;
357 	GtkWidget *fillempty;
358 } Tconvertcolumn;
359 
360 static void
convert_to_columns_lcb(GtkDialog * dialog,gint response,Tconvertcolumn * cc)361 convert_to_columns_lcb(GtkDialog * dialog, gint response, Tconvertcolumn * cc)
362 {
363 	if (response == GTK_RESPONSE_ACCEPT) {
364 		gint start, end;
365 		const gchar *separator, *fillempty;
366 
367 		if (!doc_get_selection(cc->doc, &start, &end)) {
368 			start = 0;
369 			end = -1;
370 		}
371 
372 		separator = gtk_entry_get_text(GTK_ENTRY(cc->separator));
373 		fillempty = gtk_entry_get_text(GTK_ENTRY(cc->fillempty));
374 		if (BFWIN(cc->doc->bfwin)->session->convertcolumn_separator)
375 			g_free(BFWIN(cc->doc->bfwin)->session->convertcolumn_separator);
376 		if (BFWIN(cc->doc->bfwin)->session->convertcolumn_fillempty)
377 			g_free(BFWIN(cc->doc->bfwin)->session->convertcolumn_fillempty);
378 		BFWIN(cc->doc->bfwin)->session->convertcolumn_separator = g_strdup(separator);
379 		BFWIN(cc->doc->bfwin)->session->convertcolumn_fillempty = g_strdup(fillempty);
380 		BFWIN(cc->doc->bfwin)->session->convertcolumn_horizontally =
381 			gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc->horizontally));
382 
383 		convert_to_columns_backend(cc->doc, start, end,
384 								   gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(cc->numcolumns)),
385 								   gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc->horizontally)),
386 								   separator, fillempty);
387 	}
388 	gtk_widget_destroy(cc->dialog);
389 	g_free(cc);
390 }
391 
392 void
convert_to_columns(Tdocument * doc)393 convert_to_columns(Tdocument * doc)
394 {
395 	Tconvertcolumn *cc;
396 	GtkWidget *content_area, *hbox, *table;
397 
398 	cc = g_new0(Tconvertcolumn, 1);
399 	cc->doc = doc;
400 	cc->dialog = gtk_dialog_new_with_buttons(_("Lines into columns"),
401 											 GTK_WINDOW(BFWIN(doc->bfwin)->main_window),
402 											 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL,
403 											 GTK_RESPONSE_REJECT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
404 	g_signal_connect(G_OBJECT(cc->dialog), "response", G_CALLBACK(convert_to_columns_lcb), cc);
405 	window_delete_on_escape(GTK_WINDOW(cc->dialog));
406 	gtk_container_set_border_width(GTK_CONTAINER(cc->dialog), 10);
407 	content_area = gtk_dialog_get_content_area(GTK_DIALOG(cc->dialog));
408 	gtk_box_set_spacing(GTK_BOX(content_area), 10);
409 
410 	cc->numcolumns = spinbut_with_value("2", 2, 99, 1, 5);
411 	hbox = gtk_hbox_new(FALSE, 5);
412 	gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("Number of columns:")), FALSE, FALSE, 0);
413 	gtk_box_pack_start(GTK_BOX(hbox), cc->numcolumns, FALSE, FALSE, 0);
414 	gtk_box_pack_start(GTK_BOX(content_area), hbox, FALSE, FALSE, 0);
415 	cc->horizontally =
416 		boxed_checkbut_with_value(_("Spread lines horizontally"),
417 								  BFWIN(cc->doc->bfwin)->session->convertcolumn_horizontally, content_area);
418 
419 	table = dialog_table_in_vbox_defaults(2, 2, 0, content_area);
420 
421 	cc->separator =
422 		dialog_entry_in_table(BFWIN(cc->doc->bfwin)->session->convertcolumn_separator, table, 1, 2, 0, 1);
423 	dialog_mnemonic_label_in_table(_("Column separator:"), cc->separator, table, 0, 1, 0, 1);
424 
425 	cc->fillempty =
426 		dialog_entry_in_table(BFWIN(cc->doc->bfwin)->session->convertcolumn_fillempty, table, 1, 2, 1, 2);
427 	dialog_mnemonic_label_in_table(_("Fill empty entries:"), cc->separator, table, 0, 1, 1, 2);
428 
429 	gtk_widget_show_all(cc->dialog);
430 }
431 
432 void
select_current_identifier(Tdocument * doc)433 select_current_identifier(Tdocument *doc)
434 {
435 	GtkTextIter cursor, so, eo;
436 	gtk_text_buffer_get_iter_at_mark(doc->buffer, &cursor, gtk_text_buffer_get_insert(doc->buffer));
437 	if (bluefish_text_view_get_active_identifier(BLUEFISH_TEXT_VIEW(doc->view), &cursor, &so, &eo)) {
438 		gtk_text_buffer_select_range(doc->buffer, &so, &eo);
439 	}
440 }
441 
442 void
select_between_matching_block_boundaries(Tdocument * doc)443 select_between_matching_block_boundaries(Tdocument *doc)
444 {
445 	GtkTextIter so,eo;
446 	static gboolean innerblock=FALSE;
447 	GtkTextIter cursor;
448 	guint offset;
449 	/* if we do not have a selection we start with innerblock is true, in all other
450 	cases we just revert innerblock */
451 	if (!gtk_text_buffer_get_has_selection(doc->buffer)) {
452 		innerblock = TRUE;
453 	} else {
454 		innerblock = !innerblock;
455 	}
456 
457 	gtk_text_buffer_get_iter_at_mark(doc->buffer, &cursor, gtk_text_buffer_get_insert(doc->buffer));
458 	DEBUG_MSG("select_between_matching_block_boundaries, innerblock=%d, location=%d\n", innerblock, gtk_text_iter_get_offset(&cursor));
459 	offset = gtk_text_iter_get_offset(&cursor);
460 	if (!bluefish_text_view_get_active_block_boundaries(BLUEFISH_TEXT_VIEW(doc->view),
461 					offset, innerblock, &so, &eo)) {
462 		DEBUG_MSG("select_between_matching_block_boundaries, no block, return\n");
463 		return;
464 	}
465 	if (innerblock && gtk_text_iter_equal(&so, &eo)) {
466 		DEBUG_MSG("select_between_matching_block_boundaries, iters are equal, request innerblock=FALSE\n");
467 		innerblock = FALSE;
468 		if (!bluefish_text_view_get_active_block_boundaries(BLUEFISH_TEXT_VIEW(doc->view),
469 						offset, innerblock, &so, &eo)) {
470 			DEBUG_MSG("select_between_matching_block_boundaries, innerblock=FALSE, no block, return\n");
471 			return;
472 		}
473 	}
474 	gtk_text_buffer_select_range(doc->buffer, &so, &eo);
475 }
476 
477 void
duplicate_line(Tdocument * doc)478 duplicate_line(Tdocument *doc)
479 {
480 	GtkTextIter it1, it2;
481 	gchar *text;
482 	gtk_text_buffer_get_iter_at_mark(doc->buffer, &it1, gtk_text_buffer_get_insert(doc->buffer));
483 	gtk_text_iter_set_line_offset(&it1,0);
484 	it2 = it1;
485 	gtk_text_iter_forward_line(&it2);
486 	text = gtk_text_buffer_get_text(doc->buffer,&it1,&it2,TRUE);
487 	doc_unre_new_group(doc);
488 	gtk_text_buffer_insert(doc->buffer,&it2,text,-1);
489 	g_free(text);
490 	doc_unre_new_group(doc);
491 }
492 
493 void
delete_line(Tdocument * doc)494 delete_line(Tdocument *doc)
495 {
496 	GtkTextIter it1, it2;
497 	gtk_text_buffer_get_iter_at_mark(doc->buffer, &it1, gtk_text_buffer_get_insert(doc->buffer));
498 	gtk_text_iter_set_line_offset(&it1,0);
499 	it2 = it1;
500 	gtk_text_iter_forward_line(&it2);
501 	doc_unre_new_group(doc);
502 	gtk_text_buffer_delete(doc->buffer, &it1,&it2);
503 	doc_unre_new_group(doc);
504 }
505 
506 void
doc_move_selection(Tdocument * doc,gboolean up,gboolean curline_if_no_selection)507 doc_move_selection(Tdocument *doc, gboolean up, gboolean curline_if_no_selection)
508 {
509 	GtkTextIter so, eo;
510 	gchar *text;
511 	gint offset, size;
512 	if (!gtk_text_buffer_get_selection_bounds(doc->buffer, &so, &eo)) {
513 		DEBUG_MSG("doc_move_selection, no selection, select the current line!\n");
514 		if (curline_if_no_selection) {
515 			gtk_text_buffer_get_iter_at_mark(doc->buffer,&so,gtk_text_buffer_get_insert(doc->buffer));
516 			eo = so;
517 		} else {
518 			return;
519 		}
520 	}
521 	/* so and eo are guaranteed to be in ascending order */
522 	if (gtk_text_iter_equal(&so, &eo)) {
523 		gtk_text_iter_forward_char(&eo);
524 	}
525 	if (!gtk_text_iter_starts_line(&so))
526 		gtk_text_iter_set_line_offset(&so, 0);
527 	if (!gtk_text_iter_starts_line(&eo))
528 		gtk_text_iter_forward_line(&eo);
529 
530 	doc_unre_new_group(doc);
531 	doc_block_undo_reg(doc);
532 
533 	offset = gtk_text_iter_get_offset(&so);
534 	DEBUG_MSG("start moving text from %d:%d\n",gtk_text_iter_get_offset(&so),gtk_text_iter_get_offset(&eo));
535 	size = gtk_text_iter_get_offset(&eo)-offset;
536 	text = gtk_text_buffer_get_text(doc->buffer,&so,&eo,TRUE);
537 	/*g_print("doc_move_selection, got selection %d:%d\n",offset,offset+size);*/
538 	gtk_text_buffer_delete(doc->buffer, &so, &eo);
539 	doc_unre_add(doc, text, offset, offset+size, UndoDelete);
540 
541 	/* now we have to move the cursor up,
542 	because we changed the text we invalidated all iters, so get them again */
543 	gtk_text_buffer_get_iter_at_offset(doc->buffer, &so, offset);
544 	if (up) {
545 		gtk_text_iter_backward_line(&so);
546 		DEBUG_MSG("UP -> new start location is at %d\n",gtk_text_iter_get_offset(&so));
547 	} else {
548 		gtk_text_iter_forward_line(&so);
549 		DEBUG_MSG("DOWN -> new start location is at %d\n",gtk_text_iter_get_offset(&so));
550 	}
551 	offset = gtk_text_iter_get_offset(&so);
552 	gtk_text_buffer_insert(doc->buffer,&so,text,-1);
553 	doc_unre_add(doc, text, offset, offset+size, UndoInsert);
554 	g_free(text);
555 
556 	doc_unblock_undo_reg(doc);
557 	doc_set_modified(doc, 1);
558 	doc_unre_new_group(doc);
559 
560 	/* and select the text again */
561 	DEBUG_MSG("doc_move_selection, select %d:%d\n",offset,offset+size);
562 	gtk_text_buffer_get_iter_at_offset(doc->buffer, &so, offset);
563 	gtk_text_buffer_get_iter_at_offset(doc->buffer, &eo, offset+size);
564 	DEBUG_MSG("select new location %d:%d\n",gtk_text_iter_get_offset(&so),gtk_text_iter_get_offset(&eo));
565 	gtk_text_buffer_select_range(doc->buffer,&so,&eo);
566 }
567 
568 void
doc_insert_filename(Tdocument * doc,gboolean relative)569 doc_insert_filename(Tdocument *doc, gboolean relative)
570 {
571 	gchar *tmp, *relativeto=NULL;
572 	if (relative && doc->uri) {
573 		relativeto = g_file_get_uri(doc->uri);
574 	}
575 	tmp = run_file_select_dialog(GTK_WINDOW(BFWIN(doc->bfwin)->main_window)
576 							, NULL, relativeto,GTK_FILE_CHOOSER_ACTION_OPEN);
577 	if (tmp)
578 		doc_insert_two_strings(doc, tmp, NULL);
579 	g_free(relativeto);
580 	g_free(tmp);
581 }
582