source: trunk/third/gtkhtml/src/htmlengine-edit-cut-and-paste.c @ 18136

Revision 18136, 46.1 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18135, which included commits to RCS files with non-trunk default branches.
Line 
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., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, 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 "htmllinktext.h"
48#include "htmlobject.h"
49#include "htmlplainpainter.h"
50#include "htmltable.h"
51#include "htmltablecell.h"
52#include "htmlselection.h"
53#include "htmlsettings.h"
54#include "htmltext.h"
55#include "htmlundo.h"
56#include "htmlundo-action.h"
57
58static gint        delete_object (HTMLEngine *e, HTMLObject **ret_object, guint *ret_len, HTMLUndoDirection dir,
59                                  gboolean add_prop);
60static void        insert_object_for_undo (HTMLEngine *e, HTMLObject *obj, guint len, guint position_after, gint level,
61                                           HTMLUndoDirection dir, gboolean check);
62static void        append_object (HTMLEngine *e, HTMLObject *o, guint len, HTMLUndoDirection dir);
63static void        insert_empty_paragraph (HTMLEngine *e, HTMLUndoDirection dir, gboolean add_undo);
64static void        insert_setup_undo (HTMLEngine *e, guint len, guint position_before, HTMLUndoDirection dir,
65                                      gboolean delete_paragraph_before, gboolean delete_paragraph_after);
66
67/* helper functions -- need refactor */
68
69/* #define OP_DEBUG */
70
71static void
72html_cursor_get_left (HTMLCursor *cursor, HTMLObject **obj, gint *off)
73{
74        if (cursor->offset == 0) {
75                *obj = html_object_prev_not_slave (cursor->object);
76                if (*obj) {
77                        *off = html_object_get_length (*obj);
78                        return;
79                }
80        }
81        *obj = cursor->object;
82        *off = cursor->offset;
83}
84
85static void
86html_point_get_left (HTMLPoint *source, HTMLPoint *dest)
87{
88        if (source->offset == 0) {
89                dest->object = html_object_prev_not_slave (source->object);
90                if (dest->object) {
91                        dest->offset = html_object_get_length (dest->object);
92                        return;
93                }
94        }
95
96        *dest = *source;
97}
98
99static void
100html_point_get_right (HTMLPoint *source, HTMLPoint *dest)
101{
102        if (source->offset >= html_object_get_length (source->object)) {
103                dest->object = html_object_next_not_slave (source->object);
104                if (dest->object) {
105                        dest->offset = 0;
106                        return;
107                }
108        }
109
110        *dest = *source;
111}
112
113static void
114object_get_parent_list (HTMLObject *o, gint level, GList **list)
115{
116        while (level > 0 && o) {
117                *list = g_list_prepend (*list, o);
118                o = o->parent;
119                level--;
120        }
121}
122
123static GList *
124point_get_parent_list (HTMLPoint *point, gint level, gboolean include_offset)
125{
126        GList *list;
127        HTMLObject *o;
128
129        list = include_offset ? g_list_prepend (NULL, GINT_TO_POINTER (point->offset)) : NULL;
130        o    = point->object;
131
132        object_get_parent_list (point->object, level, &list);
133
134        return list;
135}
136
137static gint
138get_parent_depth (HTMLObject *o, HTMLObject *parent)
139{
140        gint level = 1;
141
142        while (o && parent && o != parent) {
143                o = o->parent;
144                level ++;
145        }
146
147        return level;
148}
149
150static gboolean
151is_parent (HTMLObject *o,HTMLObject *parent)
152{
153        while (o) {
154                if (o == parent)
155                        return TRUE;
156                o = o->parent;
157        }
158
159        return FALSE;
160}
161
162static HTMLObject *
163try_find_common_parent_of (HTMLObject *child, HTMLObject *parent)
164{
165        while (parent) {
166                if (is_parent (child, parent))
167                        return parent;
168                parent = parent->parent;
169        }
170
171        return NULL;
172}
173
174static HTMLObject *
175get_common_parent (HTMLObject *from, HTMLObject *to)
176{
177        HTMLObject *parent;
178
179        parent = try_find_common_parent_of (from, to);
180
181        return parent ? parent : try_find_common_parent_of (to, from);
182}
183
184static gint
185prepare_delete_bounds (HTMLEngine *e, GList **from_list, GList **to_list,
186                       GList **bound_left, GList **bound_right)
187{
188        HTMLPoint b_left, b_right, begin, end;
189        HTMLObject *common_parent;
190        gint ret_level;
191
192        g_assert (e->selection);
193
194        html_point_get_right (&e->selection->from, &begin);
195        html_point_get_left  (&e->selection->to,   &end);
196
197        common_parent = get_common_parent (begin.object, end.object);
198        ret_level     = html_object_get_parent_level (common_parent);
199        /* printf ("common parent level: %d\n", ret_level); */
200
201        *from_list = point_get_parent_list (&begin, get_parent_depth (begin.object, common_parent), TRUE);
202        *to_list   = point_get_parent_list (&end,   get_parent_depth (end.object, common_parent),   TRUE);
203
204        if (bound_left && bound_right) {
205                gint level;
206
207                html_point_get_left  (&e->selection->from, &b_left);
208                html_point_get_right (&e->selection->to,   &b_right);
209
210                common_parent = get_common_parent (b_left.object, b_right.object);
211
212                level = get_parent_depth (b_left.object, common_parent);
213                *bound_left  = b_left.object  ? point_get_parent_list (&b_left, level - 1, FALSE) : NULL;
214                if (level > 1 && *bound_left)
215                        *bound_left  = g_list_prepend (*bound_left, NULL);
216
217                level = get_parent_depth (b_right.object, common_parent);
218                *bound_right = b_right.object ? point_get_parent_list (&b_right, level - 1, FALSE) : NULL;
219                if (level > 1 && *bound_right)
220                        *bound_right = g_list_prepend (*bound_right, NULL);
221        }
222
223        return ret_level;
224}
225
226static void
227remove_empty_and_merge (HTMLEngine *e, gboolean merge, GList *left, GList *right, HTMLCursor *c)
228{
229        HTMLObject *lo, *ro, *prev;
230
231#ifdef OP_DEBUG
232        /* HTMLObject *left_orig = left->data; */
233        printf ("before merge\n");
234        gtk_html_debug_dump_tree_simple (e->clue, 0);
235        if (left && left->data) {
236                printf ("left\n");
237                gtk_html_debug_dump_tree_simple (left->data, 0);
238        }
239
240        if (right && right->data) {
241                printf ("right\n");
242                gtk_html_debug_dump_tree_simple (right->data, 0);
243        }
244#endif
245        while (left && left->data && right && right->data) {
246
247                lo  = HTML_OBJECT (left->data);
248                ro  = HTML_OBJECT (right->data);
249
250                left  = left->next;
251                right = right->next;
252
253                if (HTML_IS_CLUEALIGNED (lo) && !HTML_IS_CLUEALIGNED (ro) && html_object_is_text (HTML_CLUE (lo)->head)) {
254                        HTMLObject *nlo = lo->prev;
255
256                        if (e->cursor->object->parent && e->cursor->object->parent == lo) {
257                                e->cursor->object = ro;
258                                e->cursor->offset = 0;
259                        }
260                        if (c && c->object->parent && c->object->parent == lo) {
261                                c->object = ro;
262                                c->offset = 0;
263                        }
264
265                        html_object_remove_child (lo->parent, lo);
266                        html_object_destroy (lo);
267                        lo = nlo;
268                        if (!nlo)
269                                break;
270                } else if (HTML_IS_CLUEALIGNED (ro) && !HTML_IS_CLUEALIGNED (lo) && html_object_is_text (HTML_CLUE (ro)->head)) {
271                        HTMLObject *nro = ro->next;
272
273                        if (e->cursor->object->parent && e->cursor->object->parent == ro) {
274                                e->cursor->object = lo;
275                                e->cursor->offset = html_object_get_length (lo);
276                        }
277                        html_object_remove_child (ro->parent, ro);
278                        html_object_destroy (ro);
279                        ro = nro;
280                        if (!nro)
281                                break;
282                }
283
284                if (html_object_is_text (lo) && !*HTML_TEXT (lo)->text && (html_object_prev_not_slave (lo) || merge)) {
285                        HTMLObject *nlo = html_object_prev_not_slave (lo);
286
287                        if (e->cursor->object == lo) {
288                                e->cursor->object = ro;
289                                e->cursor->offset = 0;
290                        }
291                        if (c && c->object == lo) {
292                                c->object = ro;
293                                c->offset = 0;
294                        }
295
296                        html_object_remove_child (lo->parent, lo);
297                        html_object_destroy (lo);
298                        lo = nlo;
299                } else if (html_object_is_text (ro) && !*HTML_TEXT (ro)->text && (html_object_next_not_slave (ro) || merge)) {
300                        HTMLObject *nro = html_object_next_not_slave (ro);
301
302                        if (e->cursor->object == ro) {
303                                e->cursor->object = lo;
304                                e->cursor->offset = html_object_get_length (lo);
305                        }
306
307                        html_object_remove_child (ro->parent, ro);
308                        html_object_destroy (ro);
309                        ro = nro;
310                }
311
312                if (merge && lo && ro) {
313                        if (!html_object_merge (lo, ro, e, &left, &right, c))
314                                break;
315                        if (ro == e->cursor->object) {
316                                e->cursor->object  = lo;
317                                e->cursor->offset += html_object_get_length (lo);;
318                        }
319                }
320        }
321
322        prev = html_object_prev_not_slave (e->cursor->object);
323        if (prev && e->cursor->offset == 0) {
324                e->cursor->object = prev;
325                e->cursor->offset = html_object_get_length (e->cursor->object);
326        }
327#ifdef OP_DEBUG
328        /* printf ("-- finished\n");
329           gtk_html_debug_dump_tree_simple (left_orig, 0); */
330        printf ("-- after\n");
331        gtk_html_debug_dump_tree_simple (e->clue, 0);
332        printf ("-- END merge\n");
333#endif
334}
335
336static void
337split_and_add_empty_texts (HTMLEngine *e, gint level, GList **left, GList **right)
338{
339#ifdef OP_DEBUG
340        printf ("-- SPLIT begin\n");
341        gtk_html_debug_dump_tree_simple (e->clue, 0);
342        printf ("-- SPLIT middle\n");
343#endif
344        html_object_split (e->cursor->object, e, *right ? HTML_OBJECT ((*right)->data) : NULL,
345                           e->cursor->offset, level, left, right);
346#ifdef OP_DEBUG
347        printf ("-- SPLIT middle\n");
348        gtk_html_debug_dump_tree_simple (e->clue, 0);
349        printf ("-- SPLIT finish\n");
350        if (*left && (*left)->data) {
351                printf ("left\n");
352                gtk_html_debug_dump_tree_simple (HTML_OBJECT ((*left)->data), 0);
353        }
354        if (*right && (*right)->data) {
355                printf ("right\n");
356                gtk_html_debug_dump_tree_simple (HTML_OBJECT ((*right)->data), 0);
357        }
358        printf ("-- SPLIT end\n");
359#endif
360}
361
362/* end of helper */
363
364void
365html_engine_copy_object (HTMLEngine *e, HTMLObject **o, guint *len)
366{
367        GList *from, *to;
368
369        if (e->clue && HTML_CLUE (e->clue)->head && html_engine_is_selection_active (e)) {
370                prepare_delete_bounds (e, &from, &to, NULL, NULL);
371                *len = 0;
372                *o    = html_object_op_copy (HTML_OBJECT (from->data), NULL, e,
373                                             from->next, to->next, len);
374#ifdef OP_DEBUG
375                printf ("copy len: %d (parent %p)\n", *len, (*o)->parent);
376                gtk_html_debug_dump_tree_simple (*o, 0);
377#endif
378        } else {
379                *len = 0;
380                *o = NULL;
381        }
382}
383
384void
385html_engine_copy (HTMLEngine *e)
386{
387        html_engine_copy_object(e, &e->clipboard, &e->clipboard_len);
388}
389
390struct _DeleteUndo {
391        HTMLUndoData data;
392
393        HTMLObject *buffer;
394        guint       buffer_len;
395        gint        level;
396};
397typedef struct _DeleteUndo DeleteUndo;
398
399static void
400delete_undo_destroy (HTMLUndoData *data)
401{
402        DeleteUndo *undo = (DeleteUndo *) data;
403
404        if (undo->buffer)
405                html_object_destroy (undo->buffer);
406}
407
408static void
409delete_undo_action (HTMLEngine *e, HTMLUndoData *data, HTMLUndoDirection dir, guint position_after)
410{
411        DeleteUndo *undo;
412        HTMLObject *buffer;
413        guint       len = 0;
414
415        undo         = (DeleteUndo *) data;
416        buffer       = html_object_op_copy (undo->buffer, NULL, e, NULL, NULL, &len);
417        insert_object_for_undo (e, buffer, undo->buffer_len, position_after, undo->level, html_undo_direction_reverse (dir), TRUE);
418}
419
420static void
421delete_setup_undo (HTMLEngine *e, HTMLObject *buffer, guint len, guint position_after, gint level, HTMLUndoDirection dir)
422{
423        DeleteUndo *undo;
424
425        undo = g_new (DeleteUndo, 1);
426
427        /* printf ("cursor level: %d undo level: %d\n", html_object_get_parent_level (e->cursor->object), level); */
428        html_undo_data_init (HTML_UNDO_DATA (undo));
429        undo->data.destroy = delete_undo_destroy;
430        undo->buffer       = buffer;
431        undo->buffer_len   = len;
432        undo->level        = level;
433
434        /* printf ("delete undo len %d\n", len); */
435
436        html_undo_add_action (e->undo,
437                              html_undo_action_new ("Delete object", delete_undo_action,
438                                                    HTML_UNDO_DATA (undo), html_cursor_get_position (e->cursor),
439                                                    position_after),
440                              dir);
441}
442
443static void
444move_cursor_before_delete (HTMLEngine *e)
445{
446        if (e->cursor->offset == 0) {
447                if (html_object_prev_not_slave (e->cursor->object)) {
448                        HTMLObject *obj;
449                        gint off;
450
451                        html_cursor_get_left (e->cursor, &obj, &off);
452                        if (obj) {
453                                e->cursor->object = obj;
454                                e->cursor->offset = off;
455                        }
456                }
457        }
458}
459
460static void
461place_cursor_before_mark (HTMLEngine *e)
462{
463        if (e->mark->position < e->cursor->position) {
464                HTMLCursor *tmp;
465
466                tmp = e->cursor;
467                e->cursor = e->mark;
468                e->mark = tmp;
469        }
470}
471
472static gboolean
473haligns_equal (HTMLHAlignType a1, HTMLHAlignType a2)
474{
475        return a1 == a2
476                || (a1 == HTML_HALIGN_LEFT && a2 == HTML_HALIGN_NONE)
477                || (a1 == HTML_HALIGN_NONE && a2 == HTML_HALIGN_LEFT);
478
479}
480
481static gboolean
482levels_equal (HTMLClueFlow *me, HTMLClueFlow *you)
483{
484        if (!you)
485                return FALSE;
486
487        if (me->levels->len != you->levels->len)
488                return FALSE;
489
490        if (me->levels->len == 0)
491                return TRUE;
492
493        return !memcmp (me->levels->data, you->levels->data, you->levels->len);
494}
495
496static void
497check_flows (HTMLEngine *e, HTMLUndoDirection dir)
498{
499        /* I assume here that cursor is before mark */
500        HTMLClueFlow *flow1, *flow2;
501        gint level1, level2;
502
503        g_return_if_fail (e->cursor);
504        g_return_if_fail (e->cursor->object);
505        g_return_if_fail (e->cursor->object->parent);
506        g_return_if_fail (e->mark);
507        g_return_if_fail (e->mark->object);
508        g_return_if_fail (e->mark->object->parent);
509        g_return_if_fail (e->cursor->position <= e->mark->position);
510
511        if (e->cursor->offset || e->cursor->object->parent == e->mark->object->parent
512            || !HTML_IS_CLUEFLOW (e->cursor->object->parent) || !HTML_IS_CLUEFLOW (e->mark->object->parent)
513            || e->cursor->object != HTML_CLUE (e->cursor->object->parent)->head)
514                return;
515
516        level1 = html_object_get_parent_level (e->cursor->object->parent);
517        level2 = html_object_get_parent_level (e->mark->object->parent);
518
519        flow1 = HTML_CLUEFLOW (e->cursor->object->parent);
520        flow2 = HTML_CLUEFLOW (e->mark->object->parent);
521
522        if (level1 == level2
523            && (flow1->style != flow2->style
524                || (flow1->style == HTML_CLUEFLOW_STYLE_LIST_ITEM && flow1->item_type != flow2->item_type)
525                || !levels_equal (flow1, flow2)
526                || !haligns_equal (HTML_CLUE (flow1)->halign, HTML_CLUE (flow2)->halign))) {
527                HTMLCursor *dest, *source;
528
529                dest = html_cursor_dup (e->cursor);
530                source = html_cursor_dup (e->mark);
531
532                html_engine_selection_push (e);
533                html_engine_disable_selection (e);
534                html_cursor_jump_to_position_no_spell (e->cursor, e, dest->position);
535                html_engine_set_clueflow_style (e,
536                                                HTML_CLUEFLOW (source->object->parent)->style,
537                                                HTML_CLUEFLOW (source->object->parent)->item_type,
538                                                HTML_CLUE     (source->object->parent)->halign,
539                                                HTML_CLUEFLOW (source->object->parent)->levels->len,
540                                                HTML_CLUEFLOW (source->object->parent)->levels->data,
541                                                HTML_ENGINE_SET_CLUEFLOW_INDENTATION_ALL,
542                                                dir, TRUE);
543                html_engine_selection_pop (e);
544                html_cursor_destroy (source);
545                html_cursor_destroy (dest);
546        }
547}
548
549static gint
550delete_object_do (HTMLEngine *e, HTMLObject **object, guint *len, HTMLUndoDirection dir, gboolean add_undo)
551{
552        GList *from, *to, *left, *right;
553        guint position;
554        gint level;
555
556        html_engine_freeze (e);
557        level = prepare_delete_bounds (e, &from, &to, &left, &right);
558        place_cursor_before_mark (e);
559        if (add_undo)
560                check_flows (e, dir);
561        move_cursor_before_delete (e);
562        html_engine_disable_selection (e);
563        *len     = 0;
564        *object  = html_object_op_cut  (HTML_OBJECT (from->data), e, from->next, to->next, left, right, len);
565        position = e->cursor->position;
566        remove_empty_and_merge (e, TRUE, left ? left->next : NULL, right ? right->next : NULL, NULL);
567        e->cursor->position = position;
568        html_engine_spell_check_range (e, e->cursor, e->cursor);
569        html_engine_thaw (e);
570
571        return level;
572}
573
574static void
575check_table_0 (HTMLEngine *e)
576{
577        HTMLCursor *tail;
578
579        tail = e->mark->position < e->cursor->position ? e->cursor : e->mark;
580
581        if (html_cursor_backward (tail, e) && (!HTML_IS_TABLE (tail->object) || tail->offset))
582                html_cursor_forward (tail, e);
583        while (tail->offset == 0 && HTML_IS_TABLE (tail->object) && e->mark->position != e->cursor->position)
584                html_cursor_backward (tail, e);
585}
586
587static void
588check_table_1 (HTMLEngine *e)
589{
590        HTMLCursor *head;
591
592        head = e->mark->position > e->cursor->position ? e->cursor : e->mark;
593
594        if (html_cursor_forward (head, e) && (!HTML_IS_TABLE (head->object) || head->offset == 0))
595                html_cursor_backward (head, e);
596        while (head->offset == 1 && HTML_IS_TABLE (head->object) && e->mark->position != e->cursor->position)
597                html_cursor_forward (head, e);
598}
599
600static gboolean
601validate_tables (HTMLEngine *e, HTMLUndoDirection dir, gboolean add_undo, gboolean *fix_para)
602{
603        HTMLObject *next = html_object_next_not_slave (e->cursor->object);
604
605        *fix_para = FALSE;
606
607        if (next && HTML_IS_TABLE (next)) {
608                insert_empty_paragraph (e, dir, add_undo);
609                *fix_para = FALSE;
610
611                return TRUE;
612        } else if (!next) {
613                gint steps = 0;
614
615                while (html_cursor_forward (e->cursor, e)) {
616                        steps ++;
617                        if (HTML_IS_TABLE (e->cursor->object)) {
618                                next = html_object_next_not_slave (e->cursor->object);
619                                if (next) {
620                                        insert_empty_paragraph (e, dir, FALSE);
621                                        *fix_para = TRUE;
622                                        steps ++;
623                                        break;
624                                }
625                        } else
626                                break;
627                }
628
629                if (steps)
630                        html_cursor_backward_n (e->cursor, e, steps);
631        }
632
633        return FALSE;
634}
635
636static inline gboolean
637in_aligned (HTMLCursor *cursor)
638{
639        return cursor->object->parent && HTML_IS_CLUEALIGNED (cursor->object->parent);
640}
641
642struct _FixEmptyAlignedUndo {
643        HTMLUndoData data;
644
645        HTMLObject *ac;
646};
647typedef struct _FixEmptyAlignedUndo FixEmptyAlignedUndo;
648
649static void
650fix_empty_aligned_undo_destroy (HTMLUndoData *data)
651{
652        FixEmptyAlignedUndo *undo = (FixEmptyAlignedUndo *) data;
653
654        if (undo->ac)
655                html_object_destroy (undo->ac);
656}
657
658static void
659fix_empty_aligned_undo_action (HTMLEngine *e, HTMLUndoData *data, HTMLUndoDirection dir, guint position_after)
660{
661        HTMLObject *ac, *flow;
662
663        g_return_if_fail (html_object_is_text (e->cursor->object) && HTML_TEXT (e->cursor->object)->text_len == 0
664                          && e->cursor->object->parent && HTML_IS_CLUEFLOW (e->cursor->object->parent));
665
666        ac = ((FixEmptyAlignedUndo *) data)->ac;
667        ((FixEmptyAlignedUndo *) data)->ac = NULL;
668
669        html_engine_freeze (e);
670        flow = e->cursor->object->parent;
671        html_clue_remove_text_slaves (HTML_CLUE (flow));
672        html_clue_append_after (HTML_CLUE (flow), ac, e->cursor->object);
673        html_object_remove_child (flow, e->cursor->object);
674        html_clue_append (HTML_CLUE (ac), e->cursor->object);
675        html_object_change_set_down (flow, HTML_CHANGE_ALL);
676        html_engine_thaw (e);
677}
678
679static void
680fix_empty_aligned_setup_undo (HTMLEngine *e, HTMLUndoDirection dir, HTMLObject *ac)
681{
682        FixEmptyAlignedUndo *undo;
683
684        undo = g_new (FixEmptyAlignedUndo, 1);
685
686        html_undo_data_init (HTML_UNDO_DATA (undo));
687        undo->data.destroy = fix_empty_aligned_undo_destroy;
688        undo->ac       = ac;
689
690        html_undo_add_action (e->undo,
691                              html_undo_action_new ("Remove empty aligned", fix_empty_aligned_undo_action,
692                                                    HTML_UNDO_DATA (undo), html_cursor_get_position (e->cursor),
693                                                    html_cursor_get_position (e->cursor)),
694                              dir);
695}
696
697static void
698fix_empty_aligned (HTMLEngine *e, HTMLUndoDirection dir, gboolean add_undo)
699{
700        if (html_object_is_text (e->cursor->object) && e->cursor->object->parent && HTML_IS_CLUEALIGNED (e->cursor->object->parent)) {
701                HTMLObject *ac = e->cursor->object->parent;
702
703                if (ac->parent && HTML_IS_CLUEFLOW (ac->parent)) {
704                        html_engine_freeze (e);
705                        html_clue_remove_text_slaves (HTML_CLUE (ac));
706                        html_object_remove_child (ac, e->cursor->object);
707                        html_clue_append_after (HTML_CLUE (ac->parent), e->cursor->object, ac);
708                        html_object_change_set_down (ac->parent, HTML_CHANGE_ALL);
709                        html_object_remove_child (ac->parent, ac);
710                        if (add_undo)
711                                fix_empty_aligned_setup_undo (e, dir, ac);
712                        html_engine_thaw (e);
713                }
714        }
715}
716
717static gint
718delete_object (HTMLEngine *e, HTMLObject **ret_object, guint *ret_len, HTMLUndoDirection dir, gboolean add_undo)
719{
720        html_engine_edit_selection_updater_update_now (e->selection_updater);
721        if (html_engine_is_selection_active (e)) {
722                HTMLObject *object;
723                guint len, position_before, saved_position, end_position;
724                gint level;
725                gboolean backward;
726                gboolean fix_para;
727
728                end_position = MIN (e->cursor->position, e->mark->position);
729                if (HTML_IS_TABLE (e->cursor->object)
730                    || (e->cursor->object->parent && e->cursor->object->parent->parent && HTML_IS_TABLE_CELL (e->cursor->object->parent->parent))
731                    || HTML_IS_TABLE (e->mark->object)
732                    || (e->mark->object->parent && e->mark->object->parent->parent && HTML_IS_TABLE_CELL (e->mark->object->parent->parent))) {
733                        check_table_0 (e);
734                        check_table_1 (e);
735                }
736                if (e->cursor->position == e->mark->position) {
737                        html_engine_disable_selection (e);
738                        html_cursor_jump_to_position (e->cursor, e, end_position);
739                        return 0;
740                }
741
742                position_before = MAX (e->cursor->position, e->mark->position);
743                level = delete_object_do (e, &object, &len, dir, add_undo);
744                if (ret_object && ret_len) {
745                        *ret_object = html_object_op_copy (object, NULL, e, NULL, NULL, ret_len);
746                        *ret_len    = len;
747                }
748                backward = validate_tables (e, dir, add_undo, &fix_para);
749                if (fix_para) {
750                        saved_position = e->cursor->position;
751                        e->cursor->position = position_before + 1;
752                        insert_setup_undo (e, 1, position_before, dir, FALSE, FALSE);
753                        e->cursor->position = saved_position;
754                }
755                level = html_object_get_parent_level (e->cursor->object) - level + 1;
756                if (add_undo) {
757                        delete_setup_undo (e, object, len, position_before + (backward ? 1 : 0), level, dir);
758                } else
759                        html_object_destroy (object);
760
761                if (backward)
762                        html_cursor_backward (e->cursor, e);
763                gtk_html_editor_event (e->widget, GTK_HTML_EDITOR_EVENT_DELETE, NULL);
764                fix_empty_aligned (e, dir, add_undo);
765
766                return level;
767        }
768
769        return 0;
770}
771
772void
773html_engine_delete (HTMLEngine *e)
774{
775        html_undo_level_begin (e->undo, "Delete", "Undelete");
776        delete_object (e, NULL, NULL, HTML_UNDO_UNDO, TRUE);
777        html_undo_level_end (e->undo);
778}
779
780gint
781html_engine_cut (HTMLEngine *e)
782{
783        gint rv;
784
785        html_engine_clipboard_clear (e);
786        html_undo_level_begin (e->undo, "Cut", "Uncut");
787        rv = delete_object (e, &e->clipboard, &e->clipboard_len, HTML_UNDO_UNDO, TRUE);
788        html_undo_level_end (e->undo);
789
790#ifdef OP_DEBUG
791        printf ("cut  len: %d\n", e->clipboard_len);
792        gtk_html_debug_dump_tree_simple (e->clipboard, 0);
793#endif
794
795        return rv;
796}
797
798/*
799 * PASTE/INSERT
800 */
801
802static void
803set_cursor_at_end_of_object (HTMLEngine *e, HTMLObject *o, guint len)
804{
805        guint save_position;
806        gboolean need_spell_check;
807
808        save_position       = e->cursor->position;
809        e->cursor->object   = html_object_get_tail_leaf (o);
810        need_spell_check = e->need_spell_check;
811        e->need_spell_check = FALSE;
812        while (html_cursor_forward (e->cursor, e))
813                ;
814        e->need_spell_check = need_spell_check;
815        e->cursor->position = save_position + len;
816        e->cursor->offset   = html_object_get_length (e->cursor->object);
817}
818
819static inline void
820isolate_tables (HTMLEngine *e, HTMLUndoDirection dir, guint position_before, guint position_after,
821                  gboolean *delete_paragraph_before, gboolean *delete_paragraph_after)
822{
823        HTMLObject *next;
824
825        *delete_paragraph_after  = FALSE;
826        *delete_paragraph_before = FALSE;
827
828        html_cursor_jump_to_position_no_spell (e->cursor, e, position_after);
829        next = html_object_next_not_slave (e->cursor->object);
830        if (next && e->cursor->offset == html_object_get_length (e->cursor->object)
831            && (HTML_IS_TABLE (e->cursor->object) || HTML_IS_TABLE (next))) {
832                insert_empty_paragraph (e, dir, FALSE);
833                *delete_paragraph_after = TRUE;
834        }
835
836        html_cursor_jump_to_position_no_spell (e->cursor, e, position_before);
837        next = html_object_next_not_slave (e->cursor->object);
838        if (next && e->cursor->offset == html_object_get_length (e->cursor->object)
839            && (HTML_IS_TABLE (e->cursor->object) || HTML_IS_TABLE (next))) {
840                insert_empty_paragraph (e, dir, FALSE);
841                *delete_paragraph_before = TRUE;
842        }
843}
844
845static inline void
846insert_object_do (HTMLEngine *e, HTMLObject *obj, guint *len, gint level, guint position_after,
847                  gboolean check, HTMLUndoDirection dir)
848{
849        HTMLCursor *orig;
850        GList *left = NULL, *right = NULL;
851        GList *first = NULL, *last = NULL;
852        guint position_before;
853
854        html_engine_freeze (e);
855        position_before = e->cursor->position;
856        html_object_change_set_down (obj, HTML_CHANGE_ALL);
857        split_and_add_empty_texts (e, level, &left, &right);
858        orig = html_cursor_dup (e->cursor);
859        orig->position = position_before;
860        first = html_object_heads_list (obj);
861        last  = html_object_tails_list (obj);
862        set_cursor_at_end_of_object (e, obj, *len);
863
864        if ((left && left->data) || (right && (right->data))) {
865                HTMLObject *parent, *where;
866                if (left && left->data) {
867                        where  = HTML_OBJECT (left->data);
868                        parent = where->parent;
869                } else {
870                        where  = NULL;
871                        parent = HTML_OBJECT (right->data)->parent;
872                }
873                if (parent && html_object_is_clue (parent))
874                        html_clue_append_after (HTML_CLUE (parent), obj, where);
875        }
876
877#ifdef OP_DEBUG
878        printf ("position before merge %d\n", e->cursor->position);
879#endif
880        remove_empty_and_merge (e, TRUE, last, right, orig);
881        remove_empty_and_merge (e, TRUE, left, first, orig);
882#ifdef OP_DEBUG
883        printf ("position after merge %d\n", e->cursor->position);
884#endif
885
886        html_cursor_destroy (e->cursor);
887        e->cursor = html_cursor_dup (orig);
888        html_cursor_jump_to_position_no_spell (e->cursor, e, position_after);
889
890        if (check)
891                html_engine_spell_check_range (e, orig, e->cursor);
892        html_cursor_destroy (orig);
893        html_engine_thaw (e);
894}
895
896struct _InsertUndo {
897        HTMLUndoData data;
898
899        guint len;
900        gboolean delete_paragraph_before;
901        gboolean delete_paragraph_after;
902};
903typedef struct _InsertUndo InsertUndo;
904
905static void
906insert_undo_action (HTMLEngine *e, HTMLUndoData *data, HTMLUndoDirection dir, guint position_after)
907{
908        InsertUndo *undo;
909
910        undo = (InsertUndo *) data;
911
912        html_engine_set_mark (e);
913        html_cursor_jump_to_position (e->cursor, e, position_after);
914        delete_object (e, NULL, NULL, html_undo_direction_reverse (dir), TRUE);
915
916        if (undo->delete_paragraph_after || undo->delete_paragraph_before) {
917                html_cursor_jump_to_position (e->cursor, e, position_after);
918                if (undo->delete_paragraph_before) {
919                        html_cursor_backward (e->cursor, e);
920                }
921                html_engine_set_mark (e);
922                if (undo->delete_paragraph_before) {
923                        html_cursor_forward (e->cursor, e);
924                }
925                if (undo->delete_paragraph_after) {
926                        html_cursor_forward (e->cursor, e);
927                }
928                delete_object (e, NULL, NULL, HTML_UNDO_UNDO, FALSE);
929        }
930}
931
932static void
933insert_setup_undo (HTMLEngine *e, guint len, guint position_before, HTMLUndoDirection dir,
934                   gboolean delete_paragraph_before, gboolean delete_paragraph_after)
935{
936        InsertUndo *undo;
937
938        undo = g_new (InsertUndo, 1);
939
940        html_undo_data_init (HTML_UNDO_DATA (undo));
941        undo->len = len;
942        undo->delete_paragraph_before = delete_paragraph_before;
943        undo->delete_paragraph_after  = delete_paragraph_after;
944
945        /* printf ("insert undo len %d\n", len); */
946
947        html_undo_add_action (e->undo,
948                              html_undo_action_new ("Insert", insert_undo_action,
949                                                    HTML_UNDO_DATA (undo),
950                                                    html_cursor_get_position (e->cursor),
951                                                    position_before),
952                              dir);
953}
954
955static gboolean fix_aligned_position (HTMLEngine *e, guint *position_after, HTMLUndoDirection dir);
956
957static void
958fix_aligned_redo_action (HTMLEngine *e, HTMLUndoData *data, HTMLUndoDirection dir, guint position_after)
959{
960        guint pa;
961
962        fix_aligned_position (e, &pa, html_undo_direction_reverse (dir));
963}
964
965static void
966fix_aligned_undo_action (HTMLEngine *e, HTMLUndoData *data, HTMLUndoDirection dir, guint position_after)
967{
968        HTMLObject *cf = e->cursor->object->parent;
969        HTMLUndoData *undo;
970        guint position_before = e->cursor->position;
971
972        undo = g_new (HTMLUndoData, 1);
973
974        if (!html_cursor_forward (e->cursor, e))
975                g_assert (html_cursor_backward (e->cursor, e));
976        else
977                e->cursor->position --;
978
979        html_clue_remove (HTML_CLUE (cf->parent), cf);
980        html_object_destroy (cf);
981
982        html_undo_add_action (e->undo,
983                              html_undo_action_new ("Fix aligned", fix_aligned_redo_action,
984                                                    undo, html_cursor_get_position (e->cursor),
985                                                    position_before),
986                              html_undo_direction_reverse (dir));
987}
988
989static void
990fix_align_setup_undo (HTMLEngine *e, guint position_before, HTMLUndoDirection dir)
991{
992        HTMLUndoData *undo;
993
994        undo = g_new (HTMLUndoData, 1);
995
996        html_undo_data_init (HTML_UNDO_DATA (undo));
997        /* printf ("insert undo len %d\n", len); */
998
999        html_undo_add_action (e->undo,
1000                              html_undo_action_new ("Undo aligned fix", fix_aligned_undo_action,
1001                                                    undo, html_cursor_get_position (e->cursor),
1002                                                    position_before),
1003                              dir);
1004}
1005
1006static gboolean
1007fix_aligned_position (HTMLEngine *e, guint *position_after, HTMLUndoDirection dir)
1008{
1009        gboolean rv = FALSE;
1010        if (in_aligned (e->cursor)) {
1011                /* printf ("in aligned\n"); */
1012                if (e->cursor->offset) {
1013                        if (html_cursor_forward (e->cursor, e))
1014                                (*position_after) ++;
1015                        if (in_aligned (e->cursor)) {
1016                                HTMLObject *cf;
1017                                HTMLObject *cluev;
1018                                HTMLObject *flow;
1019
1020                                /* printf ("aligned: needs fixing\n"); */
1021                                html_engine_freeze (e);
1022                                cf = html_clueflow_new_from_flow (HTML_CLUEFLOW (e->cursor->object->parent->parent));
1023                                flow = e->cursor->object->parent->parent;
1024                                cluev = flow->parent;
1025                                e->cursor->object = html_engine_new_text_empty (e);
1026                                html_clue_append (HTML_CLUE (cf), e->cursor->object);
1027                                html_clue_append_after (HTML_CLUE (cluev), cf, flow);
1028                                e->cursor->offset = 0;
1029                                e->cursor->position ++;
1030                                (*position_after) ++;
1031#ifdef OP_DEBUG
1032                                gtk_html_debug_dump_tree_simple (e->clue, 0);
1033#endif
1034                                fix_align_setup_undo (e, e->cursor->position, dir);
1035                                html_engine_thaw (e);
1036                                rv = TRUE;
1037                                if (e->cursor->object->parent && HTML_IS_CLUEALIGNED (e->cursor->object->parent))
1038                                        html_cursor_forward (e->cursor, e);
1039
1040                        }
1041                } else {
1042                        if (html_cursor_backward (e->cursor, e))
1043                                (*position_after) --;
1044                        if (in_aligned (e->cursor)) {
1045                                HTMLObject *cf;
1046                                HTMLObject *cluev;
1047                                HTMLObject *flow;
1048
1049                                /* printf ("aligned: needs fixing\n"); */
1050                                html_engine_freeze (e);
1051                                cf = html_clueflow_new_from_flow (HTML_CLUEFLOW (e->cursor->object->parent->parent));
1052                                flow = e->cursor->object->parent->parent;
1053                                cluev = flow->parent;
1054                                e->cursor->object = html_engine_new_text_empty (e);
1055                                html_clue_append (HTML_CLUE (cf), e->cursor->object);
1056                                if (flow->prev)
1057                                        html_clue_append_after (HTML_CLUE (cluev), cf, flow->prev);
1058                                else
1059                                        html_clue_prepend (HTML_CLUE (cluev), cf);
1060                                e->cursor->offset = 0;
1061#ifdef OP_DEBUG
1062                                gtk_html_debug_dump_tree_simple (e->clue, 0);
1063#endif
1064                                fix_align_setup_undo (e, e->cursor->position, dir);
1065                                html_engine_thaw (e);
1066                                rv = TRUE;
1067                        }
1068                }
1069        }
1070
1071        return rv;
1072}
1073
1074static void
1075insert_object_for_undo (HTMLEngine *e, HTMLObject *obj, guint len, guint position_after, gint level,
1076               HTMLUndoDirection dir, gboolean check)
1077{
1078        gboolean delete_paragraph_before = FALSE;
1079        gboolean delete_paragraph_after = FALSE;
1080        guint position_before;
1081
1082        position_before = e->cursor->position;
1083        insert_object_do (e, obj, &len, level, position_after, check, dir);
1084        isolate_tables (e, dir, position_before, position_after, &delete_paragraph_before, &delete_paragraph_after);
1085        html_cursor_jump_to_position_no_spell (e->cursor, e, position_after + (delete_paragraph_before ? 1 : 0));
1086        insert_setup_undo (e, len, position_before + (delete_paragraph_before ? 1 : 0),
1087                           dir, delete_paragraph_before, delete_paragraph_after);
1088}
1089
1090static void
1091insert_object (HTMLEngine *e, HTMLObject *obj, guint len, guint position_after, gint level,
1092               HTMLUndoDirection dir, gboolean check)
1093{
1094        fix_aligned_position (e, &position_after, dir);
1095        insert_object_for_undo (e, obj, len, position_after, level, dir, check);
1096}
1097
1098void
1099html_engine_insert_object (HTMLEngine *e, HTMLObject *o, guint len, gint level)
1100{
1101        insert_object (e, o, len, e->cursor->position + len, level, HTML_UNDO_UNDO, TRUE);
1102}
1103
1104void
1105html_engine_paste_object (HTMLEngine *e, HTMLObject *o, guint len)
1106{
1107        html_undo_level_begin (e->undo, "Paste", "Paste");
1108        html_engine_delete (e);
1109        html_engine_insert_object (e, o, len, html_object_get_insert_level (o));
1110        html_undo_level_end (e->undo);
1111}
1112
1113void
1114html_engine_paste (HTMLEngine *e)
1115{
1116        if (e->clipboard) {
1117                HTMLObject *copy;
1118                guint len = 0;
1119
1120                copy = html_object_op_copy (e->clipboard, NULL, e, NULL, NULL, &len);
1121                html_engine_paste_object (e, copy, e->clipboard_len);
1122        }
1123}
1124
1125static void
1126check_magic_link (HTMLEngine *e, const gchar *text, guint len)
1127{
1128        if (HTML_IS_TEXT (e->cursor->object)
1129            && GTK_HTML_PROPERTY (e->widget, magic_links) && len == 1
1130            && (*text == ' ' || text [0] == '\n' || text [0] == '>' || text [0] == ')'))
1131                html_text_magic_link (HTML_TEXT (e->cursor->object), e, html_object_get_length (e->cursor->object));
1132}
1133
1134static void
1135insert_empty_paragraph (HTMLEngine *e, HTMLUndoDirection dir, gboolean add_undo)
1136{
1137        GList *left=NULL, *right=NULL;
1138        HTMLCursor *orig;
1139        guint position_before;
1140        guint position_after;
1141
1142        if (dir == HTML_UNDO_UNDO)
1143                if (fix_aligned_position (e, &position_after, dir))
1144                        return;
1145
1146        html_engine_freeze (e);
1147
1148        position_before = e->cursor->position;
1149        orig = html_cursor_dup (e->cursor);
1150        split_and_add_empty_texts (e, 2, &left, &right);
1151        remove_empty_and_merge (e, FALSE, left, right, orig);
1152
1153        /* replace empty link in empty flow by text with the same style */
1154        if (HTML_IS_LINK_TEXT (e->cursor->object) && html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent))) {
1155                HTMLObject *flow = e->cursor->object->parent;
1156                HTMLObject *new_text;
1157
1158                new_text = html_link_text_to_text (HTML_LINK_TEXT (e->cursor->object), e);
1159                html_clue_remove (HTML_CLUE (flow), e->cursor->object);
1160                html_object_destroy (e->cursor->object);
1161                if (orig->object == e->cursor->object) {
1162                        orig->object = NULL;
1163                }
1164                e->cursor->object = new_text;
1165                if (!orig->object) {
1166                        orig->object = e->cursor->object;
1167                }
1168                html_clue_append (HTML_CLUE (flow), e->cursor->object);
1169        }
1170
1171        html_cursor_forward (e->cursor, e);
1172
1173        /* replace empty text in new empty flow by text with current style */
1174        if (html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent))) {
1175                HTMLObject *flow = e->cursor->object->parent;
1176
1177                html_clue_remove (HTML_CLUE (flow), e->cursor->object);
1178                html_object_destroy (e->cursor->object);
1179                e->cursor->object = html_engine_new_text_empty (e);
1180                html_clue_append (HTML_CLUE (flow), e->cursor->object);
1181        }
1182
1183        if (add_undo) {
1184                html_undo_level_begin (e->undo, "Insert paragraph", "Delete paragraph");
1185                insert_setup_undo (e, 1, position_before, dir, FALSE, FALSE);
1186        }
1187        g_list_free (left);
1188        g_list_free (right);
1189        html_engine_spell_check_range (e, orig, e->cursor);
1190        html_cursor_destroy (orig);
1191
1192        html_cursor_backward (e->cursor, e);
1193        check_magic_link (e, "\n", 1);
1194        html_cursor_forward (e->cursor, e);
1195       
1196        gtk_html_editor_event_command (e->widget, GTK_HTML_COMMAND_INSERT_PARAGRAPH, FALSE);
1197        if (add_undo)
1198                html_undo_level_end (e->undo);
1199
1200        html_engine_thaw (e);
1201}
1202
1203void
1204html_engine_insert_empty_paragraph (HTMLEngine *e)
1205{
1206        insert_empty_paragraph (e, HTML_UNDO_UNDO, TRUE);
1207}
1208
1209static char *picto_chars = "DO)(|/P\0:-\0:\0:-\0:\0:;=-\0:;\0:-~\0:\0:\0:-\0:\0:-\0:\0:-\0:\0";
1210static gint picto_states [] = { 8, 13, 18, 26, 34, 39, 44, 0, -1, 11, 0, -1, 0, -2, 16, 0, -2, 0, -3, -4, -5, 23, 0, -3, -4, 0, -6, 30, 32, 0, -6, 0, -11, 0, -8, 37, 0, -8, 0, -9, 42, 0, -9, 0, -10, 47, 0, -10, 0};
1211static gchar *picto_images [] = {
1212        "smiley-1.png",
1213        "smiley-2.png",
1214        "smiley-3.png",
1215        "smiley-4.png",
1216        "smiley-5.png",
1217        "smiley-6.png",
1218        "smiley-7.png",
1219        "smiley-8.png",
1220        "smiley-9.png",
1221        "smiley-10.png",
1222        "smiley-11.png",
1223};
1224
1225static void
1226use_pictograms (HTMLEngine *e)
1227{
1228        gint pos;
1229        gint state;
1230        gint relative;
1231        gunichar uc;
1232
1233        if (!html_object_is_text (e->cursor->object))
1234                return;
1235
1236        pos = e->cursor->offset - 1;
1237        state = 0;
1238        while (pos >= 0) {
1239                uc = html_text_get_char (HTML_TEXT (e->cursor->object), pos);
1240                relative = 0;
1241                while (picto_chars [state + relative]) {
1242                        if (picto_chars [state + relative] == uc)
1243                                break;
1244                        relative ++;
1245                }
1246                state = picto_states [state + relative];
1247                /* 0 .. not found, -n .. found n-th */
1248                if (state <= 0)
1249                        break;
1250                pos --;
1251        }
1252
1253        if (state < 0) {
1254                HTMLObject *picto;
1255                gchar *filename;
1256                gchar *alt;
1257                gint len;
1258
1259                if (pos > 0) {
1260                        uc = html_text_get_char (HTML_TEXT (e->cursor->object), pos - 1);
1261                        if (uc != ' ' && uc != '\t')
1262                                return;
1263                }
1264                /* printf ("found %d\n", -state); */
1265                len = e->cursor->offset  - pos;
1266                alt = g_strndup (html_text_get_text (HTML_TEXT (e->cursor->object), pos), len);
1267                html_cursor_backward_n (e->cursor, e, len);
1268                html_engine_set_mark (e);
1269                html_cursor_forward_n (e->cursor, e, len);
1270
1271                filename = g_strconcat ("file://" ICONDIR "/", picto_images [-state - 1], NULL);
1272                picto = html_image_new (e->image_factory, filename, NULL, NULL, -1, -1, FALSE, FALSE, 0, NULL,
1273                                        HTML_VALIGN_MIDDLE, FALSE);
1274                html_image_set_alt (HTML_IMAGE (picto), alt);
1275                g_free (alt);
1276                html_engine_paste_object (e, picto, html_object_get_length (picto));
1277        }
1278}
1279
1280void
1281html_engine_insert_text (HTMLEngine *e, const gchar *text, guint len)
1282{
1283        gchar *nl;
1284        gint alen;
1285
1286        if (len == -1)
1287                len = g_utf8_strlen (text, -1);
1288        if (!len)
1289                return;
1290
1291        html_undo_level_begin (e->undo, "Insert text", "Delete text");
1292        /* FIXME add insert text event */
1293        gtk_html_editor_event_command (e->widget, GTK_HTML_COMMAND_INSERT_PARAGRAPH, TRUE);
1294
1295        do {
1296                nl   = g_utf8_strchr (text, '\n');
1297                alen = nl ? g_utf8_pointer_to_offset (text, nl) : len;
1298                if (alen) {
1299                        HTMLObject *o;
1300                        gboolean check = FALSE;
1301
1302                        check_magic_link (e, text, alen);
1303
1304                        /* stop inserting links after space */
1305                        if (*text == ' ')
1306                                html_engine_set_insertion_link (e, NULL, NULL);
1307
1308                        o = html_engine_new_text (e, text, alen);
1309                        html_text_convert_nbsp (HTML_TEXT (o), TRUE);
1310
1311                        if (alen == 1 && html_is_in_word (html_text_get_char (HTML_TEXT (o), 0))
1312                            && !html_is_in_word (html_cursor_get_current_char (e->cursor))) {
1313                                /* printf ("need_spell_check\n"); */
1314                                e->need_spell_check = TRUE;
1315                        } else {
1316                                check = TRUE;
1317                        }
1318                        insert_object (e, o, html_object_get_length (o), e->cursor->position + html_object_get_length (o),
1319                                       1, HTML_UNDO_UNDO, check);
1320                        if (alen == 1 && !HTML_IS_PLAIN_PAINTER (e->painter) && GTK_HTML_PROPERTY (e->widget, magic_smileys))
1321                                use_pictograms (e);
1322                }
1323                if (nl) {
1324                        html_engine_insert_empty_paragraph (e);
1325                        len -= g_utf8_pointer_to_offset (text, nl) + 1;
1326                        text = nl + 1;
1327                }
1328        } while (nl);
1329        html_undo_level_end (e->undo);
1330}
1331
1332void
1333html_engine_paste_text (HTMLEngine *e, const gchar *text, guint len)
1334{
1335        gchar *undo_name = g_strdup_printf ("Paste text: '%s'", text);
1336        gchar *redo_name = g_strdup_printf ("Unpaste text: '%s'", text);
1337
1338        html_undo_level_begin (e->undo, undo_name, redo_name);
1339        g_free (undo_name);
1340        g_free (redo_name);
1341        html_engine_delete (e);
1342        html_engine_insert_text (e, text, len);
1343        html_undo_level_end (e->undo);
1344}
1345
1346void
1347html_engine_delete_container (HTMLEngine *e)
1348{
1349        g_assert (HTML_IS_ENGINE (e));
1350        g_assert (e->cursor->object);
1351        g_assert (html_object_is_container (e->cursor->object));
1352
1353        html_engine_set_mark (e);
1354        html_engine_update_selection_if_necessary (e);
1355        html_engine_freeze (e);
1356        if (e->cursor->offset)
1357                html_cursor_beginning_of_line (e->cursor, e);
1358        else
1359                html_cursor_end_of_line (e->cursor, e);
1360        html_engine_delete (e);
1361        html_engine_thaw (e);
1362}
1363
1364void
1365html_engine_delete_n (HTMLEngine *e, guint len, gboolean forward)
1366{
1367        if (html_engine_is_selection_active (e))
1368                html_engine_delete (e);
1369        else {
1370                html_engine_block_selection (e);
1371                html_engine_set_mark (e);
1372                html_engine_update_selection_if_necessary (e);
1373                html_engine_freeze (e);
1374                while (len != 0) {
1375                        if (forward)
1376                                html_cursor_forward (e->cursor, e);
1377                        else
1378                                html_cursor_backward (e->cursor, e);
1379                        len --;
1380                }
1381                html_engine_delete (e);
1382                html_engine_unblock_selection (e);
1383                html_engine_thaw (e);   
1384        }
1385}
1386
1387void
1388html_engine_cut_line (HTMLEngine *e)
1389{
1390        g_return_if_fail (e != NULL);
1391        g_return_if_fail (HTML_IS_ENGINE (e));
1392
1393        html_undo_level_begin (e->undo, "Cut Line", "Undo Cut Line");
1394        html_engine_set_mark (e);
1395        html_engine_end_of_line (e);
1396
1397        if (e->cursor->position == e->mark->position)
1398                html_cursor_forward (e->cursor, e);
1399
1400        html_engine_cut (e);
1401        html_undo_level_end (e->undo);
1402}
1403
1404typedef struct {
1405        HTMLColor   *color;
1406        const gchar *url;
1407        const gchar *target;
1408} HTMLEngineLinkInsertData;
1409
1410static void
1411change_link (HTMLObject *o, HTMLEngine *e, gpointer data)
1412{
1413        HTMLObject *changed;
1414        HTMLEngineLinkInsertData *d = (HTMLEngineLinkInsertData *) data;
1415
1416        changed = d->url ? html_object_set_link (o, d->color, d->url, d->target) : html_object_remove_link (o, d->color);
1417        if (changed) {
1418                if (o->parent) {
1419                        HTMLObject *prev;
1420
1421                        prev = o->prev;
1422                        g_assert (HTML_OBJECT_TYPE (o->parent) == HTML_TYPE_CLUEFLOW);
1423
1424                        html_clue_append_after (HTML_CLUE (o->parent), changed, o);
1425                        html_clue_remove (HTML_CLUE (o->parent), o);
1426                        html_object_destroy (o);
1427                        if (changed->prev)
1428                                html_object_merge (changed->prev, changed, e, NULL, NULL, NULL);
1429                } else {
1430                        html_object_destroy (e->clipboard);
1431                        e->clipboard     = changed;
1432                        e->clipboard_len = html_object_get_length (changed);
1433                }
1434        }
1435}
1436
1437void
1438html_engine_set_insertion_link (HTMLEngine *e, const gchar *url, const gchar *target)
1439{
1440        html_engine_set_url    (e, url);
1441        html_engine_set_target (e, target);
1442        if (!url && e->insertion_color == html_colorset_get_color (e->settings->color_set, HTMLLinkColor))
1443                html_engine_set_color (e, html_colorset_get_color (e->settings->color_set, HTMLTextColor));
1444        else if (url)
1445                html_engine_set_color (e, html_colorset_get_color (e->settings->color_set, HTMLLinkColor));
1446}
1447
1448void
1449html_engine_edit_set_link (HTMLEngine *e, const gchar *url, const gchar *target)
1450{
1451        if (html_engine_is_selection_active (e)) {
1452                HTMLEngineLinkInsertData data;
1453
1454                data.url    = url;
1455                data.target = target;
1456                data.color  = url
1457                        ? html_colorset_get_color (e->settings->color_set, HTMLLinkColor)
1458                        : html_colorset_get_color (e->settings->color_set, HTMLTextColor);
1459                html_engine_cut_and_paste (e,
1460                                           url ? "Insert link" : "Remove link",
1461                                           url ? "Remove link" : "Insert link",
1462                                           change_link, &data);
1463        } else
1464                html_engine_set_insertion_link (e, url, target);
1465}
1466
1467static void
1468prepare_empty_flow (HTMLEngine *e, HTMLUndoDirection dir)
1469{
1470        if (!html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent))) {
1471                insert_empty_paragraph (e, dir, TRUE);
1472                if (e->cursor->object->parent->prev
1473                    && html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent->prev))) {
1474                        html_cursor_backward (e->cursor, e);
1475                } else if (!html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent))) {
1476                        insert_empty_paragraph (e, dir, TRUE);
1477                        html_cursor_backward (e->cursor, e);
1478                }
1479        }
1480}
1481
1482static void
1483append_object (HTMLEngine *e, HTMLObject *o, guint len, HTMLUndoDirection dir)
1484{
1485        HTMLObject *c, *cn;
1486        HTMLClue *clue;
1487        guint position_before;
1488
1489        html_engine_freeze (e);
1490        prepare_empty_flow (e, dir);
1491        position_before = e->cursor->position;
1492
1493        g_return_if_fail (html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent)));
1494
1495        clue = HTML_CLUE (e->cursor->object->parent);
1496        for (c = clue->head; c; c = cn) {
1497                cn = c->next;
1498                html_object_destroy (c);
1499        }
1500        clue->head = clue->tail = o;
1501        e->cursor->object = o;
1502        e->cursor->offset = 0;
1503        o->parent = HTML_OBJECT (clue);
1504
1505        html_cursor_forward_n (e->cursor, e, len);
1506        html_object_change_set (o, HTML_CHANGE_ALL_CALC);
1507        html_engine_thaw (e);
1508
1509        insert_setup_undo (e, len, position_before, dir, FALSE, FALSE);
1510
1511        return;
1512}
1513
1514void
1515html_engine_append_object (HTMLEngine *e, HTMLObject *o, guint len)
1516{
1517        html_undo_level_begin (e->undo, "Append object", "Remove appended object");
1518        append_object (e, o, len, HTML_UNDO_UNDO);
1519        html_undo_level_end (e->undo);
1520}
1521
1522static void
1523replace_objects_in_clue_from_another (HTMLClue *dest, HTMLClue *src)
1524{
1525        HTMLObject *cur, *next;
1526
1527        for (cur = dest->head; cur; cur = next) {
1528                next = cur->next;
1529                html_object_remove_child (cur->parent, cur);
1530                html_object_destroy (cur);
1531        }
1532
1533        for (cur = src->head; cur; cur = next) {
1534                next = cur->next;
1535                html_object_remove_child (cur->parent, cur);
1536                html_clue_append (dest, cur);
1537        }
1538}
1539
1540static void
1541append_flow (HTMLEngine *e, HTMLObject *o, guint len, HTMLUndoDirection dir)
1542{
1543        HTMLObject *where;
1544        guint position, position_before;
1545
1546        html_engine_freeze (e);
1547
1548        position_before = e->cursor->position;
1549        prepare_empty_flow (e, dir);
1550
1551        g_return_if_fail (html_clueflow_is_empty (HTML_CLUEFLOW (e->cursor->object->parent)));
1552
1553        where = e->cursor->object->parent;
1554        html_object_change_set (o, HTML_CHANGE_ALL_CALC);
1555
1556        e->cursor->object = html_object_get_head_leaf (o);
1557        e->cursor->offset = 0;
1558
1559        /* be sure we have valid cursor position (like when there is a focusable container) */
1560        position = e->cursor->position;
1561        while (html_cursor_backward (e->cursor, e))
1562                ;
1563        e->cursor->position = position;
1564
1565        /* we move objects between flows to preserve attributes as indentation, ... */
1566        if (HTML_IS_CLUEFLOW (o)) {
1567                replace_objects_in_clue_from_another (HTML_CLUE (where), HTML_CLUE (o));
1568                html_object_destroy (o);
1569        } else {
1570                html_clue_append_after (HTML_CLUE (where->parent), o, where);
1571                html_object_remove_child (where->parent, where);
1572                html_object_destroy (where);
1573        }
1574
1575        html_cursor_forward_n (e->cursor, e, len);
1576        html_engine_thaw (e);
1577
1578        insert_setup_undo (e, len, position_before, dir, FALSE, FALSE);
1579
1580        return;
1581}
1582
1583void
1584html_engine_append_flow (HTMLEngine *e, HTMLObject *o, guint len)
1585{
1586        html_undo_level_begin (e->undo, "Append flow", "Remove appended flow");
1587        append_flow (e, o, len, HTML_UNDO_UNDO);
1588        html_undo_level_end (e->undo);
1589}
1590
1591void
1592html_engine_cut_and_paste_begin (HTMLEngine *e, const gchar *undo_op_name, const gchar *redo_op_name)
1593{
1594        guint position;
1595        gint level;
1596
1597        html_engine_hide_cursor (e);
1598        html_engine_selection_push (e);
1599        html_engine_clipboard_push (e);
1600        html_undo_level_begin (e->undo, undo_op_name, redo_op_name);
1601        position = e->mark ? MAX (e->cursor->position, e->mark->position) : e->cursor->position;
1602        level = html_engine_cut (e);
1603
1604        e->cut_and_paste_stack = g_list_prepend (e->cut_and_paste_stack, GINT_TO_POINTER (level));
1605        e->cut_and_paste_stack = g_list_prepend (e->cut_and_paste_stack, GUINT_TO_POINTER (position));
1606}
1607
1608void
1609html_engine_cut_and_paste_end (HTMLEngine *e)
1610{
1611        guint position;
1612        gint level;
1613
1614        position = GPOINTER_TO_UINT (e->cut_and_paste_stack->data);
1615        e->cut_and_paste_stack = g_list_remove (e->cut_and_paste_stack, e->cut_and_paste_stack->data);
1616        level    = GPOINTER_TO_INT (e->cut_and_paste_stack->data);
1617        e->cut_and_paste_stack = g_list_remove (e->cut_and_paste_stack, e->cut_and_paste_stack->data);
1618
1619        if (e->clipboard) {
1620                insert_object (e, e->clipboard, e->clipboard_len, position, level, HTML_UNDO_UNDO, TRUE);
1621                e->clipboard = NULL;
1622        }
1623        html_undo_level_end (e->undo);
1624        html_engine_clipboard_pop (e);
1625        html_engine_selection_pop (e);
1626        html_engine_show_cursor (e);
1627}
1628
1629void
1630html_engine_cut_and_paste (HTMLEngine *e, const gchar *undo_op_name, const gchar *redo_op_name,
1631                           HTMLObjectForallFunc iterator, gpointer data)
1632{
1633        html_engine_edit_selection_updater_update_now (e->selection_updater);
1634        html_engine_cut_and_paste_begin (e, undo_op_name, redo_op_name);
1635        if (e->clipboard)
1636                html_object_forall (e->clipboard, e, iterator, data);
1637        html_engine_cut_and_paste_end (e);
1638}
Note: See TracBrowser for help on using the repository browser.