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 Helix Code, Inc.
5  * Authors:           Radek Doulik (rodo@helixcode.com)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21 */
22 
23 #include <config.h>
24 #include "htmlcursor.h"
25 #include "htmlengine.h"
26 #include "htmlinterval.h"
27 #include "htmlobject.h"
28 #include "htmlselection.h"
29 
30 inline void
html_point_construct(HTMLPoint * p,HTMLObject * o,guint off)31 html_point_construct (HTMLPoint *p,
32                       HTMLObject *o,
33                       guint off)
34 {
35 	p->object = o;
36 	p->offset = off;
37 }
38 
39 HTMLPoint *
html_point_new(HTMLObject * o,guint off)40 html_point_new (HTMLObject *o,
41                 guint off)
42 {
43 	HTMLPoint *np = g_new (HTMLPoint, 1);
44 	html_point_construct (np, o, off);
45 
46 	return np;
47 }
48 
49 inline void
html_point_destroy(HTMLPoint * p)50 html_point_destroy (HTMLPoint *p)
51 {
52 	g_free (p);
53 }
54 
55 gboolean
html_point_cursor_object_eq(HTMLPoint * p,HTMLPoint * c)56 html_point_cursor_object_eq (HTMLPoint *p,
57                              HTMLPoint *c)
58 {
59 	return p->object == c->object && (!html_object_is_container (p->object) || p->offset == c->offset);
60 }
61 
62 inline void
html_point_next_cursor(HTMLPoint * p)63 html_point_next_cursor (HTMLPoint *p)
64 {
65 	p->object = html_object_next_cursor (p->object, (gint *) &p->offset);
66 }
67 
68 HTMLInterval *
html_interval_new(HTMLObject * from,HTMLObject * to,guint from_offset,guint to_offset)69 html_interval_new (HTMLObject *from,
70                    HTMLObject *to,
71                    guint from_offset,
72                    guint to_offset)
73 {
74 	HTMLInterval *i = g_new (HTMLInterval, 1);
75 
76 	html_point_construct (&i->from, from, from_offset);
77 	html_point_construct (&i->to,   to,   to_offset);
78 
79 	return i;
80 }
81 
82 inline HTMLInterval *
html_interval_new_from_cursor(HTMLCursor * a,HTMLCursor * b)83 html_interval_new_from_cursor (HTMLCursor *a,
84                                HTMLCursor *b)
85 {
86 	HTMLCursor *begin, *end;
87 
88 	if (html_cursor_get_position (a) < html_cursor_get_position (b)) {
89 		begin = a;
90 		end   = b;
91 	} else {
92 		begin = b;
93 		end   = a;
94 	}
95 
96 	return html_interval_new (begin->object, end->object, begin->offset, end->offset);
97 }
98 
99 inline HTMLInterval *
html_interval_new_from_points(HTMLPoint * from,HTMLPoint * to)100 html_interval_new_from_points (HTMLPoint *from,
101                                HTMLPoint *to)
102 {
103 	return html_interval_new (from->object, to->object, from->offset, to->offset);
104 }
105 
106 void
html_interval_destroy(HTMLInterval * i)107 html_interval_destroy (HTMLInterval *i)
108 {
109 	g_free (i);
110 }
111 
112 guint
html_interval_get_length(HTMLInterval * i,HTMLObject * obj)113 html_interval_get_length (HTMLInterval *i,
114                           HTMLObject *obj)
115 {
116 	if (obj != i->from.object && obj != i->to.object)
117 		return html_object_get_length (obj);
118 	if (obj == i->from.object) {
119 		if (obj == i->to.object)
120 			return i->to.offset - i->from.offset;
121 		else
122 			return html_object_get_length (obj) - i->from.offset;
123 	} else
124 		return i->to.offset;
125 }
126 
127 guint
html_interval_get_bytes(HTMLInterval * i,HTMLObject * obj)128 html_interval_get_bytes (HTMLInterval *i,
129                          HTMLObject *obj)
130 {
131 	if (obj != i->from.object && obj != i->to.object)
132 		return html_object_get_bytes (obj);
133 	if (obj == i->from.object) {
134 		if (obj == i->to.object)
135 			return html_interval_get_to_index (i) - html_interval_get_from_index (i);
136 		else
137 			return html_object_get_bytes (obj) - html_interval_get_from_index (i);
138 	} else
139 		return html_interval_get_to_index (i);
140 }
141 
142 guint
html_interval_get_start(HTMLInterval * i,HTMLObject * obj)143 html_interval_get_start (HTMLInterval *i,
144                          HTMLObject *obj)
145 {
146 	return (obj != i->from.object) ? 0 : i->from.offset;
147 }
148 
149 guint
html_interval_get_start_index(HTMLInterval * i,HTMLObject * obj)150 html_interval_get_start_index (HTMLInterval *i,
151                                HTMLObject *obj)
152 {
153 	return (obj != i->from.object) ? 0 : html_interval_get_from_index (i);
154 }
155 
156 static void
select_object(HTMLObject * o,HTMLEngine * e,gpointer data)157 select_object (HTMLObject *o,
158                HTMLEngine *e,
159                gpointer data)
160 {
161 	HTMLInterval *i = (HTMLInterval *) data;
162 	HTMLEngine *etop = html_engine_get_top_html_engine (e);
163 
164 	if (o == i->from.object)
165 		etop->selected_in = TRUE;
166 	if (etop->selected_in) {
167 		gint len;
168 
169 		len = html_interval_get_length (i, o);
170 		if (len || html_object_is_container (o))
171 			html_object_select_range (o, e,
172 						  html_interval_get_start (i, o), len,
173 						  !html_engine_frozen (e));
174 	}
175 
176 	if (o == i->to.object)
177 		etop->selected_in = FALSE;
178 }
179 
180 void
html_interval_select(HTMLInterval * i,HTMLEngine * e)181 html_interval_select (HTMLInterval *i,
182                       HTMLEngine *e)
183 {
184 	html_engine_get_top_html_engine (e)->selected_in = FALSE;
185 	i = html_interval_flat (i);
186 	html_interval_forall (i, e, select_object, i);
187 	html_interval_destroy (i);
188 }
189 
190 static void
unselect_object(HTMLObject * o,HTMLEngine * e,gpointer data)191 unselect_object (HTMLObject *o,
192                  HTMLEngine *e,
193                  gpointer data)
194 {
195 	if (html_interval_get_length ((HTMLInterval *) data, o))
196 		html_object_select_range (o, e, 0, 0, !html_engine_frozen (e));
197 }
198 
199 void
html_interval_unselect(HTMLInterval * i,HTMLEngine * e)200 html_interval_unselect (HTMLInterval *i,
201                         HTMLEngine *e)
202 {
203 	i = html_interval_flat (i);
204 	html_interval_forall (i, e, unselect_object, i);
205 	html_interval_destroy (i);
206 }
207 
208 gint
html_interval_get_from_index(HTMLInterval * i)209 html_interval_get_from_index (HTMLInterval *i)
210 {
211 	g_assert (i);
212 
213 	return html_object_get_index (i->from.object, i->from.offset);
214 }
215 
216 gint
html_interval_get_to_index(HTMLInterval * i)217 html_interval_get_to_index (HTMLInterval *i)
218 {
219 	g_assert (i);
220 
221 	return html_object_get_index (i->to.object, i->to.offset);
222 }
223 
224 static GSList *
get_downtree_line(HTMLObject * o)225 get_downtree_line (HTMLObject *o)
226 {
227 	GSList *list = NULL;
228 
229 	while (o) {
230 		list = g_slist_prepend (list, o);
231 		o = o->parent;
232 	}
233 
234 	return list;
235 }
236 
237 static HTMLEngine *
do_downtree_lines_intersection(GSList ** l1,GSList ** l2,HTMLEngine * e)238 do_downtree_lines_intersection (GSList **l1,
239                                 GSList **l2,
240                                 HTMLEngine *e)
241 {
242 	GSList *link;
243 
244 	g_assert ((*l1)->data == (*l2)->data);
245 
246 	while (*l1 && *l2 && (*l1)->data == (*l2)->data) {
247 		e = html_object_get_engine (HTML_OBJECT ((*l1)->data), e);
248 		link = *l1;
249 		*l1 = g_slist_remove_link (*l1, link);
250 		g_slist_free (link);
251 
252 		link = *l2;
253 		*l2 = g_slist_remove_link (*l2, link);
254 		g_slist_free (link);
255 	}
256 
257 	return e;
258 }
259 
260 static void
interval_forall(HTMLObject * parent,GSList * from_down,GSList * to_down,HTMLEngine * e,HTMLObjectForallFunc f,gpointer data)261 interval_forall (HTMLObject *parent,
262                  GSList *from_down,
263                  GSList *to_down,
264                  HTMLEngine *e,
265                  HTMLObjectForallFunc f,
266                  gpointer data)
267 {
268 	HTMLObject *o, *from, *to;
269 
270 	from = from_down ? HTML_OBJECT (from_down->data) : html_object_head (parent);
271 	to   = to_down   ? HTML_OBJECT (to_down->data)   : NULL;
272 
273 	for (o = from; o; o = html_object_next_not_slave (o)) {
274 		interval_forall (o,
275 				 (from_down && o == HTML_OBJECT (from_down->data)) ? from_down->next : NULL,
276 				 (to_down   && o == HTML_OBJECT (to_down->data))   ? to_down->next   : NULL,
277 				 html_object_get_engine (o, e), f, data);
278 		if (o == to)
279 			break;
280 	}
281 	(*f) (parent, e, data);
282 }
283 
284 static void
html_point_to_leaf(HTMLPoint * p)285 html_point_to_leaf (HTMLPoint *p)
286 {
287 	if (html_object_is_container (p->object)) {
288 		if (p->offset == 0)
289 			p->object = html_object_get_head_leaf (p->object);
290 		else if (p->offset == html_object_get_length (p->object)) {
291 			p->object = html_object_get_tail_leaf (p->object);
292 			p->offset = html_object_get_length (p->object);
293 		} else
294 			g_warning ("Can't transform point to leaf\n");
295 	}
296 }
297 
298 static inline HTMLInterval *
html_interval_dup(HTMLInterval * i)299 html_interval_dup (HTMLInterval *i)
300 {
301 	return html_interval_new_from_points (&i->from, &i->to);
302 }
303 
304 HTMLInterval *
html_interval_flat(HTMLInterval * i)305 html_interval_flat (HTMLInterval *i)
306 {
307 	HTMLInterval *ni = html_interval_dup (i);
308 
309 	html_point_to_leaf (&ni->from);
310 	html_point_to_leaf (&ni->to);
311 
312 	return ni;
313 }
314 
315 void
html_interval_forall(HTMLInterval * i,HTMLEngine * e,HTMLObjectForallFunc f,gpointer data)316 html_interval_forall (HTMLInterval *i,
317                       HTMLEngine *e,
318                       HTMLObjectForallFunc f,
319                       gpointer data)
320 {
321 	GSList *from_downline, *to_downline;
322 	HTMLEngine *engine;
323 
324 	g_return_if_fail (i->from.object);
325 	g_return_if_fail (i->to.object);
326 
327 	i = html_interval_flat (i);
328 
329 	from_downline = get_downtree_line (i->from.object);
330 	to_downline   = get_downtree_line (i->to.object);
331 	engine = do_downtree_lines_intersection  (&from_downline, &to_downline, e);
332 
333 	if (from_downline)
334 		interval_forall    (HTML_OBJECT (from_downline->data)->parent, from_downline, to_downline,
335 				    html_object_get_engine (HTML_OBJECT (from_downline->data)->parent, engine), f, data);
336 	else {
337 		g_assert (i->from.object == i->to.object);
338 		html_object_forall (i->from.object, html_object_get_engine (i->from.object, engine), f, data);
339 	}
340 
341 	g_slist_free (from_downline);
342 	g_slist_free (to_downline);
343 	html_interval_destroy (i);
344 }
345 
346 static HTMLObject *
html_object_children_max(HTMLObject * a,HTMLObject * b)347 html_object_children_max (HTMLObject *a,
348                           HTMLObject *b)
349 {
350 	HTMLObject *o;
351 
352 	g_return_val_if_fail (a->parent, NULL);
353 	g_return_val_if_fail (b->parent, NULL);
354 	g_return_val_if_fail (a->parent == b->parent, NULL);
355 
356 	for (o = a; o; o = html_object_next_not_slave (o))
357 		if (o == b)
358 			return b;
359 	return a;
360 }
361 
362 HTMLPoint *
html_point_max(HTMLPoint * a,HTMLPoint * b)363 html_point_max (HTMLPoint *a,
364                 HTMLPoint *b)
365 {
366 	GSList *a_downline, *b_downline;
367 	HTMLPoint *rv = NULL;
368 
369 	if (a->object == b->object)
370 		return a->offset < b->offset ? b : a;
371 
372 	a_downline = get_downtree_line (a->object);
373 	b_downline = get_downtree_line (b->object);
374 	do_downtree_lines_intersection (&a_downline, &b_downline, NULL);
375 
376 	if (a_downline == NULL)
377 		/* it means that a is parent (container) of b */
378 		rv = a->offset ? a : b;
379 	else if (b_downline == NULL)
380 		/* it means that b is parent (container) of a */
381 		rv = b->offset ? b : a;
382 	else
383 		rv = html_object_children_max (HTML_OBJECT (a_downline->data), HTML_OBJECT (b_downline->data))
384 			== HTML_OBJECT (a_downline->data) ? a : b;
385 	g_slist_free (a_downline);
386 	g_slist_free (b_downline);
387 
388 	return rv;
389 }
390 
391 inline HTMLPoint *
html_point_min(HTMLPoint * a,HTMLPoint * b)392 html_point_min (HTMLPoint *a,
393                 HTMLPoint *b)
394 {
395 	return a == html_point_max (a, b) ? b : a;
396 }
397 
398 static HTMLPoint *
max_from(HTMLInterval * a,HTMLInterval * b)399 max_from (HTMLInterval *a,
400           HTMLInterval *b)
401 {
402 	if (!a->from.object)
403 		return &b->from;
404 	if (!b->from.object)
405 		return &a->from;
406 
407 	return html_point_max (&a->from, &b->from);
408 }
409 
410 static HTMLPoint *
min_to(HTMLInterval * a,HTMLInterval * b)411 min_to (HTMLInterval *a,
412         HTMLInterval *b)
413 {
414 	if (!a->to.object)
415 		return &b->to;
416 	if (!b->to.object)
417 		return &a->to;
418 
419 	return html_point_min (&a->to, &b->to);
420 }
421 
422 HTMLInterval *
html_interval_intersection(HTMLInterval * a,HTMLInterval * b)423 html_interval_intersection (HTMLInterval *a,
424                             HTMLInterval *b)
425 {
426 	HTMLPoint *from, *to;
427 
428 	from = max_from (a, b);
429 	to   = min_to   (a, b);
430 
431 	return to == html_point_max (from, to) ?
432 		html_interval_new_from_points (from, to) : NULL;
433 }
434 
435 gpointer
html_interval_substract(HTMLInterval * a,HTMLInterval * b,HTMLInterval ** s1,HTMLInterval ** s2)436 html_interval_substract (HTMLInterval *a,
437                          HTMLInterval *b,
438                          HTMLInterval **s1,
439                          HTMLInterval **s2)
440 {
441 	return NULL;
442 }
443 
444 void
html_interval_validate(HTMLInterval * i)445 html_interval_validate (HTMLInterval *i)
446 {
447 	if (&i->from == html_point_max (&i->from, &i->to)) {
448 		HTMLPoint tmp;
449 
450 		tmp     = i->from;
451 		i->from = i->to;
452 		i->to   = tmp;
453 	}
454 }
455 
456 gboolean
html_point_eq(const HTMLPoint * a,const HTMLPoint * b)457 html_point_eq (const HTMLPoint *a,
458                const HTMLPoint *b)
459 {
460 	return a->object == b->object && a->offset == b->offset;
461 }
462 
463 gboolean
html_interval_eq(const HTMLInterval * a,const HTMLInterval * b)464 html_interval_eq (const HTMLInterval *a,
465                   const HTMLInterval *b)
466 {
467 	return html_point_eq (&a->from, &b->from) && html_point_eq (&a->to, &b->to);
468 }
469 
470 HTMLObject *
html_interval_get_head(HTMLInterval * i,HTMLObject * o)471 html_interval_get_head (HTMLInterval *i,
472                         HTMLObject *o)
473 {
474 	return o == i->from.object->parent ? i->from.object : html_object_head (o);
475 }
476