/* * File: dw_table.c * * Copyright (C) 2001-2003 Sebastian Geerken * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include "dw_table.h" #include "list.h" #include "prefs.h" #include "dw_gtk_viewport.h" #define DEBUG_CALC_LEVEL 10 #define DEBUG_EXTR_LEVEL 10 #define DEBUG_WIDTH_LEVEL 10 #define DEBUG_ITERATOR_LEVEL 0 /*#define DEBUG_LEVEL 10*/ #include "debug.h" /* * Some notes: * * - table->num_children always has the value table->num_cols * * table->num_rows, and shows the minimal size of * table->children, *not* the number of children. * * - table->children[i] may be NULL (unused cells), or a pointer to * DwTableChild. A DwTableChild may be a span space (blocked by * COLSPAN or ROWSPAN of another cell), or a cell starting at * this position. In the last case, the macro CHILD_DEFINED * returns TRUE. */ /*#define BORDERS*/ #define CHILD_DEFINED(table, n) ((n) < (table)->num_children && \ (table)->children[(n)] != NULL && \ (table)->children[(n)]->type != \ DW_TABLE_SPAN_SPACE) static void Dw_table_init (DwTable *table); static void Dw_table_class_init (DwTableClass *klass); static void Dw_table_destroy (GtkObject *object); static void Dw_table_size_request (DwWidget *widget, DwRequisition *requisition); static void Dw_table_get_extremes (DwWidget *widget, DwExtremes *extremes); static void Dw_table_size_allocate (DwWidget *widget, DwAllocation *allocation); static void Dw_table_set_width (DwWidget *widget, gint32 width); static void Dw_table_set_ascent (DwWidget *widget, gint32 ascent); static void Dw_table_set_descent (DwWidget *widget, gint32 descent); static void Dw_table_draw (DwWidget *widget, DwRectangle *area, GdkEventExpose *event); static gboolean Dw_table_button_press (DwWidget *widget, gint32 x, gint32 y, GdkEventButton *event); static gboolean Dw_table_button_release(DwWidget *widget, gint32 x, gint32 y, GdkEventButton *event); static gboolean Dw_table_motion_notify (DwWidget *widget, gint32 x, gint32 y, GdkEventMotion *event); static void Dw_table_add (DwContainer *container, DwWidget *widget); static void Dw_table_remove (DwContainer *container, DwWidget *widget); static void Dw_table_forall (DwContainer *container, DwCallback callback, gpointer callback_data); static DwIterator* Dw_table_iterator (DwWidget *widget, gint mask, gboolean at_end); static gboolean Dw_table_iterator_next (DwIterator *it); static gboolean Dw_table_iterator_prev (DwIterator *it); static void Dw_table_realloc_children (DwTable *table); static void Dw_table_sub_create (DwTable *table); static void Dw_table_sub_free (DwTableSub *sub); static void Dw_table_sub_get_extremes (DwTableSub *sub); static void Dw_table_sub_calc_col_widths (DwTableSub *sub, gint32 width, gint32 total_width); static DwContainerClass *parent_class; enum { /* values of use_percentage */ USE_PERCENTAGE_NO, /* No percentage in subtable, and USE_PERCENTAGE_NO * in all sub-subtables. */ USE_PERCENTAGE_SOME, /* None of the other cases. This affects the minimal, * but not the maximal width of the subtable. */ USE_PERCENTAGE_ALL /* Percentage in subtable, or USE_PERCENTAGE_ALL in * all sub-subtables. This affects the minimal and * the maximal width of the subtable. */ }; /* * Standard Gtk+ function. */ GtkType a_Dw_table_get_type (void) { static GtkType type = 0; if (!type) { GtkTypeInfo info = { "DwTable", sizeof (DwTable), sizeof (DwTableClass), (GtkClassInitFunc) Dw_table_class_init, (GtkObjectInitFunc) Dw_table_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL, (GtkClassInitFunc) NULL }; type = gtk_type_unique (DW_TYPE_CONTAINER, &info); } return type; } /* * Standard Gtk+ function. */ DwWidget* a_Dw_table_new (void) { GtkObject *object; object = gtk_object_new (DW_TYPE_TABLE, NULL); DBG_OBJ_CREATE (object, "DwTable"); return DW_WIDGET (object); } /* * Standard Gtk+ function. */ static void Dw_table_init (DwTable *table) { DW_WIDGET_SET_FLAGS (table, DW_USES_HINTS); /* random value */ table->avail_width = 100; table->cur_row = -1; table->cur_col = 0; table->num_children_max = 1; /* 16 */ table->num_children = 0; table->children = NULL; table->num_col_width_max = 1; /* 8 */ table->num_cols = 0; table->col_width = NULL; table->cum_height_max = 1; /* row cumulative height array */ table->num_rows = 0; /* num_cum_height is (num_rows + 1) */ table->cum_height = g_new(gint32, table->cum_height_max); table->row_style_max = 1; table->row_style = g_new(DwStyle*, table->row_style_max); table->baseline_max = 1; table->baseline = g_new(gint32, table->baseline_max); table->sub = NULL; } /* * Standard Gtk+ function. */ static void Dw_table_class_init (DwTableClass *klass) { GtkObjectClass *object_class; DwWidgetClass *widget_class; DwContainerClass *container_class; object_class = (GtkObjectClass*) klass; widget_class = (DwWidgetClass*) klass; container_class = (DwContainerClass*) klass; parent_class = gtk_type_class (DW_TYPE_CONTAINER); object_class->destroy = Dw_table_destroy; widget_class->size_request = Dw_table_size_request; widget_class->get_extremes = Dw_table_get_extremes; widget_class->size_allocate = Dw_table_size_allocate; widget_class->set_width = Dw_table_set_width; widget_class->set_ascent = Dw_table_set_ascent; widget_class->set_descent = Dw_table_set_descent; widget_class->draw = Dw_table_draw; widget_class->button_press_event = Dw_table_button_press; widget_class->button_release_event = Dw_table_button_release; widget_class->motion_notify_event = Dw_table_motion_notify; widget_class->iterator = Dw_table_iterator; container_class->add = Dw_table_add; container_class->remove = Dw_table_remove; container_class->forall = Dw_table_forall; } /* * Standard Gtk+ function. */ static void Dw_table_destroy (GtkObject *object) { DwTable *table = DW_TABLE (object); gint i; for (i = 0; i < table->num_children; i++) if (table->children[i]) { if (table->children[i]->type == DW_TABLE_CELL) gtk_object_unref(GTK_OBJECT(table->children[i]->data.cell.widget)); g_free (table->children[i]); } g_free (table->children); table->num_children = 0; g_free (table->col_width); g_free (table->cum_height); for (i = 0; i < table->num_rows; i++) if (table->row_style[i]) a_Dw_style_unref (table->row_style[i]); g_free (table->row_style); g_free (table->baseline); if (table->sub) Dw_table_sub_free (table->sub); GTK_OBJECT_CLASS(parent_class)->destroy (object); } /* * Standard Dw function. * Furthermore, table->width and table->cum_height are filled. */ static void Dw_table_size_request (DwWidget *widget, DwRequisition *requisition) { DwExtremes extremes; gint32 avail_width; DwTable *table = DW_TABLE (widget); gint row, row2, col, col2, n; gint32 child_height, set_width; DwWidget *child; DwRequisition child_requisition; /* Calculate the widths of the columns, and store it in table->col_width. */ if (table->num_cols > 0) { p_Dw_widget_get_extremes (DW_WIDGET (table), &extremes); if (DW_STYLE_IS_ABS_LENGTH (widget->style->width)) avail_width = DW_STYLE_ABS_LENGTH_VAL (widget->style->width); else if (DW_STYLE_IS_PER_LENGTH (widget->style->width)) /* * If the width is > 100%, we use 100%, this prevents ugly * results. (May be changed in future, when a more powerful * rendering is implemented, to handle fixed positions etc., * as defined by CSS2.) */ avail_width = table->avail_width * MIN (DW_STYLE_PER_LENGTH_VAL (widget->style->width), 1.0); else avail_width = table->avail_width; if (avail_width < extremes.min_width) avail_width = extremes.min_width; if (widget->style->width == DW_STYLE_LENGTH_AUTO && avail_width > extremes.max_width) avail_width = extremes.max_width; DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3, "--> Calculating column widths for (%d x %d) table.\n", table->num_cols, table->num_rows); DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3, " [ %d --(%d, %d)--> %d ]\n", table->avail_width, extremes.min_width, extremes.max_width, avail_width); avail_width -= (2 * widget->style->border_spacing + p_Dw_style_box_diff_width (widget->style)); Dw_table_sub_calc_col_widths (table->sub, avail_width, avail_width - (table->num_cols - 1) * widget->style->border_spacing); DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "<-- END\n"); } /* Calculate the total width. */ requisition->width = p_Dw_style_box_diff_width (widget->style) + (table->num_cols + 1) * widget->style->border_spacing; for (col = 0; col < table->num_cols; col++) requisition->width += table->col_width[col]; /* Calculate the height. */ for (row = 0; row <= table->num_rows; row++) table->cum_height[row] = widget->style->border_spacing; for (row = 0; row < table->num_rows; row++) { /* Calculate the baselines, for cells only with valign=baseline. */ table->baseline[row] = 0; for (col = 0; col < table->num_cols; col++) { n = col + row * table->num_cols; if (CHILD_DEFINED (table, n)) { child = table->children[n]->data.cell.widget; if (child->style->valign == DW_STYLE_VALIGN_BASELINE) { p_Dw_widget_size_request (child, &child_requisition); if (child_requisition.ascent > table->baseline[row]) table->baseline[row] = child_requisition.ascent; } } } for (col = 0; col < table->num_cols; col++) { n = col + row * table->num_cols; if (CHILD_DEFINED (table, n)) { child = table->children[n]->data.cell.widget; set_width = 0; for (col2 = col; col2 < col + table->children[n]->data.cell.colspan; col2++) set_width += table->col_width[col2]; p_Dw_widget_set_width (child, set_width); p_Dw_widget_set_ascent (child, table->avail_ascent); p_Dw_widget_set_descent (child, table->avail_descent); p_Dw_widget_size_request (child, &child_requisition); /* Cells with valign=baseline have a height which may * be greater than the height returned by size_request. */ if (child->style->valign == DW_STYLE_VALIGN_BASELINE) child_height = table->baseline[row] + child_requisition.descent; else child_height = child_requisition.ascent + child_requisition.descent; row2 = row + table->children[n]->data.cell.rowspan; /* row2 is the row directly forced downward by the child */ if (table->cum_height[row2] < table->cum_height[row] + child_height + widget->style->border_spacing ) { table->cum_height[row2] = table->cum_height[row] + child_height + widget->style->border_spacing; DEBUG_MSG (1, "Row %d starts at %d.\n", row2, table->cum_height[row + table->children[n] ->data.cell.rowspan]); /* todo: column widths should be adjusted a bit. */ } } } /* if we've hit a row that contains no defined children, we need to force cumulative heights to trickle down to lower rows */ if (table->cum_height[row + 1] < table->cum_height[row]) table->cum_height[row + 1] = table->cum_height[row]; } requisition->ascent = table->cum_height[table->num_rows] + p_Dw_style_box_diff_height (widget->style); requisition->descent = 0; DEBUG_MSG (2, "Dw_table_size_request: %d x %d x %d\n", requisition->width, requisition->ascent, requisition->descent); } /* * Standard Dw function. */ static void Dw_table_get_extremes (DwWidget *widget, DwExtremes *extremes) { DwTable *table = DW_TABLE (widget); gint32 diff; if (table->num_cols > 0) { if (table->sub == NULL) Dw_table_sub_create (table); DEBUG_MSG (DEBUG_EXTR_LEVEL + 3, "--> [%p] Dw_table_get_extremes for (%d x %d) table.\n", table, table->num_cols, table->num_rows); Dw_table_sub_get_extremes (table->sub); diff = 2 * widget->style->border_spacing + p_Dw_style_box_diff_width (widget->style); extremes->min_width = table->sub->total_extremes.min_width + diff; extremes->max_width = table->sub->total_extremes.max_width + diff; DEBUG_MSG (DEBUG_EXTR_LEVEL + 2, "<-- [%p] END\n", table); } else { extremes->min_width = widget->style->border_spacing + p_Dw_style_box_diff_width (widget->style); extremes->max_width = widget->style->border_spacing + p_Dw_style_box_diff_width (widget->style); } } /* * Standard Dw function. */ static void Dw_table_size_allocate (DwWidget *widget, DwAllocation *allocation) { DwTable *table = DW_TABLE (widget); gint row, col, col2, n; gint32 col_offset, cell_width, cell_height, height_diff, vdiff; DwWidget *child; DwAllocation child_allocation; DwRequisition child_requisition; /* * todo: This implementation depends on that size_request has been * called before. Can this be assumed? */ DEBUG_MSG (2, "Dw_table_size_allocate: %d x %d x %d\n", allocation->width, allocation->ascent, allocation->descent); vdiff = allocation->y + p_Dw_style_box_offset_y (widget->style); for (row = 0; row < table->num_rows; row++) { col_offset = allocation->x + widget->style->border_spacing + p_Dw_style_box_offset_x (widget->style); for (col = 0; col < table->num_cols; col++) { n = col + row * table->num_cols; if (CHILD_DEFINED (table, n)) { cell_width = (table->children[n]->data.cell.colspan - 1) * widget->style->border_spacing; for (col2 = col; col2 < col + table->children[n]->data.cell.colspan; col2++) cell_width += table->col_width[col2]; child = table->children[n]->data.cell.widget; cell_height = table->cum_height[row + table->children[n]->data.cell.rowspan] - table->cum_height[row] - widget->style->border_spacing; child_allocation.x = col_offset; child_allocation.y = vdiff + table->cum_height[row]; child_allocation.width = cell_width; /* Vertical alignment is done via allocation. */ p_Dw_widget_size_request (child, &child_requisition); switch (child->style->valign) { case DW_STYLE_VALIGN_TOP: child_allocation.ascent = child_requisition.ascent; child_allocation.descent = cell_height - child_requisition.ascent; break; case DW_STYLE_VALIGN_BOTTOM: child_allocation.ascent = cell_height - child_requisition.descent; child_allocation.descent = child_requisition.descent; break; case DW_STYLE_VALIGN_MIDDLE: height_diff = cell_height - (child_requisition.ascent + child_requisition.descent); child_allocation.ascent = child_requisition.ascent + height_diff / 2; child_allocation.descent = child_requisition.descent + (height_diff + 1) / 2; /* The "+ 1" is to avoid rounding errors. */ break; case DW_STYLE_VALIGN_BASELINE: child_allocation.ascent = table->baseline[row]; child_allocation.descent = cell_height - table->baseline[row]; break; default: g_assert_not_reached(); break; } p_Dw_widget_size_allocate (child, &child_allocation); } col_offset += table->col_width[col] + widget->style->border_spacing; } } } /* * Standard Dw function. */ static void Dw_table_set_width (DwWidget *widget, gint32 width) { DwTable *table; table = DW_TABLE (widget); /* If limit_text_width is set to YES, a queue_resize may also be necessary. */ if (table->avail_width != width || prefs.limit_text_width) { table->avail_width = width; p_Dw_widget_queue_resize (widget, 0, FALSE); } } /* * Standard Dw function. */ static void Dw_table_set_ascent (DwWidget *widget, gint32 ascent) { DwTable *table; table = DW_TABLE (widget); if (table->avail_ascent != ascent) { table->avail_ascent = ascent; p_Dw_widget_queue_resize (widget, 0, FALSE); } } /* * Standard Dw function. */ static void Dw_table_set_descent (DwWidget *widget, gint32 descent) { DwTable *table; table = DW_TABLE (widget); if (table->avail_descent != descent) { table->avail_descent = descent; p_Dw_widget_queue_resize (widget, 0, FALSE); } } /* * Standard Dw function. */ static void Dw_table_draw (DwWidget *widget, DwRectangle *area, GdkEventExpose *event) { /* can be optimized, by iterating on the lines in area */ DwTable *table = DW_TABLE (widget); DwRectangle child_area; DwWidget *child; gint i; gint32 offx, offy, width; p_Dw_widget_draw_widget_box (widget, area, FALSE); offx = p_Dw_style_box_offset_x (widget->style); offy = p_Dw_style_box_offset_y (widget->style); width = DW_WIDGET_CONTENT_WIDTH(widget); for (i = 0; i < table->num_rows; i++) if (table->row_style[i]) p_Dw_widget_draw_box (widget, table->row_style[i], area, offx, offy + table->cum_height[i], width, table->cum_height[i + 1] - table->cum_height[i] - widget->style->border_spacing, FALSE); for (i = 0; i < table->num_children; i++) if (CHILD_DEFINED (table, i)) { child = table->children[i]->data.cell.widget; if (p_Dw_widget_intersect (child, area, &child_area)) { #ifdef BORDERS gdk_draw_rectangle (DW_WIDGET_WINDOW (child), widget->viewport->style->dark_gc[widget->viewport->state], FALSE, Dw_widget_x_world_to_viewport (child, child->allocation.x) - 1, Dw_widget_y_world_to_viewport (child, child->allocation.y) - 1, child->allocation.width + 2, DW_WIDGET_HEIGHT(child) + 2); #endif p_Dw_widget_draw (child, &child_area, event); } } #ifdef BORDERS gdk_draw_rectangle (DW_WIDGET_WINDOW (widget), widget->style->color->gc, FALSE, Dw_widget_x_world_to_viewport (widget, widget->allocation.x), Dw_widget_y_world_to_viewport (widget, widget->allocation.y), widget->allocation.width, DW_WIDGET_HEIGHT(widget)); #endif } /* * Construct an iterator for a cell. */ static DwIterator *Dw_table_construct_iterator (DwTable *table, gint cell_index) { DwIterator *it; it = Dw_table_iterator (DW_WIDGET (table), DW_CONTENT_ALL, FALSE); ((DwIteratorInt*)it)->pos = cell_index; it->content.type = DW_CONTENT_WIDGET; it->content.data.widget = table->children[cell_index]->data.cell.widget; return it; } /* * Construct an iterator pointing to the end of the table. */ static DwIterator *Dw_table_construct_last_iterator (DwTable *table, int *char_pos) { int n; DwIterator *it = NULL; DwTableChild *child = NULL; *char_pos = 0; n = table->num_children -1; while (n >= 0 && ((child = table->children[n]) == NULL || child->type != DW_TABLE_CELL)) n--; if (n >= 0) { /* Try to construct an iterator for the last child of the table. */ it = a_Dw_widget_iterator (child->data.cell.widget, DW_CONTENT_ALL, TRUE); if (it != NULL && a_Dw_iterator_prev (it)) { if (it->content.type == DW_CONTENT_TEXT) *char_pos = strlen (it->content.data.text); } else { a_Dw_iterator_free (it); it = NULL; } if (it == NULL) /* This widget has no content, construct an iterator for the table. */ it = Dw_table_construct_iterator (table, n); } return it; } /* * Send event to selection. */ static gboolean Dw_table_send_selection_event (DwTable *table, gint (*fn) (Selection*, DwIterator *, gint, gint, GdkEventButton*, gboolean within_content), gint32 x, gint32 y, GdkEventButton *event) { int row, col, n, char_pos; DwIterator *it; DwTableChild *child; gint32 offy, cellspacing, cum_width; it = NULL; char_pos = 0; cellspacing = DW_WIDGET(table)->style->border_spacing; /* Search row. * Just a note: Improving the speed of this does not give very much, * since events (in dw_widget.c) are always much slower. */ offy = p_Dw_style_box_offset_y (DW_WIDGET(table)->style); row = 0; while (row < table->num_rows && y > table->cum_height[row + 1] + offy - cellspacing) row++; if (row == table->num_rows) /* Below the last row: We create an iterator of the last child, * starting at the end (if possible). */ it = Dw_table_construct_last_iterator (table, &char_pos); else { if (y < table->cum_height[row] + offy) { /* Before the row: Start at first column. */ col = 0; } else { /* Within the row: Search column. * NOTE: col may be table->num_cols, this is intended, see below */ cum_width = p_Dw_style_box_offset_x (DW_WIDGET(table)->style) + cellspacing + table->col_width[0]; col = 0; while (col < table->num_cols && x > cum_width) { col++; cum_width += table->col_width[col] + cellspacing; } /* Already right of the middle of the next column? * (Actually, this feature is only needed for cases where widgets * are added to a table, which do not process mouse events. This * is never the case in dillo. But it works.) */ if (col < table->num_cols && x >= cum_width - table->col_width[col] / 2) col++; } n = col + row * table->num_cols; /* If col == table->num_cols, this may be happen: */ if (n == table->num_children) it = Dw_table_construct_last_iterator (table, &char_pos); else { /* some corrections */ for (; n < table->num_children; n++) { if ((child = table->children[n]) && child->type == DW_TABLE_CELL) { it = Dw_table_construct_iterator (table, n); break; } } } } if (it) { return fn (GTK_DW_VIEWPORT(DW_WIDGET(table)->viewport)->selection, it, char_pos, -1, event, FALSE); } else return FALSE; } /* * Standard Dw function. */ static gboolean Dw_table_button_press (DwWidget *widget, gint32 x, gint32 y, GdkEventButton *event) { return Dw_table_send_selection_event (DW_TABLE (widget), a_Selection_button_press, x, y, event); } /* * Standard Dw function. */ static gboolean Dw_table_button_release (DwWidget *widget, gint32 x, gint32 y, GdkEventButton *event) { return Dw_table_send_selection_event (DW_TABLE (widget), a_Selection_button_release, x, y, event); } /* * Standard Dw function. */ static gboolean Dw_table_motion_notify (DwWidget *widget, gint32 x, gint32 y, GdkEventMotion *event) { if (event && (event->state & GDK_BUTTON1_MASK)) return Dw_table_send_selection_event (DW_TABLE (widget), a_Selection_button_motion, x, y, NULL); else return FALSE; } /* * Standard Dw function. */ static void Dw_table_add (DwContainer *container, DwWidget *widget) { a_Dw_table_add_cell (DW_TABLE (container), widget, 1, 1); } /* * Standard Dw function. */ static void Dw_table_remove (DwContainer *container, DwWidget *widget) { /* used? */ } /* * Standard Dw function. */ static void Dw_table_forall (DwContainer *container, DwCallback callback, gpointer callback_data) { gint i; DwTable *table = DW_TABLE (container); for (i = 0; i < table->num_children; i++) if (CHILD_DEFINED (table, i)) callback (table->children[i]->data.cell.widget, callback_data); } /* * Make sure that there is enough space for table->children, * table->cum_height etc., and fill the rest of table->children with * NULL. */ static void Dw_table_realloc_children (DwTable *table) { gint old_num_children, i; table->num_rows++; a_List_resize (table->cum_height, table->num_rows, table->cum_height_max); table->num_rows--; a_List_resize (table->row_style, table->num_rows, table->row_style_max); a_List_resize (table->baseline, table->num_rows, table->baseline_max); old_num_children = table->num_children; table->num_children = table->num_cols * table->num_rows; a_List_resize (table->children, table->num_children, table->num_children_max); for (i = old_num_children; i < table->num_children; i++) table->children[i] = NULL; } /* * Add widget as a cell to table. colspan and rowspan have the same * meaning as the attributes of and . */ void a_Dw_table_add_cell (DwTable *table, DwWidget *widget, gint colspan, gint rowspan) { gint col, row, old_num_cols, old_num_rows; DwTableChild *child; /* We limit the values for colspan and rowspan to 50, to avoid * attacks by malicious web pages. * todo: Some warnings would be nice. */ if (colspan > 50) colspan = 50; if (rowspan > 50) rowspan = 50; if (table->num_rows == 0) /* to prevent a crash */ a_Dw_table_add_row (table, NULL); /* todo: {col|row}span = 0 is not supported yet: */ if (colspan < 1) colspan = 1; if (rowspan < 1) rowspan = 1; while (table->cur_col < table->num_cols && (child = table->children[table->cur_row * table->num_cols + table->cur_col]) != NULL && child->type == DW_TABLE_SPAN_SPACE) table->cur_col++; if (table->cur_row + rowspan > table->num_rows) { old_num_rows = table->num_rows; table->num_rows = table->cur_row + rowspan; Dw_table_realloc_children (table); for (row = old_num_rows; row < table->num_rows; row++) table->row_style[row] = NULL; } if (table->cur_col + colspan > table->num_cols) { old_num_cols = table->num_cols; table->num_cols = table->cur_col + colspan; a_List_resize (table->col_width, table->num_cols, table->num_col_width_max); table->num_children = table->num_cols * table->num_rows; a_List_resize (table->children, table->num_children, table->num_children_max); for (row = table->num_rows - 1; row >= 0; row--) { for (col = old_num_cols - 1; col >= 0; col--) { table->children[row * table->num_cols + col] = table->children[row * old_num_cols + col]; } for (col = old_num_cols; col < table->num_cols; col++) table->children[row * table->num_cols + col] = NULL; } } for (col = 0; col < colspan; col++) for (row = 0; row < rowspan; row++) if (!(col == 0 && row == 0)) { child = g_new (DwTableChild, 1); child->type = DW_TABLE_SPAN_SPACE; child->data.span_space.start_col = table->cur_col; child->data.span_space.start_row = table->cur_row; table->children[(table->cur_row + row) * table->num_cols + (table->cur_col + col)] = child; } child = g_new (DwTableChild, 1); child->type = DW_TABLE_CELL; child->data.cell.widget = widget; child->data.cell.colspan = colspan; child->data.cell.rowspan = rowspan; table->children[table->cur_row * table->num_cols + table->cur_col] = child; table->cur_col += colspan; p_Dw_widget_set_parent (widget, DW_WIDGET (table)); if (table->row_style[table->cur_row] != NULL) p_Dw_widget_set_bg_color (widget, table->row_style[table->cur_row]->background_color); p_Dw_widget_queue_resize (widget, 0, TRUE); /* The cell structure has changed, so the subtables have to be rebuilt. */ if (table->sub) { Dw_table_sub_free (table->sub); table->sub = NULL; } } /* * Add a new row to the table, and start adding cells from the * left-most column. */ void a_Dw_table_add_row (DwTable *table, DwStyle *style) { gint row, old_num_rows; table->cur_row++; if (table->cur_row >= table->num_rows) { old_num_rows = table->num_rows; table->num_rows = table->cur_row + 1; Dw_table_realloc_children (table); for (row = old_num_rows; row < table->num_rows; row++) table->row_style[row] = NULL; } if (table->row_style[table->cur_row]) a_Dw_style_unref (table->row_style[table->cur_row]); table->row_style[table->cur_row] = style; if (style) a_Dw_style_ref (style); table->cur_col = 0; } /* * Returns the DwTableCell to be passed to a_Dw_table_cell_new(). */ DwTableCell* a_Dw_table_get_cell_ref (DwTable *table) { gint row, n; DwWidget *child; for (row = 0; row <= table->num_rows; row++) { n = table->cur_col + row * table->num_cols; if (CHILD_DEFINED (table, n)) { child = table->children[n]->data.cell.widget; if (DW_IS_TABLE_CELL (child)) return DW_TABLE_CELL (child); } } return NULL; } /* ---------------------------------------------------------------------- * * General Functions for Subtables * * ---------------------------------------------------------------------- */ static gboolean Dw_table_sub_spans_width (DwTableSub *sub, gint row) { gint n, start_col, start_row, start_n, colspan; n = sub->start_col + sub->table->num_cols * row; if (sub->table->children[n]) { if (sub->table->children[n]->type == DW_TABLE_CELL) { start_col = sub->start_col; start_row = row; } else { start_col = sub->table->children[n]->data.span_space.start_col; start_row = sub->table->children[n]->data.span_space.start_row; } start_n = start_col + sub->table->num_cols * start_row; colspan = sub->table->children[start_n]->data.cell.colspan; return (start_col <= sub->start_col && start_col + colspan >= sub->end_col); } else return FALSE; } /* * Fill the DwTableSub and create the sub DwTableSub's. Only for use * by Dw_table_get_sub. */ static void Dw_table_sub_calc_subs (DwTableSub *sub) { gint col, row, n, start_n, colspan, most_right, start_col, start_row; if (sub->start_col + 1 == sub->end_col) { DEBUG_MSG (DEBUG_CALC_LEVEL + 2, " single column at %d\n", sub->start_col); sub->num_subs = 0; sub->subs = NULL; for (row = 0; row < sub->table->num_rows; row++) if (!a_Bitvec_get_bit (sub->removed_rows, row)) { a_Bitvec_set_bit (sub->spanning_rows, row); DEBUG_MSG (DEBUG_CALC_LEVEL + 1, " row %d spans width\n", row); } } else { DEBUG_MSG (DEBUG_CALC_LEVEL + 2, " --> complex subtable from %d to %d\n", sub->start_col, sub->end_col); for (row = 0; row < sub->table->num_rows; row++) if (!a_Bitvec_get_bit (sub->removed_rows, row) && Dw_table_sub_spans_width (sub, row)) { a_Bitvec_set_bit (sub->spanning_rows, row); DEBUG_MSG (DEBUG_CALC_LEVEL + 1, " row %d spans width\n", row); } sub->num_subs = 0; sub->subs = g_new (DwTableSub, sub->end_col - sub->start_col); col = sub->start_col; while (col < sub->end_col) { DEBUG_MSG (DEBUG_CALC_LEVEL + 1, " column %d\n", col); sub->subs[sub->num_subs].table = sub->table; sub->subs[sub->num_subs].start_col = col; sub->subs[sub->num_subs].removed_rows = a_Bitvec_new (sub->table->num_rows); sub->subs[sub->num_subs].spanning_rows = a_Bitvec_new (sub->table->num_rows); /* Search for the greatest colspan value. */ most_right = col + 1; for (row = 0; row < sub->table->num_rows; row++) if (a_Bitvec_get_bit (sub->removed_rows, row) || a_Bitvec_get_bit (sub->spanning_rows, row)) a_Bitvec_set_bit (sub->subs[sub->num_subs].removed_rows, row); else { n = col + sub->table->num_cols * row; if (sub->table->children[n]) { if (sub->table->children[n]->type == DW_TABLE_CELL) { start_col = col; start_row = row; start_n = n; } else { start_col = sub->table->children[n]->data.span_space.start_col; start_row = sub->table->children[n]->data.span_space.start_row; start_n = start_col + sub->table->num_cols * start_row; } if (row == start_row) { colspan = sub->table->children[start_n]->data.cell.colspan - (col - start_col); if (start_col + colspan > most_right && start_col + colspan <= sub->end_col) most_right = start_col + colspan; } } } sub->subs[sub->num_subs].end_col = most_right; Dw_table_sub_calc_subs (&sub->subs[sub->num_subs]); sub->num_subs++; col = most_right; } DEBUG_MSG (DEBUG_CALC_LEVEL + 1, " <-- END\n"); } } /* * Create a DwTableSub for a table. */ static void Dw_table_sub_create (DwTable *table) { if (table->sub) Dw_table_sub_free (table->sub); DEBUG_MSG (DEBUG_CALC_LEVEL + 3, "--> Dw_table_sub_create widths for (%d x %d) table.\n", table->num_cols, table->num_rows); table->sub = g_new (DwTableSub, 1); table->sub->table = table; table->sub->start_col = 0; table->sub->end_col = table->num_cols; table->sub->removed_rows = a_Bitvec_new (table->num_rows); table->sub->spanning_rows = a_Bitvec_new (table->num_rows); Dw_table_sub_calc_subs (table->sub); DEBUG_MSG (DEBUG_CALC_LEVEL + 2, "<-- END\n"); } /* * Clean up a DwTableSub. Only for use by Dw_table_free_sub. */ static void Dw_table_sub_clean_up (DwTableSub *sub) { gint i; a_Bitvec_free (sub->removed_rows); a_Bitvec_free (sub->spanning_rows); if (sub->subs) { for (i = 0; i < sub->num_subs; i++) Dw_table_sub_clean_up (&sub->subs[i]); g_free (sub->subs); } } /* * Free a DwTableSub. */ static void Dw_table_sub_free (DwTableSub *sub) { Dw_table_sub_clean_up (sub); g_free (sub); } /* ---------------------------------------------------------------------- * * Specific Functions for Subtables * * NOTE: The spaces left of the left most column and right of the * right most column do not belong to a subtable. * * ---------------------------------------------------------------------- */ /* * Calculate the extremes of a subtable. */ static void Dw_table_sub_get_extremes (DwTableSub *sub) { gint i, row, n, start_col, start_row, start_n, num_cols, colspan; DwWidget *child, *widget; DwExtremes child_extremes, sum_extremes; gint32 col_width; gint32 border_spacing = DW_WIDGET(sub->table)->style->border_spacing; gfloat percentage; gfloat sum_percentage; gint use_percentage_map[3][3] = { { USE_PERCENTAGE_NO, USE_PERCENTAGE_SOME, -1 }, { USE_PERCENTAGE_SOME, USE_PERCENTAGE_SOME, USE_PERCENTAGE_SOME }, { USE_PERCENTAGE_SOME, USE_PERCENTAGE_SOME, USE_PERCENTAGE_ALL } }; num_cols = sub->end_col - sub->start_col; sub->use_percentage = USE_PERCENTAGE_NO; sub->fixed_width = FALSE; sub->percentage = 0; DEBUG_MSG (DEBUG_EXTR_LEVEL + 2, " [%p] subtable from %d to %d\n", sub->table, sub->start_col, sub->end_col); /* 1. cells spanning the whole width */ sub->span_extremes.min_width = 0; sub->span_extremes.max_width = 0; for (row = 0; row < sub->table->num_rows; row++) { if (!a_Bitvec_get_bit (sub->removed_rows, row) && a_Bitvec_get_bit (sub->spanning_rows, row)) { n = sub->start_col + row * sub->table->num_cols; if (sub->table->children[n]) { if (sub->table->children[n]->type == DW_TABLE_CELL) { start_row = row; start_n = n; } else { start_col = sub->table->children[n]->data.span_space.start_col; start_row = sub->table->children[n]->data.span_space.start_row; start_n = start_col + sub->table->num_cols * start_row; } if (row == start_row) { child = sub->table->children[start_n]->data.cell.widget; p_Dw_widget_get_extremes (child, &child_extremes); colspan = sub->table->children[start_n]->data.cell.colspan; /* Adjust width argument of the cell */ if (DW_STYLE_IS_ABS_LENGTH (child->style->width)) { col_width = DW_STYLE_ABS_LENGTH_VAL (child->style->width); if (col_width < child_extremes.min_width) col_width = child_extremes.min_width; child_extremes.min_width = col_width; child_extremes.max_width = col_width; sub->fixed_width = TRUE; DEBUG_MSG (DEBUG_EXTR_LEVEL, " [%p] following " "adjusted by %d pixels:\n", sub->table, col_width); } else if (DW_STYLE_IS_PER_LENGTH (child->style->width)) { percentage = DW_STYLE_PER_LENGTH_VAL (child->style->width); sub->percentage = MAX (sub->percentage, percentage); sub->use_percentage = USE_PERCENTAGE_ALL; DEBUG_MSG (DEBUG_EXTR_LEVEL, " [%p] following adjusted by %g %%\n", sub->table, 100 * sub->percentage); } if (num_cols != colspan) { child_extremes.min_width = child_extremes.min_width * num_cols / colspan; child_extremes.max_width = child_extremes.max_width * num_cols / colspan; } DEBUG_MSG (DEBUG_EXTR_LEVEL, " [%p], row %d: cell extremes: %d / %d " "[was multiplied by %d / %d]\n", sub->table, row, child_extremes.min_width, child_extremes.max_width, num_cols, colspan); if (child_extremes.min_width > sub->span_extremes.min_width) sub->span_extremes.min_width = child_extremes.min_width; if (child_extremes.max_width > sub->span_extremes.max_width) sub->span_extremes.max_width = child_extremes.max_width; } else { DEBUG_MSG (DEBUG_EXTR_LEVEL, " [%p] row %d: omitted\n", sub->table, row); } } } } DEBUG_MSG (DEBUG_EXTR_LEVEL + 1, " [%p] spanning subs: %d / %d\n", sub->table, sub->span_extremes.min_width, sub->span_extremes.max_width); if (sub->table->sub == sub) { /* Adjust width argument of the table (fixed, percentages later in the code). */ widget = DW_WIDGET (sub->table); if (DW_STYLE_IS_ABS_LENGTH (widget->style->width)) { col_width = DW_STYLE_ABS_LENGTH_VAL (widget->style->width); if (col_width < sub->span_extremes.min_width) col_width = sub->span_extremes.min_width; sub->span_extremes.min_width = col_width; sub->span_extremes.max_width = col_width; } } /* 2. subtables */ sum_extremes.min_width = (sub->num_subs - 1) * border_spacing; sum_extremes.max_width = (sub->num_subs - 1) * border_spacing; sum_percentage = 0; if (num_cols > 1) for (i = 0; i < sub->num_subs; i++) { Dw_table_sub_get_extremes (&sub->subs[i]); sum_extremes.min_width += sub->subs[i].total_extremes.min_width; sum_extremes.max_width += sub->subs[i].total_extremes.max_width; sum_percentage += sub->subs[i].percentage; if ((sub->use_percentage = use_percentage_map [sub->use_percentage][sub->subs[i].use_percentage]) == -1) sub->use_percentage = (i == 0) ? USE_PERCENTAGE_ALL : USE_PERCENTAGE_SOME; } DEBUG_MSG (DEBUG_EXTR_LEVEL + 1, " [%p] sum of subsubs: %d / %d\n", sub->table, sum_extremes.min_width, sum_extremes.max_width); if (sub->fixed_width) { sum_extremes.max_width = sum_extremes.min_width; sub->span_extremes.max_width = sub->span_extremes.min_width; } sub->total_extremes.min_width = MAX (sum_extremes.min_width, sub->span_extremes.min_width); sub->total_extremes.max_width = MAX (sum_extremes.max_width, sub->span_extremes.max_width); sub->percentage = MAX (sub->percentage, sum_percentage); DEBUG_MSG (DEBUG_EXTR_LEVEL + 1, " [%p] final: %d / %d\n", sub->table, sub->total_extremes.min_width, sub->total_extremes.max_width); } /* * Corrent minima or maxima, used by Dw_table_sub_calc_col_widths. */ #define EXTR_VALUE(e) (max ? (e).max_width : (e).min_width) static void Dw_table_sub_adjust_col_widths (DwTableSub *sub, gboolean max, gint32 width, DwExtremes *sub_extremes, gint num_nf_subs, gint num_nf_cols, gint32 sum_nf_sub_widths) { gint i, cols_per_sub, rest_n, rest_w; gint32 sum_sub_widths, sum_orig_sub_widths, sub_extr_width, delta; sum_sub_widths = 0; sum_orig_sub_widths = 0; for (i = 0; i < sub->num_subs; i++) { sum_orig_sub_widths += EXTR_VALUE (sub->subs[i].total_extremes); sum_sub_widths += EXTR_VALUE (sub_extremes[i]); } DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " Comparing %s: total: %d, sum: %d\n", (max ? "max" : "min"), width, sum_sub_widths); if (num_nf_subs == -1) { /* If all subs are fixed, unfix them all. */ sum_nf_sub_widths = sum_sub_widths; num_nf_cols = sub->end_col - sub->start_col; } if (sum_sub_widths < width) { if (sum_nf_sub_widths == 0) { /* All non-fixed columns zero: Apportion the rest to the * non-fixed columns, according to the columns per subtable. */ rest_w = width - sum_sub_widths; rest_n = num_nf_cols; for (i = 0; i < sub->num_subs; i++) { if (num_nf_subs == -1 || !(sub->subs[i].fixed_width || sub->subs[i].use_percentage == USE_PERCENTAGE_ALL)) { cols_per_sub = (sub->subs[i].end_col - sub->subs[i].start_col); sub_extr_width = rest_w * cols_per_sub / rest_n; rest_w -= sub_extr_width; rest_n -= cols_per_sub; if (max) sub_extremes[i].max_width = sub_extr_width; else sub_extremes[i].min_width = sub_extr_width; } } } else { /* Apportion the rest, according to current values. */ rest_w = width - sum_sub_widths; rest_n = sum_nf_sub_widths; for (i = 0; i < sub->num_subs; i++) if (num_nf_subs == -1 || !(sub->subs[i].fixed_width || sub->subs[i].use_percentage == USE_PERCENTAGE_ALL)) { DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " increasing sub %d: %d -> ", i, EXTR_VALUE (sub_extremes[i])); if (EXTR_VALUE (sub_extremes[i]) > 0) { delta = rest_w * EXTR_VALUE (sub_extremes[i]) / rest_n; rest_w -= delta; rest_n -= EXTR_VALUE (sub_extremes[i]); if (max) sub_extremes[i].max_width += delta; else sub_extremes[i].min_width += delta; } DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "%d\n", EXTR_VALUE (sub_extremes[i])); } } } } /* * Calculate the column widths of a subtable, fill table->width. */ static void Dw_table_sub_calc_col_widths (DwTableSub *sub, gint32 width, gint32 total_width) { enum { AT_MIN, AT_MAX, AT_NORM }; /* for sub_status */ gint *sub_status; gint32 avail_width, sub_width, width_norm_cols, diff_spacing; gint32 col_width, sum_sub_min_widths, sum_orig_sub_min_widths; gint i, num_cols, cols_per_sub, num_norm_cols, num_nf_subs, num_nf_cols; gint rest_w, rest_n, delta; gboolean success; gint32 border_spacing, diff; DwExtremes extremes, *sub_extremes, sum_nf_sub_extremes; num_cols = sub->end_col - sub->start_col; if (num_cols == 1) { /* single column */ DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " single column at %d: width = %d\n", sub->start_col, width); sub->table->col_width[sub->start_col] = width; } else { /* complex subtable * * The comments "STEP " refer to the documentation in * ../doc/DwTable.txt, "Calculating column widths". Read this * parallel. */ DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " --> complex subtable from %d to %d, width = %d " "(total = %d)\n", sub->start_col, sub->end_col, width, total_width); border_spacing = DW_WIDGET (sub->table)->style->border_spacing; diff = (sub->num_subs - 1) * border_spacing; avail_width = width - diff; extremes = sub->total_extremes; if (width > extremes.max_width) extremes.max_width = width; sub_extremes = g_new (DwExtremes, sub->num_subs); sum_sub_min_widths = 0; /* ---- STEP 1: Calculate relative widths. ---- */ for (i = 0; i < sub->num_subs; i++) { sub_extremes[i] = sub->subs[i].total_extremes; if (sub->subs[i].use_percentage != USE_PERCENTAGE_NO) { DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " sub %d [%s] has width of %g%% " "-> adjusting: %d/%d -> ", i, sub->subs[i].fixed_width ? "fixed" : "variable", 100 * sub->subs[i].percentage, sub_extremes[i].min_width, sub_extremes[i].max_width); col_width = sub->subs[i].percentage * total_width; if (col_width < sub_extremes[i].min_width) col_width = sub_extremes[i].min_width; sub_extremes[i].min_width = col_width; if (sub->subs[i].use_percentage == USE_PERCENTAGE_ALL) sub_extremes[i].max_width = col_width; DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "%d/%d\n", sub_extremes[i].min_width, sub_extremes[i].max_width); } else { DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " sub %d [%s]: %d/%d\n", i, sub->subs[i].fixed_width ? "fixed" : "variable", sub_extremes[i].min_width, sub_extremes[i].max_width); } sum_sub_min_widths += sub_extremes[i].min_width; } /* ---- STEP 2: Eventually, decrease them again. ---- */ if (sum_sub_min_widths > width) { sum_orig_sub_min_widths = 0; for (i = 0; i < sub->num_subs; i++) sum_orig_sub_min_widths += sub->subs[i].total_extremes.min_width; rest_w = sum_sub_min_widths - (width -diff); rest_n = sum_sub_min_widths - sum_orig_sub_min_widths; for (i = 0; i < sub->num_subs; i++) { DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " decreasing sub %d: %d -> ", i, sub_extremes[i].min_width); if (sub_extremes[i].min_width != sub->subs[i].total_extremes.min_width) { delta = rest_w * (sub_extremes[i].min_width - sub->subs[i].total_extremes.min_width) / rest_n; rest_w -= delta; rest_n -= (sub_extremes[i].min_width - sub->subs[i].total_extremes.min_width); } else delta = 0; sub_width = sub_extremes[i].min_width - delta; Dw_table_sub_calc_col_widths (&sub->subs[i], sub_width, total_width); DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, "%d\n", sub_width); } } else { /* ---- STEP 3: If necessary, increase the sub-subtable ---- * ---- extremes. ---- */ num_nf_subs = 0; num_nf_cols = 0; sum_nf_sub_extremes.min_width = 0; sum_nf_sub_extremes.max_width = 0; for (i = 0; i < sub->num_subs; i++) if (!(sub->subs[i].fixed_width || sub->subs[i].use_percentage == USE_PERCENTAGE_ALL)) { num_nf_cols += (sub->subs[i].end_col - sub->subs[i].start_col); num_nf_subs++; sum_nf_sub_extremes.min_width += sub_extremes[i].min_width; sum_nf_sub_extremes.max_width += sub_extremes[i].max_width; } /* If all subtables are fixed, unfix them all. */ if (num_nf_subs == 0) { num_nf_subs = -1; num_nf_cols = num_cols; } diff_spacing = (sub->end_col - sub->start_col - 1) * DW_WIDGET(sub->table)->style->border_spacing; Dw_table_sub_adjust_col_widths (sub, FALSE, extremes.min_width - diff_spacing, sub_extremes, num_nf_subs, num_nf_cols, sum_nf_sub_extremes.min_width); Dw_table_sub_adjust_col_widths (sub, TRUE, extremes.max_width - diff_spacing, sub_extremes, num_nf_subs, num_nf_cols, sum_nf_sub_extremes.max_width); /* ---- STEP 4: Finally, calculate the widths. ---- */ sub_status = g_new (gint, sub->num_subs); /* First, assume that all columns have the same width. */ for (i = 0; i < sub->num_subs; i++) sub_status[i] = AT_NORM; do { DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3, " columns: "); /* Calculate the normal width, and the number of columns at this width. */ width_norm_cols = avail_width; num_norm_cols = num_cols; for (i = 0; i < sub->num_subs; i++) { if (sub_status[i] != AT_NORM) { num_norm_cols -= (sub->subs[i].end_col - sub->subs[i].start_col); if (sub_status[i] == AT_MIN) width_norm_cols -= sub_extremes[i].min_width; else width_norm_cols -= sub_extremes[i].max_width; } DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3, "%c", "ian"[sub_status[i]]); } DEBUG_MSG (DEBUG_WIDTH_LEVEL + 3, "\n"); DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " norm: %d, width = %d\n", num_norm_cols, width_norm_cols); /* * Iteratively, test for minimum/maximum, and correct the * status. As soon as one test fails, the status is * changed, and the iteration starts again from the * beginning. */ success = TRUE; for (i = 0; success && i < sub->num_subs; i++) { cols_per_sub = (sub->subs[i].end_col - sub->subs[i].start_col); switch (sub_status[i]) { case AT_NORM: /* Columns at normal width must between min and max. */ if (width_norm_cols * cols_per_sub < num_norm_cols * sub_extremes[i].min_width) { DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " sub %d at min\n", i); sub_status[i] = AT_MIN; success = FALSE; } else if (width_norm_cols * cols_per_sub > num_norm_cols * sub_extremes[i].max_width) { DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " sub %d at max\n", i); sub_status[i] = AT_MAX; success = FALSE; } break; case AT_MIN: /* If the column is at min, the the normal width (which has been changed), must tested against the min. */ if (width_norm_cols * cols_per_sub > num_norm_cols * sub_extremes[i].min_width) { DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " sub %d at norm\n", i); sub_status[i] = AT_NORM; success = FALSE; } break; case AT_MAX: /* If the column is at max, the the normal width (which has been changed), must tested against the max. */ if (width_norm_cols * cols_per_sub < num_norm_cols * sub_extremes[i].max_width) { DEBUG_MSG (DEBUG_WIDTH_LEVEL + 2, " sub %d at norm\n", i); sub_status[i] = AT_NORM; success = FALSE; } break; } } } while (!success); /* ---- STEP 5: Apply calculate the widths. ---- */ rest_n= num_norm_cols; rest_w = width_norm_cols; for (i = 0; i < sub->num_subs; i++) { if (sub_status[i] == AT_MIN) sub_width = sub_extremes[i].min_width; else if (sub_status[i] == AT_MAX) sub_width = sub_extremes[i].max_width; else { cols_per_sub = (sub->subs[i].end_col - sub->subs[i].start_col); sub_width = rest_w * cols_per_sub / rest_n; rest_w -= sub_width; rest_n-= cols_per_sub; } Dw_table_sub_calc_col_widths (&sub->subs[i], sub_width, total_width); } g_free (sub_status); } g_free (sub_extremes); } } static DwIterator *Dw_table_iterator (DwWidget *widget, gint mask, gboolean at_end) { DwIteratorInt *it = g_new (DwIteratorInt, 1); it->it.widget = widget; it->it.mask = mask; it->it.next = Dw_table_iterator_next; it->it.prev = Dw_table_iterator_prev; it->it.clone = p_Dw_iterator_clone_std_int; it->it.compare = p_Dw_iterator_compare_std_int; it->it.free = p_Dw_iterator_free_std; it->it.highlight = p_Dw_iterator_highlight_std; it->it.get_allocation = p_Dw_iterator_get_allocation_std_only_widgets; if (at_end) { it->it.content.type = DW_CONTENT_END; it->pos = DW_TABLE(widget)->num_children; } else { it->it.content.type = DW_CONTENT_START; it->pos = -1; } return (DwIterator*)it; } static gboolean Dw_table_iterator_next (DwIterator *it) { DwTable *table = DW_TABLE (it->widget); DwIteratorInt *ii = (DwIteratorInt*)it; DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_table_iterator_next: %d of %d\n", ii->pos, table->num_children); if (it->content.type == DW_CONTENT_END) return FALSE; /* tables only contain widgets: */ if ((it->mask & DW_CONTENT_WIDGET) == 0) { it->content.type = DW_CONTENT_END; return FALSE; } do { ii->pos++; if (ii->pos >= table->num_children) { it->content.type = DW_CONTENT_END; return FALSE; } } while (table->children[ii->pos] == NULL || table->children[ii->pos]->type != DW_TABLE_CELL); it->content.type = DW_CONTENT_WIDGET; it->content.data.widget = table->children[ii->pos]->data.cell.widget; return TRUE; } static gboolean Dw_table_iterator_prev (DwIterator *it) { DwTable *table = DW_TABLE (it->widget); DwIteratorInt *ii = (DwIteratorInt*)it; if (it->content.type == DW_CONTENT_START) return FALSE; DEBUG_MSG (DEBUG_ITERATOR_LEVEL, "Dw_table_iterator_prev: %d of %d\n", ii->pos, table->num_children); /* tables only contain widgets: */ if ((it->mask & DW_CONTENT_WIDGET) == 0) { it->content.type = DW_CONTENT_START; return FALSE; } do { ii->pos--; if (ii->pos < 0) { it->content.type = DW_CONTENT_START; return FALSE; } } while (table->children[ii->pos] == NULL || table->children[ii->pos]->type != DW_TABLE_CELL); it->content.type = DW_CONTENT_WIDGET; it->content.data.widget = table->children[ii->pos]->data.cell.widget; return TRUE; }