1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* This file is part of the GtkHTML library.
3 *
4 * Copyright (C) 1999, 2000 Helix Code, Inc.
5 * Copyright (C) 2001 Ximian, Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 * Authors: Ettore Perazzoli <ettore@helixcode.com>
23 * Radek Doulik <rodo@ximian.com>
24 */
25
26
27 #include <config.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <glib.h>
31
32 #include "gtkhtml.h"
33 #include "gtkhtml-properties.h"
34
35 #include "htmlclueflow.h"
36 #include "htmlcolorset.h"
37 #include "htmlcursor.h"
38 #include "htmlobject.h"
39 #include "htmltable.h"
40 #include "htmltablecell.h"
41 #include "htmltext.h"
42 #include "htmltextslave.h"
43 #include "htmlimage.h"
44 #include "htmlinterval.h"
45 #include "htmlselection.h"
46 #include "htmlsettings.h"
47 #include "htmlundo.h"
48
49 #include "htmlengine-edit.h"
50 #include "htmlengine-edit-cut-and-paste.h"
51 #include "htmlengine-edit-cursor.h"
52 #include "htmlengine-edit-movement.h"
53 #include "htmlengine-edit-selection-updater.h"
54 #include "htmlengine-edit-table.h"
55 #include "htmlengine-edit-tablecell.h"
56
57
58 void
html_engine_undo(HTMLEngine * e)59 html_engine_undo (HTMLEngine *e)
60 {
61 HTMLUndo *undo;
62
63 g_return_if_fail (e != NULL);
64 g_return_if_fail (HTML_IS_ENGINE (e));
65 g_return_if_fail (e->undo != NULL);
66 g_return_if_fail (e->editable);
67
68 html_engine_unselect_all (e);
69
70 undo = e->undo;
71 html_undo_do_undo (undo, e);
72 }
73
74 void
html_engine_redo(HTMLEngine * e)75 html_engine_redo (HTMLEngine *e)
76 {
77 HTMLUndo *undo;
78
79 g_return_if_fail (e != NULL);
80 g_return_if_fail (HTML_IS_ENGINE (e));
81 g_return_if_fail (e->undo != NULL);
82
83 html_engine_unselect_all (e);
84
85 undo = e->undo;
86 html_undo_do_redo (undo, e);
87 }
88
89
90 void
html_engine_set_mark(HTMLEngine * e)91 html_engine_set_mark (HTMLEngine *e)
92 {
93 g_return_if_fail (e != NULL);
94 g_return_if_fail (HTML_IS_ENGINE (e));
95 g_return_if_fail (e->editable || e->caret_mode);
96
97 if (e->mark != NULL) {
98 html_engine_unselect_all (e);
99 html_cursor_destroy (e->mark);
100 }
101
102 e->mark = html_cursor_dup (e->cursor);
103
104 html_engine_edit_selection_updater_reset (e->selection_updater);
105 html_engine_edit_selection_updater_schedule (e->selection_updater);
106 }
107
108 void
html_engine_clipboard_push(HTMLEngine * e)109 html_engine_clipboard_push (HTMLEngine *e)
110 {
111 e->clipboard_stack = g_list_prepend (e->clipboard_stack, GUINT_TO_POINTER (e->clipboard_len));
112 e->clipboard_stack = g_list_prepend (e->clipboard_stack, e->clipboard);
113 e->clipboard = NULL;
114 }
115
116 void
html_engine_clipboard_pop(HTMLEngine * e)117 html_engine_clipboard_pop (HTMLEngine *e)
118 {
119 g_assert (e->clipboard_stack);
120
121 e->clipboard = HTML_OBJECT (e->clipboard_stack->data);
122 e->clipboard_stack = g_list_remove (e->clipboard_stack, e->clipboard_stack->data);
123 e->clipboard_len = GPOINTER_TO_UINT (e->clipboard_stack->data);
124 e->clipboard_stack = g_list_remove (e->clipboard_stack, e->clipboard_stack->data);
125 }
126
127 typedef struct
128 {
129 gboolean active;
130 gint cursor;
131 gint mark;
132 } Selection;
133
134 void
html_engine_selection_push(HTMLEngine * e)135 html_engine_selection_push (HTMLEngine *e)
136 {
137 Selection *selection = g_new (Selection, 1);
138
139 if (html_engine_is_selection_active (e)) {
140 selection->active = TRUE;
141 selection->cursor = html_cursor_get_position (e->cursor);
142 selection->mark = html_cursor_get_position (e->mark);
143 } else {
144 selection->active = FALSE;
145 selection->cursor = -1;
146 selection->mark = -1;
147 }
148
149 e->selection_stack = g_list_prepend (e->selection_stack, selection);
150 }
151
152 void
html_engine_selection_pop(HTMLEngine * e)153 html_engine_selection_pop (HTMLEngine *e)
154 {
155 Selection *selection;
156
157 g_assert (e->selection_stack);
158
159 selection = e->selection_stack->data;
160 e->selection_stack = g_list_delete_link (e->selection_stack, e->selection_stack);
161
162 html_engine_disable_selection (e);
163
164 if (selection->active) {
165 html_cursor_jump_to_position (e->cursor, e, selection->mark);
166 html_engine_set_mark (e);
167 html_cursor_jump_to_position (e->cursor, e, selection->cursor);
168 }
169 html_engine_edit_selection_updater_update_now (e->selection_updater);
170
171 g_free (selection);
172 }
173
174 gboolean
html_engine_selection_stack_top(HTMLEngine * e,gint * cpos,gint * mpos)175 html_engine_selection_stack_top (HTMLEngine *e,
176 gint *cpos,
177 gint *mpos)
178 {
179 Selection *selection = e->selection_stack ? e->selection_stack->data : NULL;
180
181 if (selection && selection->active) {
182 if (cpos)
183 *cpos = selection->cursor;
184 if (mpos)
185 *mpos = selection->mark;
186
187 return TRUE;
188 }
189
190 return FALSE;
191 }
192
193 gboolean
html_engine_selection_stack_top_modify(HTMLEngine * e,gint delta)194 html_engine_selection_stack_top_modify (HTMLEngine *e,
195 gint delta)
196 {
197 Selection *selection = e->selection_stack ? e->selection_stack->data : NULL;
198
199 if (selection && selection->active) {
200 selection->cursor += delta;
201 selection->mark += delta;
202
203 return TRUE;
204 }
205
206 return FALSE;
207 }
208
209 static void
spell_check_object(HTMLObject * o,HTMLEngine * e,gpointer data)210 spell_check_object (HTMLObject *o,
211 HTMLEngine *e,
212 gpointer data)
213 {
214 if (HTML_OBJECT_TYPE (o) == HTML_TYPE_CLUEFLOW)
215 html_clueflow_spell_check (HTML_CLUEFLOW (o), e, (HTMLInterval *) data);
216 }
217
218 void
html_engine_spell_check_range(HTMLEngine * e,HTMLCursor * begin,HTMLCursor * end)219 html_engine_spell_check_range (HTMLEngine *e,
220 HTMLCursor *begin,
221 HTMLCursor *end)
222 {
223 HTMLInterval *i;
224 gboolean cited;
225
226 e->need_spell_check = FALSE;
227
228 if (!e->widget->editor_api || !gtk_html_get_inline_spelling (e->widget) || !begin->object->parent)
229 return;
230
231 begin = html_cursor_dup (begin);
232 end = html_cursor_dup (end);
233
234 cited = FALSE;
235 while (html_selection_spell_word (html_cursor_get_prev_char (begin), &cited) || cited) {
236 if (html_cursor_backward (begin, e)) { ; }
237 cited = FALSE;
238 }
239
240 cited = FALSE;
241 while (html_selection_spell_word (html_cursor_get_current_char (end), &cited) || cited) {
242 if (html_cursor_forward (end, e)) { ; }
243 cited = FALSE;
244 }
245
246 i = html_interval_new_from_cursor (begin, end);
247 if (begin->object->parent != end->object->parent)
248 html_interval_forall (i, e, spell_check_object, i);
249 else if (HTML_IS_CLUEFLOW (begin->object->parent))
250 html_clueflow_spell_check (HTML_CLUEFLOW (begin->object->parent), e, i);
251 html_interval_destroy (i);
252 html_cursor_destroy (begin);
253 html_cursor_destroy (end);
254 }
255
256 gboolean
html_is_in_word(gunichar uc)257 html_is_in_word (gunichar uc)
258 {
259 /* printf ("test %d %c => %d\n", uc, uc, g_unichar_isalnum (uc) || uc == '\''); */
260 return g_unichar_isalpha (uc) || uc == '\'';
261 }
262
263 void
html_engine_select_word_editable(HTMLEngine * e)264 html_engine_select_word_editable (HTMLEngine *e)
265 {
266 while (html_selection_word (html_cursor_get_prev_char (e->cursor)))
267 html_cursor_backward (e->cursor, e);
268 html_engine_set_mark (e);
269 while (html_selection_word (html_cursor_get_current_char (e->cursor)))
270 html_cursor_forward (e->cursor, e);
271 }
272
273 void
html_engine_select_spell_word_editable(HTMLEngine * e)274 html_engine_select_spell_word_editable (HTMLEngine *e)
275 {
276 gboolean cited, cited2;
277
278 cited = cited2 = FALSE;
279 while (html_selection_spell_word (html_cursor_get_prev_char (e->cursor), &cited) || cited) {
280 html_cursor_backward (e->cursor, e);
281 cited2 = cited;
282 cited = FALSE;
283 }
284
285 if (cited2) {
286 html_cursor_forward (e->cursor, e);
287 cited = TRUE;
288 }
289
290 html_engine_set_mark (e);
291 while (html_selection_spell_word (html_cursor_get_current_char (e->cursor), &cited2) || (!cited && cited2)) {
292 html_cursor_forward (e->cursor, e);
293 cited2 = FALSE;
294 }
295 }
296
297 void
html_engine_select_line_editable(HTMLEngine * e)298 html_engine_select_line_editable (HTMLEngine *e)
299 {
300 html_engine_beginning_of_line (e);
301 html_engine_set_mark (e);
302 html_engine_end_of_line (e);
303 }
304
305 void
html_engine_select_paragraph_editable(HTMLEngine * e)306 html_engine_select_paragraph_editable (HTMLEngine *e)
307 {
308 html_engine_beginning_of_paragraph (e);
309 html_engine_set_mark (e);
310 html_engine_end_of_paragraph (e);
311 }
312
313 void
html_engine_select_paragraph_extended(HTMLEngine * e)314 html_engine_select_paragraph_extended (HTMLEngine *e)
315 {
316 gboolean fw;
317
318 html_engine_hide_cursor (e);
319 html_engine_beginning_of_paragraph (e);
320 fw = html_cursor_backward (e->cursor, e);
321 html_engine_set_mark (e);
322 if (fw)
323 html_cursor_forward (e->cursor, e);
324 html_engine_end_of_paragraph (e);
325 html_cursor_forward (e->cursor, e);
326 html_engine_show_cursor (e);
327
328 html_engine_update_selection_if_necessary (e);
329 }
330
331 void
html_engine_select_all_editable(HTMLEngine * e)332 html_engine_select_all_editable (HTMLEngine *e)
333 {
334 html_engine_beginning_of_document (e);
335 html_engine_set_mark (e);
336 html_engine_end_of_document (e);
337 }
338
339 struct SetData {
340 HTMLType object_type;
341 const gchar *key;
342 const gchar *value;
343 };
344
345 static void
set_data(HTMLObject * o,HTMLEngine * e,gpointer p)346 set_data (HTMLObject *o,
347 HTMLEngine *e,
348 gpointer p)
349 {
350 struct SetData *data = (struct SetData *) p;
351
352 if (HTML_OBJECT_TYPE (o) == data->object_type) {
353 /* printf ("set data %s --> %p\n", data->key, data->value); */
354 html_object_set_data (o, data->key, data->value);
355 }
356 }
357
358 void
html_engine_set_data_by_type(HTMLEngine * e,HTMLType object_type,const gchar * key,const gchar * value)359 html_engine_set_data_by_type (HTMLEngine *e,
360 HTMLType object_type,
361 const gchar *key,
362 const gchar *value)
363 {
364 struct SetData *data = g_new (struct SetData, 1);
365
366 /* printf ("html_engine_set_data_by_type %s\n", key); */
367
368 data->object_type = object_type;
369 data->key = key;
370 data->value = value;
371
372 html_object_forall (e->clue, NULL, set_data, data);
373
374 g_free (data);
375 }
376
377 void
html_engine_clipboard_clear(HTMLEngine * e)378 html_engine_clipboard_clear (HTMLEngine *e)
379 {
380 if (e->clipboard) {
381 html_object_destroy (e->clipboard);
382 e->clipboard = NULL;
383 }
384 }
385
386 HTMLObject *
html_engine_new_text(HTMLEngine * e,const gchar * text,gint len)387 html_engine_new_text (HTMLEngine *e,
388 const gchar *text,
389 gint len)
390 {
391 HTMLObject *to;
392
393 to = html_text_new_with_len (text, len, e->insertion_font_style, e->insertion_color);
394 if (e->insertion_font_style != GTK_HTML_FONT_STYLE_DEFAULT)
395 html_text_set_style_in_range (HTML_TEXT (to), e->insertion_font_style, e, 0, HTML_TEXT (to)->text_bytes);
396 if (e->insertion_color &&
397 e->insertion_color != html_colorset_get_color (e->settings->color_set, HTMLTextColor))
398 html_text_set_color_in_range (HTML_TEXT (to), e->insertion_color, 0, HTML_TEXT (to)->text_bytes);
399 if (e->insertion_url)
400 html_text_append_link (HTML_TEXT (to), e->insertion_url, e->insertion_target, 0, HTML_TEXT (to)->text_len);
401
402 return to;
403 }
404
405 HTMLObject *
html_engine_new_link(HTMLEngine * e,const gchar * text,gint len,gchar * url)406 html_engine_new_link (HTMLEngine *e,
407 const gchar *text,
408 gint len,
409 gchar *url)
410 {
411 HTMLObject *link;
412 gchar *real_url, *real_target;
413
414 real_target = strchr (text, '#');
415 if (real_target) {
416 real_url = g_strndup (url, real_target - url);
417 real_target++;
418 } else
419 real_url = url;
420
421 link = html_text_new_with_len (text, len, e->insertion_font_style,
422 html_colorset_get_color (e->settings->color_set, HTMLLinkColor));
423 html_text_append_link (HTML_TEXT (link), real_url, real_target, 0, HTML_TEXT (link)->text_len);
424
425 if (real_target)
426 g_free (real_url);
427
428 return link;
429 }
430
431 HTMLObject *
html_engine_new_text_empty(HTMLEngine * e)432 html_engine_new_text_empty (HTMLEngine *e)
433 {
434 return html_engine_new_text (e, "", 0);
435 }
436
437 gboolean
html_engine_cursor_on_bop(HTMLEngine * e)438 html_engine_cursor_on_bop (HTMLEngine *e)
439 {
440 g_assert (e);
441 g_assert (e->cursor);
442 g_assert (e->cursor->object);
443
444 return e->cursor->offset == 0 && html_object_prev_not_slave (e->cursor->object) == NULL;
445 }
446
447 guint
html_engine_get_indent(HTMLEngine * e)448 html_engine_get_indent (HTMLEngine *e)
449 {
450 g_assert (e);
451 g_assert (e->cursor);
452 g_assert (e->cursor->object);
453
454 return e->cursor->object && e->cursor->object->parent
455 && HTML_OBJECT_TYPE (e->cursor->object->parent) == HTML_TYPE_CLUEFLOW
456 ? html_clueflow_get_indentation (HTML_CLUEFLOW (e->cursor->object->parent)) : 0;
457 }
458
459 #define LINE_LEN 71
460
461 static inline guint
inc_line_offset(guint line_offset,gunichar uc)462 inc_line_offset (guint line_offset,
463 gunichar uc)
464 {
465 return uc == '\t'
466 ? line_offset + 8 - (line_offset % 8)
467 : line_offset + 1;
468 }
469
470 static guint
try_break_this_line(HTMLEngine * e,guint line_offset,guint last_space)471 try_break_this_line (HTMLEngine *e,
472 guint line_offset,
473 guint last_space)
474 {
475 HTMLObject *flow;
476 gunichar uc;
477
478 flow = e->cursor->object->parent;
479
480 while (html_cursor_forward (e->cursor, e) && e->cursor->object->parent == flow) {
481 uc = html_cursor_get_current_char (e->cursor);
482 line_offset = inc_line_offset (line_offset, uc);
483 if (uc == ' ' || uc == '\t')
484 last_space = line_offset;
485 if (uc && line_offset >= LINE_LEN) {
486 if (last_space) {
487 html_cursor_backward_n (e->cursor, e, line_offset - last_space);
488 uc = ' ';
489 } else {
490 /* go to end of word */
491 while (html_cursor_forward (e->cursor, e)) {
492 line_offset = inc_line_offset (line_offset, uc);
493 uc = html_cursor_get_current_char (e->cursor);
494 if (uc == ' ' || uc == '\t' || !uc)
495 break;
496 }
497 }
498 if (uc == ' ' || uc == '\t') {
499 gchar c;
500
501 html_engine_insert_empty_paragraph (e);
502 html_engine_delete_n (e, 1, TRUE);
503
504 while (c = html_cursor_get_current_char (e->cursor), c == ' ' || c == '\t')
505 html_engine_delete_n (e, 1, TRUE);
506
507 flow = e->cursor->object->parent;
508 last_space = 0;
509 line_offset = 0;
510 }
511 }
512 if (!uc)
513 return line_offset;
514 }
515
516 return line_offset;
517 }
518
519 static void
go_to_begin_of_para(HTMLEngine * e)520 go_to_begin_of_para (HTMLEngine *e)
521 {
522 HTMLObject *prev;
523
524 do {
525 gint offset;
526 html_cursor_beginning_of_paragraph (e->cursor, e);
527 offset = 0;
528 prev = html_object_prev_cursor (e->cursor->object, &offset);
529 if (prev && !html_object_is_container (prev) && html_object_get_length (prev)
530 && html_clueflow_style_equals (HTML_CLUEFLOW (e->cursor->object->parent), HTML_CLUEFLOW (prev->parent)))
531 html_cursor_backward (e->cursor, e);
532 else
533 break;
534 } while (1);
535 }
536
537 void
html_engine_indent_paragraph(HTMLEngine * e)538 html_engine_indent_paragraph (HTMLEngine *e)
539 {
540 guint position;
541 guint line_offset;
542 guint last_space;
543 guint i, selection_start, selection_len;
544
545 g_assert (e->cursor->object);
546 if (!HTML_IS_CLUEFLOW (e->cursor->object->parent))
547 return;
548
549 position = e->cursor->position;
550
551 if (e->selection) {
552 HTMLInterval selection = *e->selection;
553
554 html_cursor_jump_to (e->cursor, e, selection.from.object, selection.from.offset);
555 selection_start = e->cursor->position;
556
557 html_cursor_jump_to (e->cursor, e, selection.to.object, selection.to.offset);
558 selection_len = e->cursor->position - selection_start;
559 } else {
560 selection_start = -1;
561 selection_len = 0;
562 }
563
564 html_engine_disable_selection (e);
565
566 if (selection_start == -1)
567 selection_start = position;
568
569 html_undo_level_begin (e->undo, "Indent paragraph", "Reverse paragraph indentation");
570 html_engine_freeze (e);
571
572 for (i = 0; i <= selection_len; i++) {
573 html_cursor_jump_to_position (e->cursor, e, selection_start + selection_len - i);
574
575 go_to_begin_of_para (e);
576
577 i = selection_start + selection_len - e->cursor->position;
578
579 line_offset = 0;
580 last_space = 0;
581 do {
582 HTMLObject *flow;
583
584 line_offset = try_break_this_line (e, line_offset, last_space);
585 flow = e->cursor->object->parent;
586 if (html_cursor_forward (e->cursor, e)
587 && e->cursor->offset == 0 && html_object_get_length (e->cursor->object)
588 && !html_object_is_container (e->cursor->object)
589 && html_clueflow_style_equals (HTML_CLUEFLOW (e->cursor->object->parent), HTML_CLUEFLOW (flow))
590 && html_object_prev_not_slave (e->cursor->object) == NULL) {
591 if (line_offset < LINE_LEN - 1) {
592 gunichar prev;
593 html_engine_delete_n (e, 1, FALSE);
594 prev = html_cursor_get_prev_char (e->cursor);
595 if (prev != ' ' && prev != '\t') {
596 html_engine_insert_text (e, " ", 1);
597 line_offset++;
598 } else if (position > e->cursor->position)
599 position--;
600 last_space = line_offset - 1;
601 } else {
602 line_offset = 0;
603 last_space = 0;
604 }
605 } else
606 break;
607 } while (1);
608 }
609
610 html_cursor_jump_to_position (e->cursor, e, position);
611 html_engine_thaw (e);
612 html_undo_level_end (e->undo, e);
613 }
614
615 void
html_engine_indent_pre_line(HTMLEngine * e)616 html_engine_indent_pre_line (HTMLEngine *e)
617 {
618 guint position;
619 guint line_offset;
620 guint last_space;
621 HTMLObject *flow;
622 gunichar uc;
623
624 g_assert (e->cursor->object);
625 if (HTML_OBJECT_TYPE (e->cursor->object->parent) != HTML_TYPE_CLUEFLOW
626 || html_clueflow_get_style (HTML_CLUEFLOW (e->cursor->object->parent)) != HTML_CLUEFLOW_STYLE_PRE)
627 return;
628
629 html_engine_disable_selection (e);
630 position = e->cursor->position;
631
632 html_undo_level_begin (e->undo, "Indent PRE paragraph", "Reverse paragraph indentation");
633 html_engine_freeze (e);
634
635 last_space = 0;
636 line_offset = 0;
637
638 html_cursor_beginning_of_paragraph (e->cursor, e);
639
640 flow = e->cursor->object->parent;
641 while (html_cursor_forward (e->cursor, e) && e->cursor->object->parent == flow) {
642 uc = html_cursor_get_current_char (e->cursor);
643 line_offset = inc_line_offset (line_offset, uc);
644
645 if (uc == ' ' || uc == '\t') {
646 last_space = line_offset;
647 }
648
649 if (line_offset >= LINE_LEN) {
650 if (last_space) {
651 html_cursor_backward_n (e->cursor, e, line_offset - last_space);
652
653 html_cursor_forward (e->cursor, e);
654 if ((uc = html_cursor_get_current_char (e->cursor))) {
655 html_engine_insert_empty_paragraph (e);
656 if (position >= e->cursor->position)
657 position++;
658 }
659 }
660 }
661 if (!uc)
662 break;
663 }
664
665 html_cursor_jump_to_position (e->cursor, e, position);
666 html_engine_thaw (e);
667 html_undo_level_end (e->undo, e);
668 }
669
670 void
html_engine_fill_pre_line(HTMLEngine * e)671 html_engine_fill_pre_line (HTMLEngine *e)
672 {
673 guint position;
674 guint line_offset;
675 guint last_space;
676 gunichar uc;
677
678 g_assert (e->cursor->object);
679 position = e->cursor->position;
680
681 if (HTML_OBJECT_TYPE (e->cursor->object->parent) != HTML_TYPE_CLUEFLOW
682 || html_clueflow_get_style (HTML_CLUEFLOW (e->cursor->object->parent)) != HTML_CLUEFLOW_STYLE_PRE) {
683 return;
684 }
685
686 last_space = 0;
687 line_offset = 0;
688
689 html_cursor_beginning_of_paragraph (e->cursor, e);
690
691 while (html_cursor_forward (e->cursor, e) && (e->cursor->position < position - 1)) {
692 uc = html_cursor_get_current_char (e->cursor);
693 line_offset = inc_line_offset (line_offset, uc);
694
695 if (uc == ' ' || uc == '\t') {
696 last_space = line_offset;
697 }
698
699 if (line_offset >= LINE_LEN) {
700 if (last_space) {
701 html_cursor_backward_n (e->cursor, e, line_offset - last_space);
702
703 html_cursor_forward (e->cursor, e);
704 if ((uc = html_cursor_get_current_char (e->cursor))) {
705 html_engine_insert_empty_paragraph (e);
706 if (position >= e->cursor->position)
707 position++;
708
709 line_offset = 0;
710 last_space = 0;
711 }
712 }
713 }
714 if (!uc)
715 break;
716 }
717 html_cursor_jump_to_position (e->cursor, e, position);
718 }
719
720 void
html_engine_space_and_fill_line(HTMLEngine * e)721 html_engine_space_and_fill_line (HTMLEngine *e)
722 {
723
724 g_assert (e->cursor->object);
725 html_undo_level_begin (e->undo, "insert and fill", "reverse insert and fill");
726
727 html_engine_disable_selection (e);
728 html_engine_freeze (e);
729 html_engine_insert_text (e, " ", 1);
730
731 html_engine_fill_pre_line (e);
732
733 html_engine_thaw (e);
734 html_undo_level_end (e->undo, e);
735 }
736
737 void
html_engine_break_and_fill_line(HTMLEngine * e)738 html_engine_break_and_fill_line (HTMLEngine *e)
739 {
740 html_undo_level_begin (e->undo, "break and fill", "reverse break and fill");
741
742 html_engine_disable_selection (e);
743 html_engine_freeze (e);
744
745 html_engine_fill_pre_line (e);
746
747 html_engine_insert_empty_paragraph (e);
748 html_engine_thaw (e);
749 html_undo_level_end (e->undo, e);
750 }
751
752 gboolean
html_engine_next_cell(HTMLEngine * e,gboolean create)753 html_engine_next_cell (HTMLEngine *e,
754 gboolean create)
755 {
756 HTMLTableCell *cell, *current_cell;
757
758 cell = html_engine_get_table_cell (e);
759 if (cell) {
760 html_engine_hide_cursor (e);
761 do {
762 html_cursor_end_of_line (e->cursor, e);
763 html_cursor_forward (e->cursor, e);
764 current_cell = html_engine_get_table_cell (e);
765 } while (current_cell == cell);
766
767 if (create && HTML_IS_TABLE (e->cursor->object)) {
768 html_cursor_backward (e->cursor, e);
769 html_engine_insert_table_row (e, TRUE);
770 html_cursor_forward (e->cursor, e);
771 }
772 html_engine_show_cursor (e);
773
774 return TRUE;
775 }
776
777 return FALSE;
778 }
779
780 gboolean
html_engine_prev_cell(HTMLEngine * e)781 html_engine_prev_cell (HTMLEngine *e)
782 {
783 HTMLTableCell *cell, *current_cell;
784
785 cell = html_engine_get_table_cell (e);
786 if (cell) {
787 html_engine_hide_cursor (e);
788 do {
789 html_cursor_beginning_of_line (e->cursor, e);
790 html_cursor_backward (e->cursor, e);
791 current_cell = html_engine_get_table_cell (e);
792 } while (current_cell == cell);
793
794 html_engine_show_cursor (e);
795
796 return TRUE;
797 }
798
799 return FALSE;
800 }
801
802 void
html_engine_set_title(HTMLEngine * e,const gchar * title)803 html_engine_set_title (HTMLEngine *e,
804 const gchar *title)
805 {
806 if (e->title)
807 g_string_free (e->title, TRUE);
808 e->title = g_string_new (title);
809 g_signal_emit_by_name (e, "title_changed");
810 }
811
html_engine_edit_set_direction(HTMLEngine * e,HTMLDirection dir)812 void html_engine_edit_set_direction (HTMLEngine *e,
813 HTMLDirection dir)
814 {
815 HTMLClueFlow *cf = html_object_get_flow (e->cursor->object);
816
817 if (cf && cf->dir != dir && html_clueflow_is_empty (cf)) {
818 html_engine_freeze (e);
819 cf->dir = dir;
820 html_engine_thaw (e);
821 }
822 }
823
824 gint
html_engine_get_insert_level_for_object(HTMLEngine * e,HTMLObject * o)825 html_engine_get_insert_level_for_object (HTMLEngine *e,
826 HTMLObject *o)
827 {
828 gint cursor_level = 3, level = html_object_get_insert_level (o);
829
830 if (level > 3) {
831 if (e && e->cursor->object && e->cursor->object->parent && e->cursor->object->parent->parent && html_object_is_clue (e->cursor->object->parent->parent)) {
832 HTMLObject *clue = e->cursor->object->parent->parent;
833
834 while (clue && clue->parent && (HTML_IS_CLUEV (clue->parent) || HTML_IS_TABLE_CELL (clue->parent))) {
835 clue = clue->parent;
836 cursor_level++;
837 }
838 }
839 }
840
841 return MIN (level, cursor_level);
842 }
843