source: trunk/third/gtkhtml/src/htmltable.c @ 18136

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