source: trunk/third/gtkhtml3/src/htmlcursor.c @ 19539

Revision 19539, 19.3 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19538, 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 1999, 2000 Helix Code, Inc.
5
6   This library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Library General Public
8   License as published by the Free Software Foundation; either
9   version 2 of the License, or (at your option) any later version.
10
11   This library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Library General Public License for more details.
15
16   You should have received a copy of the GNU Library General Public License
17   along with this library; see the file COPYING.LIB.  If not, write to
18   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19   Boston, MA 02111-1307, USA.
20*/
21
22/* This file is a bit of a hack.  To make things work in a really nice way, we
23   should have some extra methods in the various subclasses to implement cursor
24   movement.  But for now, I think this is a reasonable way to get things to
25   work.  */
26
27#include <config.h>
28#include <glib.h>
29
30#include "gtkhtml-private.h"
31#include "htmlclue.h"
32#include "htmlengine.h"
33#include "htmlengine-edit.h"
34#include "htmltext.h"
35#include "htmltextslave.h"
36#include "htmltype.h"
37
38#include "htmlcursor.h"
39
40/* #define _HTML_CURSOR_DEBUG */
41
42#ifdef _HTML_CURSOR_DEBUG
43static void
44debug_location (const HTMLCursor *cursor)
45{
46        HTMLObject *object;
47
48        object = cursor->object;
49        if (object == NULL) {
50                g_print ("Cursor has no position.\n");
51                return;
52        }
53
54        g_print ("Cursor in %s (%p), offset %d, position %d\n",
55                 html_type_name (HTML_OBJECT_TYPE (object)),
56                 object, cursor->offset, cursor->position);
57}
58#else
59#define debug_location(cursor)
60#endif
61
62
63static void
64normalize (HTMLObject **object,
65           guint *offset)
66{
67        if (*offset == 0 && (*object)->prev != NULL) {
68                *object = html_object_prev_not_slave (*object);
69                *offset = html_object_get_length (*object);
70        }
71}
72
73
74
75inline void
76html_cursor_init (HTMLCursor *cursor, HTMLObject *o, guint offset)
77{
78        cursor->object = o;
79        cursor->offset = offset;
80
81        cursor->target_x = 0;
82        cursor->have_target_x = FALSE;
83
84        cursor->position = 0;
85}
86
87HTMLCursor *
88html_cursor_new (void)
89{
90        HTMLCursor *new_cursor;
91
92        new_cursor = g_new (HTMLCursor, 1);
93        html_cursor_init (new_cursor, NULL, 0);
94
95        return new_cursor;
96}
97
98void
99html_cursor_destroy (HTMLCursor *cursor)
100{
101        g_return_if_fail (cursor != NULL);
102
103        g_free (cursor);
104}
105
106/**
107 * html_cursor_copy:
108 * @dest: A cursor object to copy into
109 * @src: A cursor object to copy from
110 *
111 * Copy @src into @dest.  @dest does not need to be an initialized cursor, so
112 * for example declaring a cursor as a local variable and then calling
113 * html_cursor_copy() to initialize it from another cursor's position works.
114 **/
115void
116html_cursor_copy (HTMLCursor *dest,
117                  const HTMLCursor *src)
118{
119        g_return_if_fail (dest != NULL);
120        g_return_if_fail (src != NULL);
121
122        dest->object = src->object;
123        dest->offset = src->offset;
124        dest->target_x = src->target_x;
125        dest->have_target_x = src->have_target_x;
126        dest->position = src->position;
127}
128
129HTMLCursor *
130html_cursor_dup (const HTMLCursor *cursor)
131{
132        HTMLCursor *new;
133
134        new = html_cursor_new ();
135        html_cursor_copy (new, cursor);
136
137        return new;
138}
139
140void
141html_cursor_normalize (HTMLCursor *cursor)
142{
143        g_return_if_fail (cursor != NULL);
144
145        normalize (&cursor->object, &cursor->offset);
146}
147
148void
149html_cursor_home (HTMLCursor *cursor,
150                  HTMLEngine *engine)
151{
152        HTMLObject *obj;
153
154        g_return_if_fail (cursor != NULL);
155        g_return_if_fail (engine != NULL);
156
157        gtk_html_im_reset (engine->widget);
158
159        if (engine->clue == NULL) {
160                cursor->object = NULL;
161                cursor->offset = 0;
162                return;
163        }
164
165        if (engine->need_spell_check)
166                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
167
168        obj = engine->clue;
169        while (!html_object_accepts_cursor (obj)) {
170                HTMLObject *head = html_object_head (obj);
171                if (obj)
172                        obj = head;
173                else
174                        break;
175        }
176
177        cursor->object = obj;
178        cursor->offset = 0;
179
180        if (!html_object_accepts_cursor (obj))
181                html_cursor_forward (cursor, engine);
182
183        cursor->position = 0;
184
185        debug_location (cursor);
186}
187
188
189
190static gboolean
191forward (HTMLCursor *cursor)
192{
193        gboolean retval;
194
195        retval = TRUE;
196        if (!html_object_cursor_forward (cursor->object, cursor)) {
197                HTMLObject *next;
198
199                next = html_object_next_cursor (cursor->object, &cursor->offset);
200                if (next) {
201                        if (!html_object_is_container (next))
202                                cursor->offset = (next->parent == cursor->object->parent) ? 1 : 0;
203                        cursor->object = next;
204                        cursor->position ++;
205                } else
206                        retval = FALSE;
207        }
208        return retval;
209}
210
211static gboolean
212forward_in_flow (HTMLCursor *cursor)
213{
214        gboolean retval;
215
216        retval = TRUE;
217        if (cursor->offset != html_object_get_length (cursor->object)) {
218                if (html_object_is_container (cursor->object)) {
219                        HTMLObject *obj;
220
221                        obj = cursor->object;
222                        while ((retval = forward (cursor)) && cursor->object != obj)
223                                ;
224                } else
225                        retval = html_object_cursor_forward (cursor->object, cursor);
226        } else {
227                if (html_object_next_not_slave (cursor->object))
228                        retval = forward (cursor);
229                else
230                        retval = FALSE;
231        }
232
233        return retval;
234}
235
236gboolean
237html_cursor_forward (HTMLCursor *cursor, HTMLEngine *engine)
238{
239        gboolean retval;
240
241        g_return_val_if_fail (cursor != NULL, FALSE);
242        g_return_val_if_fail (engine != NULL, FALSE);
243
244        gtk_html_im_reset (engine->widget);
245
246        if (engine->need_spell_check)
247                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
248
249        cursor->have_target_x = FALSE;
250        retval = forward (cursor);
251
252        debug_location (cursor);
253
254        return retval;
255}
256
257static gboolean
258backward (HTMLCursor *cursor)
259{
260        gboolean retval;
261
262        retval = TRUE;
263        if (!html_object_cursor_backward (cursor->object, cursor)) {
264                HTMLObject *prev;
265
266                prev = html_object_prev_cursor (cursor->object, &cursor->offset);
267                if (prev) {
268                        if (!html_object_is_container (prev))
269                                cursor->offset = html_object_get_length (prev);
270                        cursor->object = prev;
271                        cursor->position --;
272                } else
273                        retval = FALSE;
274        }
275        return retval;
276}
277
278static gboolean
279backward_in_flow (HTMLCursor *cursor)
280{
281        gboolean retval;
282
283        retval = TRUE;
284        if (cursor->offset  && html_object_is_container (cursor->object)) {
285                HTMLObject *obj;
286
287                obj = cursor->object;
288                while ((retval = backward (cursor)) && cursor->object != obj)
289                        ;
290        } else {
291                if (cursor->offset > 1 || !cursor->object->prev)
292                        retval = html_object_cursor_backward (cursor->object, cursor);
293                else if (cursor->object->prev)
294                        retval = backward (cursor);
295                else
296                        retval = FALSE;
297        }
298
299        return retval;
300}
301
302gboolean
303html_cursor_backward (HTMLCursor *cursor,
304                      HTMLEngine *engine)
305{
306        gboolean retval;
307
308        g_return_val_if_fail (cursor != NULL, FALSE);
309        g_return_val_if_fail (engine != NULL, FALSE);
310
311        gtk_html_im_reset (engine->widget);
312
313        if (engine->need_spell_check)
314                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
315
316        cursor->have_target_x = FALSE;
317        retval = backward (cursor);
318
319        debug_location (cursor);
320
321        return retval;
322}
323
324
325gboolean
326html_cursor_up (HTMLCursor *cursor,
327                HTMLEngine *engine)
328{
329        HTMLCursor orig_cursor;
330        HTMLCursor prev_cursor;
331        gint prev_x, prev_y;
332        gint x, y;
333        gint target_x;
334        gint orig_y;
335        gboolean new_line;
336
337        gtk_html_im_reset (engine->widget);
338
339        if (cursor->object == NULL) {
340                g_warning ("The cursor is in a NULL position: going home.");
341                html_cursor_home (cursor, engine);
342                return TRUE;
343        }
344
345        if (engine->need_spell_check)
346                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
347
348        html_cursor_copy (&orig_cursor, cursor);
349
350        html_object_get_cursor_base (cursor->object,
351                                     engine->painter, cursor->offset,
352                                     &x, &y);
353
354        if (! cursor->have_target_x) {
355                cursor->target_x = x;
356                cursor->have_target_x = TRUE;
357        }
358
359        target_x = cursor->target_x;
360
361        orig_y = y;
362
363        new_line = FALSE;
364
365        while (1) {
366                html_cursor_copy (&prev_cursor, cursor);
367
368                prev_x = x;
369                prev_y = y;
370
371                if (! backward (cursor))
372                        return FALSE;
373
374                html_object_get_cursor_base (cursor->object,
375                                             engine->painter, cursor->offset,
376                                             &x, &y);
377
378                if (html_cursor_equal (&prev_cursor, cursor)) {
379                        html_cursor_copy (cursor, &orig_cursor);
380                        return FALSE;
381                }
382
383                if (y + cursor->object->descent - 1 < prev_y - prev_cursor.object->ascent) {
384                        if (new_line) {
385                                html_cursor_copy (cursor, &prev_cursor);
386                                return FALSE;
387                        }
388
389                        new_line = TRUE;
390                }
391
392                if (new_line && x <= target_x) {
393                        if (! cursor->have_target_x) {
394                                cursor->have_target_x = TRUE;
395                                cursor->target_x = target_x;
396                        }
397
398                        /* Choose the character which is the nearest to the
399                           target X.  */
400                        if (prev_y == y && target_x - x >= prev_x - target_x) {
401                                cursor->object = prev_cursor.object;
402                                cursor->offset = prev_cursor.offset;
403                                cursor->position = prev_cursor.position;
404                        }
405
406                        debug_location (cursor);
407                        return TRUE;
408                }
409        }
410}
411
412
413gboolean
414html_cursor_down (HTMLCursor *cursor,
415                  HTMLEngine *engine)
416{
417        HTMLCursor orig_cursor;
418        HTMLCursor prev_cursor;
419        gint prev_x, prev_y;
420        gint x, y;
421        gint target_x;
422        gint orig_y;
423        gboolean new_line;
424
425        gtk_html_im_reset (engine->widget);
426
427        if (cursor->object == NULL) {
428                g_warning ("The cursor is in a NULL position: going home.");
429                html_cursor_home (cursor, engine);
430                return TRUE;
431        }
432
433        if (engine->need_spell_check)
434                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
435
436        html_object_get_cursor_base (cursor->object,
437                                     engine->painter, cursor->offset,
438                                     &x, &y);
439
440        if (! cursor->have_target_x) {
441                cursor->target_x = x;
442                cursor->have_target_x = TRUE;
443        }
444
445        target_x = cursor->target_x;
446
447        orig_y = y;
448
449        new_line = FALSE;
450
451        while (1) {
452                prev_cursor = *cursor;
453                prev_x = x;
454                prev_y = y;
455
456                if (! forward (cursor))
457                        return FALSE;
458
459                html_object_get_cursor_base (cursor->object,
460                                             engine->painter, cursor->offset,
461                                             &x, &y);
462
463                if (html_cursor_equal (&prev_cursor, cursor)) {
464                        html_cursor_copy (cursor, &orig_cursor);
465                        return FALSE;
466                }
467
468                if (y - cursor->object->ascent > prev_y + prev_cursor.object->descent - 1) {
469                        if (new_line) {
470                                html_cursor_copy (cursor, &prev_cursor);
471                                return FALSE;
472                        }
473
474                        new_line = TRUE;
475                }
476
477                if (new_line && x >= target_x) {
478                        if (! cursor->have_target_x) {
479                                cursor->have_target_x = TRUE;
480                                cursor->target_x = target_x;
481                        }
482
483                        /* Choose the character which is the nearest to the
484                           target X.  */
485                        if (prev_y == y && x - target_x >= target_x - prev_x) {
486                                cursor->object = prev_cursor.object;
487                                cursor->offset = prev_cursor.offset;
488                                cursor->position = prev_cursor.position;
489                        }
490
491                        debug_location (cursor);
492                        return TRUE;
493                }
494        }
495}
496
497
498/**
499 * html_cursor_jump_to:
500 * @cursor:
501 * @object:
502 * @offset:
503 *
504 * Move the cursor to the specified @offset in the specified @object.
505 *
506 * Return value: %TRUE if successfull, %FALSE if failed.
507 **/
508gboolean
509html_cursor_jump_to (HTMLCursor *cursor,
510                     HTMLEngine *engine,
511                     HTMLObject *object,
512                     guint offset)
513{
514        HTMLCursor original;
515
516        g_return_val_if_fail (cursor != NULL, FALSE);
517        g_return_val_if_fail (object != NULL, FALSE);
518
519        gtk_html_im_reset (engine->widget);
520
521        if (engine->need_spell_check)
522                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
523
524        html_cursor_normalize (cursor);
525        normalize (&object, &offset);
526
527        if (cursor->object == object && cursor->offset == offset)
528                return TRUE;
529
530        html_cursor_copy (&original, cursor);
531
532        while (forward (cursor)) {
533                if (cursor->object == object && cursor->offset == offset)
534                        return TRUE;
535        }
536
537        html_cursor_copy (cursor, &original);
538
539        while (backward (cursor)) {
540                if (cursor->object == object && cursor->offset == offset)
541                        return TRUE;
542        }
543
544        return FALSE;
545}
546
547
548/* Complex cursor movement commands.  */
549
550void
551html_cursor_beginning_of_document (HTMLCursor *cursor,
552                                   HTMLEngine *engine)
553{
554        g_return_if_fail (cursor != NULL);
555        g_return_if_fail (engine != NULL);
556        g_return_if_fail (HTML_IS_ENGINE (engine));
557
558        gtk_html_im_reset (engine->widget);
559
560        if (engine->need_spell_check)
561                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
562
563        while (backward (cursor))
564                ;
565}
566
567void
568html_cursor_end_of_document (HTMLCursor *cursor,
569                             HTMLEngine *engine)
570{
571        g_return_if_fail (cursor != NULL);
572        g_return_if_fail (engine != NULL);
573        g_return_if_fail (HTML_IS_ENGINE (engine));
574
575        gtk_html_im_reset (engine->widget);
576
577        if (engine->need_spell_check)
578                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
579
580        while (forward (cursor))
581                ;
582}
583
584gboolean
585html_cursor_end_of_line (HTMLCursor *cursor,
586                         HTMLEngine *engine)
587{
588        HTMLCursor prev_cursor;
589        gint x, y, prev_y;
590
591        g_return_val_if_fail (cursor != NULL, FALSE);
592        g_return_val_if_fail (engine != NULL, FALSE);
593        g_return_val_if_fail (HTML_IS_ENGINE (engine), FALSE);
594
595        gtk_html_im_reset (engine->widget);
596
597        cursor->have_target_x = FALSE;
598
599        if (engine->need_spell_check)
600                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
601
602        html_cursor_copy (&prev_cursor, cursor);
603        html_object_get_cursor_base (cursor->object, engine->painter, cursor->offset,
604                                     &x, &prev_y);
605
606        while (1) {
607                if (! forward_in_flow (cursor))
608                        return TRUE;
609
610                html_object_get_cursor_base (cursor->object, engine->painter, cursor->offset,
611                                             &x, &y);
612
613                if (y - cursor->object->ascent > prev_y + prev_cursor.object->descent - 1) {
614                        html_cursor_copy (cursor, &prev_cursor);
615                        return TRUE;
616                }
617                prev_y = y;
618                html_cursor_copy (&prev_cursor, cursor);
619        }
620}
621
622gboolean
623html_cursor_beginning_of_line (HTMLCursor *cursor,
624                               HTMLEngine *engine)
625{
626        HTMLCursor prev_cursor;
627        gint x, y, prev_y;
628
629        g_return_val_if_fail (cursor != NULL, FALSE);
630        g_return_val_if_fail (engine != NULL, FALSE);
631        g_return_val_if_fail (HTML_IS_ENGINE (engine), FALSE);
632
633        gtk_html_im_reset (engine->widget);
634
635        cursor->have_target_x = FALSE;
636
637        if (engine->need_spell_check)
638                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
639
640        html_cursor_copy (&prev_cursor, cursor);
641        html_object_get_cursor_base (cursor->object, engine->painter, cursor->offset,
642                                     &x, &prev_y);
643
644        while (1) {
645                if (! backward_in_flow (cursor))
646                        return TRUE;
647
648                html_object_get_cursor_base (cursor->object, engine->painter, cursor->offset,
649                                             &x, &y);
650
651                if (y + cursor->object->descent - 1 < prev_y - prev_cursor.object->ascent) {
652                        html_cursor_copy (cursor, &prev_cursor);
653                        return TRUE;
654                }
655
656                prev_y = y;
657                html_cursor_copy (&prev_cursor, cursor);
658        }
659}
660
661
662gint
663html_cursor_get_position (HTMLCursor *cursor)
664{
665        g_return_val_if_fail (cursor != NULL, 0);
666
667        return cursor->position;
668}
669
670void
671html_cursor_jump_to_position_no_spell (HTMLCursor *cursor, HTMLEngine *engine, gint position)
672{
673        gboolean need_spell_check;
674
675        need_spell_check = engine->need_spell_check;
676        engine->need_spell_check = FALSE;
677        html_cursor_jump_to_position (cursor, engine, position);
678        engine->need_spell_check = need_spell_check;
679}
680
681void
682html_cursor_jump_to_position (HTMLCursor *cursor, HTMLEngine *engine, gint position)
683{
684        g_return_if_fail (cursor != NULL);
685        g_return_if_fail (position >= 0);
686
687        gtk_html_im_reset (engine->widget);
688
689        if (engine->need_spell_check)
690                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
691
692        if (cursor->position < position) {
693                while (cursor->position < position) {
694                        if (! forward (cursor))
695                                break;
696                }
697        } else if (cursor->position > position) {
698                while (cursor->position > position) {
699                        if (! backward (cursor))
700                                break;
701                }
702        }
703}
704
705
706/* Comparison.  */
707
708gboolean
709html_cursor_equal (const HTMLCursor *a,
710                   const HTMLCursor *b)
711{
712        g_return_val_if_fail (a != NULL, FALSE);
713        g_return_val_if_fail (b != NULL, FALSE);
714
715        return a->object == b->object && a->offset == b->offset;
716}
717
718gboolean
719html_cursor_precedes (const HTMLCursor *a,
720                      const HTMLCursor *b)
721{
722        g_return_val_if_fail (a != NULL, FALSE);
723        g_return_val_if_fail (b != NULL, FALSE);
724
725        return a->position < b->position;
726}
727
728gboolean
729html_cursor_follows (const HTMLCursor *a,
730                     const HTMLCursor *b)
731{
732        g_return_val_if_fail (a != NULL, FALSE);
733        g_return_val_if_fail (b != NULL, FALSE);
734
735        return a->position > b->position;
736}
737
738
739gunichar
740html_cursor_get_current_char (const HTMLCursor *cursor)
741{
742        HTMLObject *next;
743
744        g_return_val_if_fail (cursor != NULL, 0);
745
746        if (! html_object_is_text (cursor->object)) {
747                if (cursor->offset < html_object_get_length (cursor->object))
748                        return 0;
749
750                next = html_object_next_not_slave (cursor->object);
751                if (next != NULL && html_object_is_text (next))
752                        return html_text_get_char (HTML_TEXT (next), 0);
753
754                return 0;
755        }
756
757        if (cursor->offset < HTML_TEXT (cursor->object)->text_len)
758                return html_text_get_char (HTML_TEXT (cursor->object), cursor->offset);
759
760        next = html_object_next_not_slave (cursor->object);
761        if (next == NULL || ! html_object_is_text (next))
762                return 0;
763
764        return html_text_get_char (HTML_TEXT (next), 0);
765}
766
767gunichar
768html_cursor_get_prev_char (const HTMLCursor *cursor)
769{
770        HTMLObject *prev;
771
772        g_return_val_if_fail (cursor != NULL, 0);
773
774        if (cursor->offset)
775                return (html_object_is_text (cursor->object))
776                        ? html_text_get_char (HTML_TEXT (cursor->object), cursor->offset - 1)
777                        : 0;
778        prev = html_object_prev_not_slave (cursor->object);
779        return (prev && html_object_is_text (prev))
780                ? html_text_get_char (HTML_TEXT (prev), HTML_TEXT (prev)->text_len - 1)
781                : 0;
782}
783
784gboolean
785html_cursor_beginning_of_paragraph (HTMLCursor *cursor, HTMLEngine *engine)
786{
787        HTMLCursor *copy;
788        HTMLObject *flow;
789        gboolean rv = FALSE;
790        gint level, new_level;
791
792        gtk_html_im_reset (engine->widget);
793
794        level = html_object_get_parent_level (cursor->object);
795        flow  = cursor->object->parent;
796
797        if (engine->need_spell_check)
798                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
799
800        while (1) {
801                if (!cursor->offset) {
802                        copy = html_cursor_dup (cursor);
803                        if (backward (cursor)) {
804                                new_level = html_object_get_parent_level (cursor->object);
805                                if (new_level < level
806                                    || (new_level == level && flow != cursor->object->parent)) {
807                                        html_cursor_copy (cursor, copy);
808                                        break;
809                                }
810                                html_cursor_destroy (copy);
811                        } else
812                                break;
813                }
814                        else
815                                if (!backward (cursor))
816                                        break;
817                rv = TRUE;
818        }
819
820        return rv;
821}
822
823gboolean
824html_cursor_end_of_paragraph (HTMLCursor *cursor, HTMLEngine *engine)
825{
826        HTMLCursor *copy;
827        HTMLObject *flow;
828        gboolean rv = FALSE;
829        gint level, new_level;
830
831        gtk_html_im_reset (engine->widget);
832
833        level = html_object_get_parent_level (cursor->object);
834        flow  = cursor->object->parent;
835
836        if (engine->need_spell_check)
837                html_engine_spell_check_range (engine, engine->cursor, engine->cursor);
838
839        while (1) {
840                if (cursor->offset == html_object_get_length (cursor->object)) {
841                        copy = html_cursor_dup (cursor);
842                        if (forward (cursor)) {
843                                new_level = html_object_get_parent_level (cursor->object);
844                                if (new_level < level
845                                    || (new_level == level && flow != cursor->object->parent)) {
846                                        html_cursor_copy (cursor, copy);
847                                        break;
848                                }
849                                html_cursor_destroy (copy);
850                        } else
851                                break;
852                }
853                        else
854                                if (!forward (cursor))
855                                        break;
856                rv = TRUE;
857        }
858
859        return rv;
860}
861
862gboolean
863html_cursor_forward_n (HTMLCursor *cursor, HTMLEngine *e, guint n)
864{
865        gboolean rv = FALSE;
866
867        while (n && html_cursor_forward (cursor, e)) {
868                n --;
869                rv = TRUE;
870        }
871
872        return rv;
873}
874
875gboolean
876html_cursor_backward_n (HTMLCursor *cursor, HTMLEngine *e, guint n)
877{
878        gboolean rv = FALSE;
879
880        while (n && html_cursor_backward (cursor, e)) {
881                n --;
882                rv = TRUE;
883        }
884
885        return rv;
886}
887
888HTMLObject *
889html_cursor_child_of (HTMLCursor *cursor, HTMLObject *parent)
890{
891        HTMLObject *child = cursor->object;
892
893        while (child) {
894                if (child->parent == parent)
895                        return child;
896                child = child->parent;
897        }
898
899        return NULL;
900}
Note: See TracBrowser for help on using the repository browser.