source: trunk/third/gtkhtml3/src/htmltable.c @ 21460

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