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

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