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) 1997 Martin Jones (mjones@kde.org)
5  * Copyright (C) 1997 Torben Weis (weis@kde.org)
6  * Copyright (C) 1999 Anders Carlsson (andersca@gnu.org)
7  * Copyright (C) 2000 Helix Code, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23 */
24 
25 #include <config.h>
26 #include <stdio.h>
27 #include <string.h>
28 
29 #include "gtkhtmldebug.h"
30 #include "htmlcolor.h"
31 #include "htmlcolorset.h"
32 #include "htmlengine.h"
33 #include "htmlengine-edit.h"
34 #include "htmlengine-edit-table.h"
35 #include "htmlengine-save.h"
36 #include "htmlimage.h"
37 #include "htmlpainter.h"
38 #include "htmlplainpainter.h"
39 #include "htmlsearch.h"
40 #include "htmltable.h"
41 #include "htmltablepriv.h"
42 #include "htmltablecell.h"
43 
44 /* #define GTKHTML_DEBUG_TABLE */
45 
46 #define COLUMN_MIN(table, i)				\
47 	(g_array_index (table->columnMin, gint, i))
48 
49 #define COLUMN_PREF(table, i)				\
50 	(g_array_index (table->columnPref, gint, i))
51 
52 #define COLUMN_FIX(table, i)				\
53 	(g_array_index (table->columnFixed, gint, i))
54 
55 #define COLUMN_OPT(table, i)				\
56 	(g_array_index (table->columnOpt, gint, i))
57 
58 #define ROW_HEIGHT(table, i)				\
59 	(g_array_index (table->rowHeights, gint, i))
60 
61 
62 HTMLTableClass html_table_class;
63 static HTMLObjectClass *parent_class = NULL;
64 
65 static void do_cspan   (HTMLTable *table, gint row, gint col, HTMLTableCell *cell);
66 static void do_rspan   (HTMLTable *table, gint row);
67 
68 static void html_table_set_max_width (HTMLObject *o, HTMLPainter *painter, gint max_width);
69 
70 
71 
72 static inline gboolean
invalid_cell(HTMLTable * table,gint r,gint c)73 invalid_cell (HTMLTable *table,
74               gint r,
75               gint c)
76 {
77 	return (table->cells[r][c] == NULL
78 		|| c != table->cells[r][c]->col
79 		|| r != table->cells[r][c]->row);
80 }
81 
82 /* HTMLObject methods.  */
83 
84 static void
destroy(HTMLObject * o)85 destroy (HTMLObject *o)
86 {
87 	HTMLTable *table = HTML_TABLE (o);
88 	HTMLTableCell *cell;
89 	guint r, c;
90 
91 	if (table->allocRows && table->totalCols)
92 		for (r = table->allocRows - 1; ; r--) {
93 			for (c = table->totalCols - 1; ; c--) {
94 				if ((cell = table->cells[r][c]) && cell->row == r && cell->col == c)
95 					html_object_destroy (HTML_OBJECT (cell));
96 				if (c == 0)
97 					break;
98 			}
99 			g_free (table->cells[r]);
100 			if (r == 0)
101 				break;
102 		}
103 	g_free (table->cells);
104 
105 	g_array_free (table->columnMin, TRUE);
106 	g_array_free (table->columnPref, TRUE);
107 	g_array_free (table->columnOpt, TRUE);
108 	g_array_free (table->columnFixed, TRUE);
109 	g_array_free (table->rowHeights, TRUE);
110 
111 	if (table->bgColor)
112 		gdk_color_free (table->bgColor);
113 	if (table->bgPixmap)
114 		html_image_factory_unregister (table->bgPixmap->factory, table->bgPixmap, NULL);
115 
116 	HTML_OBJECT_CLASS (parent_class)->destroy (o);
117 }
118 
119 static void
copy_sized(HTMLObject * self,HTMLObject * dest,gint rows,gint cols)120 copy_sized (HTMLObject *self,
121             HTMLObject *dest,
122             gint rows,
123             gint cols)
124 {
125 	HTMLTable *d = HTML_TABLE (dest);
126 	HTMLTable *s = HTML_TABLE (self);
127 	gint r;
128 
129 	memcpy (dest, self, sizeof (HTMLTable));
130 	(* HTML_OBJECT_CLASS (parent_class)->copy) (self, dest);
131 
132 	d->bgColor     = s->bgColor ? gdk_color_copy (s->bgColor) : NULL;
133 	d->caption     = s->caption ? HTML_CLUEV (html_object_dup (HTML_OBJECT (s->caption))) : NULL;
134 	d->bgPixmap    = s->bgPixmap ? html_image_factory_register (s->bgPixmap->factory, NULL, s->bgPixmap->url, FALSE) : NULL;
135 
136 	d->columnMin   = g_array_new (FALSE, FALSE, sizeof (gint));
137 	d->columnFixed = g_array_new (FALSE, FALSE, sizeof (gint));
138 	d->columnPref  = g_array_new (FALSE, FALSE, sizeof (gint));
139 	d->columnOpt   = g_array_new (FALSE, FALSE, sizeof (gint));
140 	d->rowHeights  = g_array_new (FALSE, FALSE, sizeof (gint));
141 
142 	d->totalCols = cols;
143 	d->totalRows = rows;
144 	d->allocRows = rows;
145 
146 	d->cells = g_new (HTMLTableCell **, rows);
147 	for (r = 0; r < rows; r++)
148 		d->cells[r] = g_new0 (HTMLTableCell *, cols);
149 
150 	dest->change = HTML_CHANGE_ALL_CALC;
151 }
152 
153 static void
copy(HTMLObject * self,HTMLObject * dest)154 copy (HTMLObject *self,
155       HTMLObject *dest)
156 {
157 	copy_sized (self, dest, HTML_TABLE (self)->totalRows, HTML_TABLE (self)->totalCols);
158 }
159 
160 static HTMLObject * op_copy (HTMLObject *self, HTMLObject *parent, HTMLEngine *e, GList *from, GList *to, guint *len);
161 
162 static HTMLObject *
copy_as_leaf(HTMLObject * self,HTMLObject * parent,HTMLEngine * e,GList * from,GList * to,guint * len)163 copy_as_leaf (HTMLObject *self,
164               HTMLObject *parent,
165               HTMLEngine *e,
166               GList *from,
167               GList *to,
168               guint *len)
169 {
170 	if ((!from || GPOINTER_TO_INT (from->data) == 0)
171 	    && (!to || GPOINTER_TO_INT (to->data) == html_object_get_length (self)))
172 		return op_copy (self, parent, e, NULL, NULL, len);
173 	else
174 		return html_engine_new_text_empty (e);
175 }
176 
177 static HTMLObject *
op_copy(HTMLObject * self,HTMLObject * parent,HTMLEngine * e,GList * from,GList * to,guint * len)178 op_copy (HTMLObject *self,
179          HTMLObject *parent,
180          HTMLEngine *e,
181          GList *from,
182          GList *to,
183          guint *len)
184 {
185 	HTMLTableCell *start, *end;
186 	HTMLTable *nt, *t;
187 	gint r, c, rows, cols, start_col;
188 
189 	g_assert (HTML_IS_TABLE (self));
190 
191 	if ((from || to)
192 	    && (!from || !from->next)
193 	    && (!to || !to->next))
194 		return copy_as_leaf (self, parent, e, from, to, len);
195 
196 	t  = HTML_TABLE (self);
197 	nt = g_new0 (HTMLTable, 1);
198 
199 	start = HTML_TABLE_CELL ((from && from->next) ? from->data : html_object_head (self));
200 	end   = HTML_TABLE_CELL ((to && to->next)     ? to->data   : html_object_tail (self));
201 
202 	if (!start || !end) {
203 		copy_sized (self, HTML_OBJECT (nt), 0, 0);
204 		(*len) ++;
205 		return HTML_OBJECT (nt);
206 	}
207 
208 	rows  = end->row - start->row + 1;
209 	cols  = end->row == start->row ? end->col - start->col + 1 : t->totalCols;
210 
211 	copy_sized (self, HTML_OBJECT (nt), rows, cols);
212 
213 	start_col = end->row == start->row ? start->col : 0;
214 
215 #ifdef GTKHTML_DEBUG_TABLE
216 	printf ("cols: %d rows: %d\n", cols, rows);
217 #endif
218 	for (r = 0; r < rows; r++)
219 		for (c = 0; c < cols; c++) {
220 			HTMLTableCell *cell = t->cells[start->row + r][c + start_col];
221 
222 			if (!cell || (end->row != start->row
223 				      && ((r == 0 && c < start->col) || (r == rows - 1 && c > end->col)))) {
224 				html_table_set_cell (nt, r, c, html_engine_new_cell (e, nt));
225 				html_table_cell_set_position (nt->cells[r][c], r, c);
226 			} else {
227 				if (cell->row == r + start->row && cell->col == c + start_col) {
228 					HTMLTableCell *cell_copy;
229 					cell_copy = HTML_TABLE_CELL
230 						(html_object_op_copy (HTML_OBJECT (cell), HTML_OBJECT (nt), e,
231 								      html_object_get_bound_list (HTML_OBJECT (cell), from),
232 								      html_object_get_bound_list (HTML_OBJECT (cell), to),
233 								      len));
234 					html_table_set_cell (nt, r, c, cell_copy);
235 					html_table_cell_set_position (cell_copy, r, c);
236 				} else {
237 					if (cell->row - start->row >= 0 && cell->col - start_col >= 0) {
238 						nt->cells[r][c] = nt->cells[cell->row - start->row][cell->col - start_col];
239 					} else {
240 						html_table_set_cell (nt, r, c, html_engine_new_cell (e, nt));
241 						html_table_cell_set_position (nt->cells[r][c], r, c);
242 					}
243 				}
244 			}
245 			(*len) ++;
246 		}
247 	(*len) ++;
248 
249 #ifdef GTKHTML_DEBUG_TABLE
250 	printf ("copy end: %d\n", *len);
251 #endif
252 
253 	return HTML_OBJECT (nt);
254 }
255 
256 static gint
get_n_children(HTMLObject * self)257 get_n_children (HTMLObject *self)
258 {
259 	HTMLTable *t = HTML_TABLE (self);
260 	guint r, c, n_children = 0;
261 
262 	for (r = 0; r < t->totalRows; r++)
263 		for (c = 0; c < t->totalCols; c++)
264 			if (t->cells[r][c] && t->cells[r][c]->row == r && t->cells[r][c]->col == c)
265 				n_children++;
266 
267 	/* printf ("table n_children %d\n", n_children); */
268 
269 	return n_children;
270 }
271 
272 static HTMLObject *
get_child(HTMLObject * self,gint index)273 get_child (HTMLObject *self,
274            gint index)
275 {
276 	HTMLTable *t = HTML_TABLE (self);
277 	HTMLObject *child = NULL;
278 	guint r, c, n = 0;
279 
280 	for (r = 0; r < t->totalRows && !child; r++)
281 		for (c = 0; c < t->totalCols; c++)
282 			if (t->cells[r][c] && t->cells[r][c]->row == r && t->cells[r][c]->col == c) {
283 				if (n == index) {
284 					child = HTML_OBJECT (t->cells[r][c]);
285 					break;
286 				}
287 				n++;
288 			}
289 
290 	/* printf ("table ref %d child %p\n", index, child); */
291 
292 	return child;
293 }
294 
295 static gint
get_child_index(HTMLObject * self,HTMLObject * child)296 get_child_index (HTMLObject *self,
297                  HTMLObject *child)
298 {
299 	HTMLTable *t = HTML_TABLE (self);
300 	guint r, c;
301 	gint n = 0;
302 
303 	for (r = 0; r < t->totalRows; r++)
304 		for (c = 0; c < t->totalCols; c++) {
305 			if (t->cells[r][c] && t->cells[r][c]->row == r && t->cells[r][c]->col == c) {
306 				if (HTML_OBJECT (t->cells[r][c]) == child) {
307 					/* printf ("table child %p index %d\n", child, n); */
308 					return n;
309 				}
310 				n++;
311 			}
312 		}
313 
314 	/* printf ("table child %p index %d\n", child, -1); */
315 
316 	return -1;
317 }
318 
319 static guint
get_recursive_length(HTMLObject * self)320 get_recursive_length (HTMLObject *self)
321 {
322 	HTMLTable *t = HTML_TABLE (self);
323 	guint r, c, len = 0;
324 
325 	for (r = 0; r < t->totalRows; r++)
326 		for (c = 0; c < t->totalCols; c++)
327 			if (t->cells[r][c] && t->cells[r][c]->row == r && t->cells[r][c]->col == c)
328 				len += html_object_get_recursive_length (HTML_OBJECT (t->cells[r][c])) + 1;
329 
330 	/* if (len > 0)
331 	 * len--; */
332 	len++;
333 	return len;
334 }
335 
336 static void
remove_cell(HTMLTable * t,HTMLTableCell * cell)337 remove_cell (HTMLTable *t,
338              HTMLTableCell *cell)
339 {
340 	gint r, c;
341 
342 	g_return_if_fail (t);
343 	g_return_if_fail (HTML_IS_TABLE (t));
344 	g_return_if_fail (cell);
345 	g_return_if_fail (HTML_IS_TABLE_CELL (cell));
346 
347 #ifdef GTKHTML_DEBUG_TABLE
348 	printf ("remove cell: %d,%d %d,%d %d,%d\n",
349 		cell->row, cell->col, cell->rspan, cell->cspan, t->totalCols, t->totalRows);
350 #endif
351 
352 	for (r = 0; r < cell->rspan && r + cell->row < t->totalRows; r++)
353 		for (c = 0; c < cell->cspan && c + cell->col < t->totalCols; c++) {
354 
355 #ifdef GTKHTML_DEBUG_TABLE
356 			printf ("clear:       %d,%d (%d,%d) %d,%d\n",
357 				cell->row + r, cell->col + c, cell->rspan, cell->cspan, r, c);
358 #endif
359 
360 			t->cells[cell->row + r][cell->col + c] = NULL;
361 		}
362 	HTML_OBJECT (cell)->parent = NULL;
363 }
364 
365 static HTMLObject *
cut_whole(HTMLObject * self,guint * len)366 cut_whole (HTMLObject *self,
367            guint *len)
368 {
369 	if (self->parent)
370 		html_object_remove_child (self->parent, self);
371 	*len = html_object_get_recursive_length (self) + 1;
372 
373 #ifdef GTKHTML_DEBUG_TABLE
374 	printf ("removed whole table len: %d\n", *len);
375 #endif
376 	return self;
377 }
378 
379 static HTMLObject *
cut_partial(HTMLObject * self,HTMLEngine * e,GList * from,GList * to,GList * left,GList * right,guint * len)380 cut_partial (HTMLObject *self,
381              HTMLEngine *e,
382              GList *from,
383              GList *to,
384              GList *left,
385              GList *right,
386              guint *len)
387 {
388 	HTMLObject *rv;
389 
390 	HTMLTableCell *start, *end, *cell;
391 	HTMLTable *t, *nt;
392 	gint r, c;
393 	gint start_row, start_col, end_row, end_col;
394 
395 #ifdef GTKHTML_DEBUG_TABLE
396 	printf ("partial cut\n");
397 #endif
398 	start = HTML_TABLE_CELL (from && from->next ? from->data : html_object_head (self));
399 	end   = HTML_TABLE_CELL (to   && to->next   ? to->data   : html_object_tail (self));
400 
401 	start_row = start->row;
402 	start_col = start->col;
403 	end_row   = end->row;
404 	end_col   = end->col;
405 
406 	t    = HTML_TABLE (self);
407 	rv   = HTML_OBJECT (g_new0 (HTMLTable, 1));
408 	nt   = HTML_TABLE (rv);
409 	copy_sized (self, rv, t->totalRows, t->totalCols);
410 
411 	for (r = 0; r < t->totalRows; r++) {
412 		for (c = 0; c < t->totalCols; c++) {
413 			cell = t->cells[r][c];
414 			if (cell && cell->row == r && cell->col == c) {
415 				if (((r == start_row && c < start_col) || r < start_row)
416 				    || ((r == end_row && c > end_col) || r > end_row)) {
417 					html_table_set_cell (nt, r, c, html_engine_new_cell (e, nt));
418 					html_table_cell_set_position (nt->cells[r][c], r, c);
419 				} else {
420 					HTMLTableCell *cell_cut;
421 
422 					cell_cut = HTML_TABLE_CELL
423 						(html_object_op_cut
424 						 (HTML_OBJECT (cell), e,
425 						  html_object_get_bound_list (HTML_OBJECT (cell), from),
426 						  html_object_get_bound_list (HTML_OBJECT (cell), to),
427 						  left ? left->next : NULL, right ? right->next : NULL, len));
428 					html_table_set_cell (nt, r, c, cell_cut);
429 					html_table_cell_set_position (cell_cut, r, c);
430 
431 					if (t->cells[r][c] == NULL) {
432 						html_table_set_cell (t, r, c, html_engine_new_cell (e, t));
433 						html_table_cell_set_position (t->cells[r][c], r, c);
434 					}
435 				}
436 				(*len) ++;
437 			}
438 		}
439 	}
440 	(*len) ++;
441 
442 #ifdef GTKHTML_DEBUG_TABLE
443 	printf ("removed partial table len: %d\n", *len);
444 	gtk_html_debug_dump_tree_simple (rv, 0);
445 #endif
446 
447 	return rv;
448 }
449 
450 static HTMLObject *
op_cut(HTMLObject * self,HTMLEngine * e,GList * from,GList * to,GList * left,GList * right,guint * len)451 op_cut (HTMLObject *self,
452         HTMLEngine *e,
453         GList *from,
454         GList *to,
455         GList *left,
456         GList *right,
457         guint *len)
458 {
459 	if ((!from || !from->next) && (!to || !to->next))
460 		return (*parent_class->op_cut) (self, e, from, to, left, right, len);
461 
462 	if (from || to)
463 		return cut_partial (self, e, from, to, left, right, len);
464 	else
465 		return cut_whole (self, len);
466 }
467 
468 static void
split(HTMLObject * self,HTMLEngine * e,HTMLObject * child,gint offset,gint level,GList ** left,GList ** right)469 split (HTMLObject *self,
470        HTMLEngine *e,
471        HTMLObject *child,
472        gint offset,
473        gint level,
474        GList **left,
475        GList **right)
476 {
477 	HTMLObject *dup;
478 	HTMLTable *t = HTML_TABLE (self);
479 	HTMLTable *dup_table;
480 	HTMLTableCell *dup_cell;
481 	HTMLTableCell *cell;
482 	gint r, c;
483 
484 	if (*left == NULL && *right == NULL) {
485 		(*parent_class->split)(self, e, child, offset, level, left, right);
486 		return;
487 	}
488 
489 	dup_cell  = HTML_TABLE_CELL ((*right)->data);
490 	cell      = HTML_TABLE_CELL ((*left)->data);
491 
492 	if (dup_cell->row == t->totalRows - 1 && dup_cell->col == t->totalCols - 1 && html_clue_is_empty (HTML_CLUE (dup_cell))) {
493 		dup = html_engine_new_text_empty (e);
494 		html_object_destroy ((*right)->data);
495 		g_list_free (*right);
496 		*right = NULL;
497 	} else {
498 
499 #ifdef GTKHTML_DEBUG_TABLE
500 	printf ("before split\n");
501 	printf ("-- self --\n");
502 	gtk_html_debug_dump_tree_simple (self, 0);
503 	printf ("-- child --\n");
504 	gtk_html_debug_dump_tree_simple (child, 0);
505 	printf ("-- child end --\n");
506 #endif
507 
508 	dup = HTML_OBJECT (g_new0 (HTMLTable, 1));
509 	dup_table = HTML_TABLE (dup);
510 	copy_sized (self, dup, t->totalRows, t->totalCols);
511 	for (r = 0; r < t->totalRows; r++) {
512 		for (c = 0; c < t->totalCols; c++) {
513 			HTMLTableCell *cc;
514 
515 			cc = t->cells[r][c];
516 			if (cc && cc->row == r && cc->col == c) {
517 				if ((r == cell->row && c < cell->col) || r < cell->row) {
518 					/* empty cell in dup table */
519 					html_table_set_cell (dup_table, r, c, html_engine_new_cell (e, dup_table));
520 					html_table_cell_set_position (dup_table->cells[r][c], r, c);
521 				} else if ((r == dup_cell->row && c > dup_cell->col) || r > dup_cell->row) {
522 					/* move cc to dup table */
523 					remove_cell (t, cc);
524 					html_table_set_cell (dup_table, r, c, cc);
525 					html_table_cell_set_position (dup_table->cells[r][c], r, c);
526 					/* place empty cell in t table */
527 					html_table_set_cell (t, r, c, html_engine_new_cell (e, t));
528 					html_table_cell_set_position (t->cells[r][c], r, c);
529 
530 				} else {
531 					if (r == cell->row && c == cell->col) {
532 						if (r != dup_cell->row || c != dup_cell->col) {
533 							/* empty cell in dup table */
534 							html_table_set_cell (dup_table, r, c,
535 									     html_engine_new_cell (e, dup_table));
536 							html_table_cell_set_position (dup_table->cells[r][c], r, c);
537 						}
538 
539 					}
540 					if (r == dup_cell->row && c == dup_cell->col) {
541 						/* dup_cell to dup table */
542 						if ((r != cell->row || c != cell->col)
543 						    && HTML_OBJECT (dup_cell)->parent == self)
544 							remove_cell (t, cell);
545 
546 						html_table_set_cell (dup_table, r, c, dup_cell);
547 						html_table_cell_set_position (dup_table->cells[r][c], r, c);
548 
549 						if (r != cell->row || c != cell->col) {
550 							/* empty cell in orig table */
551 							html_table_set_cell (t, r, c, html_engine_new_cell (e, t));
552 							html_table_cell_set_position (t->cells[r][c], r, c);
553 						}
554 					}
555 				}
556 			}
557 		}
558 	}
559 	}
560 	html_clue_append_after (HTML_CLUE (self->parent), dup, self);
561 
562 	*left  = g_list_prepend (*left, self);
563 	*right = g_list_prepend (*right, dup);
564 
565 	html_object_change_set (self, HTML_CHANGE_ALL_CALC);
566 	html_object_change_set (dup,  HTML_CHANGE_ALL_CALC);
567 
568 #ifdef GTKHTML_DEBUG_TABLE
569 	printf ("after split\n");
570 	printf ("-- self --\n");
571 	gtk_html_debug_dump_tree_simple (self,  0);
572 	printf ("-- dup --\n");
573 	gtk_html_debug_dump_tree_simple (dup, 0);
574 	printf ("-- end split --\n");
575 #endif
576 
577 	level--;
578 	if (level)
579 		html_object_split (self->parent, e, dup, 0, level, left, right);
580 }
581 
582 static gboolean
could_merge(HTMLTable * t1,HTMLTable * t2)583 could_merge (HTMLTable *t1,
584              HTMLTable *t2)
585 {
586 	gint r, c;
587 	gboolean first = TRUE;
588 
589 	if (t1->specified_width != t2->specified_width
590 	    || t1->spacing != t2->spacing
591 	    || t1->padding != t2->padding
592 	    || t1->border != t2->border
593 	    || t1->capAlign != t2->capAlign
594 	    || (t1->bgColor && t2->bgColor && !gdk_color_equal (t1->bgColor, t2->bgColor))
595 	    || (t1->bgColor && !t2->bgColor) || (!t1->bgColor && t2->bgColor)
596 	    || t1->bgPixmap != t2->bgPixmap
597 	    || t1->totalCols != t2->totalCols || t1->totalRows != t2->totalRows)
598 		return FALSE;
599 
600 	for (r = 0; r < t1->totalRows; r++) {
601 		for (c = 0; c < t1->totalCols; c++) {
602 			HTMLTableCell *c1, *c2;
603 
604 			c1 = t1->cells[r][c];
605 			c2 = t2->cells[r][c];
606 			if (!c1 || !c2)
607 				return FALSE;
608 
609 			if (first) {
610 				if (!html_clue_is_empty (HTML_CLUE (c2)))
611 					first = FALSE;
612 			} else {
613 				if (!html_clue_is_empty (HTML_CLUE (c1)))
614 					return FALSE;
615 			}
616 		}
617 	}
618 
619 	return TRUE;
620 }
621 
622 static HTMLTableCell *
object_get_parent_cell(HTMLObject * o,HTMLObject * parent_table)623 object_get_parent_cell (HTMLObject *o,
624                         HTMLObject *parent_table)
625 {
626 	while (o) {
627 		if (o->parent == parent_table)
628 			return HTML_TABLE_CELL (o);
629 		o = o->parent;
630 	}
631 
632 	return NULL;
633 }
634 
635 static void
update_cursor(HTMLCursor * cursor,HTMLTableCell * c)636 update_cursor (HTMLCursor *cursor,
637                HTMLTableCell *c)
638 {
639 	cursor->object = html_object_get_head_leaf (HTML_OBJECT (c));
640 	cursor->offset = 0;
641 }
642 
643 static void
move_cell(HTMLTable * t1,HTMLTable * t2,HTMLTableCell * c1,HTMLTableCell * c2,HTMLTableCell * cursor_cell_1,HTMLTableCell * cursor_cell_2,gint r,gint c,HTMLCursor * cursor_1,HTMLCursor * cursor_2)644 move_cell (HTMLTable *t1,
645            HTMLTable *t2,
646            HTMLTableCell *c1,
647            HTMLTableCell *c2,
648            HTMLTableCell *cursor_cell_1,
649            HTMLTableCell *cursor_cell_2,
650            gint r,
651            gint c,
652            HTMLCursor *cursor_1,
653            HTMLCursor *cursor_2)
654 {
655 	if (cursor_1 && cursor_cell_1 == c1)
656 		update_cursor (cursor_1, c2);
657 	if (cursor_2 && cursor_cell_2 == c1)
658 		update_cursor (cursor_2, c2);
659 	remove_cell (t1, c1);
660 	html_object_destroy (HTML_OBJECT (c1));
661 	remove_cell (t2, c2);
662 	html_table_set_cell (t1, r, c, c2);
663 	html_table_cell_set_position (t1->cells[r][c], r, c);
664 }
665 
666 static gboolean
merge(HTMLObject * self,HTMLObject * with,HTMLEngine * e,GList ** left,GList ** right,HTMLCursor * cursor)667 merge (HTMLObject *self,
668        HTMLObject *with,
669        HTMLEngine *e,
670        GList **left,
671        GList **right,
672        HTMLCursor *cursor)
673 {
674 	HTMLTable *t1 = HTML_TABLE (self);
675 	HTMLTable *t2 = HTML_TABLE (with);
676 	HTMLTableCell *cursor_cell_1 = NULL;
677 	HTMLTableCell *cursor_cell_2 = NULL;
678 	HTMLTableCell *cursor_cell_3 = NULL;
679 	HTMLTableCell *prev_c1 = NULL;
680 	HTMLTableCell *t1_tail = NULL;
681 	gint r, c;
682 	gboolean first = TRUE;
683 	gboolean cursor_in_t2;
684 
685 #ifdef GTKHTML_DEBUG_TABLE
686 	printf ("before merge\n");
687 	printf ("-- self --\n");
688 	gtk_html_debug_dump_tree_simple (self, 0);
689 	printf ("-- with --\n");
690 	gtk_html_debug_dump_tree_simple (with, 0);
691 	printf ("-- end with --\n");
692 #endif
693 
694 	if (!could_merge (t1, t2))
695 		return FALSE;
696 
697 	g_list_free (*left);
698 	*left = NULL;
699 	g_list_free (*right);
700 	*right = NULL;
701 
702 	cursor_in_t2 = object_get_parent_cell (e->cursor->object, HTML_OBJECT (t2)) != NULL;
703 
704 	cursor_cell_1 = HTML_TABLE_CELL (object_get_parent_cell (e->cursor->object, HTML_OBJECT (t1)));
705 	if (cursor)
706 		cursor_cell_2 = HTML_TABLE_CELL (object_get_parent_cell (cursor->object, HTML_OBJECT (t1)));
707 	cursor_cell_3 = HTML_TABLE_CELL (object_get_parent_cell (e->cursor->object, HTML_OBJECT (t2)));
708 
709 	for (r = 0; r < t1->totalRows; r++) {
710 		for (c = 0; c < t1->totalCols; c++) {
711 			HTMLTableCell *c1, *c2;
712 
713 			c1 = t1->cells[r][c];
714 			c2 = t2->cells[r][c];
715 
716 			if (first) {
717 				if (!html_clue_is_empty (HTML_CLUE (c2))) {
718 					t1_tail = prev_c1;
719 					if (html_clue_is_empty (HTML_CLUE (c1))) {
720 						move_cell (t1, t2, c1, c2, cursor_cell_1, cursor_cell_2,
721 							   r, c, e->cursor, cursor);
722 						c1 = c2;
723 					} else {
724 						*left  = html_object_tails_list (HTML_OBJECT (c1));
725 						*right = html_object_heads_list (HTML_OBJECT (c2));
726 						html_object_remove_child (HTML_OBJECT (t2), HTML_OBJECT (c2));
727 						if (e->cursor->object == HTML_OBJECT (t1)) {
728 							GList *list;
729 
730 							e->cursor->object = html_object_get_tail_leaf (HTML_OBJECT (c1));
731 							e->cursor->offset = html_object_get_length (e->cursor->object);
732 							e->cursor->position -= (t1->totalRows - c1->row - 1) * t1->totalCols
733 								+ (t1->totalCols - c1->col);
734 							for (list = *left; list; list = list->next)
735 								if (list->data && HTML_IS_TABLE (list->data))
736 									e->cursor->position--;
737 
738 							/* printf ("3rd dec: %d t1_tail %d,%d\n",
739 								(t1->totalRows - c1->row - 1) * t1->totalCols
740 								+ (t1->totalCols - c1->col), c1->row, c1->col); */
741 
742 						}
743 					}
744 					first = FALSE;
745 				} else {
746 					if (cursor_cell_3 && cursor_cell_3 == c2)
747 						e->cursor->object = html_object_get_head_leaf (HTML_OBJECT (c1));
748 				}
749 			} else {
750 				move_cell (t1, t2, c1, c2, cursor_cell_1, cursor_cell_2,
751 					   r, c, e->cursor, cursor);
752 				c1 = c2;
753 			}
754 			prev_c1 = c1;
755 		}
756 	}
757 
758 	if (!t1_tail)
759 		t1_tail = prev_c1;
760 
761 	if (e->cursor->object == self && t1_tail) {
762 		e->cursor->object = html_object_get_tail_leaf (HTML_OBJECT (t1_tail));
763 		e->cursor->offset = html_object_get_length (HTML_OBJECT (e->cursor->object));
764 		e->cursor->position -= (t1->totalRows - t1_tail->row - 1) * t1->totalCols
765 			+ (t1->totalCols - t1_tail->col);
766 		/* printf ("1st dec: %d t1_tail %d,%d\n", (t1->totalRows - t1_tail->row - 1)*t1->totalCols
767 		 * + (t1->totalCols - t1_tail->col), t1_tail->row, t1_tail->col); */
768 	}
769 
770 	if (cursor_in_t2 && cursor && cursor_cell_2) {
771 		e->cursor->position -= cursor_cell_2->row * t1->totalCols + cursor_cell_2->col + 1;
772 		/* printf ("2nd dec: %d cell_2  %d,%d\n", cursor_cell_2->row * t1->totalCols + cursor_cell_2->col + 1,
773 		 * cursor_cell_2->row, cursor_cell_2->col); */
774 	}
775 
776 	if (cursor && cursor->object == with)
777 		cursor->object = self;
778 
779 	return TRUE;
780 }
781 
782 static void
remove_child(HTMLObject * self,HTMLObject * child)783 remove_child (HTMLObject *self,
784               HTMLObject *child)
785 {
786 	remove_cell (HTML_TABLE (self), HTML_TABLE_CELL (child));
787 }
788 
789 static gboolean
accepts_cursor(HTMLObject * self)790 accepts_cursor (HTMLObject *self)
791 {
792 	return TRUE;
793 }
794 
795 static gboolean
is_container(HTMLObject * object)796 is_container (HTMLObject *object)
797 {
798 	return TRUE;
799 }
800 
801 static void
forall(HTMLObject * self,HTMLEngine * e,HTMLObjectForallFunc func,gpointer data)802 forall (HTMLObject *self,
803         HTMLEngine *e,
804         HTMLObjectForallFunc func,
805         gpointer data)
806 {
807 	HTMLTableCell *cell;
808 	HTMLTable *table;
809 	guint r, c;
810 
811 	table = HTML_TABLE (self);
812 
813 	for (r = 0; r < table->totalRows; r++) {
814 		for (c = 0; c < table->totalCols; c++) {
815 			cell = table->cells[r][c];
816 
817 			if (cell == NULL || cell->col != c || cell->row != r)
818 				continue;
819 
820 			html_object_forall (HTML_OBJECT (cell), e, func, data);
821 		}
822 	}
823 	(* func) (self, e, data);
824 }
825 
826 static void
previous_rows_do_cspan(HTMLTable * table,gint c)827 previous_rows_do_cspan (HTMLTable *table,
828                         gint c)
829 {
830 	gint i;
831 	if (c)
832 		for (i = 0; i < table->totalRows - 1; i++)
833 			if (table->cells[i][c - 1])
834 				do_cspan (table, i, c, table->cells[i][c - 1]);
835 }
836 
837 static void
expand_columns(HTMLTable * table,gint num)838 expand_columns (HTMLTable *table,
839                 gint num)
840 {
841 	gint r;
842 
843 	for (r = 0; r < table->allocRows; r++) {
844 		table->cells[r] = g_renew (HTMLTableCell *, table->cells[r], table->totalCols + num);
845 		memset (table->cells[r] + table->totalCols, 0, num * sizeof (HTMLTableCell *));
846 	}
847 	table->totalCols += num;
848 }
849 
850 static void
inc_columns(HTMLTable * table,gint num)851 inc_columns (HTMLTable *table,
852              gint num)
853 {
854 	expand_columns (table, num);
855 	previous_rows_do_cspan (table, table->totalCols - num);
856 }
857 
858 static void
expand_rows(HTMLTable * table,gint num)859 expand_rows (HTMLTable *table,
860              gint num)
861 {
862 	gint r;
863 
864 	table->cells = g_renew (HTMLTableCell **, table->cells, table->allocRows + num);
865 
866 	for (r = table->allocRows; r < table->allocRows + num; r++) {
867 		table->cells[r] = g_new (HTMLTableCell *, table->totalCols);
868 		memset (table->cells[r], 0, table->totalCols * sizeof (HTMLTableCell *));
869 	}
870 
871 	table->allocRows += num;
872 }
873 
874 static void
inc_rows(HTMLTable * table,gint num)875 inc_rows (HTMLTable *table,
876           gint num)
877 {
878 	if (table->totalRows + num > table->allocRows)
879 		expand_rows (table, num + MAX (10, table->allocRows >> 2));
880 	table->totalRows += num;
881 	if (table->totalRows - num > 0)
882 		do_rspan (table, table->totalRows - num);
883 }
884 
885 static inline gint
cell_end_col(HTMLTable * table,HTMLTableCell * cell)886 cell_end_col (HTMLTable *table,
887               HTMLTableCell *cell)
888 {
889 	return MIN (table->totalCols, cell->col + cell->cspan);
890 }
891 
892 static inline gint
cell_end_row(HTMLTable * table,HTMLTableCell * cell)893 cell_end_row (HTMLTable *table,
894               HTMLTableCell *cell)
895 {
896 	return MIN (table->totalRows, cell->row + cell->rspan);
897 }
898 
899 #define ARR(i) (g_array_index (array, gint, i))
900 #define LL (unsigned long long)
901 
902 static gboolean
calc_column_width_step(HTMLTable * table,HTMLPainter * painter,GArray * array,gint * sizes,gint (* calc_fn)(HTMLObject *,HTMLPainter *),gint span)903 calc_column_width_step (HTMLTable *table,
904                         HTMLPainter *painter,
905                         GArray *array,
906                         gint *sizes,
907                         gint (*calc_fn)(HTMLObject *, HTMLPainter *), gint span)
908 {
909 	gboolean has_greater_cspan = FALSE;
910 	gint r, c, i, pixel_size = html_painter_get_pixel_size (painter);
911 	gint border_extra = table->border ? 2 : 0;
912 
913 	for (c = 0; c < table->totalCols - span + 1; c++) {
914 		for (r = 0; r < table->totalRows; r++) {
915 			HTMLTableCell *cell = table->cells[r][c];
916 			gint col_width, span_width, cspan, new_width, added;
917 
918 			if (!cell || cell->col != c || cell->row != r)
919 				continue;
920 			cspan = MIN (cell->cspan, table->totalCols - cell->col);
921 			if (cspan > span)
922 				has_greater_cspan = TRUE;
923 			if (cspan != span)
924 				continue;
925 
926 			col_width  = (*calc_fn) (HTML_OBJECT (cell), painter)
927 				- (span - 1) * (table->spacing + border_extra) * pixel_size;
928 			if (col_width <= 0)
929 				continue;
930 			span_width = ARR (cell->col + span) - ARR (cell->col);
931 			added      = 0;
932 			for (i = 0; i < span; i++) {
933 				if (span_width) {
934 					new_width = (LL col_width * (ARR (cell->col + i + 1) - ARR (cell->col)))
935 						/ span_width;
936 					if (LL col_width * (ARR (cell->col + i + 1) - ARR (cell->col))
937 					    - LL new_width * span_width > LL (new_width + 1) * span_width
938 					    - LL col_width * (ARR (cell->col + i + 1) - ARR (cell->col)))
939 						new_width++;
940 				} else {
941 					new_width = added + col_width / span;
942 					if (col_width - LL span * new_width > LL span * (new_width + 1) - col_width)
943 						new_width++;
944 				}
945 				new_width -= added;
946 				added     += new_width;
947 
948 				if (sizes[cell->col + i] < new_width)
949 					sizes[cell->col + i] = new_width;
950 			}
951 			/* printf ("%d added %d col_width %d span_width %d\n",
952 			 * col_width - added, added, col_width, span_width); */
953 		}
954 	}
955 
956 	return has_greater_cspan;
957 }
958 
959 static void
calc_column_width_template(HTMLTable * table,HTMLPainter * painter,GArray * array,gint (* calc_fn)(HTMLObject *,HTMLPainter *),GArray * pref)960 calc_column_width_template (HTMLTable *table,
961                             HTMLPainter *painter,
962                             GArray *array,
963                             gint (*calc_fn)(HTMLObject *, HTMLPainter *), GArray *pref)
964 {
965 	gint c, add, span;
966 	gint pixel_size = html_painter_get_pixel_size (painter);
967 	gint border_extra = table->border ? 1 : 0;
968 	gint cell_space = pixel_size * (table->spacing + 2 * border_extra);
969 	gint *arr;
970 	gboolean next = TRUE;
971 
972 	g_array_set_size (array, table->totalCols + 1);
973 	for (c = 0; c <= table->totalCols; c++)
974 		ARR (c) = pixel_size * (table->border + table->spacing);
975 
976 	span = 1;
977 	while (span <= table->totalCols && next) {
978 		arr  = g_new0 (gint, table->totalCols);
979 		next = calc_column_width_step (table, painter, pref, arr, calc_fn, span);
980 		add  = 0;
981 		for (c = 0; c < table->totalCols; c++) {
982 			ARR (c + 1) += add;
983 			if (ARR (c + 1) - ARR (c) < arr[c]) {
984 				add += arr[c] - (ARR (c + 1) - ARR (c));
985 				ARR (c + 1) = ARR (c) + arr[c];
986 			}
987 		}
988 		g_free (arr);
989 		span++;
990 	}
991 
992 	for (c = 0; c < table->totalCols; c++)
993 		ARR (c + 1) += (c + 1) * cell_space;
994 }
995 
996 static void
do_cspan(HTMLTable * table,gint row,gint col,HTMLTableCell * cell)997 do_cspan (HTMLTable *table,
998           gint row,
999           gint col,
1000           HTMLTableCell *cell)
1001 {
1002 	gint i;
1003 
1004 	g_assert (cell);
1005 	g_assert (cell->col <= col);
1006 
1007 	for (i = col - cell->col; i < cell->cspan && cell->col + i < table->totalCols; i++)
1008 		html_table_set_cell (table, row, cell->col + i, cell);
1009 }
1010 
1011 static void
prev_col_do_cspan(HTMLTable * table,gint row)1012 prev_col_do_cspan (HTMLTable *table,
1013                    gint row)
1014 {
1015 	g_assert (row >= 0);
1016 
1017 	/* add previous column cell which has cspan > 1 */
1018 	while (table->col < table->totalCols && table->cells[row][table->col] != 0) {
1019 		html_table_alloc_cell (table, row, table->col + table->cells[row][table->col]->cspan);
1020 		do_cspan (table, row, table->col + 1, table->cells[row][table->col]);
1021 		table->col += (table->cells[row][table->col])->cspan;
1022 	}
1023 }
1024 
1025 static void
do_rspan(HTMLTable * table,gint row)1026 do_rspan (HTMLTable *table,
1027           gint row)
1028 {
1029 	gint i;
1030 
1031 	g_assert (row > 0);
1032 
1033 	for (i = 0; i < table->totalCols; i++)
1034 		if (table->cells[row - 1][i]
1035 		    && (table->cells[row - 1][i])->row + (table->cells[row - 1][i])->rspan
1036 		    > row) {
1037 			html_table_set_cell (table, table->row, i, table->cells[table->row - 1][i]);
1038 			do_cspan (table, table->row, i + 1, table->cells[table->row -1][i]);
1039 		}
1040 }
1041 
1042 void
html_table_set_cell(HTMLTable * table,gint r,gint c,HTMLTableCell * cell)1043 html_table_set_cell (HTMLTable *table,
1044                      gint r,
1045                      gint c,
1046                      HTMLTableCell *cell)
1047 {
1048 	if (!table->cells[r][c]) {
1049 #ifdef GTKHTML_DEBUG_TABLE
1050 		printf ("set cell:    %d,%d %p\n", r, c, cell);
1051 #endif
1052 		table->cells[r][c] = cell;
1053 		HTML_OBJECT (cell)->parent = HTML_OBJECT (table);
1054 	}
1055 }
1056 
1057 void
html_table_alloc_cell(HTMLTable * table,gint r,gint c)1058 html_table_alloc_cell (HTMLTable *table,
1059                        gint r,
1060                        gint c)
1061 {
1062 	if (c >= table->totalCols)
1063 		inc_columns (table, c + 1 - table->totalCols);
1064 
1065 	if (r >= table->totalRows)
1066 		inc_rows (table, r + 1 - table->totalRows);
1067 }
1068 
1069 #define RSPAN (MIN (cell->row + cell->rspan, table->totalRows) - cell->row - 1)
1070 
1071 static void
calc_row_heights(HTMLTable * table,HTMLPainter * painter)1072 calc_row_heights (HTMLTable *table,
1073                   HTMLPainter *painter)
1074 {
1075 	HTMLTableCell *cell;
1076 	gint r, c, rl, height, pixel_size = html_painter_get_pixel_size (painter);
1077 	gint border_extra = table->border ? 2 : 0;
1078 
1079 	g_array_set_size (table->rowHeights, table->totalRows + 1);
1080 	for (r = 0; r <= table->totalRows; r++)
1081 		ROW_HEIGHT (table, r) = pixel_size * (table->border + table->spacing);
1082 
1083 	for (r = 0; r < table->totalRows; r++) {
1084 		if (ROW_HEIGHT (table, r + 1) < ROW_HEIGHT (table, r))
1085 			ROW_HEIGHT (table, r + 1) = ROW_HEIGHT (table, r);
1086 		for (c = 0; c < table->totalCols; c++) {
1087 			cell = table->cells[r][c];
1088 			if (cell && cell->row == r && cell->col == c) {
1089 				rl = cell_end_row (table, cell);
1090 				height = (ROW_HEIGHT (table, cell->row)
1091 					  + HTML_OBJECT (cell)->ascent + HTML_OBJECT (cell)->descent
1092 					  + (pixel_size * (table->spacing + border_extra)));
1093 				if (height > ROW_HEIGHT (table, rl))
1094 					ROW_HEIGHT (table, rl) = height;
1095 			}
1096 		}
1097 		/* printf ("height %d: %d\n", r, ROW_HEIGHT (table, r)); */
1098 	}
1099 	/* printf ("height %d: %d\n", r, ROW_HEIGHT (table, r)); */
1100 }
1101 
1102 static void
calc_cells_size(HTMLTable * table,HTMLPainter * painter,GList ** changed_objs)1103 calc_cells_size (HTMLTable *table,
1104                  HTMLPainter *painter,
1105                  GList **changed_objs)
1106 {
1107 	HTMLTableCell *cell;
1108 	gint r, c;
1109 
1110 	for (r = 0; r < table->totalRows; r++)
1111 		for (c = 0; c < table->totalCols; c++) {
1112 			cell = table->cells[r][c];
1113 			if (cell && cell->col == c && cell->row == r)
1114 				html_object_calc_size (HTML_OBJECT (cell), painter, changed_objs);
1115 		}
1116 }
1117 
1118 static void
html_table_set_cells_position(HTMLTable * table,HTMLPainter * painter)1119 html_table_set_cells_position (HTMLTable *table,
1120                                HTMLPainter *painter)
1121 {
1122 	HTMLTableCell *cell;
1123 	gint r, c, rl, pixel_size = html_painter_get_pixel_size (painter);
1124 	gint border_extra = table->border ? 1 : 0;
1125 
1126 	for (r = 0; r < table->totalRows; r++)
1127 		for (c = 0; c < table->totalCols; c++) {
1128 			cell = table->cells[r][c];
1129 			if (cell && cell->row == r && cell->col == c) {
1130 				rl = cell_end_row (table, cell);
1131 				HTML_OBJECT (cell)->x = COLUMN_OPT (table, c) + pixel_size * border_extra;
1132 				HTML_OBJECT (cell)->y = ROW_HEIGHT (table, rl) + pixel_size * (- table->spacing)
1133 					- HTML_OBJECT (cell)->descent;
1134 				/* printf ("y: %d\n", HTML_OBJECT (cell)->y); */
1135 				html_object_set_max_height (HTML_OBJECT (cell), painter,
1136 							    ROW_HEIGHT (table, rl) - ROW_HEIGHT (table, cell->row)
1137 							    - pixel_size * (table->spacing + border_extra));
1138 			}
1139 		}
1140 }
1141 
1142 static void
add_clear_area(GList ** changed_objs,HTMLObject * o,gint x,gint w)1143 add_clear_area (GList **changed_objs,
1144                 HTMLObject *o,
1145                 gint x,
1146                 gint w)
1147 {
1148 	HTMLObjectClearRectangle *cr;
1149 
1150 	if (!changed_objs)
1151 		return;
1152 
1153 	cr = g_new (HTMLObjectClearRectangle, 1);
1154 
1155 	cr->object = o;
1156 	cr->x = x;
1157 	cr->y = 0;
1158 	cr->width = w;
1159 	cr->height = o->ascent + o->descent;
1160 
1161 	*changed_objs = g_list_prepend (*changed_objs, cr);
1162 	/* NULL meens: clear rectangle follows */
1163 	*changed_objs = g_list_prepend (*changed_objs, NULL);
1164 }
1165 
1166 static void
html_table_set_max_height(HTMLObject * o,HTMLPainter * painter,gint height)1167 html_table_set_max_height (HTMLObject *o,
1168                            HTMLPainter *painter,
1169                            gint height)
1170 {
1171 	/* for now just remember it, it will be passed down once size is calculated */
1172 	HTML_TABLE (o)->max_height = height;
1173 }
1174 
1175 static gboolean
html_table_real_calc_size(HTMLObject * o,HTMLPainter * painter,GList ** changed_objs)1176 html_table_real_calc_size (HTMLObject *o,
1177                            HTMLPainter *painter,
1178                            GList **changed_objs)
1179 {
1180 	HTMLTable *table = HTML_TABLE (o);
1181 	gint old_width, old_ascent, pixel_size;
1182 
1183 	old_width   = o->width;
1184 	old_ascent  = o->ascent;
1185 	pixel_size  = html_painter_get_pixel_size (painter);
1186 
1187 	if (!table->columnOpt->data)
1188 		html_table_set_max_width (o, painter, o->max_width);
1189 
1190 	calc_cells_size (table, painter, changed_objs);
1191 	calc_row_heights (table, painter);
1192 	html_table_set_cells_position (table, painter);
1193 
1194 	o->ascent = ROW_HEIGHT (table, table->totalRows) + pixel_size * table->border;
1195 	o->width  = COLUMN_OPT (table, table->totalCols) + pixel_size * table->border;
1196 
1197 	if (o->width != old_width || o->ascent != old_ascent) {
1198 		html_object_add_to_changed (changed_objs, o);
1199 		if (o->width < old_width) {
1200 			if (o->parent && HTML_IS_CLUEFLOW (o->parent)) {
1201 				switch (HTML_CLUE (o->parent)->halign) {
1202 				case HTML_HALIGN_NONE:
1203 				case HTML_HALIGN_LEFT:
1204 					add_clear_area (changed_objs, o, o->width, old_width - o->width);
1205 					break;
1206 				case HTML_HALIGN_RIGHT:
1207 					add_clear_area (changed_objs, o, - (old_width - o->width), old_width - o->width);
1208 					break;
1209 				case HTML_HALIGN_CENTER:
1210 					/* FIXME +/-1 pixel */
1211 					add_clear_area (changed_objs, o, -(old_width - o->width) / 2,
1212 							(old_width - o->width) / 2);
1213 					add_clear_area (changed_objs, o, o->width,
1214 							(old_width - o->width) / 2);
1215 					break;
1216 				}
1217 			}
1218 		}
1219 		return TRUE;
1220 	}
1221 
1222 	return FALSE;
1223 }
1224 
1225 #define NEW_INDEX(l,h) ((l+h) / 2)
1226 #undef  ARR
1227 #define ARR(i) g_array_index (a, gint, i)
1228 
1229 static gint
bin_search_index(GArray * a,gint l,gint h,gint val)1230 bin_search_index (GArray *a,
1231                   gint l,
1232                   gint h,
1233                   gint val)
1234 {
1235 	gint i;
1236 
1237 	i = NEW_INDEX (l, h);
1238 
1239 	while (l < h && val != ARR (i)) {
1240 		if (val < ARR (i))
1241 			h = i - 1;
1242 		else
1243 			l = i + 1;
1244 		i = NEW_INDEX (l, h);
1245 	}
1246 
1247 	return i;
1248 }
1249 
1250 static inline gint
to_index(gint val,gint l,gint h)1251 to_index (gint val,
1252           gint l,
1253           gint h)
1254 {
1255 	return MIN (MAX (val, l), h);
1256 }
1257 
1258 static void
get_bounds(HTMLTable * table,gint x,gint y,gint width,gint height,gint * sc,gint * ec,gint * sr,gint * er)1259 get_bounds (HTMLTable *table,
1260             gint x,
1261             gint y,
1262             gint width,
1263             gint height,
1264             gint *sc,
1265             gint *ec,
1266             gint *sr,
1267             gint *er)
1268 {
1269 	g_return_if_fail (table->rowHeights);
1270 	g_return_if_fail (table->columnOpt);
1271 	g_return_if_fail (table->rowHeights->data);
1272 	g_return_if_fail (table->columnOpt->data);
1273 
1274 	*sr = to_index (bin_search_index (table->rowHeights, 0, table->totalRows, y), 0, table->totalRows - 1);
1275 	if (y < ROW_HEIGHT (table, *sr) && (*sr) > 0)
1276 		(*sr)--;
1277 	*er = to_index (bin_search_index (table->rowHeights, *sr, table->totalRows, y + height), 0, table->totalRows - 1);
1278 	if (y > ROW_HEIGHT (table, *er) && (*er) < table->totalRows - 1)
1279 		(*er)++;
1280 
1281 	*sc = to_index (bin_search_index (table->columnOpt, 0, table->totalCols, x), 0, table->totalCols-1);
1282 	if (x < COLUMN_OPT (table, *sc) && (*sc) > 0)
1283 		(*sc)--;
1284 	*ec = to_index (bin_search_index (table->columnOpt, *sc, table->totalCols, x + width), 0, table->totalCols - 1);
1285 	if (x > COLUMN_OPT (table, *ec) && (*ec) < table->totalCols - 1)
1286 		(*ec)++;
1287 }
1288 
1289 static void
draw_background_helper(HTMLTable * table,HTMLPainter * p,GdkRectangle * paint,gint tx,gint ty)1290 draw_background_helper (HTMLTable *table,
1291                         HTMLPainter *p,
1292                         GdkRectangle *paint,
1293                         gint tx,
1294                         gint ty)
1295 {
1296 	GdkPixbuf  *pixbuf = NULL;
1297 	GdkColor   *color = table->bgColor;
1298 	HTMLObject *o = HTML_OBJECT (table);
1299 
1300 	if (table->bgPixmap && table->bgPixmap->animation)
1301 		pixbuf = gdk_pixbuf_animation_get_static_image (table->bgPixmap->animation);
1302 
1303 	if (color)
1304 		html_painter_alloc_color (p, color);
1305 
1306 	if (!HTML_IS_PLAIN_PAINTER (p))
1307 		html_painter_draw_background (p,
1308 					      color,
1309 					      pixbuf,
1310 					      tx + paint->x,
1311 					      ty + paint->y,
1312 					      paint->width,
1313 					      paint->height,
1314 					      paint->x - o->x,
1315 					      paint->y - (o->y - o->ascent));
1316 }
1317 
1318 static void
draw(HTMLObject * o,HTMLPainter * p,gint x,gint y,gint width,gint height,gint tx,gint ty)1319 draw (HTMLObject *o,
1320       HTMLPainter *p,
1321       gint x,
1322       gint y,
1323       gint width,
1324       gint height,
1325       gint tx,
1326       gint ty)
1327 {
1328 	HTMLTableCell *cell;
1329 	HTMLTable *table = HTML_TABLE (o);
1330 	gint pixel_size;
1331 	gint r, c, start_row, end_row, start_col, end_col;
1332 	GdkRectangle paint;
1333 
1334 	if (!html_object_intersect (o, &paint, x, y, width, height))
1335 		return;
1336 
1337 	pixel_size = html_painter_get_pixel_size (p);
1338 
1339 	/* Draw the background */
1340 	draw_background_helper (table, p, &paint, tx, ty);
1341 
1342 	tx += o->x;
1343 	ty += o->y - o->ascent;
1344 
1345 	/* Draw the cells */
1346 	get_bounds (table, x - o->x, y - o->y + o->ascent, width, height, &start_col, &end_col, &start_row, &end_row);
1347 	for (r = start_row; r <= end_row; r++) {
1348 		for (c = start_col; c <= end_col; c++) {
1349 			cell = table->cells[r][c];
1350 
1351 			if (cell == NULL)
1352 				continue;
1353 			if (c < end_col && cell == table->cells[r][c + 1])
1354 				continue;
1355 			if (r < end_row && table->cells[r + 1][c] == cell)
1356 				continue;
1357 
1358 			html_object_draw (HTML_OBJECT (cell), p,
1359 					  x - o->x, y - o->y + o->ascent,
1360 					  width,
1361 					  height,
1362 					  tx, ty);
1363 		}
1364 	}
1365 
1366 	/* Draw the border */
1367 	if (table->border > 0 && table->rowHeights->len > 0) {
1368 		gint capOffset;
1369 
1370 		capOffset = 0;
1371 
1372 		if (table->caption && table->capAlign == HTML_VALIGN_TOP)
1373 			g_print ("FIXME: Support captions\n");
1374 
1375 		html_painter_draw_border (p, html_object_get_bg_color (o->parent, p),
1376 					  tx, ty + capOffset,
1377 					  HTML_OBJECT (table)->width,
1378 					  ROW_HEIGHT (table, table->totalRows) +
1379 					  pixel_size * table->border, HTML_BORDER_OUTSET,
1380 					  pixel_size * table->border);
1381 
1382 		/* Draw borders around each cell */
1383 		for (r = start_row; r <= end_row; r++) {
1384 			for (c = start_col; c <= end_col; c++) {
1385 				if ((cell = table->cells[r][c]) == 0)
1386 					continue;
1387 				if (c < end_col &&
1388 				    cell == table->cells[r][c + 1])
1389 					continue;
1390 				if (r < end_row &&
1391 				    table->cells[r + 1][c] == cell)
1392 					continue;
1393 
1394 				html_painter_draw_border (p, html_object_get_bg_color (HTML_OBJECT (cell), p),
1395 							  tx + COLUMN_OPT (table, cell->col),
1396 							  ty + ROW_HEIGHT (table, cell->row) + capOffset,
1397 							  (COLUMN_OPT (table, c + 1)
1398 							   - COLUMN_OPT (table, cell->col)
1399 							   - pixel_size * table->spacing),
1400 							  (ROW_HEIGHT (table, r + 1)
1401 							   - ROW_HEIGHT (table, cell->row)
1402 							   - pixel_size * table->spacing),
1403 							  HTML_BORDER_INSET, pixel_size);
1404 
1405 			}
1406 		}
1407 	}
1408 }
1409 
1410 static gint
calc_min_width(HTMLObject * o,HTMLPainter * painter)1411 calc_min_width (HTMLObject *o,
1412                 HTMLPainter *painter)
1413 {
1414 	HTMLTable *table = HTML_TABLE (o);
1415 
1416 	calc_column_width_template (table, painter, table->columnPref, html_object_calc_preferred_width, table->columnPref);
1417 	calc_column_width_template (table, painter, table->columnMin, html_object_calc_min_width, table->columnPref);
1418 
1419 	return o->flags & HTML_OBJECT_FLAG_FIXEDWIDTH
1420 		? MAX (html_painter_get_pixel_size (painter) * table->specified_width,
1421 		       COLUMN_MIN (table, table->totalCols) + table->border * html_painter_get_pixel_size (painter))
1422 		: COLUMN_MIN (table, table->totalCols) + table->border * html_painter_get_pixel_size (painter);
1423 }
1424 
1425 static gint
calc_preferred_width(HTMLObject * o,HTMLPainter * painter)1426 calc_preferred_width (HTMLObject *o,
1427                       HTMLPainter *painter)
1428 {
1429 	HTMLTable *table = HTML_TABLE (o);
1430 	gint min_width;
1431 
1432 	/* note that calculating min width prepares columnPref for us */
1433 	min_width = html_object_calc_min_width (o, painter);
1434 
1435 	calc_column_width_template (table, painter, table->columnFixed,
1436 				    (gint (*)(HTMLObject *, HTMLPainter *)) html_table_cell_get_fixed_width,
1437 				    table->columnPref);
1438 
1439 	return o->flags & HTML_OBJECT_FLAG_FIXEDWIDTH
1440 		? MAX (html_painter_get_pixel_size (painter) * table->specified_width, min_width)
1441 		: COLUMN_PREF (table, table->totalCols) + table->border * html_painter_get_pixel_size (painter);
1442 }
1443 
1444 #define PERC(c) (col_percent [c + 1] - col_percent [c])
1445 
1446 static gboolean
calc_percentage_step(HTMLTable * table,gint * col_percent,gint * span_percent,gint span)1447 calc_percentage_step (HTMLTable *table,
1448                       gint *col_percent,
1449                       gint *span_percent,
1450                       gint span)
1451 {
1452 	HTMLTableCell *cell;
1453 	gboolean higher_span = FALSE;
1454 	gint r, c, cl, cspan;
1455 
1456 	for (c = 0; c < table->totalCols; c++)
1457 		for (r = 0; r < table->totalRows; r++) {
1458 			cell = table->cells[r][c];
1459 
1460 			if (!cell || cell->col != c || cell->row != r)
1461 				continue;
1462 
1463 			if (HTML_OBJECT (cell)->flags & HTML_OBJECT_FLAG_FIXEDWIDTH || !cell->percent_width)
1464 				continue;
1465 
1466 			cspan = MIN (cell->cspan, table->totalCols - cell->col);
1467 			if (cspan > span)
1468 				higher_span = TRUE;
1469 			if (cspan != span)
1470 				continue;
1471 
1472 			cl = cell_end_col (table, cell);
1473 			if (col_percent[cl] - col_percent[c] < cell->fixed_width) {
1474 				gint cp, part, added, pleft, not_percented, np;
1475 				part = 0;
1476 				not_percented = 0;
1477 				for (cp = 0; cp < span; cp++)
1478 					if (!PERC (c + cp))
1479 						not_percented++;
1480 
1481 				np    = 1;
1482 				added = 0;
1483 				pleft = cell->fixed_width - (col_percent[cl] - col_percent[c]);
1484 				for (cp = 0; cp < span; cp++) {
1485 					if (not_percented) {
1486 						if (!PERC (c + cp)) {
1487 							part = np * pleft / not_percented;
1488 							if (np * pleft - part * not_percented >
1489 							    (part + 1) * not_percented - np * pleft)
1490 								part++;
1491 							np++;
1492 						}
1493 					} else {
1494 						part = ((col_percent[c + cp + 1] - col_percent[c]) * pleft)
1495 							/ (col_percent[cl] - col_percent[cell->col]);
1496 						if ((col_percent[c + cp + 1] - col_percent[c]) * pleft
1497 						    - part * (col_percent[cl] - col_percent[c])
1498 						    > (part + 1) * (col_percent[cl] - col_percent[c])
1499 						    - (col_percent[c + cp + 1] - col_percent[c]) * pleft)
1500 							part++;
1501 					}
1502 					part  -= added;
1503 					added += part;
1504 					span_percent[c + cp] = PERC (c + cp) + part;
1505 				}
1506 			}
1507 		}
1508 
1509 	return higher_span;
1510 }
1511 
1512 static void
calc_col_percentage(HTMLTable * table,gint * col_percent)1513 calc_col_percentage (HTMLTable *table,
1514                      gint *col_percent)
1515 {
1516 	gint c, span, *percent, add;
1517 	gboolean next = TRUE;
1518 
1519 	percent = g_new0 (gint, table->totalCols);
1520 	for (span = 1; next && span <= table->totalCols; span++) {
1521 		for (c = 0; c < table->totalCols; c++)
1522 			percent[c] = 0;
1523 
1524 		next    = calc_percentage_step (table, col_percent, percent, span);
1525 		add     = 0;
1526 
1527 		for (c = 0; c < table->totalCols; c++) {
1528 			col_percent[c + 1] += add;
1529 			if (PERC (c) < percent[c]) {
1530 				add += percent[c] - PERC (c);
1531 				col_percent[c + 1] = col_percent[c] + percent[c];
1532 			}
1533 		}
1534 	}
1535 	g_free (percent);
1536 }
1537 
1538 static gint
calc_not_percented(HTMLTable * table,gint * col_percent)1539 calc_not_percented (HTMLTable *table,
1540                     gint *col_percent)
1541 {
1542 	gint c, not_percented;
1543 
1544 	not_percented = 0;
1545 	for (c = 0; c < table->totalCols; c++)
1546 		if (col_percent[c + 1] == col_percent[c])
1547 			not_percented++;
1548 
1549 	return not_percented;
1550 }
1551 
1552 static gint
divide_into_percented(HTMLTable * table,gint * col_percent,gint * max_size,gint max_width,gint left)1553 divide_into_percented (HTMLTable *table,
1554                        gint *col_percent,
1555                        gint *max_size,
1556                        gint max_width,
1557                        gint left)
1558 {
1559 	gint added, add, c, to_fill, request, filled;
1560 
1561 	to_fill = 0;
1562 	for (c = 0; c < table->totalCols; c++) {
1563 		request = (LL max_width * (PERC (c))) / 100;
1564 		if (max_size[c] < request)
1565 			to_fill += request - max_size[c];
1566 	}
1567 
1568 	/* printf ("to fill %d\n", to_fill); */
1569 	left  = MIN (to_fill, left);
1570 	added  = 0;
1571 	filled = 0;
1572 	if (left) {
1573 		for (c = 0; c < table->totalCols; c++) {
1574 			request = (LL max_width * (PERC (c))) / 100;
1575 			if (max_size[c] < request) {
1576 				add     = LL left * (request - max_size[c] + filled) / to_fill;
1577 				if (LL left * (request - max_size[c] + filled) - LL add * to_fill >
1578 				    LL (add + 1) * to_fill - LL left * (request - max_size[c] + filled))
1579 					add++;
1580 				add          -= added;
1581 				added        += add;
1582 				filled       += request - max_size[c];
1583 				max_size[c] += add;
1584 			}
1585 		}
1586 	}
1587 	/* printf ("%d added %d left %d\n", left - added, added, left); */
1588 
1589 	return added;
1590 }
1591 
1592 #define PREF(i) (g_array_index (pref, gint, i))
1593 
1594 static gboolean
calc_lowest_fill(HTMLTable * table,GArray * pref,gint * max_size,gint * col_percent,gint pixel_size,gint * ret_col,gint * ret_total_pref,gint * ret_total)1595 calc_lowest_fill (HTMLTable *table,
1596                   GArray *pref,
1597                   gint *max_size,
1598                   gint *col_percent,
1599                   gint pixel_size,
1600                   gint *ret_col,
1601                   gint *ret_total_pref,
1602                   gint *ret_total)
1603 {
1604 	gint c, pw, border_extra = table->border ? 2 : 0, min_fill = COLUMN_PREF (table, table->totalCols);
1605 
1606 	*ret_total_pref = 0;
1607 	*ret_total = 0;
1608 	for (c = 0; c < table->totalCols; c++)
1609 		if (col_percent[c + 1] == col_percent[c]) {
1610 			pw = PREF (c + 1) - PREF (c)
1611 				- pixel_size * (table->spacing + border_extra);
1612 			/* printf ("col %d pw %d size %d\n", c, pw, max_size [c]); */
1613 			if (max_size[c] < pw) {
1614 				if (pw - max_size[c] < min_fill) {
1615 					*ret_col = c;
1616 					min_fill = pw - max_size[c];
1617 				}
1618 
1619 				(*ret_total_pref) += pw;
1620 				(*ret_total) += max_size[c];
1621 			}
1622 		}
1623 
1624 	return min_fill == COLUMN_PREF (table, table->totalCols) ? FALSE : TRUE;
1625 }
1626 
1627 inline static gint
divide_upto_preferred_width(HTMLTable * table,HTMLPainter * painter,GArray * pref,gint * col_percent,gint * max_size,gint left)1628 divide_upto_preferred_width (HTMLTable *table,
1629                              HTMLPainter *painter,
1630                              GArray *pref,
1631                              gint *col_percent,
1632                              gint *max_size,
1633                              gint left)
1634 {
1635 	gint added, part, c, pw, pixel_size = html_painter_get_pixel_size (painter);
1636 	gint total, total_pref, to_divide, min_col, min_fill, min_pw, processed_pw, border_extra = table->border ? 2 : 0;
1637 
1638 	/* printf ("cols: %d left: %d\n", table->totalCols, left); */
1639 	while (left > 0 && calc_lowest_fill (table, pref, max_size, col_percent, pixel_size, &min_col, &total_pref, &total)) {
1640 		min_pw   = PREF (min_col + 1) - PREF (min_col)
1641 			- pixel_size * (table->spacing + border_extra);
1642 		/* printf ("min: %d left: %d\n", min_col, left); */
1643 		to_divide = MIN (total_pref - total, left);
1644 		if (min_pw - max_size[min_col] < ((gdouble) min_pw * to_divide) / total_pref) {
1645 			added = min_pw - max_size[min_col];
1646 			left -= added;
1647 			min_fill = to_divide - added;
1648 			max_size[min_col] += added;
1649 			total_pref -= min_pw;
1650 		} else {
1651 			min_fill = to_divide;
1652 		}
1653 
1654 		/* printf ("min satisfied %d, (%d=%d) left: %d\n", min_fill, max_size [min_col], min_pw, left); */
1655 		processed_pw = 0;
1656 		added = 0;
1657 
1658 		for (c = 0; c < table->totalCols; c++) {
1659 			if (col_percent[c + 1] == col_percent[c]) {
1660 				pw = PREF (c + 1) - PREF (c)
1661 					- pixel_size * (table->spacing + border_extra);
1662 				if (max_size[c] < pw) {
1663 					processed_pw += pw;
1664 					part = (LL min_fill * processed_pw) / total_pref;
1665 					if (LL min_fill * processed_pw - LL part * total_pref
1666 					    > LL (part + 1) * total_pref - LL min_fill * processed_pw)
1667 						part++;
1668 					part         -= added;
1669 					if (max_size[c] + part > pw)
1670 						part = pw - max_size[c];
1671 					added        += part;
1672 					max_size[c] += part;
1673 					left         -= part;
1674 					/* printf ("cell %d add: %d --> %d\n", c, part, max_size [c]); */
1675 				}
1676 			}
1677 		}
1678 	}
1679 
1680 	/* printf ("cols: %d left: %d (END)\n", table->totalCols, left); */
1681 
1682 	return left;
1683 }
1684 
1685 inline static void
divide_left_by_preferred_width(HTMLTable * table,HTMLPainter * painter,gint * col_percent,gint * max_size,gint left)1686 divide_left_by_preferred_width (HTMLTable *table,
1687                                 HTMLPainter *painter,
1688                                 gint *col_percent,
1689                                 gint *max_size,
1690                                 gint left)
1691 {
1692 	gint added, part, c, pref, pw, pixel_size = html_painter_get_pixel_size (painter);
1693 	gint total_fill, processed_pw, border_extra = table->border ? 2 : 0;
1694 
1695 	/* printf ("round 2 left: %d\n", left); */
1696 #define PW(c) COLUMN_PREF (table, c + 1) - COLUMN_PREF (table, c)
1697 #define FW(c) COLUMN_FIX (table, c + 1) - COLUMN_FIX (table, c)
1698 	pref = 0;
1699 	for (c = 0; c < table->totalCols; c++)
1700 		if (col_percent[c + 1] == col_percent[c] && PW (c) > FW (c))
1701 			pref += COLUMN_PREF (table, c + 1) - COLUMN_PREF (table, c)
1702 				- pixel_size * (table->spacing + border_extra);
1703 			/* printf ("col pref: %d size: %d\n", COLUMN_PREF (table, c + 1)
1704 			 * - COLUMN_PREF (table, c)
1705 			 * - pixel_size * (table->spacing + border_extra), max_size[c]); */
1706 
1707 	added        = 0;
1708 	processed_pw = 0;
1709 	total_fill   = left;
1710 
1711 	if (pref)
1712 		for (c = 0; c < table->totalCols; c++) {
1713 			if (col_percent[c + 1] == col_percent[c] && PW (c) > FW (c)) {
1714 				pw  = COLUMN_PREF (table, c + 1) - COLUMN_PREF (table, c)
1715 					- pixel_size * (table->spacing + border_extra);
1716 				processed_pw += pw;
1717 				part = (LL total_fill * processed_pw) / pref;
1718 				/* printf ("pw %d part %d total %d processed %d\n",
1719 				 * pw, part, total_fill, processed_pw); */
1720 				if (LL total_fill * processed_pw - LL part * pref
1721 				    > LL (part + 1) * pref - LL total_fill * processed_pw)
1722 					part++;
1723 				part         -= added;
1724 				max_size[c] += part;
1725 				added        += part;
1726 				left         -= part;
1727 				/* printf ("col %d (add %d) --> %d (pw=%d)\n", c, part, max_size [c], pw); */
1728 			}
1729 		}
1730 
1731 	/* printf ("------------------------------------\n*left*: %d\n-------------------------------\n", left); */
1732 }
1733 
1734 inline static void
divide_into_variable_all(HTMLTable * table,HTMLPainter * painter,gint * col_percent,gint * max_size,gint left)1735 divide_into_variable_all (HTMLTable *table,
1736                           HTMLPainter *painter,
1737                           gint *col_percent,
1738                           gint *max_size,
1739                           gint left)
1740 {
1741 	/* printf ("left %d cols: %d\n", left, table->totalCols); */
1742 	html_object_calc_preferred_width (HTML_OBJECT (table), painter);
1743 
1744 	left = divide_upto_preferred_width (table, painter, table->columnFixed, col_percent, max_size, left);
1745 	left = divide_upto_preferred_width (table, painter, table->columnPref,  col_percent, max_size, left);
1746 
1747 	if (left)
1748 		divide_left_by_preferred_width (table, painter, col_percent, max_size, left);
1749 }
1750 
1751 inline static void
divide_into_percented_all(HTMLTable * table,gint * col_percent,gint * max_size,gint max_width,gint left)1752 divide_into_percented_all (HTMLTable *table,
1753                            gint *col_percent,
1754                            gint *max_size,
1755                            gint max_width,
1756                            gint left)
1757 {
1758 	gdouble sub_percent, percent;
1759 	gint c, sub_width, width;
1760 	gboolean all_active, *active;
1761 
1762 	active = g_new (gboolean, table->totalCols);
1763 	for (c = 0; c < table->totalCols; c++)
1764 		active[c] = TRUE;
1765 
1766 	percent = col_percent[table->totalCols];
1767 	width   = max_width;
1768 	do {
1769 		sub_percent = 0.0;
1770 		sub_width   = width;
1771 		all_active  = TRUE;
1772 		for (c = 0; c < table->totalCols; c++) {
1773 			if (active[c]) {
1774 				if (max_size[c] < ((gdouble) width * PERC (c)) / percent)
1775 					sub_percent += PERC (c);
1776 				else {
1777 					sub_width -= max_size[c];
1778 					all_active = FALSE;
1779 					active[c] = FALSE;
1780 				}
1781 			}
1782 		}
1783 		percent = sub_percent;
1784 		width   = sub_width;
1785 	} while (!all_active);
1786 
1787 	/* printf ("sub_width %d\n", sub_width); */
1788 	for (c = 0; c < table->totalCols; c++)
1789 		if (active[c] && max_size[c] < ((gdouble) width * PERC (c)) / percent)
1790 			max_size[c] = ((gdouble) width) * (PERC (c)) / percent;
1791 
1792 	g_free (active);
1793 }
1794 
1795 #define CSPAN (MIN (cell->col + cell->cspan, table->totalCols) - cell->col - 1)
1796 
1797 static void
html_table_set_cells_max_width(HTMLTable * table,HTMLPainter * painter,gint * max_size)1798 html_table_set_cells_max_width (HTMLTable *table,
1799                                 HTMLPainter *painter,
1800                                 gint *max_size)
1801 {
1802 	HTMLTableCell *cell;
1803 	gint r, c, size, pixel_size = html_painter_get_pixel_size (painter);
1804 	gint border_extra = table->border ? 2 : 0;
1805 	size = 0;
1806 
1807 	for (r = 0; r < table->totalRows; r++)
1808 		for (c = 0; c < table->totalCols; c++) {
1809 			cell = table->cells[r][c];
1810 			if (cell) {
1811 				size = max_size[c] + (cell->col != c ? size : 0);
1812 				if (cell_end_col (table, cell) - 1 == c && cell->row == r)
1813 					html_object_set_max_width (HTML_OBJECT (cell), painter, size
1814 								   + pixel_size * (table->spacing + border_extra) * CSPAN);
1815 			}
1816 		}
1817 }
1818 
1819 static void
set_columns_optimal_width(HTMLTable * table,gint * max_size,gint pixel_size)1820 set_columns_optimal_width (HTMLTable *table,
1821                            gint *max_size,
1822                            gint pixel_size)
1823 {
1824 	gint c;
1825 
1826 	g_array_set_size (table->columnOpt, table->totalCols + 1);
1827 	COLUMN_OPT (table, 0) = COLUMN_MIN (table, 0);
1828 
1829 	for (c = 0; c < table->totalCols; c++)
1830 		COLUMN_OPT (table, c + 1) = COLUMN_OPT (table, c) + max_size[c]
1831 			+ pixel_size * (table->spacing + (table->border ? 2 : 0));
1832 }
1833 
1834 static void
divide_left_width(HTMLTable * table,HTMLPainter * painter,gint * max_size,gint max_width,gint width_left)1835 divide_left_width (HTMLTable *table,
1836                    HTMLPainter *painter,
1837                    gint *max_size,
1838                    gint max_width,
1839                    gint width_left)
1840 {
1841 	gint not_percented, c;
1842 	gint *col_percent;
1843 
1844 	if (!width_left)
1845 		return;
1846 
1847 	col_percent  = g_new (gint, table->totalCols + 1);
1848 	for (c = 0; c <= table->totalCols; c++)
1849 		col_percent[c] = 0;
1850 
1851 	calc_col_percentage (table, col_percent);
1852 	/* printf ("width_left: %d percented: %d\n", width_left, col_percent [table->totalCols]); */
1853 	not_percented  = calc_not_percented (table, col_percent);
1854 	if (not_percented < table->totalCols)
1855 		width_left    -= divide_into_percented (table, col_percent, max_size, max_width, width_left);
1856 
1857 	/* printf ("width_left: %d\n", width_left); */
1858 	if (width_left > 0) {
1859 		if (not_percented)
1860 			divide_into_variable_all  (table, painter, col_percent, max_size, width_left);
1861 		else
1862 			divide_into_percented_all (table, col_percent, max_size, max_width, width_left);
1863 	}
1864 
1865 	g_free (col_percent);
1866 }
1867 
1868 static gint *
alloc_max_size(HTMLTable * table,gint pixel_size)1869 alloc_max_size (HTMLTable *table,
1870                 gint pixel_size)
1871 {
1872 	gint *max_size, c, border_extra = table->border ? 2 : 0;
1873 
1874 	max_size = g_new (gint, table->totalCols);
1875 	for (c = 0; c < table->totalCols; c++)
1876 		max_size[c] = COLUMN_MIN (table, c + 1) - COLUMN_MIN (table, c) - pixel_size * (table->spacing + border_extra);
1877 
1878 	return max_size;
1879 }
1880 
1881 static void
html_table_set_max_width(HTMLObject * o,HTMLPainter * painter,gint max_width)1882 html_table_set_max_width (HTMLObject *o,
1883                           HTMLPainter *painter,
1884                           gint max_width)
1885 {
1886 	HTMLTable *table = HTML_TABLE (o);
1887 	gint *max_size, pixel_size, glue, border_extra = table->border ? 2 : 0;
1888 	gint min_width;
1889 
1890 	/* printf ("max_width: %d\n", max_width); */
1891 	pixel_size   = html_painter_get_pixel_size (painter);
1892 	o->max_width = MAX (html_object_calc_min_width (o, painter), max_width);
1893 	max_width    = o->flags & HTML_OBJECT_FLAG_FIXEDWIDTH
1894 		? pixel_size * table->specified_width
1895 		: (o->percent
1896 		   ? ((gdouble) MIN (100, o->percent) / 100 * max_width)
1897 		   : MIN (html_object_calc_preferred_width (HTML_OBJECT (table), painter), max_width));
1898 	min_width = html_object_calc_min_width (o, painter);
1899 	if (max_width < min_width)
1900 		max_width = min_width;
1901 	/* printf ("corected to: %d\n", max_width); */
1902 	glue         = pixel_size * (table->border * 2 + (table->totalCols + 1) * table->spacing
1903 				     + (table->totalCols * border_extra));
1904 	max_width   -= glue;
1905 	max_size     = alloc_max_size (table, pixel_size);
1906 
1907 	divide_left_width (table, painter, max_size, max_width,
1908 			   max_width + glue - COLUMN_MIN (table, table->totalCols)
1909 			   - pixel_size * table->border);
1910 
1911 	html_table_set_cells_max_width (table, painter, max_size);
1912 	set_columns_optimal_width (table, max_size, pixel_size);
1913 
1914 	/* printf ("max_width %d opt_width %d\n", o->max_width, COLUMN_OPT (table, table->totalCols) +); */
1915 
1916 	g_free (max_size);
1917 }
1918 
1919 static void
reset(HTMLObject * o)1920 reset (HTMLObject *o)
1921 {
1922 	HTMLTable *table = HTML_TABLE (o);
1923 	HTMLTableCell *cell;
1924 	guint r, c;
1925 
1926 	for (r = 0; r < table->totalRows; r++)
1927 		for (c = 0; c < table->totalCols; c++) {
1928 			cell = table->cells[r][c];
1929 			if (cell && cell->row == r && cell->col == c)
1930 				html_object_reset (HTML_OBJECT (cell));
1931 		}
1932 }
1933 
1934 static HTMLAnchor *
find_anchor(HTMLObject * self,const gchar * name,gint * x,gint * y)1935 find_anchor (HTMLObject *self,
1936              const gchar *name,
1937              gint *x,
1938              gint *y)
1939 {
1940 	HTMLTable *table;
1941 	HTMLTableCell *cell;
1942 	HTMLAnchor *anchor;
1943 	guint r, c;
1944 
1945 	table = HTML_TABLE (self);
1946 
1947 	*x += self->x;
1948 	*y += self->y - self->ascent;
1949 
1950 	for (r = 0; r < table->totalRows; r++) {
1951 		for (c = 0; c < table->totalCols; c++) {
1952 			if ((cell = table->cells[r][c]) == 0)
1953 				continue;
1954 
1955 			if (c < table->totalCols - 1
1956 			    && cell == table->cells[r][c + 1])
1957 				continue;
1958 			if (r < table->totalRows - 1
1959 			    && table->cells[r + 1][c] == cell)
1960 				continue;
1961 
1962 			anchor = html_object_find_anchor (HTML_OBJECT (cell),
1963 							  name, x, y);
1964 
1965 			if (anchor != NULL)
1966 				return anchor;
1967 		}
1968 	}
1969 	*x -= self->x;
1970 	*y -= self->y - self->ascent;
1971 
1972 	return 0;
1973 }
1974 
1975 static HTMLObject *
check_point(HTMLObject * self,HTMLPainter * painter,gint x,gint y,guint * offset_return,gboolean for_cursor)1976 check_point (HTMLObject *self,
1977              HTMLPainter *painter,
1978              gint x,
1979              gint y,
1980              guint *offset_return,
1981              gboolean for_cursor)
1982 {
1983 	HTMLTableCell *cell;
1984 	HTMLObject *obj;
1985 	HTMLTable *table;
1986 	gint r, c, start_row, end_row, start_col, end_col, hsb, hsa, tbc;
1987 
1988 	if (x < self->x || x >= self->x + self->width
1989 	    || y >= self->y + self->descent || y < self->y - self->ascent)
1990 		return NULL;
1991 
1992 	table = HTML_TABLE (self);
1993 	hsb = table->spacing >> 1;
1994 	hsa = hsb + (table->spacing & 1);
1995 	tbc = table->border ? 1 : 0;
1996 
1997 	if (for_cursor) {
1998 		/* table boundaries */
1999 		if (x == self->x || x == self->x + self->width - 1) {
2000 			if (offset_return)
2001 				*offset_return = x == self->x ? 0 : 1;
2002 			return self;
2003 		}
2004 
2005 		/* border */
2006 		if (x < self->x + table->border + hsb || y < self->y - self->ascent + table->border + hsb) {
2007 			if (offset_return)
2008 				*offset_return = 0;
2009 			return self;
2010 		}
2011 		if (x > self->x + self->width - table->border - hsa || y > self->y + self->descent - table->border - hsa) {
2012 			if (offset_return)
2013 				*offset_return = 1;
2014 			return self;
2015 		}
2016 	}
2017 
2018 	x -= self->x;
2019 	y -= self->y - self->ascent;
2020 
2021 	get_bounds (table, x, y, 0, 0, &start_col, &end_col, &start_row, &end_row);
2022 	for (r = start_row; r <= end_row; r++) {
2023 		for (c = 0; c < table->totalCols; c++) {
2024 			HTMLObject *co;
2025 			gint cx, cy;
2026 
2027 			cell = table->cells[r][c];
2028 			if (cell == NULL)
2029 				continue;
2030 
2031 			if (c < end_col - 1 && cell == table->cells[r][c + 1])
2032 				continue;
2033 			if (r < end_row - 1 && table->cells[r + 1][c] == cell)
2034 				continue;
2035 
2036 			co = HTML_OBJECT (cell);
2037 			cx = x;
2038 			cy = y;
2039 			if (for_cursor) {
2040 				/* correct to include cell spacing */
2041 				if (x < co->x && x >= co->x - hsa - tbc)
2042 					cx = co->x;
2043 				if (x >= co->x + co->width && x < co->x + co->width + hsb + tbc)
2044 					cx = co->x + co->width - 1;
2045 				if (y < co->y - co->ascent && y >= co->y - co->ascent - hsa - tbc)
2046 					cy = co->y - co->ascent;
2047 				if (y >= co->y + co->descent && y < co->y + co->descent + hsb + tbc)
2048 					cy = co->y + co->descent - 1;
2049 			}
2050 
2051 			obj = html_object_check_point (HTML_OBJECT (cell), painter, cx, cy, offset_return, for_cursor);
2052 			if (obj != NULL)
2053 				return obj;
2054 		}
2055 	}
2056 
2057 	if (!for_cursor) {
2058 		if (x >= 0 && y >= 0 && x < self->width && y < self->ascent + self->descent) {
2059 			if (offset_return) {
2060 				if (x < self->width / 2)
2061 					*offset_return = 0;
2062 				else
2063 					*offset_return = 1;
2064 			}
2065 			return self;
2066 		}
2067 	}
2068 
2069 	return NULL;
2070 }
2071 
2072 static gboolean
search(HTMLObject * obj,HTMLSearch * info)2073 search (HTMLObject *obj,
2074         HTMLSearch *info)
2075 {
2076 	HTMLTable *table = HTML_TABLE (obj);
2077 	HTMLTableCell *cell;
2078 	HTMLObject    *cur = NULL;
2079 	guint r, c, i, j;
2080 	gboolean next = FALSE;
2081 
2082 	/* search_next? */
2083 	if (html_search_child_on_stack (info, obj)) {
2084 		cur  = html_search_pop (info);
2085 		next = TRUE;
2086 	}
2087 
2088 	if (info->forward) {
2089 		for (r = 0; r < table->totalRows; r++) {
2090 			for (c = 0; c < table->totalCols; c++) {
2091 
2092 				if ((cell = table->cells[r][c]) == 0)
2093 					continue;
2094 				if (c < table->totalCols - 1 &&
2095 				    cell == table->cells[r][c + 1])
2096 					continue;
2097 				if (r < table->totalRows - 1 &&
2098 				    cell == table->cells[r + 1][c])
2099 					continue;
2100 
2101 				if (next && cur) {
2102 					if (HTML_OBJECT (cell) == cur) {
2103 						cur = NULL;
2104 					}
2105 					continue;
2106 				}
2107 
2108 				html_search_push (info, HTML_OBJECT (cell));
2109 				if (html_object_search (HTML_OBJECT (cell), info)) {
2110 					return TRUE;
2111 				}
2112 				html_search_pop (info);
2113 			}
2114 		}
2115 	} else {
2116 		for (i = 0, r = table->totalRows - 1; i < table->totalRows; i++, r--) {
2117 			for (j = 0, c = table->totalCols - 1; j < table->totalCols; j++, c--) {
2118 
2119 				if ((cell = table->cells[r][c]) == 0)
2120 					continue;
2121 				if (c < table->totalCols - 1 &&
2122 				    cell == table->cells[r][c + 1])
2123 					continue;
2124 				if (r < table->totalRows - 1 &&
2125 				    cell == table->cells[r + 1][c])
2126 					continue;
2127 
2128 				if (next && cur) {
2129 					if (HTML_OBJECT (cell) == cur) {
2130 						cur = NULL;
2131 					}
2132 					continue;
2133 				}
2134 
2135 				html_search_push (info, HTML_OBJECT (cell));
2136 				if (html_object_search (HTML_OBJECT (cell), info)) {
2137 					return TRUE;
2138 				}
2139 				html_search_pop (info);
2140 			}
2141 		}
2142 	}
2143 
2144 	if (next) {
2145 		return html_search_next_parent (info);
2146 	}
2147 
2148 	return FALSE;
2149 }
2150 
2151 static void
append_selection_string(HTMLObject * self,GString * buffer)2152 append_selection_string (HTMLObject *self,
2153                          GString *buffer)
2154 {
2155 	HTMLTable *table;
2156 	HTMLTableCell *cell;
2157 	gboolean tab;
2158 	gint r, c, len, rlen, tabs;
2159 
2160 	table = HTML_TABLE (self);
2161 	for (r = 0; r < table->totalRows; r++) {
2162 		tab = FALSE;
2163 		tabs = 0;
2164 		rlen = buffer->len;
2165 		for (c = 0; c < table->totalCols; c++) {
2166 			if ((cell = table->cells[r][c]) == 0)
2167 				continue;
2168 			if (c < table->totalCols - 1 &&
2169 			    cell == table->cells[r][c + 1])
2170 				continue;
2171 			if (r < table->totalRows - 1 &&
2172 			    table->cells[r + 1][c] == cell)
2173 				continue;
2174 			if (tab) {
2175 				g_string_append_c (buffer, '\t');
2176 				tabs++;
2177 			}
2178 			len = buffer->len;
2179 			html_object_append_selection_string (HTML_OBJECT (cell), buffer);
2180 			/* remove last \n from added text */
2181 			if (buffer->len != len) {
2182 				tab = TRUE;
2183 				if (buffer->str[buffer->len - 1] == '\n')
2184 					g_string_truncate (buffer, buffer->len - 1);
2185 			}
2186 		}
2187 		if (rlen + tabs == buffer->len)
2188 			g_string_truncate (buffer, rlen);
2189 		else if (r + 1 < table->totalRows)
2190 			g_string_append_c (buffer, '\n');
2191 	}
2192 }
2193 
2194 static HTMLObject *
head(HTMLObject * self)2195 head (HTMLObject *self)
2196 {
2197 	HTMLTable *table;
2198 	gint r, c;
2199 
2200 	table = HTML_TABLE (self);
2201 	for (r = 0; r < table->totalRows; r++)
2202 		for (c = 0; c < table->totalCols; c++) {
2203 			if (invalid_cell (table, r, c))
2204 				continue;
2205 			return HTML_OBJECT (table->cells[r][c]);
2206 		}
2207 	return NULL;
2208 }
2209 
2210 static HTMLObject *
tail(HTMLObject * self)2211 tail (HTMLObject *self)
2212 {
2213 	HTMLTable *table;
2214 	gint r, c;
2215 
2216 	table = HTML_TABLE (self);
2217 	for (r = table->totalRows - 1; r >= 0; r--)
2218 		for (c = table->totalCols - 1; c >= 0; c--) {
2219 			if (invalid_cell (table, r, c))
2220 				continue;
2221 			return HTML_OBJECT (table->cells[r][c]);
2222 		}
2223 	return NULL;
2224 }
2225 
2226 static HTMLObject *
next(HTMLObject * self,HTMLObject * child)2227 next (HTMLObject *self,
2228       HTMLObject *child)
2229 {
2230 	HTMLTable *table;
2231 	gint r, c;
2232 
2233 	table = HTML_TABLE (self);
2234 	r = HTML_TABLE_CELL (child)->row;
2235 	c = HTML_TABLE_CELL (child)->col + 1;
2236 	for (; r < table->totalRows; r++) {
2237 		for (; c < table->totalCols; c++) {
2238 			if (invalid_cell (table, r, c))
2239 				continue;
2240 			return HTML_OBJECT (table->cells[r][c]);
2241 		}
2242 		c = 0;
2243 	}
2244 	return NULL;
2245 }
2246 
2247 static HTMLObject *
prev(HTMLObject * self,HTMLObject * child)2248 prev (HTMLObject *self,
2249       HTMLObject *child)
2250 {
2251 	HTMLTable *table;
2252 	gint r, c;
2253 
2254 	table = HTML_TABLE (self);
2255 	r = HTML_TABLE_CELL (child)->row;
2256 	c = HTML_TABLE_CELL (child)->col - 1;
2257 	for (; r >= 0; r--) {
2258 		for (; c >=0; c--) {
2259 			if (invalid_cell (table, r, c))
2260 				continue;
2261 			return HTML_OBJECT (table->cells[r][c]);
2262 		}
2263 		c = table->totalCols - 1;
2264 	}
2265 	return NULL;
2266 }
2267 
2268 #define SB if (!html_engine_save_output_string (state,
2269 #define SE )) return FALSE
2270 
2271 static gboolean
save(HTMLObject * self,HTMLEngineSaveState * state)2272 save (HTMLObject *self,
2273       HTMLEngineSaveState *state)
2274 {
2275 	HTMLTable *table;
2276 	gint r, c;
2277 
2278 	table = HTML_TABLE (self);
2279 
2280 	SB "<TABLE" SE;
2281 	if (table->bgColor)
2282 		SB " BGCOLOR=\"#%02x%02x%02x\"",
2283 			table->bgColor->red >> 8,
2284 			table->bgColor->green >> 8,
2285 			table->bgColor->blue >> 8 SE;
2286 	if (table->bgPixmap) {
2287 		gchar * url = html_image_resolve_image_url (state->engine->widget, table->bgPixmap->url);
2288 		if (!html_engine_save_delims_and_vals (state, " BACKGROUND=\"", url, "\"", NULL))
2289 		{
2290 			g_free (url);
2291 			return FALSE;
2292 		}
2293 		g_free (url);
2294 	}
2295 	if (table->spacing != 2)
2296 		SB " CELLSPACING=\"%d\"", table->spacing SE;
2297 	if (table->padding != 1)
2298 		SB " CELLPADDING=\"%d\"", table->padding SE;
2299 	if (self->percent > 0) {
2300 		SB " WIDTH=\"%d%%\"", self->percent SE;
2301 	} else if (self->flags & HTML_OBJECT_FLAG_FIXEDWIDTH)
2302 		SB " WIDTH=\"%d\"", table->specified_width SE;
2303 	if (table->border)
2304 		SB " BORDER=\"%d\"", table->border SE;
2305 	SB ">\n" SE;
2306 
2307 	for (r = 0; r < table->totalRows; r++) {
2308 		SB "<TR>\n" SE;
2309 		for (c = 0; c < table->totalCols; c++) {
2310 			if (!table->cells[r][c]
2311 			    || table->cells[r][c]->row != r
2312 			    || table->cells[r][c]->col != c)
2313 				continue;
2314 			if (!html_object_save (HTML_OBJECT (table->cells[r][c]), state))
2315 				return FALSE;
2316 		}
2317 		SB "</TR>\n" SE;
2318 	}
2319 	SB "</TABLE>" SE;
2320 
2321 	return TRUE;
2322 }
2323 
2324 static gboolean
save_plain(HTMLObject * self,HTMLEngineSaveState * state,gint requested_width)2325 save_plain (HTMLObject *self,
2326             HTMLEngineSaveState *state,
2327             gint requested_width)
2328 {
2329 	HTMLTable *table;
2330 	gint r, c;
2331 	gboolean result = TRUE;
2332 
2333 	table = HTML_TABLE (self);
2334 
2335 	for (r = 0; r < table->totalRows; r++) {
2336 		for (c = 0; c < table->totalCols; c++) {
2337 			if (!table->cells[r][c]
2338 			    || table->cells[r][c]->row != r
2339 			    || table->cells[r][c]->col != c)
2340 				continue;
2341 			/*  FIXME the width calculation for the column here is completely broken */
2342 			result &= html_object_save_plain (HTML_OBJECT (table->cells[r][c]), state, requested_width / table->totalCols);
2343 		}
2344 	}
2345 
2346 	return result;
2347 }
2348 
2349 static gboolean
check_row_split(HTMLTable * table,HTMLPainter * painter,gint r,gint * min_y)2350 check_row_split (HTMLTable *table,
2351                  HTMLPainter *painter,
2352                  gint r,
2353                  gint *min_y)
2354 {
2355 	HTMLTableCell *cell;
2356 	gboolean changed = FALSE;
2357 	gint c, cs;
2358 
2359 	for (c = 0; c < table->totalCols; c++) {
2360 		gint y1, y2;
2361 
2362 		cell = table->cells[r][c];
2363 		if (cell == NULL || cell->col != c)
2364 			continue;
2365 
2366 		y1 = HTML_OBJECT (cell)->y - HTML_OBJECT (cell)->ascent;
2367 		y2 = HTML_OBJECT (cell)->y + HTML_OBJECT (cell)->descent;
2368 
2369 		if (y1 <= *min_y && *min_y < y2) {
2370 			cs = html_object_check_page_split (HTML_OBJECT (cell), painter, *min_y - y1) + y1;
2371 			/* printf ("r %d min_y: %d y1: %d y2: %d --> cs=%d\n", *min_y, y1, y2, cs); */
2372 
2373 			if (cs < *min_y) {
2374 				*min_y = cs;
2375 				changed = TRUE;
2376 			}
2377 		}
2378 	}
2379 
2380 	return changed;
2381 }
2382 
2383 static gint
check_page_split(HTMLObject * self,HTMLPainter * painter,gint y)2384 check_page_split (HTMLObject *self,
2385                   HTMLPainter *painter,
2386                   gint y)
2387 {
2388 	HTMLTable     *table;
2389 	gint r, min_y;
2390 
2391 	table = HTML_TABLE (self);
2392 	r     = to_index (bin_search_index (table->rowHeights, 0, table->totalRows, y), 0, table->totalRows - 1);
2393 	if (y < ROW_HEIGHT (table, r) && r > 0)
2394 		r--;
2395 
2396 	/* printf ("y: %d rh: %d rh+1: %d\n", y, ROW_HEIGHT (table, r), ROW_HEIGHT (table, r+1)); */
2397 
2398 	/* if (r >= table->totalRows)
2399 	 * return MIN (y, ROW_HEIGHT (table, table->totalRows)); */
2400 
2401 	min_y = MIN (y, ROW_HEIGHT (table, r + 1));
2402 	while (check_row_split (table, painter, r, &min_y));
2403 
2404 	/* printf ("min_y=%d\n", min_y); */
2405 
2406 	return min_y;
2407 }
2408 
2409 static GdkColor *
get_bg_color(HTMLObject * o,HTMLPainter * p)2410 get_bg_color (HTMLObject *o,
2411               HTMLPainter *p)
2412 {
2413 
2414 	return HTML_TABLE (o)->bgColor
2415 		? HTML_TABLE (o)->bgColor
2416 		: html_object_get_bg_color (o->parent, p);
2417 }
2418 
2419 
2420 void
html_table_type_init(void)2421 html_table_type_init (void)
2422 {
2423 	html_table_class_init (&html_table_class, HTML_TYPE_TABLE, sizeof (HTMLTable));
2424 }
2425 
2426 void
html_table_class_init(HTMLTableClass * klass,HTMLType type,guint object_size)2427 html_table_class_init (HTMLTableClass *klass,
2428                        HTMLType type,
2429                        guint object_size)
2430 {
2431 	HTMLObjectClass *object_class;
2432 
2433 	object_class = HTML_OBJECT_CLASS (klass);
2434 
2435 	html_object_class_init (object_class, type, object_size);
2436 
2437 	object_class->copy = copy;
2438 	object_class->op_copy = op_copy;
2439 	object_class->op_cut = op_cut;
2440 	object_class->split = split;
2441 	object_class->merge = merge;
2442 	object_class->accepts_cursor = accepts_cursor;
2443 	object_class->calc_size = html_table_real_calc_size;
2444 	object_class->draw = draw;
2445 	object_class->destroy = destroy;
2446 	object_class->calc_min_width = calc_min_width;
2447 	object_class->calc_preferred_width = calc_preferred_width;
2448 	object_class->set_max_width = html_table_set_max_width;
2449 	object_class->set_max_height = html_table_set_max_height;
2450 	object_class->reset = reset;
2451 	object_class->check_point = check_point;
2452 	object_class->find_anchor = find_anchor;
2453 	object_class->is_container = is_container;
2454 	object_class->forall = forall;
2455 	object_class->search = search;
2456 	object_class->append_selection_string = append_selection_string;
2457 	object_class->head = head;
2458 	object_class->tail = tail;
2459 	object_class->next = next;
2460 	object_class->prev = prev;
2461 	object_class->save = save;
2462 	object_class->save_plain = save_plain;
2463 	object_class->check_page_split = check_page_split;
2464 	object_class->get_bg_color = get_bg_color;
2465 	object_class->get_recursive_length = get_recursive_length;
2466 	object_class->remove_child = remove_child;
2467 	object_class->get_n_children = get_n_children;
2468 	object_class->get_child = get_child;
2469 	object_class->get_child_index = get_child_index;
2470 
2471 	parent_class = &html_object_class;
2472 }
2473 
2474 void
html_table_init(HTMLTable * table,HTMLTableClass * klass,gint width,gint percent,gint padding,gint spacing,gint border)2475 html_table_init (HTMLTable *table,
2476                  HTMLTableClass *klass,
2477                  gint width,
2478                  gint percent,
2479                  gint padding,
2480                  gint spacing,
2481                  gint border)
2482 {
2483 	HTMLObject *object;
2484 	gint r;
2485 
2486 	object = HTML_OBJECT (table);
2487 
2488 	html_object_init (object, HTML_OBJECT_CLASS (klass));
2489 
2490 	object->percent = percent;
2491 
2492 	table->specified_width = width;
2493 	if (width == 0)
2494 		object->flags &= ~ HTML_OBJECT_FLAG_FIXEDWIDTH;
2495 	else
2496 		object->flags |= HTML_OBJECT_FLAG_FIXEDWIDTH;
2497 
2498 	table->padding  = padding;
2499 	table->spacing  = spacing;
2500 	table->border   = border;
2501 	table->caption  = NULL;
2502 	table->capAlign = HTML_VALIGN_TOP;
2503 	table->bgColor  = NULL;
2504 	table->bgPixmap = NULL;
2505 
2506 	table->row = 0;
2507 	table->col = 0;
2508 
2509 	table->totalCols = 1; /* this should be expanded to the maximum number
2510 				 of cols by the first row parsed */
2511 	table->totalRows = 1;
2512 	table->allocRows = 5; /* allocate five rows initially */
2513 
2514 	table->cells = g_new0 (HTMLTableCell **, table->allocRows);
2515 	for (r = 0; r < table->allocRows; r++)
2516 		table->cells[r] = g_new0 (HTMLTableCell *, table->totalCols);;
2517 
2518 	table->columnMin   = g_array_new (FALSE, FALSE, sizeof (gint));
2519 	table->columnFixed = g_array_new (FALSE, FALSE, sizeof (gint));
2520 	table->columnPref  = g_array_new (FALSE, FALSE, sizeof (gint));
2521 	table->columnOpt   = g_array_new (FALSE, FALSE, sizeof (gint));
2522 	table->rowHeights  = g_array_new (FALSE, FALSE, sizeof (gint));
2523 }
2524 
2525 HTMLObject *
html_table_new(gint width,gint percent,gint padding,gint spacing,gint border)2526 html_table_new (gint width,
2527                 gint percent,
2528                 gint padding,
2529                 gint spacing,
2530                 gint border)
2531 {
2532 	HTMLTable *table;
2533 
2534 	table = g_new (HTMLTable, 1);
2535 	html_table_init (table, &html_table_class,
2536 			 width, percent, padding, spacing, border);
2537 
2538 	return HTML_OBJECT (table);
2539 }
2540 
2541 
2542 
2543 void
html_table_add_cell(HTMLTable * table,HTMLTableCell * cell)2544 html_table_add_cell (HTMLTable *table,
2545                      HTMLTableCell *cell)
2546 {
2547 	html_table_alloc_cell (table, table->row, table->col);
2548 	prev_col_do_cspan (table, table->row);
2549 
2550 	/* look for first free cell */
2551 	while (table->cells[table->row][table->col] && table->col < table->totalCols)
2552 		table->col++;
2553 
2554 	html_table_alloc_cell (table, table->row, table->col);
2555 	html_table_set_cell (table, table->row, table->col, cell);
2556 	html_table_cell_set_position (cell, table->row, table->col);
2557 	do_cspan (table, table->row, table->col, cell);
2558 }
2559 
2560 void
html_table_start_row(HTMLTable * table)2561 html_table_start_row (HTMLTable *table)
2562 {
2563 	table->col = 0;
2564 }
2565 
2566 void
html_table_end_row(HTMLTable * table)2567 html_table_end_row (HTMLTable *table)
2568 {
2569 	if (table->row >= table->totalRows)
2570 		inc_rows (table, 1);
2571 	table->row++;
2572 }
2573 
2574 gint
html_table_end_table(HTMLTable * table)2575 html_table_end_table (HTMLTable *table)
2576 {
2577 	gint r, c, cells = 0;
2578 
2579 	for (r = 0; r < table->totalRows; r++)
2580 		for (c = 0; c < table->totalCols; c++)
2581 			if (table->cells[r][c]) {
2582 				if (HTML_CLUE (table->cells[r][c])->head == NULL) {
2583 					HTMLTableCell *cell = table->cells[r][c];
2584 
2585 					remove_cell (table, cell);
2586 					html_object_destroy (HTML_OBJECT (cell));
2587 				} else
2588 					cells++;
2589 			}
2590 	return cells;
2591 }
2592