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