/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* This file is part of the GtkHTML library Copyright (C) 2000 Helix Code, Inc. Authors: Radek Doulik (rodo@helixcode.com) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "htmlcursor.h" #include "htmlengine.h" #include "htmlinterval.h" #include "htmlobject.h" #include "htmlselection.h" inline void html_point_construct (HTMLPoint *p, HTMLObject *o, guint off) { p->object = o; p->offset = off; } HTMLPoint * html_point_new (HTMLObject *o, guint off) { HTMLPoint *np = g_new (HTMLPoint, 1); html_point_construct (np, o, off); return np; } inline void html_point_destroy (HTMLPoint *p) { g_free (p); } gboolean html_point_cursor_object_eq (HTMLPoint *p, HTMLPoint *c) { return p->object == c->object && (!html_object_is_container (p->object) || p->offset == c->offset); } inline void html_point_next_cursor (HTMLPoint *p) { p->object = html_object_next_cursor (p->object, &p->offset); } HTMLInterval * html_interval_new (HTMLObject *from, HTMLObject *to, guint from_offset, guint to_offset) { HTMLInterval *i = g_new (HTMLInterval, 1); html_point_construct (&i->from, from, from_offset); html_point_construct (&i->to, to, to_offset); return i; } inline HTMLInterval * html_interval_new_from_cursor (HTMLCursor *a, HTMLCursor *b) { HTMLCursor *begin, *end; if (html_cursor_get_position (a) < html_cursor_get_position (b)) { begin = a; end = b; } else { begin = b; end = a; } return html_interval_new (begin->object, end->object, begin->offset, end->offset); } inline HTMLInterval * html_interval_new_from_points (HTMLPoint *from, HTMLPoint *to) { return html_interval_new (from->object, to->object, from->offset, to->offset); } void html_interval_destroy (HTMLInterval *i) { g_free (i); } guint html_interval_get_length (HTMLInterval *i, HTMLObject *obj) { if (obj != i->from.object && obj != i->to.object) return html_object_get_length (obj); if (obj == i->from.object) { if (obj == i->to.object) return i->to.offset - i->from.offset; else return html_object_get_length (obj) - i->from.offset; } else return i->to.offset; } guint html_interval_get_bytes (HTMLInterval *i, HTMLObject *obj) { if (obj != i->from.object && obj != i->to.object) return html_object_get_bytes (obj); if (obj == i->from.object) { if (obj == i->to.object) return html_interval_get_to_index (i) - html_interval_get_from_index (i); else return html_object_get_bytes (obj) - html_interval_get_from_index (i); } else return html_interval_get_to_index (i); } guint html_interval_get_start (HTMLInterval *i, HTMLObject *obj) { return (obj != i->from.object) ? 0 : i->from.offset; } guint html_interval_get_start_index (HTMLInterval *i, HTMLObject *obj) { return (obj != i->from.object) ? 0 : html_interval_get_from_index (i); } static void select_object (HTMLObject *o, HTMLEngine *e, gpointer data) { HTMLInterval *i = (HTMLInterval *) data; HTMLEngine *etop = html_engine_get_top_html_engine (e); if (o == i->from.object) etop->selected_in = TRUE; if (etop->selected_in) { gint len; len = html_interval_get_length (i, o); if (len) html_object_select_range (o, e, html_interval_get_start (i, o), len, !html_engine_frozen (e)); } if (o == i->to.object) etop->selected_in = FALSE; } void html_interval_select (HTMLInterval *i, HTMLEngine *e) { html_engine_get_top_html_engine (e)->selected_in = FALSE; i = html_interval_flat (i); html_interval_forall (i, e, select_object, i); html_interval_destroy (i); } static void unselect_object (HTMLObject *o, HTMLEngine *e, gpointer data) { if (html_interval_get_length ((HTMLInterval *) data, o)) html_object_select_range (o, e, 0, 0, !html_engine_frozen (e)); } void html_interval_unselect (HTMLInterval *i, HTMLEngine *e) { i = html_interval_flat (i); html_interval_forall (i, e, unselect_object, i); html_interval_destroy (i); } gint html_interval_get_from_index (HTMLInterval *i) { g_assert (i); return html_object_get_index (i->from.object, i->from.offset); } gint html_interval_get_to_index (HTMLInterval *i) { g_assert (i); return html_object_get_index (i->to.object, i->to.offset); } static GSList * get_downtree_line (HTMLObject *o) { GSList *list = NULL; while (o) { list = g_slist_prepend (list, o); o = o->parent; } return list; } static HTMLEngine * do_downtree_lines_intersection (GSList **l1, GSList **l2, HTMLEngine *e) { g_assert ((*l1)->data == (*l2)->data); while (*l1 && *l2 && (*l1)->data == (*l2)->data) { e = html_object_get_engine (HTML_OBJECT ((*l1)->data), e); *l1 = g_slist_remove_link (*l1, *l1); *l2 = g_slist_remove_link (*l2, *l2); } return e; } static void interval_forall (HTMLObject *parent, GSList *from_down, GSList *to_down, HTMLEngine *e, HTMLObjectForallFunc f, gpointer data) { HTMLObject *o, *from, *to; from = from_down ? HTML_OBJECT (from_down->data) : html_object_head (parent); to = to_down ? HTML_OBJECT (to_down->data) : NULL; for (o = from; o; o = html_object_next_not_slave (o)) { interval_forall (o, (from_down && o == HTML_OBJECT (from_down->data)) ? from_down->next : NULL, (to_down && o == HTML_OBJECT (to_down->data)) ? to_down->next : NULL, html_object_get_engine (o, e), f, data); if (o == to) break; } (*f) (parent, e, data); } static void html_point_to_leaf (HTMLPoint *p) { if (html_object_is_container (p->object)) { if (p->offset == 0) p->object = html_object_get_head_leaf (p->object); else if (p->offset == html_object_get_length (p->object)) { p->object = html_object_get_tail_leaf (p->object); p->offset = html_object_get_length (p->object); } else g_warning ("Can't transform point to leaf\n"); } } static inline HTMLInterval * html_interval_dup (HTMLInterval *i) { return html_interval_new_from_points (&i->from, &i->to); } HTMLInterval * html_interval_flat (HTMLInterval *i) { HTMLInterval *ni = html_interval_dup (i); html_point_to_leaf (&ni->from); html_point_to_leaf (&ni->to); return ni; } void html_interval_forall (HTMLInterval *i, HTMLEngine *e, HTMLObjectForallFunc f, gpointer data) { GSList *from_downline, *to_downline; HTMLEngine *engine; g_return_if_fail (i->from.object); g_return_if_fail (i->to.object); i = html_interval_flat (i); from_downline = get_downtree_line (i->from.object); to_downline = get_downtree_line (i->to.object); engine = do_downtree_lines_intersection (&from_downline, &to_downline, e); if (from_downline) interval_forall (HTML_OBJECT (from_downline->data)->parent, from_downline, to_downline, html_object_get_engine (HTML_OBJECT (from_downline->data)->parent, engine), f, data); else { g_assert (i->from.object == i->to.object); html_object_forall (i->from.object, html_object_get_engine (i->from.object, engine), f, data); } g_slist_free (from_downline); g_slist_free (to_downline); html_interval_destroy (i); } static HTMLObject * html_object_children_max (HTMLObject *a, HTMLObject *b) { HTMLObject *o; g_return_val_if_fail (a->parent, NULL); g_return_val_if_fail (b->parent, NULL); g_return_val_if_fail (a->parent == b->parent, NULL); for (o=a; o; o = html_object_next_not_slave (o)) if (o == b) return b; return a; } static inline HTMLObject * html_object_children_min (HTMLObject *a, HTMLObject *b) { return a == html_object_children_max (b, a) ? b : a; } HTMLPoint * html_point_max (HTMLPoint *a, HTMLPoint *b) { GSList *a_downline, *b_downline; HTMLPoint *rv = NULL; if (a->object == b->object) return a->offset < b->offset ? b : a; a_downline = get_downtree_line (a->object); b_downline = get_downtree_line (b->object); do_downtree_lines_intersection (&a_downline, &b_downline, NULL); if (a_downline == NULL) /* it means that a is parent (container) of b */ rv = a->offset ? a : b; else if (b_downline == NULL) /* it means that b is parent (container) of a */ rv = b->offset ? b : a; else rv = html_object_children_max (HTML_OBJECT (a_downline->data), HTML_OBJECT (b_downline->data)) == HTML_OBJECT (a_downline->data) ? a : b; g_slist_free (a_downline); g_slist_free (b_downline); return rv; } inline HTMLPoint * html_point_min (HTMLPoint *a, HTMLPoint *b) { return a == html_point_max (a, b) ? b : a; } static HTMLPoint * max_from (HTMLInterval *a, HTMLInterval *b) { if (!a->from.object) return &b->from; if (!b->from.object) return &a->from; return html_point_max (&a->from, &b->from); } static HTMLPoint * min_to (HTMLInterval *a, HTMLInterval *b) { if (!a->to.object) return &b->to; if (!b->to.object) return &a->to; return html_point_min (&a->to, &b->to); } HTMLInterval * html_interval_intersection (HTMLInterval *a, HTMLInterval *b) { HTMLPoint *from, *to; from = max_from (a, b); to = min_to (a, b); return to == html_point_max (from, to) ? html_interval_new_from_points (from, to) : NULL; } void * html_interval_substract (HTMLInterval *a, HTMLInterval *b, HTMLInterval **s1, HTMLInterval **s2) { return NULL; } void html_interval_validate (HTMLInterval *i) { if (&i->from == html_point_max (&i->from, &i->to)) { HTMLPoint tmp; tmp = i->from; i->from = i->to; i->to = tmp; } } gboolean html_point_eq (const HTMLPoint *a, const HTMLPoint *b) { return a->object == b->object && a->offset == b->offset; } gboolean html_interval_eq (const HTMLInterval *a, const HTMLInterval *b) { return html_point_eq (&a->from, &b->from) && html_point_eq (&a->to, &b->to); } HTMLObject * html_interval_get_head (HTMLInterval *i, HTMLObject *o) { return o == i->from.object->parent ? i->from.object : html_object_head (o); }