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) 2000, 2001, 2002 Ximian, Inc.
5 * Authors: Radek Doulik (rodo@ximian.com)
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 */
23
24 #include <config.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "gtkhtmldebug.h"
29 #include "gtkhtml-private.h"
30 #include "gtkhtml-properties.h"
31
32 #include "htmlclue.h"
33 #include "htmlcluealigned.h"
34 #include "htmlclueflow.h"
35 #include "htmlcursor.h"
36 #include "htmlcolorset.h"
37 #include "htmlengine.h"
38 #include "htmlengine-edit.h"
39 #include "htmlengine-edit-clueflowstyle.h"
40 #include "htmlengine-edit-cursor.h"
41 #include "htmlengine-edit-cut-and-paste.h"
42 #include "htmlengine-edit-fontstyle.h"
43 #include "htmlengine-edit-movement.h"
44 #include "htmlengine-edit-selection-updater.h"
45 #include "htmlimage.h"
46 #include "htmlinterval.h"
47 #include "htmlobject.h"
48 #include "htmlplainpainter.h"
49 #include "htmltable.h"
50 #include "htmltablecell.h"
51 #include "htmlselection.h"
52 #include "htmlsettings.h"
53 #include "htmltext.h"
54 #include "htmlundo.h"
55 #include "htmlundo-action.h"
56
57 static gint delete_object (HTMLEngine *e, HTMLObject **ret_object, guint *ret_len, HTMLUndoDirection dir,
58 gboolean add_prop);
59 static void insert_object_for_undo (HTMLEngine *e, HTMLObject *obj, guint len, guint position_after, gint level,
60 HTMLUndoDirection dir, gboolean check);
61 static void append_object (HTMLEngine *e, HTMLObject *o, guint len, HTMLUndoDirection dir);
62 static void insert_empty_paragraph (HTMLEngine *e, HTMLUndoDirection dir, gboolean add_undo);
63 static void insert_setup_undo (HTMLEngine *e, guint len, guint position_before, HTMLUndoDirection dir,
64 gboolean delete_paragraph_before, gboolean delete_paragraph_after);
65
66 /* helper functions -- need refactor */
67
68 /* #define OP_DEBUG */
69
70 static void
html_cursor_get_left(HTMLCursor * cursor,HTMLObject ** obj,gint * off)71 html_cursor_get_left (HTMLCursor *cursor,
72 HTMLObject **obj,
73 gint *off)
74 {
75 if (cursor->offset == 0) {
76 *obj = html_object_prev_not_slave (cursor->object);
77 if (*obj) {
78 *off = html_object_get_length (*obj);
79 return;
80 }
81 }
82 *obj = cursor->object;
83 *off = cursor->offset;
84 }
85
86 static void
html_point_get_left(HTMLPoint * source,HTMLPoint * dest)87 html_point_get_left (HTMLPoint *source,
88 HTMLPoint *dest)
89 {
90 if (source->offset == 0) {
91 dest->object = html_object_prev_not_slave (source->object);
92 if (dest->object) {
93 dest->offset = html_object_get_length (dest->object);
94 return;
95 }
96 }
97
98 *dest = *source;
99 }
100
101 static void
html_point_get_right(HTMLPoint * source,HTMLPoint * dest)102 html_point_get_right (HTMLPoint *source,
103 HTMLPoint *dest)
104 {
105 if (source->offset >= html_object_get_length (source->object)) {
106 dest->object = html_object_next_not_slave (source->object);
107 if (dest->object) {
108 dest->offset = 0;
109 return;
110 }
111 }
112
113 *dest = *source;
114 }
115
116 static void
object_get_parent_list(HTMLObject * o,gint level,GList ** list)117 object_get_parent_list (HTMLObject *o,
118 gint level,
119 GList **list)
120 {
121 while (level > 0 && o) {
122 *list = g_list_prepend (*list, o);
123 o = o->parent;
124 level--;
125 }
126 }
127
128 static GList *
point_get_parent_list(HTMLPoint * point,gint level,gboolean include_offset)129 point_get_parent_list (HTMLPoint *point,
130 gint level,
131 gboolean include_offset)
132 {
133 GList *list;
134
135 list = include_offset ? g_list_prepend (NULL, GINT_TO_POINTER (point->offset)) : NULL;
136
137 object_get_parent_list (point->object, level, &list);
138
139 return list;
140 }
141
142 static gint
get_parent_depth(HTMLObject * o,HTMLObject * parent)143 get_parent_depth (HTMLObject *o,
144 HTMLObject *parent)
145 {
146 gint level = 1;
147
148 while (o && parent && o != parent) {
149 o = o->parent;
150 level++;
151 }
152
153 return level;
154 }
155
156 static gboolean
is_parent(HTMLObject * o,HTMLObject * parent)157 is_parent (HTMLObject *o,HTMLObject *parent)
158 {
159 while (o) {
160 if (o == parent)
161 return TRUE;
162 o = o->parent;
163 }
164
165 return FALSE;
166 }
167
168 static HTMLObject *
try_find_common_parent_of(HTMLObject * child,HTMLObject * parent)169 try_find_common_parent_of (HTMLObject *child,
170 HTMLObject *parent)
171 {
172 while (parent) {
173 if (is_parent (child, parent))
174 return parent;
175 parent = parent->parent;
176 }
177
178 return NULL;
179 }
180
181 static HTMLObject *
get_common_parent(HTMLObject * from,HTMLObject * to)182 get_common_parent (HTMLObject *from,
183 HTMLObject *to)
184 {
185 HTMLObject *parent;
186
187 parent = try_find_common_parent_of (from, to);
188
189 return parent ? parent : try_find_common_parent_of (to, from);
190 }
191
192 static gint
prepare_delete_bounds(HTMLEngine * e,GList ** from_list,GList ** to_list,GList ** bound_left,GList ** bound_right)193 prepare_delete_bounds (HTMLEngine *e,
194 GList **from_list,
195 GList **to_list,
196 GList **bound_left,
197 GList **bound_right)
198 {
199 HTMLPoint b_left, b_right, begin, end;
200 HTMLObject *common_parent;
201 gint ret_level;
202
203 g_assert (e->selection);
204
205 html_point_get_right (&e->selection->from, &begin);
206 html_point_get_left (&e->selection->to, &end);
207
208 common_parent = get_common_parent (begin.object, end.object);
209 ret_level = html_object_get_parent_level (common_parent);
210 #ifdef OP_DEBUG
211 printf ("common parent level: %d\n", ret_level);
212 #endif
213 *from_list = point_get_parent_list (&begin, get_parent_depth (begin.object, common_parent), TRUE);
214 *to_list = point_get_parent_list (&end, get_parent_depth (end.object, common_parent), TRUE);
215
216 if (bound_left && bound_right) {
217 gint level;
218
219 html_point_get_left (&e->selection->from, &b_left);
220 html_point_get_right (&e->selection->to, &b_right);
221
222 common_parent = get_common_parent (b_left.object, b_right.object);
223
224 level = get_parent_depth (b_left.object, common_parent);
225 *bound_left = b_left.object ? point_get_parent_list (&b_left, level - 1, FALSE) : NULL;
226 if (level > 1 && *bound_left)
227 *bound_left = g_list_prepend (*bound_left, NULL);
228
229 level = get_parent_depth (b_right.object, common_parent);
230 *bound_right = b_right.object ? point_get_parent_list (&b_right, level - 1, FALSE) : NULL;
231 if (level > 1 && *bound_right)
232 *bound_right = g_list_prepend (*bound_right, NULL);
233 }
234
235 return ret_level;
236 }
237
238 static void
remove_empty_and_merge(HTMLEngine * e,gboolean merge,GList * left_orig,GList * right_orig,HTMLCursor * c)239 remove_empty_and_merge (HTMLEngine *e,
240 gboolean merge,
241 GList *left_orig,
242 GList *right_orig,
243 HTMLCursor *c)
244 {
245 HTMLObject *lo, *ro, *prev;
246
247 GList *left, *right;
248 GList *left_old, *right_old;
249
250 left_old = g_list_copy (left_orig);
251 right_old = g_list_copy (right_orig);
252
253 left = left_old;
254 right = right_old;
255
256 #ifdef OP_DEBUG
257 /* HTMLObject *left_orig = left->data; */
258 printf ("before merge\n");
259 gtk_html_debug_dump_tree_simple (e->clue, 0);
260 if (left && left->data) {
261 printf ("left\n");
262 gtk_html_debug_dump_tree_simple (left->data, 0);
263 }
264
265 if (right && right->data) {
266 printf ("right\n");
267 gtk_html_debug_dump_tree_simple (right->data, 0);
268 }
269 #endif
270 while (left && left->data && right && right->data) {
271
272 lo = HTML_OBJECT (left->data);
273 ro = HTML_OBJECT (right->data);
274
275 left = left->next;
276 right = right->next;
277
278 if (HTML_IS_CLUEALIGNED (lo) && !HTML_IS_CLUEALIGNED (ro) && html_object_is_text (HTML_CLUE (lo)->head)) {
279 HTMLObject *nlo = lo->prev;
280
281 if (e->cursor->object->parent && e->cursor->object->parent == lo) {
282 e->cursor->object = ro;
283 e->cursor->offset = 0;
284 }
285 if (c && c->object->parent && c->object->parent == lo) {
286 c->object = ro;
287 c->offset = 0;
288 }
289
290 html_object_remove_child (lo->parent, lo);
291 html_object_destroy (lo);
292 lo = nlo;
293 if (!nlo)
294 break;
295 } else if (HTML_IS_CLUEALIGNED (ro) && !HTML_IS_CLUEALIGNED (lo) && html_object_is_text (HTML_CLUE (ro)->head)) {
296 HTMLObject *nro = ro->next;
297
298 if (e->cursor->object->parent && e->cursor->object->parent == ro) {
299 e->cursor->object = lo;
300 e->cursor->offset = html_object_get_length (lo);
301 }
302 html_object_remove_child (ro->parent, ro);
303 html_object_destroy (ro);
304 ro = nro;
305 if (!nro)
306 break;
307 }
308
309 if (html_object_is_text (lo) && !*HTML_TEXT (lo)->text && (html_object_prev_not_slave (lo) || merge)) {
310 HTMLObject *nlo = html_object_prev_not_slave (lo);
311
312 if (e->cursor->object == lo) {
313 e->cursor->object = ro;
314 e->cursor->offset = 0;
315 }
316 if (c && c->object == lo) {
317 c->object = ro;
318 c->offset = 0;
319 }
320
321 html_object_remove_child (lo->parent, lo);
322 html_object_destroy (lo);
323 lo = nlo;
324 } else if (html_object_is_text (ro) && !*HTML_TEXT (ro)->text && (html_object_next_not_slave (ro) || merge)) {
325 HTMLObject *nro = html_object_next_not_slave (ro);
326
327 if (e->cursor->object == ro) {
328 e->cursor->object = lo;
329 e->cursor->offset = html_object_get_length (lo);
330 }
331
332 html_object_remove_child (ro->parent, ro);
333 html_object_destroy (ro);
334 ro = nro;
335 }
336
337 if (merge && lo && ro) {
338 GList *left_copy, *right_copy;
339 gboolean merge_flag;
340
341 left_copy = g_list_copy (left);
342 right_copy = g_list_copy (right);
343 merge_flag = html_object_merge (lo, ro, e, &left_copy, &right_copy, c);
344
345 g_list_free (left_old);
346 g_list_free (right_old);
347
348 left = left_copy;
349 right = right_copy;
350 left_old = left;
351 right_old = right;
352
353 if (!merge_flag)
354 break;
355 if (ro == e->cursor->object) {
356 e->cursor->object = lo;
357 e->cursor->offset += html_object_get_length (lo);;
358 }
359 }
360 }
361
362 prev = html_object_prev_not_slave (e->cursor->object);
363 if (prev && e->cursor->offset == 0) {
364 e->cursor->object = prev;
365 e->cursor->offset = html_object_get_length (e->cursor->object);
366 }
367
368 g_list_free (left_old);
369 g_list_free (right_old);
370 #ifdef OP_DEBUG
371 /* printf ("-- finished\n");
372 * gtk_html_debug_dump_tree_simple (left_orig, 0); */
373 printf ("-- after\n");
374 gtk_html_debug_dump_tree_simple (e->clue, 0);
375 printf ("-- END merge\n");
376 #endif
377 }
378
379 static void
split_and_add_empty_texts(HTMLEngine * e,gint level,GList ** left,GList ** right)380 split_and_add_empty_texts (HTMLEngine *e,
381 gint level,
382 GList **left,
383 GList **right)
384 {
385 #ifdef OP_DEBUG
386 printf ("-- SPLIT begin\ne->clue:\n");
387 gtk_html_debug_dump_tree_simple (e->clue, 0);
388 printf ("-- SPLIT middle\n");
389 #endif
390 html_object_split (e->cursor->object, e, *right ? HTML_OBJECT ((*right)->data) : NULL,
391 e->cursor->offset, level, left, right);
392 #ifdef OP_DEBUG
393 printf ("-- SPLIT middle\ne->clue:\n");
394 gtk_html_debug_dump_tree_simple (e->clue, 0);
395 printf ("-- SPLIT finish\n");
396 if (*left && (*left)->data) {
397 printf ("left:\n");
398 gtk_html_debug_dump_tree_simple (HTML_OBJECT ((*left)->data), 0);
399 }
400 if (*right && (*right)->data) {
401 printf ("right:\n");
402 gtk_html_debug_dump_tree_simple (HTML_OBJECT ((*right)->data), 0);
403 }
404 printf ("-- SPLIT end\n");
405 #endif
406 }
407
408 /* end of helper */
409
410 void
html_engine_copy_object(HTMLEngine * e,HTMLObject ** o,guint * len)411 html_engine_copy_object (HTMLEngine *e,
412 HTMLObject **o,
413 guint *len)
414 {
415 if (e->clue && HTML_CLUE (e->clue)->head && html_engine_is_selection_active (e)) {
416 GList *from = NULL, *to = NULL;
417
418 prepare_delete_bounds (e, &from, &to, NULL, NULL);
419 *len = 0;
420 *o = html_object_op_copy (HTML_OBJECT (from->data), NULL, e,
421 from->next, to->next, len);
422 #ifdef OP_DEBUG
423 printf ("copy len: %d (parent %p)\n", *len, (*o)->parent);
424 gtk_html_debug_dump_tree_simple (*o, 0);
425 #endif
426 g_list_free (from);
427 g_list_free (to);
428 } else {
429 *len = 0;
430 *o = NULL;
431 }
432 }
433
434 void
html_engine_copy(HTMLEngine * e)435 html_engine_copy (HTMLEngine *e)
436 {
437 html_engine_copy_object (e, &e->clipboard, &e->clipboard_len);
438 }
439
440 struct _DeleteUndo {
441 HTMLUndoData data;
442
443 HTMLObject *buffer;
444 guint buffer_len;
445 gint level;
446 };
447 typedef struct _DeleteUndo DeleteUndo;
448
449 static void
delete_undo_destroy(HTMLUndoData * data)450 delete_undo_destroy (HTMLUndoData *data)
451 {
452 DeleteUndo *undo = (DeleteUndo *) data;
453
454 if (undo->buffer)
455 html_object_destroy (undo->buffer);
456 }
457
458 static void
delete_undo_action(HTMLEngine * e,HTMLUndoData * data,HTMLUndoDirection dir,guint position_after)459 delete_undo_action (HTMLEngine *e,
460 HTMLUndoData *data,
461 HTMLUndoDirection dir,
462 guint position_after)
463 {
464 DeleteUndo *undo;
465 HTMLObject *buffer;
466 guint len = 0;
467
468 undo = (DeleteUndo *) data;
469 buffer = html_object_op_copy (undo->buffer, NULL, e, NULL, NULL, &len);
470 insert_object_for_undo (e, buffer, undo->buffer_len, position_after, undo->level, html_undo_direction_reverse (dir), TRUE);
471 }
472
473 static void
delete_setup_undo(HTMLEngine * e,HTMLObject * buffer,guint len,guint position_after,gint level,HTMLUndoDirection dir)474 delete_setup_undo (HTMLEngine *e,
475 HTMLObject *buffer,
476 guint len,
477 guint position_after,
478 gint level,
479 HTMLUndoDirection dir)
480 {
481 DeleteUndo *undo;
482
483 undo = g_new (DeleteUndo, 1);
484
485 #ifdef OP_DEBUG
486 printf ("cursor level: %d undo level: %d\n", html_object_get_parent_level (e->cursor->object), level);
487 #endif
488 html_undo_data_init (HTML_UNDO_DATA (undo));
489 undo->data.destroy = delete_undo_destroy;
490 undo->buffer = buffer;
491 undo->buffer_len = len;
492 undo->level = level;
493
494 #ifdef OP_DEBUG
495 printf ("delete undo len %d\n", len);
496 #endif
497 html_undo_add_action (e->undo, e,
498 html_undo_action_new ("Delete object", delete_undo_action,
499 HTML_UNDO_DATA (undo), html_cursor_get_position (e->cursor),
500 position_after),
501 dir);
502 }
503
504 static void
move_cursor_before_delete(HTMLEngine * e)505 move_cursor_before_delete (HTMLEngine *e)
506 {
507 if (e->cursor->offset == 0) {
508 if (html_object_prev_not_slave (e->cursor->object)) {
509 HTMLObject *obj;
510 gint off;
511
512 html_cursor_get_left (e->cursor, &obj, &off);
513 if (obj) {
514 e->cursor->object = obj;
515 e->cursor->offset = off;
516 }
517 }
518 }
519 }
520
521 static void
place_cursor_before_mark(HTMLEngine * e)522 place_cursor_before_mark (HTMLEngine *e)
523 {
524 if (e->mark->position < e->cursor->position) {
525 HTMLCursor *tmp;
526
527 tmp = e->cursor;
528 e->cursor = e->mark;
529 e->mark = tmp;
530 }
531 }
532
533 static gboolean
haligns_equal(HTMLHAlignType a1,HTMLHAlignType a2)534 haligns_equal (HTMLHAlignType a1,
535 HTMLHAlignType a2)
536 {
537 return a1 == a2
538 || (a1 == HTML_HALIGN_LEFT && a2 == HTML_HALIGN_NONE)
539 || (a1 == HTML_HALIGN_NONE && a2 == HTML_HALIGN_LEFT);
540
541 }
542
543 static gboolean
levels_equal(HTMLClueFlow * me,HTMLClueFlow * you)544 levels_equal (HTMLClueFlow *me,
545 HTMLClueFlow *you)
546 {
547 if (!you)
548 return FALSE;
549
550 if (me->levels->len != you->levels->len)
551 return FALSE;
552
553 if (me->levels->len == 0)
554 return TRUE;
555
556 return !memcmp (me->levels->data, you->levels->data, you->levels->len);
557 }
558
559 static void
check_flows(HTMLEngine * e,HTMLUndoDirection dir)560 check_flows (HTMLEngine *e,
561 HTMLUndoDirection dir)
562 {
563 /* I assume here that cursor is before mark */
564 HTMLClueFlow *flow1, *flow2;
565 gint level1, level2;
566
567 g_return_if_fail (e->cursor);
568 g_return_if_fail (e->cursor->object);
569 g_return_if_fail (e->cursor->object->parent);
570 g_return_if_fail (e->mark);
571 g_return_if_fail (e->mark->object);
572 g_return_if_fail (e->mark->object->parent);
573 g_return_if_fail (e->cursor->position <= e->mark->position);
574
575 if (e->cursor->offset || e->cursor->object->parent == e->mark->object->parent
576 || !HTML_IS_CLUEFLOW (e->cursor->object->parent) || !HTML_IS_CLUEFLOW (e->mark->object->parent)
577 || e->cursor->object != HTML_CLUE (e->cursor->object->parent)->head)
578 return;
579
580 level1 = html_object_get_parent_level (e->cursor->object->parent);
581 level2 = html_object_get_parent_level (e->mark->object->parent);
582
583 flow1 = HTML_CLUEFLOW (e->cursor->object->parent);
584 flow2 = HTML_CLUEFLOW (e->mark->object->parent);
585
586 if (level1 == level2
587 && (flow1->style != flow2->style
588 || (flow1->style == HTML_CLUEFLOW_STYLE_LIST_ITEM && flow1->item_type != flow2->item_type)
589 || !levels_equal (flow1, flow2)
590 || !haligns_equal (HTML_CLUE (flow1)->halign, HTML_CLUE (flow2)->halign))) {
591 HTMLCursor *dest, *source;
592
593 dest = html_cursor_dup (e->cursor);
594 source = html_cursor_dup (e->mark);
595
596 html_engine_selection_push (e);
597 html_engine_disable_selection (e);
598 html_cursor_jump_to_position_no_spell (e->cursor, e, dest->position);
599 html_engine_set_clueflow_style (e,
600 HTML_CLUEFLOW (source->object->parent)->style,
601 HTML_CLUEFLOW (source->object->parent)->item_type,
602 HTML_CLUE (source->object->parent)->halign,
603 HTML_CLUEFLOW (source->object->parent)->levels->len,
604 HTML_CLUEFLOW (source->object->parent)->levels->data,
605 HTML_ENGINE_SET_CLUEFLOW_INDENTATION_ALL,
606 dir, TRUE);
607 html_engine_selection_pop (e);
608 html_cursor_destroy (source);
609 html_cursor_destroy (dest);
610 }
611 }
612
613 static gint
delete_object_do(HTMLEngine * e,HTMLObject ** object,guint * len,HTMLUndoDirection dir,gboolean add_undo)614 delete_object_do (HTMLEngine *e,
615 HTMLObject **object,
616 guint *len,
617 HTMLUndoDirection dir,
618 gboolean add_undo)
619 {
620 GList *from, *to, *left, *right;
621 guint position;
622 gint level;
623
624 html_engine_freeze (e);
625 level = prepare_delete_bounds (e, &from, &to, &left, &right);
626 place_cursor_before_mark (e);
627 if (add_undo)
628 check_flows (e, dir);
629 move_cursor_before_delete (e);
630 html_engine_disable_selection (e);
631 *len = 0;
632 *object = html_object_op_cut (HTML_OBJECT (from->data), e, from->next, to->next, left, right, len);
633 position = e->cursor->position;
634 remove_empty_and_merge (e, TRUE, left ? left->next : NULL, right ? right->next : NULL, NULL);
635
636 g_list_free (from);
637 g_list_free (to);
638 g_list_free (left);
639 g_list_free (right);
640
641 e->cursor->position = position;
642 html_engine_spell_check_range (e, e->cursor, e->cursor);
643 html_engine_thaw (e);
644
645 return level;
646 }
647
648 static void
check_table_0(HTMLEngine * e)649 check_table_0 (HTMLEngine *e)
650 {
651 HTMLCursor *tail;
652
653 tail = e->mark->position < e->cursor->position ? e->cursor : e->mark;
654
655 if (html_cursor_backward (tail, e) && (!HTML_IS_TABLE (tail->object) || tail->offset))
656 html_cursor_forward (tail, e);
657 while (tail->offset == 0 && HTML_IS_TABLE (tail->object) && e->mark->position != e->cursor->position)
658 html_cursor_backward (tail, e);
659 }
660
661 static void
check_table_1(HTMLEngine * e)662 check_table_1 (HTMLEngine *e)
663 {
664 HTMLCursor *head;
665
666 head = e->mark->position > e->cursor->position ? e->cursor : e->mark;
667
668 if (html_cursor_forward (head, e) && (!HTML_IS_TABLE (head->object) || head->offset == 0))
669 html_cursor_backward (head, e);
670 while (head->offset == 1 && HTML_IS_TABLE (head->object) && e->mark->position != e->cursor->position)
671 html_cursor_forward (head, e);
672 }
673
674 static gboolean
validate_tables(HTMLEngine * e,HTMLUndoDirection dir,gboolean add_undo,gboolean * fix_para)675 validate_tables (HTMLEngine *e,
676 HTMLUndoDirection dir,
677 gboolean add_undo,
678 gboolean *fix_para)
679 {
680 HTMLObject *next = html_object_next_not_slave (e->cursor->object);
681
682 *fix_para = FALSE;
683
684 if (next && HTML_IS_TABLE (next)) {
685 insert_empty_paragraph (e, dir, add_undo);
686 *fix_para = FALSE;
687
688 return TRUE;
689 } else if (!next) {
690 gint steps = 0;
691
692 while (html_cursor_forward (e->cursor, e)) {
693 steps++;
694 if (HTML_IS_TABLE (e->cursor->object)) {
695 next = html_object_next_not_slave (e->cursor->object);
696 if (next) {
697 insert_empty_paragraph (e, dir, FALSE);
698 *fix_para = TRUE;
699 steps++;
700 break;
701 }
702 } else
703 break;
704 }
705
706 if (steps)
707 html_cursor_backward_n (e->cursor, e, steps);
708 }
709
710 return FALSE;
711 }
712
713 static inline gboolean
in_aligned(HTMLCursor * cursor)714 in_aligned (HTMLCursor *cursor)
715 {
716 return cursor->object->parent && HTML_IS_CLUEALIGNED (cursor->object->parent);
717 }
718
719 struct _FixEmptyAlignedUndo {
720 HTMLUndoData data;
721
722 HTMLObject *ac;
723 };
724 typedef struct _FixEmptyAlignedUndo FixEmptyAlignedUndo;
725
726 static void
fix_empty_aligned_undo_destroy(HTMLUndoData * data)727 fix_empty_aligned_undo_destroy (HTMLUndoData *data)
728 {
729 FixEmptyAlignedUndo *undo = (FixEmptyAlignedUndo *) data;
730
731 if (undo->ac)
732 html_object_destroy (undo->ac);
733 }
734
735 static void
fix_empty_aligned_undo_action(HTMLEngine * e,HTMLUndoData * data,HTMLUndoDirection dir,guint position_after)736 fix_empty_aligned_undo_action (HTMLEngine *e,
737 HTMLUndoData *data,
738 HTMLUndoDirection dir,
739 guint position_after)
740 {
741 HTMLObject *ac, *flow;
742
743 g_return_if_fail (html_object_is_text (e->cursor->object) && HTML_TEXT (e->cursor->object)->text_len == 0
744 && e->cursor->object->parent && HTML_IS_CLUEFLOW (e->cursor->object->parent));
745
746 ac = ((FixEmptyAlignedUndo *) data)->ac;
747 ((FixEmptyAlignedUndo *) data)->ac = NULL;
748
749 html_engine_freeze (e);
750 flow = e->cursor->object->parent;
751 html_clue_remove_text_slaves (HTML_CLUE (flow));
752 html_clue_append_after (HTML_CLUE (flow), ac, e->cursor->object);
753 html_object_remove_child (flow, e->cursor->object);
754 html_clue_append (HTML_CLUE (ac), e->cursor->object);
755 html_object_change_set_down (flow, HTML_CHANGE_ALL);
756 html_engine_thaw (e);
757 }
758
759 static void
fix_empty_aligned_setup_undo(HTMLEngine * e,HTMLUndoDirection dir,HTMLObject * ac)760 fix_empty_aligned_setup_undo (HTMLEngine *e,
761 HTMLUndoDirection dir,
762 HTMLObject *ac)
763 {
764 FixEmptyAlignedUndo *undo;
765
766 undo = g_new (FixEmptyAlignedUndo, 1);
767
768 html_undo_data_init (HTML_UNDO_DATA (undo));
769 undo->data.destroy = fix_empty_aligned_undo_destroy;
770 undo->ac = ac;
771
772 html_undo_add_action (e->undo, e,
773 html_undo_action_new ("Remove empty aligned", fix_empty_aligned_undo_action,
774 HTML_UNDO_DATA (undo), html_cursor_get_position (e->cursor),
775 html_cursor_get_position (e->cursor)),
776 dir);
777 }
778
779 static void
fix_empty_aligned(HTMLEngine * e,HTMLUndoDirection dir,gboolean add_undo)780 fix_empty_aligned (HTMLEngine *e,
781 HTMLUndoDirection dir,
782 gboolean add_undo)
783 {
784 if (html_object_is_text (e->cursor->object) && e->cursor->object->parent && HTML_IS_CLUEALIGNED (e->cursor->object->parent)) {
785 HTMLObject *ac = e->cursor->object->parent;
786
787 if (ac->parent && HTML_IS_CLUEFLOW (ac->parent)) {
788 html_engine_freeze (e);
789 html_clue_remove_text_slaves (HTML_CLUE (ac));
790 html_object_remove_child (ac, e->cursor->object);
791 html_clue_append_after (HTML_CLUE (ac->parent), e->cursor->object, ac);
792 html_object_change_set_down (ac->parent, HTML_CHANGE_ALL);
793 html_object_remove_child (ac->parent, ac);
794 if (add_undo)
795 fix_empty_aligned_setup_undo (e, dir, ac);
796 html_engine_thaw (e);
797 }
798 }
799 }
800
801 static gint
delete_object(HTMLEngine * e,HTMLObject ** ret_object,guint * ret_len,HTMLUndoDirection dir,gboolean add_undo)802 delete_object (HTMLEngine *e,
803 HTMLObject **ret_object,
804 guint *ret_len,
805 HTMLUndoDirection dir,
806 gboolean add_undo)
807 {
808 html_engine_edit_selection_updater_update_now (e->selection_updater);
809 if (html_engine_is_selection_active (e)) {
810 HTMLObject *object;
811 guint len, position_before, saved_position, end_position;
812 gint level;
813 gboolean backward;
814 gboolean fix_para;
815
816 end_position = MIN (e->cursor->position, e->mark->position);
817 if (HTML_IS_TABLE (e->cursor->object)
818 || (e->cursor->object->parent && e->cursor->object->parent->parent && HTML_IS_TABLE_CELL (e->cursor->object->parent->parent))
819 || HTML_IS_TABLE (e->mark->object)
820 || (e->mark->object->parent && e->mark->object->parent->parent && HTML_IS_TABLE_CELL (e->mark->object->parent->parent))) {
821 check_table_0 (e);
822 check_table_1 (e);
823 html_engine_edit_selection_updater_update_now (e->selection_updater);
824 }
825 if (!html_engine_is_selection_active (e) || e->cursor->position == e->mark->position) {
826 html_engine_disable_selection (e);
827 html_cursor_jump_to_position (e->cursor, e, end_position);
828 return 0;
829 }
830
831 position_before = MAX (e->cursor->position, e->mark->position);
832 level = delete_object_do (e, &object, &len, dir, add_undo);
833 if (ret_object && ret_len) {
834 *ret_object = html_object_op_copy (object, NULL, e, NULL, NULL, ret_len);
835 *ret_len = len;
836 }
837 backward = validate_tables (e, dir, add_undo, &fix_para);
838 if (fix_para) {
839 saved_position = e->cursor->position;
840 e->cursor->position = position_before + 1;
841 insert_setup_undo (e, 1, position_before, dir, FALSE, FALSE);
842 e->cursor->position = saved_position;
843 }
844 level = html_object_get_parent_level (e->cursor->object) - level + 1;
845 if (add_undo) {
846 delete_setup_undo (e, object, len, position_before + (backward ? 1 : 0), level, dir);
847 } else
848 html_object_destroy (object);
849
850 if (backward)
851 html_cursor_backward (e->cursor, e);
852 gtk_html_editor_event (e->widget, GTK_HTML_EDITOR_EVENT_DELETE, NULL);
853 fix_empty_aligned (e, dir, add_undo);
854
855 return level;
856 }
857
858 return 0;
859 }
860
861 gint
html_engine_cut(HTMLEngine * e)862 html_engine_cut (HTMLEngine *e)
863 {
864 gint rv;
865
866 html_engine_clipboard_clear (e);
867 html_undo_level_begin (e->undo, "Cut", "Uncut");
868 if (html_engine_is_selection_active (e)) {
869 HTMLCursor *start = html_cursor_dup (e->mark->position < e->cursor->position ? e->mark : e->cursor);
870 HTMLCursor *end = html_cursor_dup (e->mark->position < e->cursor->position ? e->cursor : e->mark);
871 gint start_position = start->position;
872 gint end_position = end->position;
873 if (end_position - start_position > 0) {
874 gint len = end_position - start_position;
875 g_signal_emit_by_name (e->widget, "object_delete", start_position, len);
876 }
877 html_cursor_destroy (start);
878 html_cursor_destroy (end);
879 }
880
881 rv = delete_object (e, &e->clipboard, &e->clipboard_len, HTML_UNDO_UNDO, TRUE);
882 html_undo_level_end (e->undo, e);
883
884 #ifdef OP_DEBUG
885 printf ("cut len: %d\n", e->clipboard_len);
886 gtk_html_debug_dump_tree_simple (e->clipboard, 0);
887 #endif
888
889 return rv;
890 }
891
892 /*
893 * PASTE/INSERT
894 */
895
896 static void
set_cursor_at_end_of_object(HTMLEngine * e,HTMLObject * o,guint len)897 set_cursor_at_end_of_object (HTMLEngine *e,
898 HTMLObject *o,
899 guint len)
900 {
901 guint save_position;
902 gboolean need_spell_check;
903
904 save_position = e->cursor->position;
905 e->cursor->object = html_object_get_tail_leaf (o);
906 need_spell_check = e->need_spell_check;
907 e->need_spell_check = FALSE;
908 while (html_cursor_forward (e->cursor, e))
909 ;
910 e->need_spell_check = need_spell_check;
911 e->cursor->position = save_position + len;
912 e->cursor->offset = html_object_get_length (e->cursor->object);
913 }
914
915 static inline void
isolate_tables(HTMLEngine * e,HTMLUndoDirection dir,guint position_before,guint position_after,gboolean * delete_paragraph_before,gboolean * delete_paragraph_after)916 isolate_tables (HTMLEngine *e,
917 HTMLUndoDirection dir,
918 guint position_before,
919 guint position_after,
920 gboolean *delete_paragraph_before,
921 gboolean *delete_paragraph_after)
922 {
923 HTMLObject *next;
924
925 *delete_paragraph_after = FALSE;
926 *delete_paragraph_before = FALSE;
927
928 html_cursor_jump_to_position_no_spell (e->cursor, e, position_after);
929 next = html_object_next_not_slave (e->cursor->object);
930 if (next && e->cursor->offset == html_object_get_length (e->cursor->object)
931 && (HTML_IS_TABLE (e->cursor->object) || HTML_IS_TABLE (next))) {
932 insert_empty_paragraph (e, dir, FALSE);
933 *delete_paragraph_after = TRUE;
934 }
935
936 html_cursor_jump_to_position_no_spell (e->cursor, e, position_before);
937 next = html_object_next_not_slave (e->cursor->object);
938 if (next && e->cursor->offset == html_object_get_length (e->cursor->object)
939 && (HTML_IS_TABLE (e->cursor->object) || HTML_IS_TABLE (next))) {
940 insert_empty_paragraph (e, dir, FALSE);
941 *delete_paragraph_before = TRUE;
942 }
943 }
944
945 static inline void
insert_object_do(HTMLEngine * e,HTMLObject * obj,guint * len,gint level,guint position_after,gboolean check,HTMLUndoDirection dir)946 insert_object_do (HTMLEngine *e,
947 HTMLObject *obj,
948 guint *len,
949 gint level,
950 guint position_after,
951 gboolean check,
952 HTMLUndoDirection dir)
953 {
954 HTMLCursor *orig;
955 GList *left = NULL, *right = NULL;
956 GList *first = NULL, *last = NULL;
957 guint position_before;
958
959 html_engine_freeze (e);
960 position_before = e->cursor->position;
961 html_object_change_set_down (obj, HTML_CHANGE_ALL);
962 split_and_add_empty_texts (e, level, &left, &right);
963 orig = html_cursor_dup (e->cursor);
964 orig->position = position_before;
965 first = html_object_heads_list (obj);
966 last = html_object_tails_list (obj);
967 set_cursor_at_end_of_object (e, obj, *len);
968
969 if ((left && left->data) || (right && (right->data))) {
970 HTMLObject *parent, *where;
971 if (left && left->data) {
972 where = HTML_OBJECT (left->data);
973 parent = where->parent;
974 } else {
975 where = NULL;
976 parent = HTML_OBJECT (right->data)->parent;
977 }
978 if (parent && html_object_is_clue (parent))
979 html_clue_append_after (HTML_CLUE (parent), obj, where);
980 }
981
982 #ifdef OP_DEBUG
983 printf ("position before merge %d\n", e->cursor->position);
984 #endif
985 remove_empty_and_merge (e, TRUE, last, right, orig);
986 remove_empty_and_merge (e, TRUE, left, first, orig);
987
988 g_list_free (first);
989 g_list_free (last);
990 g_list_free (left);
991 g_list_free (right);
992
993 #ifdef OP_DEBUG
994 printf ("position after merge %d\n", e->cursor->position);
995 #endif
996
997 html_cursor_copy (e->cursor, orig);
998 html_cursor_jump_to_position_no_spell (e->cursor, e, position_after);
999
1000 if (check)
1001 html_engine_spell_check_range (e, orig, e->cursor);
1002 html_cursor_destroy (orig);
1003 html_engine_thaw (e);
1004 }
1005
1006 struct _InsertUndo {
1007 HTMLUndoData data;
1008
1009 guint len;
1010 gboolean delete_paragraph_before;
1011 gboolean delete_paragraph_after;
1012 };
1013 typedef struct _InsertUndo InsertUndo;
1014
1015 static void
insert_undo_action(HTMLEngine * e,HTMLUndoData * data,HTMLUndoDirection dir,guint position_after)1016 insert_undo_action (HTMLEngine *e,
1017 HTMLUndoData *data,
1018 HTMLUndoDirection dir,
1019 guint position_after)
1020 {
1021 InsertUndo *undo;
1022
1023 undo = (InsertUndo *) data;
1024
1025 html_engine_set_mark (e);
1026 html_cursor_jump_to_position (e->cursor, e, position_after);
1027 delete_object (e, NULL, NULL, html_undo_direction_reverse (dir), TRUE);
1028
1029 if (undo->delete_paragraph_after || undo->delete_paragraph_before) {
1030 html_cursor_jump_to_position (e->cursor, e, position_after);
1031 if (undo->delete_paragraph_before) {
1032 html_cursor_backward (e->cursor, e);
1033 }
1034 html_engine_set_mark (e);
1035 if (undo->delete_paragraph_before) {
1036 html_cursor_forward (e->cursor, e);
1037 }
1038 if (undo->delete_paragraph_after) {
1039 html_cursor_forward (e->cursor, e);
1040 }
1041 delete_object (e, NULL, NULL, HTML_UNDO_UNDO, FALSE);
1042 }
1043 }
1044
1045 static void
insert_setup_undo(HTMLEngine * e,guint len,guint position_before,HTMLUndoDirection dir,gboolean delete_paragraph_before,gboolean delete_paragraph_after)1046 insert_setup_undo (HTMLEngine *e,
1047 guint len,
1048 guint position_before,
1049 HTMLUndoDirection dir,
1050 gboolean delete_paragraph_before,
1051 gboolean delete_paragraph_after)
1052 {
1053 InsertUndo *undo;
1054
1055 undo = g_new (InsertUndo, 1);
1056
1057 html_undo_data_init (HTML_UNDO_DATA (undo));
1058 undo->len = len;
1059 undo->delete_paragraph_before = delete_paragraph_before;
1060 undo->delete_paragraph_after = delete_paragraph_after;
1061
1062 /* printf ("insert undo len %d\n", len); */
1063
1064 html_undo_add_action (e->undo, e,
1065 html_undo_action_new ("Insert", insert_undo_action,
1066 HTML_UNDO_DATA (undo),
1067 html_cursor_get_position (e->cursor),
1068 position_before),
1069 dir);
1070 }
1071
1072 static gboolean fix_aligned_position (HTMLEngine *e, guint *position_after, HTMLUndoDirection dir);
1073
1074 static void
fix_aligned_redo_action(HTMLEngine * e,HTMLUndoData * data,HTMLUndoDirection dir,guint position_after)1075 fix_aligned_redo_action (HTMLEngine *e,
1076 HTMLUndoData *data,
1077 HTMLUndoDirection dir,
1078 guint position_after)
1079 {
1080 guint pa;
1081
1082 fix_aligned_position (e, &pa, html_undo_direction_reverse (dir));
1083 }
1084
1085 static void
fix_aligned_undo_action(HTMLEngine * e,HTMLUndoData * data,HTMLUndoDirection dir,guint position_after)1086 fix_aligned_undo_action (HTMLEngine *e,
1087 HTMLUndoData *data,
1088 HTMLUndoDirection dir,
1089 guint position_after)
1090 {
1091 HTMLObject *cf = e->cursor->object->parent;
1092 HTMLUndoData *undo;
1093 guint position_before = e->cursor->position;
1094
1095 undo = g_new (HTMLUndoData, 1);
1096
1097 if (!html_cursor_forward (e->cursor, e))
1098 g_assert (html_cursor_backward (e->cursor, e));
1099 else
1100 e->cursor->position--;
1101
1102 html_clue_remove (HTML_CLUE (cf->parent), cf);
1103 html_object_destroy (cf);
1104
1105 html_undo_add_action (e->undo, e,
1106 html_undo_action_new ("Fix aligned", fix_aligned_redo_action,
1107 undo, html_cursor_get_position (e->cursor),
1108 position_before),
1109 html_undo_direction_reverse (dir));
1110 }
1111
1112 static void
fix_align_setup_undo(HTMLEngine * e,guint position_before,HTMLUndoDirection dir)1113 fix_align_setup_undo (HTMLEngine *e,
1114 guint position_before,
1115 HTMLUndoDirection dir)
1116 {
1117 HTMLUndoData *undo;
1118
1119 undo = g_new (HTMLUndoData, 1);
1120
1121 html_undo_data_init (HTML_UNDO_DATA (undo));
1122 /* printf ("insert undo len %d\n", len); */
1123
1124 html_undo_add_action (e->undo, e,
1125 html_undo_action_new ("Undo aligned fix", fix_aligned_undo_action,
1126 undo, html_cursor_get_position (e->cursor),
1127 position_before),
1128 dir);
1129 }
1130
1131 static gboolean
fix_aligned_position(HTMLEngine * e,guint * position_after,HTMLUndoDirection dir)1132 fix_aligned_position (HTMLEngine *e,
1133 guint *position_after,
1134 HTMLUndoDirection dir)
1135 {
1136 gboolean rv = FALSE;
1137 if (in_aligned (e->cursor)) {
1138 /* printf ("in aligned\n"); */
1139 if (e->cursor->offset) {
1140 if (html_cursor_forward (e->cursor, e))
1141 (*position_after) ++;
1142 if (in_aligned (e->cursor)) {
1143 HTMLObject *cf;
1144 HTMLObject *cluev;
1145 HTMLObject *flow;
1146
1147 /* printf ("aligned: needs fixing\n"); */
1148 html_engine_freeze (e);
1149 cf = html_clueflow_new_from_flow (HTML_CLUEFLOW (e->cursor->object->parent->parent));
1150 flow = e->cursor->object->parent->parent;
1151 cluev = flow->parent;
1152 e->cursor->object = html_engine_new_text_empty (e);
1153 html_clue_append (HTML_CLUE (cf), e->cursor->object);
1154 html_clue_append_after (HTML_CLUE (cluev), cf, flow);
1155 e->cursor->offset = 0;
1156 e->cursor->position++;
1157 (*position_after) ++;
1158 #ifdef OP_DEBUG
1159 gtk_html_debug_dump_tree_simple (e->clue, 0);
1160 #endif
1161 fix_align_setup_undo (e, e->cursor->position, dir);
1162 html_engine_thaw (e);
1163 rv = TRUE;
1164 if (e->cursor->object->parent && HTML_IS_CLUEALIGNED (e->cursor->object->parent))
1165 html_cursor_forward (e->cursor, e);
1166
1167 }
1168 } else {
1169 if (html_cursor_backward (e->cursor, e))
1170 (*position_after) --;
1171 if (in_aligned (e->cursor)) {
1172 HTMLObject *cf;
1173 HTMLObject *cluev;
1174 HTMLObject *flow;
1175
1176 /* printf ("aligned: needs fixing\n"); */
1177 html_engine_freeze (e);
1178 cf = html_clueflow_new_from_flow (HTML_CLUEFLOW (e->cursor->object->parent->parent));
1179 flow = e->cursor->object->parent->parent;
1180 cluev = flow->parent;
1181 e->cursor->object = html_engine_new_text_empty (e);
1182 html_clue_append (HTML_CLUE (cf), e->cursor->object);
1183 if (flow->prev)
1184 html_clue_append_after (HTML_CLUE (cluev), cf, flow->prev);
1185 else
1186 html_clue_prepend (HTML_CLUE (cluev), cf);
1187 e->cursor->offset = 0;
1188 #ifdef OP_DEBUG
1189 gtk_html_debug_dump_tree_simple (e->clue, 0);
1190 #endif
1191 fix_align_setup_undo (e, e->cursor->position, dir);
1192 html_engine_thaw (e);
1193 rv = TRUE;
1194 }
1195 }
1196 }
1197
1198 return rv;
1199 }
1200
1201 static void
insert_object_for_undo(HTMLEngine * e,HTMLObject * obj,guint len,guint position_after,gint level,HTMLUndoDirection dir,gboolean check)1202 insert_object_for_undo (HTMLEngine *e,
1203 HTMLObject *obj,
1204 guint len,
1205 guint position_after,
1206 gint level,
1207 HTMLUndoDirection dir,
1208 gboolean check)
1209 {
1210 gboolean delete_paragraph_before = FALSE;
1211 gboolean delete_paragraph_after = FALSE;
1212 guint position_before;
1213
1214 position_before = e->cursor->position;
1215 insert_object_do (e, obj, &len, level, position_after, check, dir);
1216 isolate_tables (e, dir, position_before, position_after, &delete_paragraph_before, &delete_paragraph_after);
1217 html_cursor_jump_to_position_no_spell (e->cursor, e, position_after + (delete_paragraph_before ? 1 : 0));
1218 insert_setup_undo (e, len, position_before + (delete_paragraph_before ? 1 : 0),
1219 dir, delete_paragraph_before, delete_paragraph_after);
1220 g_signal_emit_by_name (e->widget, "object_inserted", position_before, len);
1221 }
1222
1223 static void
insert_object(HTMLEngine * e,HTMLObject * obj,guint len,guint position_after,gint level,HTMLUndoDirection dir,gboolean check)1224 insert_object (HTMLEngine *e,
1225 HTMLObject *obj,
1226 guint len,
1227 guint position_after,
1228 gint level,
1229 HTMLUndoDirection dir,
1230 gboolean check)
1231 {
1232 fix_aligned_position (e, &position_after, dir);
1233 insert_object_for_undo (e, obj, len, position_after, level, dir, check);
1234 }
1235
1236 void
html_engine_insert_object(HTMLEngine * e,HTMLObject * o,guint len,gint level)1237 html_engine_insert_object (HTMLEngine *e,
1238 HTMLObject *o,
1239 guint len,
1240 gint level)
1241 {
1242 insert_object (e, o, len, e->cursor->position + len, level, HTML_UNDO_UNDO, TRUE);
1243 }
1244
1245 void
html_engine_paste_object(HTMLEngine * e,HTMLObject * o,guint len)1246 html_engine_paste_object (HTMLEngine *e,
1247 HTMLObject *o,
1248 guint len)
1249 {
1250 html_undo_level_begin (e->undo, "Paste", "Paste");
1251 html_engine_delete (e);
1252 html_engine_insert_object (e, o, len, html_engine_get_insert_level_for_object (e, o));
1253 html_undo_level_end (e->undo, e);
1254 }
1255
1256 void
html_engine_paste(HTMLEngine * e)1257 html_engine_paste (HTMLEngine *e)
1258 {
1259 if (e->clipboard) {
1260 HTMLObject *copy;
1261 guint len = 0;
1262
1263 copy = html_object_op_copy (e->clipboard, NULL, e, NULL, NULL, &len);
1264 html_engine_paste_object (e, copy, e->clipboard_len);
1265 }
1266 }
1267
1268 static void
check_magic_link(HTMLEngine * e,const gchar * text,guint len)1269 check_magic_link (HTMLEngine *e,
1270 const gchar *text,
1271 guint len)
1272 {
1273 if (HTML_IS_TEXT (e->cursor->object)
1274 && gtk_html_get_magic_links (e->widget)
1275 && len == 1
1276 && (*text == ' ' || text[0] == '\n' || text[0] == '>' || text[0] == ')'))
1277 html_text_magic_link (HTML_TEXT (e->cursor->object), e, 1);
1278 }
1279
1280 static void
insert_empty_paragraph(HTMLEngine * e,HTMLUndoDirection dir,gboolean add_undo)1281 insert_empty_paragraph (HTMLEngine *e,
1282 HTMLUndoDirection dir,
1283 gboolean add_undo)
1284 {
1285 GList *left = NULL, *right = NULL;
1286 HTMLCursor *orig;
1287 guint position_before;
1288 guint position_after;
1289
1290 if (dir == HTML_UNDO_UNDO)
1291 if (fix_aligned_position (e, &position_after, dir))
1292 return;
1293
1294 html_engine_freeze (e);
1295
1296 position_before = e->cursor->position;
1297 orig = html_cursor_dup (e->cursor);
1298 split_and_add_empty_texts (e, 2, &left, &right);
1299 remove_empty_and_merge (e, FALSE, left, right, orig);
1300
1301 /* replace empty link in empty flow by text with the same style */
1302 /* FIXME-link if (HTML_IS_LINK_TEXT (e->cursor->object) && html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent))) {
1303 HTMLObject *flow = e->cursor->object->parent;
1304 HTMLObject *new_text;
1305 *
1306 new_text = html_link_text_to_text (HTML_LINK_TEXT (e->cursor->object), e);
1307 html_clue_remove (HTML_CLUE (flow), e->cursor->object);
1308 html_object_destroy (e->cursor->object);
1309 if (orig->object == e->cursor->object) {
1310 orig->object = NULL;
1311 }
1312 e->cursor->object = new_text;
1313 if (!orig->object) {
1314 orig->object = e->cursor->object;
1315 }
1316 html_clue_append (HTML_CLUE (flow), e->cursor->object);
1317 } */
1318
1319 html_cursor_forward (e->cursor, e);
1320
1321 /* replace empty text in new empty flow by text with current style */
1322 if (html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent))) {
1323 HTMLObject *flow = e->cursor->object->parent;
1324
1325 html_clue_remove (HTML_CLUE (flow), e->cursor->object);
1326 html_object_destroy (e->cursor->object);
1327 e->cursor->object = html_engine_new_text_empty (e);
1328 html_clue_append (HTML_CLUE (flow), e->cursor->object);
1329 }
1330
1331 if (add_undo) {
1332 html_undo_level_begin (e->undo, "Insert paragraph", "Delete paragraph");
1333 insert_setup_undo (e, 1, position_before, dir, FALSE, FALSE);
1334 }
1335 g_list_free (left);
1336 g_list_free (right);
1337 html_engine_spell_check_range (e, orig, e->cursor);
1338 html_cursor_destroy (orig);
1339
1340 html_cursor_backward (e->cursor, e);
1341 check_magic_link (e, "\n", 1);
1342 html_cursor_forward (e->cursor, e);
1343
1344 gtk_html_editor_event_command (e->widget, GTK_HTML_COMMAND_INSERT_PARAGRAPH, FALSE);
1345 if (add_undo)
1346 html_undo_level_end (e->undo, e);
1347
1348 html_engine_thaw (e);
1349 g_signal_emit_by_name (e->widget, "object_inserted", 0, 0);
1350 }
1351
1352 void
html_engine_insert_empty_paragraph(HTMLEngine * e)1353 html_engine_insert_empty_paragraph (HTMLEngine *e)
1354 {
1355 HTMLClueFlow *cf;
1356 HTMLClueFlowStyle cfs;
1357
1358 html_engine_freeze (e);
1359 insert_empty_paragraph (e, HTML_UNDO_UNDO, TRUE);
1360 cf = html_object_get_flow (e->cursor->object);
1361 cfs = html_clueflow_get_style (cf);
1362 if (cfs == HTML_CLUEFLOW_STYLE_H1 || cfs == HTML_CLUEFLOW_STYLE_H2 || cfs == HTML_CLUEFLOW_STYLE_H3 || cfs == HTML_CLUEFLOW_STYLE_H4 || cfs == HTML_CLUEFLOW_STYLE_H5 || cfs == HTML_CLUEFLOW_STYLE_H6)
1363 html_clueflow_set_style (cf, e, HTML_CLUEFLOW_STYLE_NORMAL);
1364 if (cf) {
1365 cf->dir = html_text_direction_pango_to_html (gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (e->widget)))));
1366 }
1367 html_engine_thaw (e);
1368 }
1369
1370 static const gchar *picto_chars =
1371 /* 0 */ "DO)(|/PQ*!"
1372 /* 10 */ "S\0:-\0:\0:-\0"
1373 /* 20 */ ":\0:;=-\"\0:;"
1374 /* 30 */ "B\"|\0:-'\0:X"
1375 /* 40 */ "\0:\0:-\0:\0:-"
1376 /* 50 */ "\0:\0:-\0:\0:-"
1377 /* 60 */ "\0:\0:\0:-\0:\0"
1378 /* 70 */ ":-\0:\0:-\0:\0";
1379 static gint picto_states[] = {
1380 /* 0 */ 12, 17, 22, 34, 43, 48, 53, 58, 65, 70,
1381 /* 10 */ 75, 0, -15, 15, 0, -15, 0, -17, 20, 0,
1382 /* 20 */ -17, 0, -14, -20, -14, 28, 63, 0, -14, -20,
1383 /* 30 */ -3, 63, -18, 0, -12, 38, 41, 0, -12, -2,
1384 /* 40 */ 0, -4, 0, -10, 46, 0, -10, 0, -19, 51,
1385 /* 50 */ 0, -19, 0, -11, 56, 0, -11, 0, -13, 61,
1386 /* 60 */ 0, -13, 0, -6, 0, 68, -7, 0, -7, 0,
1387 /* 70 */ -16, 73, 0, -16, 0, -21, 78, 0, -21, 0 };
1388 static const gchar *picto_icon_names[] = {
1389 "face-angel",
1390 "face-angry",
1391 "face-cool",
1392 "face-crying",
1393 "face-devilish",
1394 "face-embarrassed",
1395 "face-kiss",
1396 "face-laugh", /* not used */
1397 "face-monkey", /* not used */
1398 "face-plain",
1399 "face-raspberry",
1400 "face-sad",
1401 "face-sick",
1402 "face-smile",
1403 "face-smile-big",
1404 "face-smirk",
1405 "face-surprise",
1406 "face-tired",
1407 "face-uncertain",
1408 "face-wink",
1409 "face-worried"
1410 };
1411
1412 static void
use_pictograms(HTMLEngine * e)1413 use_pictograms (HTMLEngine *e)
1414 {
1415 gint pos;
1416 gint state;
1417 gint relative;
1418 gunichar uc;
1419
1420 if (!html_object_is_text (e->cursor->object) || !gtk_html_get_magic_smileys (e->widget))
1421 return;
1422
1423 pos = e->cursor->offset - 1;
1424 state = 0;
1425 while (pos >= 0) {
1426 uc = html_text_get_char (HTML_TEXT (e->cursor->object), pos);
1427 relative = 0;
1428 while (picto_chars[state + relative]) {
1429 if (picto_chars[state + relative] == uc)
1430 break;
1431 relative++;
1432 }
1433 state = picto_states[state + relative];
1434 /* 0 .. not found, -n .. found n-th */
1435 if (state <= 0)
1436 break;
1437 pos--;
1438 }
1439
1440 /* Special case needed to recognize angel and devilish. */
1441 if (pos > 0 && state == -14) {
1442 uc = html_text_get_char (HTML_TEXT (e->cursor->object), pos - 1);
1443 if (uc == 'O') {
1444 state = -1;
1445 pos--;
1446 } else if (uc == '>') {
1447 state = -5;
1448 pos--;
1449 }
1450 }
1451
1452 if (state < 0) {
1453 HTMLObject *image;
1454 GtkIconInfo *icon_info;
1455 const gchar *filename;
1456 gchar *filename_uri;
1457 gchar *alt;
1458 gint len;
1459
1460 if (pos > 0) {
1461 uc = html_text_get_char (HTML_TEXT (e->cursor->object), pos - 1);
1462 if (uc != ' ' && uc != '\t')
1463 return;
1464 }
1465 /* printf ("found %d\n", -state); */
1466 len = e->cursor->offset - pos;
1467 alt = g_strndup (html_text_get_text (HTML_TEXT (e->cursor->object), pos), len);
1468 html_cursor_backward_n (e->cursor, e, len);
1469 html_engine_set_mark (e);
1470 html_cursor_forward_n (e->cursor, e, len);
1471
1472 /* Convert a named icon to a file URI. */
1473 icon_info = gtk_icon_theme_lookup_icon (
1474 gtk_icon_theme_get_default (),
1475 picto_icon_names[-state - 1], 16, 0);
1476 g_return_if_fail (icon_info != NULL);
1477 filename = gtk_icon_info_get_filename (icon_info);
1478 g_return_if_fail (filename != NULL);
1479 filename_uri = g_filename_to_uri (filename, NULL, NULL);
1480 image = html_image_new (
1481 html_engine_get_image_factory (e),
1482 filename_uri, NULL, NULL, -1, -1, FALSE, FALSE,
1483 0, NULL, HTML_VALIGN_MIDDLE, FALSE);
1484
1485 html_image_set_alt (HTML_IMAGE (image), alt);
1486 html_object_set_data (HTML_OBJECT (image), "picto", alt);
1487
1488 html_engine_paste_object (
1489 e, image, html_object_get_length (image));
1490
1491 g_free (alt);
1492 g_free (filename_uri);
1493 gtk_icon_info_free (icon_info);
1494 }
1495 }
1496
1497 void
html_engine_insert_text_with_extra_attributes(HTMLEngine * e,const gchar * ptext,gint len,PangoAttrList * attrs)1498 html_engine_insert_text_with_extra_attributes (HTMLEngine *e,
1499 const gchar *ptext,
1500 gint len,
1501 PangoAttrList *attrs)
1502 {
1503 gchar *nl, *sanitized_text = NULL;
1504 const gchar *text;
1505 gint alen;
1506 gsize bytes;
1507
1508 bytes = html_text_sanitize (ptext, &sanitized_text, &len);
1509 if (!len || !sanitized_text) {
1510 g_free (sanitized_text);
1511 return;
1512 }
1513
1514 text = sanitized_text;
1515
1516 html_undo_level_begin (e->undo, "Insert text", "Delete text");
1517 /* FIXME add insert text event */
1518 gtk_html_editor_event_command (e->widget, GTK_HTML_COMMAND_INSERT_PARAGRAPH, TRUE);
1519
1520 do {
1521 nl = memchr (text, '\n', bytes);
1522 alen = nl ? g_utf8_pointer_to_offset (text, nl) : len;
1523 if (alen) {
1524 HTMLObject *o;
1525 gboolean check = FALSE;
1526
1527 check_magic_link (e, text, alen);
1528
1529 /* stop inserting links after space */
1530 if (*text == ' ')
1531 html_engine_set_insertion_link (e, NULL, NULL);
1532
1533 o = html_engine_new_text (e, text, alen);
1534 if (attrs)
1535 HTML_TEXT (o)->extra_attr_list = pango_attr_list_copy (attrs);
1536 html_text_convert_nbsp (HTML_TEXT (o), TRUE);
1537
1538 if (alen == 1 && html_is_in_word (html_text_get_char (HTML_TEXT (o), 0))
1539 && !html_is_in_word (html_cursor_get_current_char (e->cursor))) {
1540 /* printf ("need_spell_check\n"); */
1541 e->need_spell_check = TRUE;
1542 } else {
1543 check = TRUE;
1544 }
1545 insert_object (e, o, html_object_get_length (o), e->cursor->position + html_object_get_length (o),
1546 1, HTML_UNDO_UNDO, check);
1547 if (alen == 1 && !HTML_IS_PLAIN_PAINTER (e->painter))
1548 use_pictograms (e);
1549 }
1550 if (nl) {
1551 html_engine_insert_empty_paragraph (e);
1552 len -= alen + 1;
1553 bytes -= (nl - text) + 1;
1554 text = nl + 1;
1555 }
1556 } while (nl);
1557 html_undo_level_end (e->undo, e);
1558
1559 g_free (sanitized_text);
1560 }
1561
1562 void
html_engine_insert_text(HTMLEngine * e,const gchar * text,gint len)1563 html_engine_insert_text (HTMLEngine *e,
1564 const gchar *text,
1565 gint len)
1566 {
1567 html_engine_insert_text_with_extra_attributes (e, text, len, NULL);
1568 }
1569
1570 void
html_engine_paste_text_with_extra_attributes(HTMLEngine * e,const gchar * text,guint len,PangoAttrList * attrs)1571 html_engine_paste_text_with_extra_attributes (HTMLEngine *e,
1572 const gchar *text,
1573 guint len,
1574 PangoAttrList *attrs)
1575 {
1576 gchar *undo_name = g_strdup_printf ("Paste text: '%s'", text);
1577 gchar *redo_name = g_strdup_printf ("Unpaste text: '%s'", text);
1578
1579 html_undo_level_begin (e->undo, undo_name, redo_name);
1580 g_free (undo_name);
1581 g_free (redo_name);
1582 html_engine_delete (e);
1583 html_engine_insert_text_with_extra_attributes (e, text, len, attrs);
1584 html_undo_level_end (e->undo, e);
1585 }
1586
1587 void
html_engine_paste_text(HTMLEngine * e,const gchar * text,guint len)1588 html_engine_paste_text (HTMLEngine *e,
1589 const gchar *text,
1590 guint len)
1591 {
1592 html_engine_paste_text_with_extra_attributes (e, text, len, NULL);
1593 }
1594
1595 void
html_engine_paste_link(HTMLEngine * e,const gchar * text,gint len,const gchar * complete_url)1596 html_engine_paste_link (HTMLEngine *e,
1597 const gchar *text,
1598 gint len,
1599 const gchar *complete_url)
1600 {
1601 gchar *url, *target;
1602
1603 if (len == -1)
1604 len = g_utf8_strlen (text, -1);
1605
1606 url = g_strdup (complete_url);
1607 target = strrchr (url, '#');
1608 if (target) {
1609 *target = 0;
1610 target++;
1611 }
1612
1613 html_engine_paste_text (e, text, len);
1614 html_text_add_link (HTML_TEXT (e->cursor->object), e, url, target, e->cursor->offset >= len ? e->cursor->offset - len : 0, e->cursor->offset);
1615
1616 g_free (url);
1617 }
1618
1619 void
html_engine_delete_container(HTMLEngine * e)1620 html_engine_delete_container (HTMLEngine *e)
1621 {
1622 g_assert (HTML_IS_ENGINE (e));
1623 g_assert (e->cursor->object);
1624 g_assert (html_object_is_container (e->cursor->object));
1625
1626 html_engine_set_mark (e);
1627 html_engine_update_selection_if_necessary (e);
1628 html_engine_freeze (e);
1629 if (e->cursor->offset)
1630 html_cursor_beginning_of_line (e->cursor, e);
1631 else
1632 html_cursor_end_of_line (e->cursor, e);
1633 html_engine_delete (e);
1634 html_engine_thaw (e);
1635 }
1636
1637 void
html_engine_delete_n(HTMLEngine * e,guint len,gboolean forward)1638 html_engine_delete_n (HTMLEngine *e,
1639 guint len,
1640 gboolean forward)
1641 {
1642 if (html_engine_is_selection_active (e))
1643 html_engine_delete (e);
1644 else {
1645 html_engine_block_selection (e);
1646 html_engine_set_mark (e);
1647 html_engine_update_selection_if_necessary (e);
1648 html_engine_freeze (e);
1649 /* Remove magic smiley */
1650 if (!forward && len == 1 && gtk_html_get_magic_smileys (e->widget)) {
1651 HTMLObject *object = html_object_get_tail_leaf (e->cursor->object);
1652
1653 if (HTML_IS_IMAGE (object) && html_object_get_data (object, "picto") != NULL) {
1654 gchar *picto = g_strdup (html_object_get_data (object, "picto"));
1655 html_undo_level_begin (e->undo, "Remove Magic Smiley", "Undo Remove Magic Smiley");
1656 html_cursor_backward (e->cursor, e);
1657 html_engine_delete (e);
1658 html_engine_insert_text (e, picto, -1);
1659 html_undo_level_end (e->undo, e);
1660 g_free (picto);
1661
1662 html_engine_unblock_selection (e);
1663 html_engine_thaw (e);
1664 return;
1665 }
1666 }
1667 if (forward) {
1668 gint i;
1669
1670 for (i = len; i > 0; i--)
1671 html_cursor_forward (e->cursor, e);
1672 html_engine_delete (e);
1673 } else {
1674 html_object_backspace (e->cursor->object, e->cursor, e);
1675 }
1676 html_engine_unblock_selection (e);
1677 html_engine_thaw (e);
1678 }
1679 }
1680
1681 void
html_engine_cut_line(HTMLEngine * e)1682 html_engine_cut_line (HTMLEngine *e)
1683 {
1684 g_return_if_fail (e != NULL);
1685 g_return_if_fail (HTML_IS_ENGINE (e));
1686
1687 html_undo_level_begin (e->undo, "Cut Line", "Undo Cut Line");
1688 html_engine_set_mark (e);
1689 html_engine_end_of_line (e);
1690
1691 if (e->cursor->position == e->mark->position)
1692 html_cursor_forward (e->cursor, e);
1693
1694 html_engine_cut (e);
1695 html_undo_level_end (e->undo, e);
1696 }
1697
1698 typedef struct {
1699 HTMLColor *color;
1700 const gchar *url;
1701 const gchar *target;
1702 } HTMLEngineLinkInsertData;
1703
1704 static void
change_link(HTMLObject * o,HTMLEngine * e,gpointer data)1705 change_link (HTMLObject *o,
1706 HTMLEngine *e,
1707 gpointer data)
1708 {
1709 HTMLObject *changed;
1710 HTMLEngineLinkInsertData *d = (HTMLEngineLinkInsertData *) data;
1711
1712 changed = d->url ? html_object_set_link (o, d->color, d->url, d->target) : html_object_remove_link (o, d->color);
1713 if (changed) {
1714 if (o->parent) {
1715 g_assert (HTML_OBJECT_TYPE (o->parent) == HTML_TYPE_CLUEFLOW);
1716
1717 html_clue_append_after (HTML_CLUE (o->parent), changed, o);
1718 html_clue_remove (HTML_CLUE (o->parent), o);
1719 html_object_destroy (o);
1720 if (changed->prev)
1721 html_object_merge (changed->prev, changed, e, NULL, NULL, NULL);
1722 } else {
1723 html_object_destroy (e->clipboard);
1724 e->clipboard = changed;
1725 e->clipboard_len = html_object_get_length (changed);
1726 }
1727 }
1728 }
1729
1730 void
html_engine_set_insertion_link(HTMLEngine * e,const gchar * url,const gchar * target)1731 html_engine_set_insertion_link (HTMLEngine *e,
1732 const gchar *url,
1733 const gchar *target)
1734 {
1735 html_engine_set_url (e, url);
1736 html_engine_set_target (e, target);
1737 if (!url && e->insertion_color == html_colorset_get_color (e->settings->color_set, HTMLLinkColor))
1738 html_engine_set_color (e, html_colorset_get_color (e->settings->color_set, HTMLTextColor));
1739 else if (url)
1740 html_engine_set_color (e, html_colorset_get_color (e->settings->color_set, HTMLLinkColor));
1741 }
1742
1743 void
html_engine_edit_set_link(HTMLEngine * e,const gchar * url,const gchar * target)1744 html_engine_edit_set_link (HTMLEngine *e,
1745 const gchar *url,
1746 const gchar *target)
1747 {
1748 if (html_engine_is_selection_active (e)) {
1749 HTMLEngineLinkInsertData data;
1750
1751 data.url = url;
1752 data.target = target;
1753 data.color = url
1754 ? html_colorset_get_color (e->settings->color_set, HTMLLinkColor)
1755 : html_colorset_get_color (e->settings->color_set, HTMLTextColor);
1756 html_engine_cut_and_paste (e,
1757 url ? "Insert link" : "Remove link",
1758 url ? "Remove link" : "Insert link",
1759 change_link, &data);
1760 } else
1761 html_engine_set_insertion_link (e, url, target);
1762 }
1763
1764 static void
prepare_empty_flow(HTMLEngine * e,HTMLUndoDirection dir)1765 prepare_empty_flow (HTMLEngine *e,
1766 HTMLUndoDirection dir)
1767 {
1768 if (!html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent))) {
1769 insert_empty_paragraph (e, dir, TRUE);
1770 if (e->cursor->object->parent->prev
1771 && html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent->prev))) {
1772 html_cursor_backward (e->cursor, e);
1773 } else if (!html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent))) {
1774 insert_empty_paragraph (e, dir, TRUE);
1775 html_cursor_backward (e->cursor, e);
1776 }
1777 }
1778 }
1779
1780 static void
append_object(HTMLEngine * e,HTMLObject * o,guint len,HTMLUndoDirection dir)1781 append_object (HTMLEngine *e,
1782 HTMLObject *o,
1783 guint len,
1784 HTMLUndoDirection dir)
1785 {
1786 HTMLObject *c, *cn;
1787 HTMLClue *clue;
1788 guint position_before;
1789
1790 html_engine_freeze (e);
1791 prepare_empty_flow (e, dir);
1792 position_before = e->cursor->position;
1793
1794 g_return_if_fail (html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent)));
1795
1796 clue = HTML_CLUE (e->cursor->object->parent);
1797 for (c = clue->head; c; c = cn) {
1798 cn = c->next;
1799 html_object_destroy (c);
1800 }
1801 clue->head = clue->tail = o;
1802 e->cursor->object = o;
1803 e->cursor->offset = 0;
1804 o->parent = HTML_OBJECT (clue);
1805
1806 html_cursor_forward_n (e->cursor, e, len);
1807 html_object_change_set (o, HTML_CHANGE_ALL_CALC);
1808 html_engine_thaw (e);
1809
1810 insert_setup_undo (e, len, position_before, dir, FALSE, FALSE);
1811
1812 return;
1813 }
1814
1815 void
html_engine_append_object(HTMLEngine * e,HTMLObject * o,guint len)1816 html_engine_append_object (HTMLEngine *e,
1817 HTMLObject *o,
1818 guint len)
1819 {
1820 html_undo_level_begin (e->undo, "Append object", "Remove appended object");
1821 append_object (e, o, len, HTML_UNDO_UNDO);
1822 html_undo_level_end (e->undo, e);
1823 }
1824
1825 static void
replace_objects_in_clue_from_another(HTMLClue * dest,HTMLClue * src)1826 replace_objects_in_clue_from_another (HTMLClue *dest,
1827 HTMLClue *src)
1828 {
1829 HTMLObject *cur, *next;
1830
1831 for (cur = dest->head; cur; cur = next) {
1832 next = cur->next;
1833 html_object_remove_child (cur->parent, cur);
1834 html_object_destroy (cur);
1835 }
1836
1837 for (cur = src->head; cur; cur = next) {
1838 next = cur->next;
1839 html_object_remove_child (cur->parent, cur);
1840 html_clue_append (dest, cur);
1841 }
1842 }
1843
1844 static void
append_flow(HTMLEngine * e,HTMLObject * o,guint len,HTMLUndoDirection dir)1845 append_flow (HTMLEngine *e,
1846 HTMLObject *o,
1847 guint len,
1848 HTMLUndoDirection dir)
1849 {
1850 HTMLObject *where;
1851 guint position, position_before;
1852
1853 html_engine_freeze (e);
1854
1855 position_before = e->cursor->position;
1856 prepare_empty_flow (e, dir);
1857
1858 g_return_if_fail (html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent)));
1859
1860 where = e->cursor->object->parent;
1861 html_object_change_set (o, HTML_CHANGE_ALL_CALC);
1862
1863 e->cursor->object = html_object_get_head_leaf (o);
1864 e->cursor->offset = 0;
1865
1866 /* be sure we have valid cursor position (like when there is a focusable container) */
1867 position = e->cursor->position;
1868 while (html_cursor_backward (e->cursor, e))
1869 ;
1870 e->cursor->position = position;
1871
1872 /* we move objects between flows to preserve attributes as indentation, ... */
1873 if (HTML_IS_CLUEFLOW (o)) {
1874 replace_objects_in_clue_from_another (HTML_CLUE (where), HTML_CLUE (o));
1875 html_object_destroy (o);
1876 } else {
1877 html_clue_append_after (HTML_CLUE (where->parent), o, where);
1878 html_object_remove_child (where->parent, where);
1879 html_object_destroy (where);
1880 }
1881
1882 html_cursor_forward_n (e->cursor, e, len);
1883 html_engine_thaw (e);
1884
1885 insert_setup_undo (e, len, position_before, dir, FALSE, FALSE);
1886
1887 return;
1888 }
1889
1890 void
html_engine_append_flow(HTMLEngine * e,HTMLObject * o,guint len)1891 html_engine_append_flow (HTMLEngine *e,
1892 HTMLObject *o,
1893 guint len)
1894 {
1895 html_undo_level_begin (e->undo, "Append flow", "Remove appended flow");
1896 append_flow (e, o, len, HTML_UNDO_UNDO);
1897 html_undo_level_end (e->undo, e);
1898 }
1899
1900 void
html_engine_cut_and_paste_begin(HTMLEngine * e,const gchar * undo_op_name,const gchar * redo_op_name)1901 html_engine_cut_and_paste_begin (HTMLEngine *e,
1902 const gchar *undo_op_name,
1903 const gchar *redo_op_name)
1904 {
1905 guint position;
1906 gint level;
1907
1908 html_engine_hide_cursor (e);
1909 html_engine_selection_push (e);
1910 html_engine_clipboard_push (e);
1911 html_undo_level_begin (e->undo, undo_op_name, redo_op_name);
1912 position = e->mark ? MAX (e->cursor->position, e->mark->position) : e->cursor->position;
1913 level = html_engine_cut (e);
1914
1915 e->cut_and_paste_stack = g_list_prepend (e->cut_and_paste_stack, GINT_TO_POINTER (level));
1916 e->cut_and_paste_stack = g_list_prepend (e->cut_and_paste_stack, GUINT_TO_POINTER (position));
1917 }
1918
1919 void
html_engine_cut_and_paste_end(HTMLEngine * e)1920 html_engine_cut_and_paste_end (HTMLEngine *e)
1921 {
1922 guint position;
1923 gint level;
1924
1925 position = GPOINTER_TO_UINT (e->cut_and_paste_stack->data);
1926 e->cut_and_paste_stack = g_list_remove (e->cut_and_paste_stack, e->cut_and_paste_stack->data);
1927 level = GPOINTER_TO_INT (e->cut_and_paste_stack->data);
1928 e->cut_and_paste_stack = g_list_remove (e->cut_and_paste_stack, e->cut_and_paste_stack->data);
1929
1930 if (e->clipboard) {
1931 insert_object (e, e->clipboard, e->clipboard_len, position, level, HTML_UNDO_UNDO, TRUE);
1932 e->clipboard = NULL;
1933 }
1934 html_undo_level_end (e->undo, e);
1935 html_engine_clipboard_pop (e);
1936 html_engine_selection_pop (e);
1937 html_engine_show_cursor (e);
1938 }
1939
1940 void
html_engine_cut_and_paste(HTMLEngine * e,const gchar * undo_op_name,const gchar * redo_op_name,HTMLObjectForallFunc iterator,gpointer data)1941 html_engine_cut_and_paste (HTMLEngine *e,
1942 const gchar *undo_op_name,
1943 const gchar *redo_op_name,
1944 HTMLObjectForallFunc iterator,
1945 gpointer data)
1946 {
1947 html_engine_edit_selection_updater_update_now (e->selection_updater);
1948 html_engine_cut_and_paste_begin (e, undo_op_name, redo_op_name);
1949 if (e->clipboard)
1950 html_object_forall (e->clipboard, e, iterator, data);
1951 html_engine_cut_and_paste_end (e);
1952 }
1953
1954 static void
delete_upto(HTMLEngine * e,HTMLCursor ** start,HTMLCursor ** end,HTMLObject * object,guint offset)1955 delete_upto (HTMLEngine *e,
1956 HTMLCursor **start,
1957 HTMLCursor **end,
1958 HTMLObject *object,
1959 guint offset)
1960 {
1961 guint position;
1962
1963 if (e->mark)
1964 html_cursor_destroy (e->mark);
1965 e->mark = *start;
1966 html_cursor_jump_to (e->cursor, e, object, offset);
1967 position = e->cursor->position;
1968 delete_object (e, NULL, NULL, HTML_UNDO_UNDO, TRUE);
1969 *start = html_cursor_dup (e->cursor);
1970 html_cursor_forward (*start, e);
1971 (*end)->position -= position - e->cursor->position;
1972 }
1973
1974 static gboolean
check_for_simple_containers(HTMLObject * child,HTMLObject * parent)1975 check_for_simple_containers (HTMLObject *child,
1976 HTMLObject *parent)
1977 {
1978 while (child && child != parent) {
1979 if (html_object_is_container (child)) {
1980 switch (child->klass->type) {
1981 case HTML_TYPE_CLUEFLOW:
1982 case HTML_TYPE_CLUEV:
1983 case HTML_TYPE_TABLECELL:
1984 case HTML_TYPE_TABLE:
1985 break;
1986 default:
1987 return FALSE;
1988 }
1989 }
1990 child = child->parent;
1991 }
1992
1993 return TRUE;
1994 }
1995
1996 static gboolean
check_for_simple_delete(HTMLObject * start,HTMLObject * end)1997 check_for_simple_delete (HTMLObject *start,
1998 HTMLObject *end)
1999 {
2000 HTMLObject *common_parent = get_common_parent (start, end);
2001
2002 if (common_parent && check_for_simple_containers (start->parent, common_parent) && check_for_simple_containers (end->parent, common_parent))
2003 return TRUE;
2004
2005 return FALSE;
2006 }
2007
2008 void
html_engine_delete(HTMLEngine * e)2009 html_engine_delete (HTMLEngine *e)
2010 {
2011 html_undo_level_begin (e->undo, "Delete", "Undelete");
2012 html_engine_edit_selection_updater_update_now (e->selection_updater);
2013 if (html_engine_is_selection_active (e)) {
2014 HTMLCursor *start = html_cursor_dup (e->mark->position < e->cursor->position ? e->mark : e->cursor);
2015 HTMLCursor *end = html_cursor_dup (e->mark->position < e->cursor->position ? e->cursor : e->mark);
2016 gint start_position = start->position;
2017 gint end_position = end->position;
2018
2019 if (end_position - start_position > 0) {
2020 gint len = end_position - start_position;
2021 g_signal_emit_by_name (e->widget, "object_delete", start_position, len);
2022 }
2023
2024 while (start->position < end->position) {
2025 if (check_for_simple_delete (start->object, end->object)) {
2026 if (e->mark)
2027 html_cursor_destroy (e->mark);
2028 html_cursor_destroy (e->cursor);
2029 e->mark = start;
2030 e->cursor = end;
2031 start = end = NULL;
2032 delete_object (e, NULL, NULL, HTML_UNDO_UNDO, TRUE);
2033 break;
2034 } else {
2035 HTMLObject *prev = NULL, *cur = start->object;
2036
2037 /* go thru current cluev */
2038 do {
2039 /* go thru current flow */
2040 while (cur) {
2041 /* lets look if container is whole contained in the selection */
2042 if (html_object_is_container (cur)) {
2043 html_cursor_jump_to (e->cursor, e, cur, html_object_get_length (cur));
2044 if (e->cursor->position > end->position) {
2045 /* it's not => delete upto this container */
2046
2047 delete_upto (e, &start, &end, cur, 0);
2048 prev = NULL;
2049 break;
2050 }
2051 }
2052 prev = cur;
2053 cur = html_object_next_not_slave (cur);
2054 }
2055 } while (prev && prev->parent->next && (cur = html_object_head (prev->parent->next)));
2056
2057 if (prev) {
2058 /* cluev end is in the selection. Lets handle this case just like simple delete
2059 since text is the selection itself */
2060 if (e->mark)
2061 html_cursor_destroy (e->mark);
2062 html_cursor_destroy (e->cursor);
2063 e->mark = start;
2064 e->cursor = end;
2065 start = end = NULL;
2066 delete_object (e, NULL, NULL, HTML_UNDO_UNDO, TRUE);
2067 break;
2068 }
2069 }
2070 }
2071
2072 if (start)
2073 html_cursor_destroy (start);
2074 if (end)
2075 html_cursor_destroy (end);
2076 html_cursor_jump_to_position (e->cursor, e, start_position);
2077
2078 }
2079 html_undo_level_end (e->undo, e);
2080 }
2081