/* Copyright 1997 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /***************************************************/ /* File : Tables.c */ /* */ /* Purpose: Table handling functions. */ /* */ /* Author : T.Cheal, adapted by A.D.Hodgkinson */ /* */ /* History: 18-Mar-97: Datestamp on code received */ /* from T.Cheal. */ /* 12-Apr-97: Tidied up, more comments, */ /* and from here on continued */ /* development of the code */ /* content. */ /* 18-Jun-97: Moved several functions */ /* that were in the */ /* reformatter here, as this */ /* is a more appropriate */ /* location for them. */ /***************************************************/ #include <stdlib.h> #include <string.h> #include "swis.h" #include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */ #include "wimp.h" #include "wimplib.h" #include "event.h" #include "toolbox.h" #include "svcprint.h" #include "Global.h" #include "FromROSLib.h" #include "MiscDefs.h" #include "Utils.h" #include "Memory.h" #include "Redraw.h" #include "Reformat.h" #include "TokenUtils.h" #include "Toolbars.h" #include "Tables.h" /* Local definitions */ #define Tables_Hourglass_WidthingTableDelay 100 #define Tables_Hourglass_FreeingTablesDelay 25 #define CorrectRowSpan(D,p) ((D)->RowSpan > (p)->RowSpan ? (p)->RowSpan : (D)->RowSpan) #define CorrectColSpan(D,p) ((D)->ColSpan > (p)->ColSpan ? (p)->ColSpan : (D)->ColSpan) /* Statics */ static int width_table_leds = 1; /* Static function prototypes */ static void tables_align_contents(browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int Row, int Column); /*************************************************/ /* tables_count_table() */ /* */ /* Works out the number of rows and columns in a */ /* given table, and writes back into the given */ /* table_stream structure these values plus */ /* pointers to two allocated blocks of memory */ /* that can hold one integer for each row and */ /* column. */ /* */ /* Parameters: Pointer to a browser_data struct, */ /* which is the parent of the table; */ /* */ /* Pointer to a table_stream struct */ /* relevant to the table. */ /* */ /* Returns: Writes to the given table_stream */ /* struct, so this must not be in a */ /* read only area. The ColSpan and */ /* RowSpan fields are filled in with */ /* the number of columns and rows */ /* respectively, and the ColOffs and */ /* RowOffs fields are filled with */ /* the pointers to the arrays of */ /* Cols and Rows ints, respectively. */ /*************************************************/ void tables_count_table(browser_data * b, table_stream * p) { table_row * R; table_headdata * D; int I; int * RowSpill; int ColCount, RowCount; int Cols, Rows; /* Avoid compiler warning - b may be used in later revisions of this code */ b = b; #ifdef TRACE if (tl & (1u<<20)) Printf("\ntables_count_table entered: 0x%x\n",(int) P); #endif /* How many rows are there? 'Rows' will keep count of the current row */ /* number according to the table structure, 'RowCount' will represent */ /* a maximum value of the current row number, plus any defined */ /* rowspan value for the cell. */ Rows = 0; RowCount = 0; R = p->List; while (R) { D = R->List; if (!D) { if (Rows + 1 > RowCount) RowCount = Rows + 1; } else { while (D) { switch (D->Tag) { case TagTableData: case TagTableHead: { int this_count; if (D->RowSpan > 1) this_count = Rows + D->RowSpan; else this_count = Rows + 1; if (this_count > RowCount) RowCount = this_count; } break; } D = D->Next; } } Rows ++; R = R->Next; } /* The RowSpill array is used to hold the number of columns */ /* defined in other rows, when a cell 'spills' onto those */ /* other rows by having a rowspan value specified for it. */ /* */ /* When scanning rows that a cell above has spilled into, */ /* the table structures themselves will skip over that */ /* column - as a cell above that row has already defined */ /* contents here. To be able to work out how many actual */ /* columns there are, then, need to fill this array with */ /* the number of columns any such cells span. */ /* */ /* If not clear already, hopefully it'll become more so in */ /* the code later which actually deals with the array. */ RowSpill = (int *) memory_alloc_and_set(RowCount * (sizeof(int)), 0); /* If allocation fails, report the error and jump out to */ /* polling - can't possibly continue here. */ if (!RowSpill) show_error_cont(make_no_table_memory_error(1)); /* Now restart the process starting the */ /* column count as the total of all row */ /* spillage to date. */ Rows = 0; Cols = 0; R = p->List; /* Move through all rows */ while (R) { #ifdef TRACE if (tl & (1u<<20)) Printf("tables_count_table row: 0x%x\n",(int) R); #endif ColCount = RowSpill[Rows]; D = R->List; /* Move through all columns in the current row */ while (D) { #ifdef TRACE if (tl & (1u<<20)) Printf("tables_count_table headdata: 0x%x\n",(int) D); #endif switch (D->Tag) { /* Items which if present, define a column */ case TagTableData: case TagTableHead: { if (D->ColSpan) ColCount += D->ColSpan; else ColCount ++; /* If this column is meant to span several rows, */ /* fill in the RowSpill arrays for all the rows */ /* it spans (except the current one) with the */ /* number of columns this tag defines. So when */ /* we get to those rows, they'll already have */ /* some columns counted in from previous columns */ /* which had a rowspan specified. */ if (D->RowSpan > 1) { int index; for (I = 1; I < RowCount; I++) { index = Rows + I; if (index < RowCount) /* Stupidity check */ { if (D->ColSpan) RowSpill[index] += D->ColSpan; else RowSpill[index] ++; } } } } break; } D = D->Next; } /* Keep Cols up to date as the highest column count */ /* encountered so far, and move to the next row. */ if (Cols < ColCount) Cols = ColCount; Rows ++; R = R->Next; } /* Free the temporary rowspill array */ free(RowSpill); #ifdef TRACE if (tl & (1u<<20)) Printf("Rows, Cols: %d, %d\n", RowCount, Cols); #endif /* Fill in the table_stream structure with the number of rows */ /* and columns, and pointers to two allocated blocks of Rows */ /* and Cols integers. */ /* */ /* Note that if a table cell specifies colspans or rowspans */ /* that extend into rows or columns that would otherwise not */ /* be defined anywhere, these extra rows/cols will **NOT** be */ /* included in the count here. This is why loops dealing with */ /* such values *must* be very careful not to run off the end */ /* of arrays and suchlike! */ p->ColSpan = Cols; p->RowSpan = RowCount; /* May be reformatting the table, in which case there will already */ /* be arrays attached to p. */ if (p->ColOffs) { HtmlFree(p->ColOffs); p->ColOffs = NULL; } if (p->RowOffs) { HtmlFree(p->RowOffs); p->RowOffs = NULL; } /* Now allocate the array, in the context of the HStream list. */ /* When the list is eventually discarded this will automatically */ /* be discard too. */ if (p->ColSpan) { if (p->ColSpan) p->ColOffs = HtmlMalloc(p->ColSpan * sizeof(int), p); if (!p->ColOffs) show_error_cont(make_no_table_memory_error(10)); } if (p->RowSpan) { p->RowOffs = HtmlMalloc(p->RowSpan * sizeof(int), p); if (!p->RowOffs) show_error_cont(make_no_table_memory_error(11)); } return; } /*************************************************/ /* tables_position_table() */ /* */ /* Fills in the RowOffs and ColOffs fields for */ /* the given table and all cells in a given */ /* table, taking account of multiple row and */ /* column spanning. These are integers which, */ /* for a given cell, give the number of rows and */ /* columns from the top left of the table */ /* (starting at (0, 0)) that the cell is offset */ /* by. */ /* */ /* Parameters: Pointer to a browser_data struct, */ /* which is the parent of the table; */ /* */ /* Pointer to a table_stream struct */ /* relevant to the table. */ /*************************************************/ void tables_position_table(browser_data * b, table_stream * p) { table_row * R; table_headdata * D; int Cols, Rows, ColCount; int * ColOffs; int * RowOffs; unsigned char * RowSpill; if (!p->ColSpan || !p->RowSpan) return; /* Avoid compiler warning - b may be used in later revisions of this code */ b = b; #ifdef TRACE if (tl & (1u<<20)) Printf("tables_position_table entered: 0x%x\n",(int) P); #endif Rows = 0; RowSpill = (unsigned char *) memory_alloc_and_set(p->ColSpan * p->RowSpan, 0); /* Enough for all rows & cols */ ColCount = p->ColSpan; ColOffs = p->ColOffs; RowOffs = p->RowOffs; /* If allocation fails, report the error and jump out to */ /* polling - can't possibly continue here. */ if (!RowSpill) show_error_cont(make_no_table_memory_error(2)); R = p->List; /* Loop through all rows */ while (R && Rows < p->RowSpan) { Cols = 0; /* Look through the RowSpill array for this row, skipping */ /* any columns which have already been done - marked by a */ /* non-zero value in the array. */ while (RowSpill[Rows * ColCount + Cols]) Cols++; /* Cols now points at a column that hasn't been positioned yet. */ if (R->Width) { if (ColOffs[Cols] < R->Width) ColOffs[Cols] = R->Width; } D = R->List; /* Loop through all columns pointed to by this row */ while (D && Cols < ColCount) { int C, R, I, J; switch (D->Tag) { case TagTableData: case TagTableHead: { #ifdef TRACE if (tl & (1u<<20)) Printf("tables_position_table: In scan, D (0x%x) RowOffs = %d, ColOffs = %d\n", D, Rows, Cols); #endif D->ColOffs = Cols; D->RowOffs = Rows; /* This is where this cell is, honest */ if (D->ColSpan) C = CorrectColSpan(D, p); else C = 1; if (D->RowSpan) R = CorrectRowSpan(D, p); else R = 1; /* Loops contain a stupidity check... */ for (I = Cols; I <= Cols + C - 1; I++) { for (J = Rows; J <= Rows + R - 1; J++) { /* For all columns and rows that this cell covers, mark that these */ /* have been dealt with in the RowSpill array. */ if (I < p->ColSpan && J < p->RowSpan) RowSpill[J * ColCount + I] = 0xff; } } Cols += C; /* Ensure that Cols is advanced appropriately to get past */ /* anything marked as dealt with by the above code. */ while (RowSpill[Rows * ColCount + Cols]) Cols++; } break; } D = D->Next; } Rows ++; R = R->Next; } /* Free up the temporary array and exit */ free(RowSpill); } /*************************************************/ /* tables_init_table() */ /* */ /* For a new, uninitialised array of */ /* reformat_cell structures, fills in any */ /* initial information prior to sizing and */ /* positioning (as in, coordinate origins) of */ /* the cells. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the tables; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the cell array. */ /* */ /* Assumes: At least tables_count_table must */ /* have been called to work out how */ /* many rows and columns there are. */ /*************************************************/ void tables_init_table(browser_data * b, table_stream * table, reformat_cell * cellarray) { int i, size; size = table->ColSpan * table->RowSpan; if (!size) return; memset(&cellarray[0], 0, sizeof(reformat_cell) * size); for (i = 0; i < size; i++) { cellarray[i].table = table; cellarray[i].minwid = -1; cellarray[i].maxwid = -1; } return; } /*************************************************/ /* tables_width_table() */ /* */ /* Works out the widths in millipoints for all */ /* cells in a given table. This is called by the */ /* reformatter routines as they try to find the */ /* width of various page elements. To find out */ /* the width of, and finally format a particular */ /* cell, this function then itself calls the */ /* reformatter. This can of course then call */ /* back here for nested tables - i.e. this is a */ /* recursive function, though it's not */ /* immediately obvious as the callers and */ /* callees are spread over a wide area of code. */ /* */ /* Because of the way tables define column */ /* widths, for each cell three reformats take */ /* place - minimum width find, maximum width */ /* find, and final decided width. */ /* */ /* Coupled with all the juggling of those maxs */ /* and mins - taking into account anything that */ /* was specified in the HTML - this is something */ /* of a monster function and it is certainly not */ /* efficient at present. Hopefully this will */ /* improve in a future interation of the tables */ /* code... */ /* */ /* Parameters: 1 for a top level call, else 0 if */ /* being called as part of a nested */ /* tables reformat session; */ /* */ /* Pointer to a browser_data struct, */ /* which is the parent of the table; */ /* */ /* Pointer to a table_stream struct */ /* relevant to the table; */ /* */ /* The available width for the table */ /* in millipoints; */ /* */ /* Pointer to an array of points to */ /* reformat_cell structures which */ /* will hold the final table layout; */ /* */ /* Flags as for reformat_token_width */ /* - needed as the widthing routines */ /* need to know if the widest a */ /* table can be is being found or */ /* not (for nested tables - avoids */ /* nested tables saying they're */ /* 100% wide, say, ending up huge). */ /* */ /* Returns: The table width, in millipoints. */ /*************************************************/ int tables_width_table(int toplevel, browser_data * b, table_stream * p, int available_width, reformat_cell * cellarray, unsigned int flags) { table_row * R; table_headdata * D; int I; // S, T; // For code commented out below int * ColOffs; int finding_widest; int cellmax = p->ColSpan * p->RowSpan; int rowcount = 0; int real_width, new_width; int min_table_width, max_table_width; int display_width; int * mins, * maxs; int * overmins, * overmaxs; int * pcwidths; table_headdata ** usedcells; int have_colspans = 0; int have_percents = 0; int have_pixels = 0; int have_checked = 0; int cellspacingos = p->cellspacing * 2; /* 1 'web pixel' = 2 OS units */ int cellspacingmp; int tbordmp; // Trying to work out why the final 'are we underwidth' pass is needed, // but due to code freeze (29/01/1998) have to put this on hold... :-( // // Printf("Table %p, aw %d, flags %x\n",p,available_width/400,flags); #ifdef TRACE if (tl & (1u<<20)) Printf("tables_width_table entered: 0x%x %d (%d OS) 0x%x\n", (int) P, available_width, available_width / 400, cellarray); #endif if (!cellmax) return 1600; convert_to_points(cellspacingos, &cellspacingmp); convert_to_points(TABLE_BORDER(p) * 2, &tbordmp); /* 1 'web pixel' = 2 OS units */ /* We may need the browser page width available in millipoints quite */ /* frequently, so do the conversion just the once for speed. */ convert_to_points(b->display_width, &display_width); /* If we've been given a very large value for available_width, we're */ /* trying to find the widest a table can be. */ /* */ /* If 'finding_widest' is 2, the available_width field is valid even */ /* though we're finding the greatest width because the table gave an */ /* absolute width in pixels. */ if (flags & Reformatter_FindingWidest) finding_widest = 1; else finding_widest = 0; /* Deal with specific non-zero table widths */ if (TABLE_HAS_WIDTH(p) && TABLE_WIDTH(p)) { int width = 0, dx; switch (TABLE_WIDTH_UNITS(p)) { default: case UNITS_PIXELS: { convert_to_points(wimpt_dx(), &dx); width = TABLE_WIDTH(p) * dx; if (finding_widest) finding_widest = 2; } break; case UNITS_PERCENT: { /* We may be finding the widest the table can be, and if so we shouldn't */ /* try to calculate the percentage as this will give a coordinate */ /* overflow. */ if (!finding_widest) width = (TABLE_WIDTH(p) * available_width) / 100; } break; } if (width) available_width = width; } /* Allocate various arrays. */ /* First, two arrays of ints to hold the minimum and */ /* maximum widths of the table's cells. */ maxs = (int *) memory_alloc_and_set(sizeof(int) * cellmax, 0); if (!maxs) { show_error_ret(make_no_table_memory_error(3)); return 0; } mins = (int *) memory_alloc_and_set(sizeof(int) * cellmax, 0); if (!mins) { free(maxs); show_error_ret(make_no_table_memory_error(4)); return 0; } /* Allocate arrays to hold min and max widths of columns */ /* worked out overall. */ overmaxs = (int *) memory_alloc_and_set(sizeof(int) * p->ColSpan, 0); if (!overmaxs) { free(mins); free(maxs); show_error_ret(make_no_table_memory_error(5)); return 0; } overmins = (int *) memory_alloc_and_set(sizeof(int) * p->ColSpan, 0); if (!overmins) { free(overmaxs); free(mins); free(maxs); show_error_ret(make_no_table_memory_error(6)); return 0; } /* Array to hold the stated or inferred percentage widths of cells */ pcwidths = (int *) memory_alloc_and_set(sizeof(int) * cellmax, 0); if (!pcwidths) { free(overmins); free(overmaxs); free(mins); free(maxs); show_error_ret(make_no_table_memory_error(7)); return 0; } /* Array to hold addresses of the cells used to decide the overall */ /* column widths - the attributes the HTML uses to define these */ /* cells can alter the final stage table widthing. The first 'row' */ /* of the array is used for the addresses of min values, the */ /* second for addresses of max values. */ usedcells = (table_headdata **) memory_alloc_and_set(sizeof(table_headdata *) * p->ColSpan * 2, 0); if (!usedcells) { free(pcwidths); free(overmins); free(overmaxs); free(mins); free(maxs); show_error_ret(make_no_table_memory_error(7)); return 0; } if (finding_widest != 1) { /* Decrease available width according to cell spacing constraints */ if (p->ColSpan) available_width -= cellspacingmp * (p->ColSpan + 1); /* Decrease available width according to overall table border */ available_width -= tbordmp * 2; } /* This can get s l o w... */ _swix(Hourglass_Start, _IN(0), Tables_Hourglass_WidthingTableDelay); /* Treat the p->ColOffs field as an array of millipoint offsets */ /* from the left of the table for each column. */ ColOffs = p->ColOffs; /* First pass - find min and max widths of the columns. */ R = p->List; /* p is a table_stream, so R now points to the first table_row */ while (R && rowcount < p->RowSpan) { /* Preliminaries */ int row_pc_total; #ifdef TRACE if (tl & (1u<<20)) Printf("tables_width_table: Row struct 0x%x\n",(int) R); #endif /* This is used to keep track of the total of */ /* width specifiers in the row */ row_pc_total = 0; have_percents = 0; have_pixels = 0; /* Flick the LEDs around a bit to show progress. Not much point */ /* trying to do %s because of nested tables. */ _swix(Hourglass_LEDs, _INR(0,1), 3, width_table_leds); width_table_leds ^= 3; /* R is a table_row, so D now points to a table_headdata */ D = R->List; while (D && D->ColOffs < p->ColSpan) { #ifdef TRACE if (tl & (1u<<20)) Printf("tables_width_table: Head struct, tag 0x%x 0x%x\n",(int) D,(int) D->Tag); #endif switch (D->Tag) { case TagTableData: case TagTableHead: { int width = -1; int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; if (cellnumber < cellmax) { /* First stage of dealing with specific cell widths, and find cell size limits from reformatter */ #ifdef TRACE if (tl & (1u<<20)) Printf("tables_width_table: Dealing with column tag 0x%x\n",(int) D->Tag); #endif /* Need a fair bit of extra work if there are any */ /* column spanning cells, but can avoid another */ /* scan of the table if we flag whether or not */ /* there are any present. */ if (D->ColSpan > 1) have_colspans = 1; /* This cell starts with no actual or implied percentage */ /* sizespecifier */ pcwidths[cellnumber] = -1; /* Does the cell specify a width? */ if (TD_HAS_WIDTH(D)) { switch (TD_WIDTH_UNITS(D)) { default: case UNITS_PIXELS: { int dx; have_pixels ++; convert_to_points(wimpt_dx(), &dx); /* Millipoints per pixel in dx */ width = TD_WIDTH(D) * dx; } break; /* Don't allow percentage specifiers on a row to exceed 100 */ case UNITS_PERCENT: { have_percents ++; if (row_pc_total == 100) pcwidths[cellnumber] = 0; else if (TD_WIDTH(D) + row_pc_total > 100) pcwidths[cellnumber] = 100 - row_pc_total, row_pc_total = 100; else pcwidths[cellnumber] = TD_WIDTH(D), row_pc_total += TD_WIDTH(D); } break; } if (finding_widest != 1) { /* Deal with garbage like 'TD WIDTH="2000"' - if a cell */ /* wants to be wider than the table can be, ignore the */ /* width specifier completely. */ if (width > available_width) width = -1; } else { /* Because this is a scan to find the widest limit of a table, */ /* we can't use available_width to limit the item; so instead */ /* use the browser's display width as a second best option. */ if (width > display_width) width = -1; } } /* Unfortunately, still need to find the minimum size. So bang */ /* goes any possible speed up from knowing it in advance. If */ /* only web page designers could count... */ reformat_find_cell_limits(toplevel, b, (HStream *) D->List, p, cellarray, D->RowOffs, D->ColOffs, &mins[cellnumber], &maxs[cellnumber]); /* If the table cell specified a particular width, and it's */ /* greater than the minimum width size, then the maximum */ /* cell size should be considered as the given width value. */ /* Otherwise, the maximum size should be as close as */ /* possible - i.e., the same as the minimum size. */ /* */ /* This is so that table cells containing, say, a column of */ /* images, work - if there are no BRs between the images, */ /* then the max width is large. But we shouldn't let the */ /* cell get wider than its specified width (if it has one), */ /* or images that are meant to be in a column could appear */ /* side by side. */ if (width >= 0) { if (width > mins[cellnumber]) maxs[cellnumber] = width; else maxs[cellnumber] = mins[cellnumber]; /* (Keep it is small as possible) */ } /* Closure of 'if (cellnumber < cellmax)' */ } } break; /* Closure of 'switch (D->Tag)' */ } D = D->Next; /* Closure of 'while (D && ...)' */ } /* Right, we've done that row, marking out any cells which had */ /* percentage specifiers (and cropping values where the total */ /* exceeds 100%). If there were any % specifiers on the row, */ /* we need to width them and deal with any effects this has on */ /* other cells in the row. */ if (have_percents > 0) /* have_percents holds the number of % specified cells */ { int min_pc = 100; int explicit_pc = 0; int pc_size = 0; int max_row = 0; int min_row = 0; int do_inference = 0; /* If the whole row contains % cells, MSIE / NN seem to find the smallest */ /* non-zero % specifier in the row. If there is more than one cell with */ /* this specifier, find the one with the greatest max size value. The */ /* width of the chosen cell, coupled with the percentage width specifier */ /* on the cell, gives you the width of 1% by division. The other cells */ /* can thus be widthed. */ /* */ /* If the row has some unspecified cells, these aquire inferred */ /* percentage widths based on 100% minus the total of all percentage */ /* specified cells on that row, divided by the number of unspecified */ /* cells on the row. */ /* */ /* If there are any pixel specified values, it gets complex. First, if */ /* the total of explicit % specified cells is < 100, then infer % sizes */ /* on the pixel specified cells. You *don't* use this for widthing - you */ /* use their pixel values - but you *do* use it as part of the 'what is */ /* 1%' algorithm as described above. However, if the total of explicit % */ /* specified cells is greater than 100%, then the meaning of 1% will now */ /* change. It now becomes 1/100th of the total available width for the */ /* row, after all the pixel specified cell widths have been subtracted. */ /* */ /* First, do we have any unspecified cells to infer percentages on? We */ /* can look for this at the same time as we look for the smallest */ /* percentage specifier. */ D = R->List; while (D && D->ColOffs < p->ColSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; if (cellnumber < cellmax) { if (pcwidths[cellnumber] == -1) { /* If everything is either %s of pixels, count the */ /* pixels (pcwidths[] = -1, so this doesn't have a */ /* percentage specifier) */ if (p->ColSpan == have_percents + have_pixels) { max_row += maxs[cellnumber]; min_row += mins[cellnumber]; } /* Otherwise, there is a mixture of %s, pixels (maybe) */ /* and unspecified cells - only count the latter. */ else if (!TD_HAS_WIDTH(D)) { max_row += maxs[cellnumber]; min_row += mins[cellnumber]; } } } } break; } D = D->Next; } D = R->List; while (D && D->ColOffs < p->ColSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; if (cellnumber < cellmax) { int infer = pcwidths[cellnumber]; /* If we have any cells with no percentage specifiers, then there are two */ /* cases; the other cells all have pixel specifiers on them, or only some */ /* of the cells do. In the former case, infer percentage specifiers on */ /* all of the other cells. In the latter, only infer percentages on the */ /* cells that don't specify a width in any other way. */ /* */ /* We need to find the lowest % specifier, and of those in the row, the */ /* one with the greatest maximum width (if there are several with the */ /* same minimum % value). For this, the cells we infer %s onto get a */ /* distribution of (100% - explicitly specifieds) / (number of cells we */ /* are inferring sizes on); i.e. a linear distribution. So this is used */ /* to work out what 1% means. However, for later calculation of the sizes */ /* based on this value of 1%, we infer %s in proportion to the sizes of */ /* the columns in question. That's why the 'infer' variable is used at */ /* first, but then there's a second bit of code to fill in pcwidths[] for */ /* an inferred cell with a proportionally scaled, rather than linearly */ /* scaled value. */ if (infer == -1) { if (p->ColSpan == have_percents + have_pixels) { int div = p->ColSpan - have_percents; if (!div) div = 1; infer = (100 - row_pc_total) / div; } else if (!TD_HAS_WIDTH(D)) { int div = p->ColSpan - (have_percents + have_pixels); if (!div) div = 1; infer = (100 - row_pc_total) / div; } } else { explicit_pc += infer; } /* Remember the smallest non-zero value */ if ( infer > 0 && infer <= min_pc ) { if (min_pc == infer) { if (maxs[cellnumber] > pc_size) { pc_size = maxs[cellnumber]; if (pcwidths[cellnumber] == -1) do_inference = 0; else do_inference = 1; } } else { min_pc = infer; pc_size = maxs[cellnumber]; if (pcwidths[cellnumber] == -1) do_inference = 0; else do_inference = 1; } } } } break; } D = D->Next; } if (do_inference) { D = R->List; while (D && D->ColOffs < p->ColSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; if (cellnumber < cellmax) { /* The *actual* inferred value is based on the cell size */ if (pcwidths[cellnumber] == -1) { if (p->ColSpan == have_percents + have_pixels) { pcwidths[cellnumber] = ((100 - row_pc_total) * maxs[cellnumber]) / (max_row ? max_row : 1); } else if (!TD_HAS_WIDTH(D)) { pcwidths[cellnumber] = ((100 - row_pc_total) * maxs[cellnumber]) / (max_row ? max_row : 1); } } } } break; } D = D->Next; } } /* If we have less than 100% explicitly specified total in the row, */ /* find the smallest explicit or inferred non-zero % specifier cell */ /* (and if there are several with this value, find the one with the */ /* greatest maximum content width), and use this to find what 1% */ /* should be. */ if (explicit_pc < 100) { /* We now know what min_pc percent is in OS units (pc_size), and thus */ /* what 1 percent is in OS units. We can thus assign actual widths to */ /* the columns and store them in overmaxs array if larger than the */ /* existing value. */ D = R->List; while (D && D->ColOffs < p->ColSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; int pc_based_width; if (cellnumber < cellmax) { if (!TD_HAS_WIDTH(D) || TD_WIDTH_UNITS(D) != UNITS_PIXELS) { pc_based_width = (pcwidths[cellnumber] * pc_size) / (min_pc ? min_pc : 1); if (pc_based_width > maxs[cellnumber]) maxs[cellnumber] = pc_based_width; } } } break; } D = D->Next; } } /* If, instead, we do have pixel specifiers, 1% comes from the width */ /* left over after the pixel specifier widths have been subtracted */ /* from the whole width available to the row. */ /* */ /* If we're finding the widest a table can be, just leave the full */ /* width from the reformatter for all cells - 1% of 'very very wide' */ /* doens't make much sense... */ else if (finding_widest != 1) { int remaining = available_width; /* Find how much is left for the percentage cells */ D = R->List; while (D && D->ColOffs < p->ColSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; if (cellnumber < cellmax) { /* If the cell has no specified or inferred percentage width, */ /* subtract the cell's *minimum* size from the remaining */ /* available width - the remainder thus represents a maximum */ /* value. */ if (pcwidths[cellnumber] < 0) remaining -= mins[cellnumber]; if (remaining < 0) remaining = 0; } } break; } D = D->Next; } /* Now calculate all of the percentage specified widths based on this */ D = R->List; while (D && D->ColOffs < p->ColSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; int pc_based_width; if (cellnumber < cellmax) { if (TD_HAS_WIDTH(D) && TD_WIDTH_UNITS(D) == UNITS_PERCENT) { pc_based_width = (remaining * pcwidths[cellnumber]) / 100; if (pc_based_width > maxs[cellnumber]) maxs[cellnumber] = pc_based_width; } } } break; } D = D->Next; } } } /* Carry on for the other rows. */ rowcount ++; R = R->Next; /* Closure of 'while (R && ...)' */ } /* Debug output to show mins and maxs */ #ifdef TRACE if (tl & (1u<<20)) { int rowc,colc; Printf("\nTable %p, size before COLSPAN/ROWSPAN: mins then maxs\n", p); for(rowc=0;rowc<p->RowSpan;rowc++) { Printf("%d: ",rowc); for(colc=0;colc<p->ColSpan;colc++) { Printf("%d (%d) ",mins[rowc*p->ColSpan+colc],mins[rowc*p->ColSpan+colc]/400); } Printf("\n"); } Printf("\n"); for(rowc=0;rowc<p->RowSpan;rowc++) { Printf("%d: ",rowc); for(colc=0;colc<p->ColSpan;colc++) { Printf("%d (%d) ",maxs[rowc*p->ColSpan+colc],maxs[rowc*p->ColSpan+colc]/400); } Printf("\n"); } Printf("\n"); } #endif /* mins and maxs arrays consequently now hold the min and max */ /* sizes of all cells (not accounting for row or column */ /* spanning, though). */ /* */ /* We now need to work out the overall greatest minimum and */ /* maximum width for each column. Any cells specifying a */ /* colspan > 1 are ignored in this, as they will have a large */ /* width (they span several cells, after all). We're only */ /* interested in single cells for now. */ /* */ /* We follow the table structure to do this as we must record */ /* the addresses of cells used in the final width, and want */ /* to be able to skip those cells that specify a colspan. */ R = p->List; while (R) { int col; D = R->List; while (D && D->ColOffs < p->ColSpan) { col = D->ColOffs; switch (D->Tag) { case TagTableData: case TagTableHead: { int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; if (cellnumber < cellmax && col < p->ColSpan) { /* If the cell doesn't span more than 1 column and has a min or max */ /* value wider than the cells in the same column above it, use the */ /* min or max values of this cell instead. Record the cell in the */ /* usedcells array accordingly. */ if (D->ColSpan < 2) { if (mins[cellnumber] > overmins[col]) { overmins [col] = mins[cellnumber]; usedcells[col] = D; } if (maxs[cellnumber] > overmaxs[col]) { table_headdata * lc = usedcells[p->ColSpan + col]; /* Only use the maximum value if the cell isn't 'locked' by one */ /* already in use that specifies a pixel size */ if (TD_HAS_WIDTH(lc) && TD_WIDTH_UNITS(lc) == UNITS_PIXELS) { /* May be forced to use this cell if the *minimum* value */ /* ends up greater than the maximum! */ if (mins[cellnumber] > overmaxs[col]) { overmaxs [col] = mins [cellnumber]; usedcells[p->ColSpan + col] = usedcells[col]; } } else { overmaxs [col] = maxs[cellnumber]; usedcells[p->ColSpan + col] = D; } } } } } break; } D = D->Next; } R = R->Next; } /* Debug output to show the results so far... */ #ifdef TRACE if (tl & (1u<<20)) { int colc; Printf("\nTable %p, overall 1: mins / maxs\n", p); for(colc=0;colc<p->ColSpan;colc++) { Printf("%d (%d) ",overmins[colc],overmins[colc]/400); } Printf("\n"); for(colc=0;colc<p->ColSpan;colc++) { Printf("%d (%d) ",overmaxs[colc],overmaxs[colc]/400); } Printf("\n\n"); } #endif /* Now deal with cells with colspans > 1. For each case, add up */ /* the width of the spanned cells from the array just calculated. */ /* If these work out more than the width calculated for that */ /* cell, then nothing needs to be done; else distribute the */ /* difference over the spanned cells. */ if (have_colspans) { R = p->List; rowcount = 0; while (R && rowcount < p->RowSpan) { D = R->List; while (D && D->ColOffs < p->ColSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { if (D->ColSpan > 1) { int currcol; int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; int spanned_total_mins = 0; int spanned_total_maxs = 0; int px_specifiers = 0; int pc_specifiers = 0; if (cellnumber < cellmax) { /* Count the spanned columns' minimum and maximum widths, and */ /* gather some statistics on the width specifiers on the cells */ /* used to arrive at those widths. */ for ( currcol = D->ColOffs; currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan; currcol ++ ) { spanned_total_mins += overmins[currcol]; spanned_total_maxs += overmaxs[currcol]; if (usedcells[currcol + p->ColSpan]) { table_headdata * head = usedcells[currcol + p->ColSpan]; if (TD_HAS_WIDTH(head)) { if (TD_WIDTH_UNITS(head) == UNITS_PERCENT) pc_specifiers ++; else px_specifiers ++; } } } #ifdef TRACE if (tl & (1u<<20)) Printf("spanned total maxs, mins: %d, %d (%d, %d)\n", spanned_total_mins, spanned_total_maxs, spanned_total_mins/400, spanned_total_maxs/400); #endif /* If not big enough, distribute the difference over the columns. */ /* We can't start altering the minimum values here as when the */ /* distribution over the table width (if any) takes place, the */ /* total of the spanned columns may end up large enough. So we */ /* only increase the potential maximum values, and then must */ /* check after the 'final decision' on the column widths that all */ /* column spanning cells got allocated at least as much as they */ /* require. */ if (spanned_total_maxs < maxs[cellnumber] && D->ColSpan) { int max_diff = maxs[cellnumber] - spanned_total_maxs; int basic; int remainder; int distribute_over; /* If there are any pixel specified columns spanned, but they aren't */ /* all pixel specified, then distribute the width over the others */ /* instead. */ distribute_over = D->ColSpan - px_specifiers; basic = max_diff / (distribute_over ? distribute_over : D->ColSpan); remainder = max_diff - (basic * (distribute_over ? distribute_over : D->ColSpan)); for ( currcol = D->ColOffs; currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan; currcol ++ ) { if ( !distribute_over || !usedcells[currcol + p->ColSpan] || !TD_HAS_WIDTH(usedcells[currcol + p->ColSpan]) || TD_WIDTH_UNITS(usedcells[currcol + p->ColSpan]) != UNITS_PIXELS ) { if (remainder < 0) { overmaxs[currcol] += basic - 1; remainder ++; } else if (remainder > 0) { overmaxs[currcol] += basic + 1; remainder --; } else overmaxs[currcol] += basic; } /* Make sure the minimum value is less than the maximum! They */ /* may have swapped when we added to overmins - but of course */ /* can't do this check until afterwards, or overmaxs will get */ /* set to overmins, and then added to *again* by the above */ /* loop. */ if (overmaxs[currcol] < overmins[currcol]) overmaxs[currcol] = overmins[currcol]; } } /* Closure of 'if (cellnumber < cellmax)' */ } } } break; /* Closure of 'switch (D->Tag)' */ } D = D->Next; /* Closure of 'while (D && ...)' */ } rowcount ++; R = R->Next; /* Closure of 'while (R && ...)' */ } /* Closure of 'if (have_colspans)' */ } tables_width_table_assign_finals: /* (The colspan code below this section may jump back here) */ /* RFC 1942-like autolayout algorithm - if the *minimum* widths exceed */ /* the width we're trying to fit the table into, assign the min sizes */ /* anyway and (elsewhere) allow horizontal scrolling. */ /* */ /* NB, note the 'usedcells' array needs to have its two 'rows' of */ /* cells used for min and max values rationalised at the same time as */ /* the final widths are chosen. We'll put the final values in the */ /* first 'row' of usedcells. */ min_table_width = max_table_width = 0; for (I = 0; I < p->ColSpan; I++) min_table_width += overmins[I], max_table_width += overmaxs[I]; if (finding_widest != 1 && min_table_width > available_width) { /* Assign minimum widths. All of the cells used for minimum values */ /* are already stored in the first 'row' of usedcells, so we have */ /* no more work to do there in this case. */ #ifdef TRACE if (tl & (1u<<20)) Printf("\ntables_width_table: Forced to assign minimum widths:\n\n"); #endif for (I = 0; I < p->ColSpan; I++) { ColOffs[I] = overmins[I]; #ifdef TRACE if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, overmins[I] / 400); #endif } #ifdef TRACE if (tl & (1u<<20)) Printf("\n"); #endif } /* Otherwise, do the maximum widths fit in? If so, assign */ /* those maximum widths. */ else if (finding_widest == 1 || max_table_width <= available_width) { /* Assign maximum widths */ #ifdef TRACE if (tl & (1u<<20)) Printf("\ntables_width_table: Table fits, assigning maximum widths:\n\n"); #endif for (I = 0; I < p->ColSpan; I++) { ColOffs[I] = overmaxs[I]; /* Move all of the second 'row' of usedcells to the first row, as we're */ /* using the maximum value for all cells. */ usedcells[I] = usedcells[p->ColSpan + I]; #ifdef TRACE if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, overmaxs[I] / 400); #endif } #ifdef TRACE if (tl & (1u<<20)) Printf("\n"); #endif } /* Otherwise the maximum width of the table is greater than the available */ /* space, but the minimum width is less than it. */ /* */ /* According to RFC 1942, the following widthing algorithm is best. */ else { float d, W, D; /* Must be floats as ints will overflow, hmph. */ #ifdef TRACE if (tl & (1u<<20)) Printf("\ntables_width_table: Assigning variable column widths:\n\n"); #endif W = (float) available_width - (float) min_table_width; /* Difference between minimum table width and available width */ D = (float) max_table_width - (float) min_table_width; /* Difference between minimum and maximum table width */ if (D > 0) { for (I = 0; I < p->ColSpan; I++) { d = (float) overmaxs[I] - (float) overmins[I]; /* Difference between minimum and maximum column width */ if (d < 0) d = 0; /* The following makes columns with large differences between their minimum and maximum */ /* widths wider than columns with small differences. */ ColOffs[I] = overmins[I] + (int) (d * W / D); /* Hmm, hard to know what to do with 'usedcells' here. For now, move all */ /* of the second 'row' of usedcells to the first row, as if we're using */ /* the max values. */ usedcells[I] = usedcells[p->ColSpan + I]; #ifdef TRACE if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, ColOffs[I] / 400); #endif } } #ifdef TRACE else { if (tl & (1u<<20)) Printf("...can't, max_table_width = min_table_width\n"); } if (tl & (1u<<20)) Printf("\n"); #endif } /* If we have any colspan cells, we must ensure that the columns they span */ /* give at least as much space as the spanning cell requires. If not, */ /* distribute the space over the minimum size values of the spanned */ /* columns and rewidth the table overall. */ if (have_colspans && !have_checked) { int must_rewidth = 0; have_checked = 1; R = p->List; rowcount = 0; while (R && rowcount < p->RowSpan) { D = R->List; while (D && D->ColOffs < p->ColSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { if (D->ColSpan > 1) { int currcol; int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; int spanned_total = 0; int mins_total = 0; if (cellnumber < cellmax) { /* Count the spanned columns */ for ( currcol = D->ColOffs; currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan; currcol ++ ) spanned_total += ColOffs[currcol], mins_total += overmins[currcol]; /* If not big enough, distribute the difference between the minimum */ /* size of the colspan cell and the sum of the minimum sizes of the */ /* columns it spans. Using minimums ensures that when rewidthing */ /* the table, we won't need to check colspan cells again in case */ /* they are still undersize...! */ if (spanned_total < mins[cellnumber]) { int min_diff = mins[cellnumber] - mins_total; int basic = min_diff / (D->ColSpan ? D->ColSpan : 1); int remainder = min_diff - (basic * D->ColSpan); must_rewidth = 1; /* Distribute over the minimums, handling rounding carefully */ for ( currcol = D->ColOffs; currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan; currcol ++ ) { if (remainder < 0) { overmins[currcol] += basic - 1; remainder ++; } else if (remainder > 0) { overmins[currcol] += basic + 1; remainder --; } else overmins[currcol] += basic; if (overmaxs[currcol] < overmins[currcol]) overmaxs[currcol] = overmins[currcol]; } } /* Closure of 'if (cellnumber < cellmax)' */ } } } break; /* Closure of 'switch (D->Tag)' */ } D = D->Next; /* Closure of 'while (D && ...)' */ } rowcount ++; R = R->Next; /* Closure of 'while (R && ...)' */ } if (must_rewidth) { /* Now need to rewidth the table on the basis that the */ /* minimum widths in overmins[] changed. */ #ifdef TRACE if (tl & (1u<<20)) Printf("\ntables_width_table: Second table widthing pass due to colspan cells:\n"); #endif goto tables_width_table_assign_finals; /* Closure of 'if (must_rewidth)' */ } /* Closure of 'if (have_colspans)' */ } // #ifdef TRACE /* Final sanity check - the algorithms above should ensure that */ /* by this stage, no cell whether spanning multiple columns or */ /* not should be below its minimum size. In TRACE builds, this */ /* check warns if this is not the case (this indicates a bug in */ /* the widthing code above). */ R = p->List; rowcount = 0; while (R && rowcount < p->RowSpan) { D = R->List; while (D && D->ColOffs < p->ColSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { int currcol = D->ColOffs; int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; if (cellnumber < cellmax) { if ( D->ColSpan <= 1 && ColOffs[currcol] < mins[cellnumber] ) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Non-colspan cell %d would have ended up at %d OS units, below minimum width of %d", cellnumber, ColOffs[currcol] / 400, mins[cellnumber] / 400); show_error_ret(&erb); #endif ColOffs[currcol] = mins[cellnumber]; } } } break; } D = D->Next; } rowcount ++; R = R->Next; } /* Now deal with cells with colspans > 1. For each case, add up */ /* the width of the spanned cells from the array just calculated. */ /* If these work out more than the width calculated for that */ /* cell, then nothing needs to be done; else distribute the */ /* difference over the spanned cells. */ if (have_colspans) { R = p->List; rowcount = 0; while (R && rowcount < p->RowSpan) { D = R->List; while (D && D->ColOffs < p->ColSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { if (D->ColSpan > 1) { int currcol; int cellnumber = D->RowOffs * p->ColSpan + D->ColOffs; int spanned_total = 0; if (cellnumber < cellmax) { /* Count the spanned columns */ for ( currcol = D->ColOffs; currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan; currcol ++ ) spanned_total += ColOffs[currcol]; /* If not big enough, distribute the difference over the columns. */ /* We can't start altering the minimum values here as when the */ /* distribution over the table width (if any) takes place, the */ /* total of the spanned columns may end up large enough. */ if (spanned_total < mins[cellnumber]) { int min_diff = mins[cellnumber] - spanned_total; int basic = min_diff / (D->ColSpan ? D->ColSpan : 1); int remainder = min_diff - (basic * D->ColSpan); #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Colspan cell %d would have ended up at %d OS units, below minimum width of %d", cellnumber, spanned_total / 400, mins[cellnumber] / 400); show_error_ret(&erb); #endif /* Distribute for minimums, handling rounding carefully */ for ( currcol = D->ColOffs; currcol < D->ColOffs + D->ColSpan && currcol < p->ColSpan; currcol ++ ) { if (remainder < 0) { ColOffs[currcol] += basic - 1; remainder ++; } else if (remainder > 0) { ColOffs[currcol] += basic + 1; remainder --; } else ColOffs[currcol] += basic; } } /* Closure of 'if (cellnumber < cellmax)' */ } } } break; /* Closure of 'switch (D->Tag)' */ } D = D->Next; /* Closure of 'while (D && ...)' */ } rowcount ++; R = R->Next; /* Closure of 'while (R && ...)' */ } /* Closure of 'if (have_colspans)' */ } // #endif /* Add up the widths */ real_width = 0; for (I = 0; I < p->ColSpan; I++) real_width += ColOffs[I]; /* Did the table specify a non-zero width to fit to? */ if (TABLE_HAS_WIDTH(p) && TABLE_WIDTH(p)) { /* If the width is less than that available, distribute */ /* the extra width over the table to make it fit. */ #ifdef TRACE if (tl & (1u<<20)) Printf("\nTable specifies a width - should columns be extended to fit?\n"); #endif if (finding_widest != 1 && real_width && real_width < available_width) { float W, add = 0; int rw, aw; int pix = 0; int per = 0; int usp = 0; rw = real_width; aw = available_width; /* If we have any pixel specified cells, only distribute over */ /* non-pixel specified cells. For some reason, MSIE only does */ /* this if there are some unspecified cells on the row. If */ /* there are only pixel or percentage specified cells, the */ /* distribution occurs across all cells. */ /* */ /* Of course, by this stage we're working on an amalgamation */ /* of individual rows into overall columns, but the principle */ /* is the same... ;-) */ for (I = 0; I < p->ColSpan; I++) { if (usedcells[I]) { if (!TD_HAS_WIDTH(usedcells[I])) usp += ColOffs[I]; else if (TD_WIDTH_UNITS(usedcells[I]) == UNITS_PIXELS) pix += ColOffs[I]; else if (TD_WIDTH_UNITS(usedcells[I]) == UNITS_PERCENT) per += ColOffs[I]; else usp += ColOffs[I]; } } if (usp) { /* If we have pixel specifiers and also some other types of cell, */ /* reduce the apparent table width by the width of the pixel */ /* specified cells. The distribution routine below will then skip */ /* these cells. */ if (pix && pix != rw) rw -= pix, aw -= pix; else pix = 0; } else pix = 0; #ifdef TRACE if (tl & (1u<<20)) Printf("\ntables_width_table: Resizing all columns to fit space:\n\n"); #endif W = (float) aw - (float) rw; for (I = 0; I < p->ColSpan; I++) { if ( !pix || ( pix && ( !usedcells[I] || !TD_HAS_WIDTH(usedcells[I]) || TD_WIDTH_UNITS(usedcells[I]) != UNITS_PIXELS ) ) ) { add = W * ((float) ColOffs[I] / (float) (rw ? rw : 1)); ColOffs[I] += (int) add; #ifdef TRACE if (tl & (1u<<20)) Printf("%d: %d OS units\n", I, ColOffs[I] / 400); #endif } } } #ifdef TRACE else { if (tl & (1u<<20)) Printf("...can't, real_width = %d\n", real_width); } if (tl & (1u<<20)) Printf("\n"); #endif } /* We've finished with the temporary arrays */ free(maxs); free(mins); free(overmaxs); free(overmins); free(pcwidths); free(usedcells); /* Now we need to go back and reformat the cell contents to the known width */ rowcount = 0; R = p->List; while (R && rowcount < p->RowSpan) { D = R->List; while (D && D->ColOffs < p->ColSpan) { switch (D->Tag) { case TagTableData: case TagTableHead: { /* Use the tag's number of columns offset from the left */ /* of the table as an index into the array of millipoint */ /* offsets. */ new_width = abs(ColOffs[D->ColOffs]); /* If the tag spans more than one column, add in the widths */ /* of the extra columns it covers. */ if (D->ColSpan > 1) { int index; for(I = 1; I < D->ColSpan; I++) { index = D->ColOffs + I; if (index < p->ColSpan) new_width += abs(ColOffs[index]); } } /* Reformat the cell, recursing for inner tables */ real_width = tables_width_cell(toplevel, b, (HStream *) D->List, p, cellarray, new_width, D->RowOffs, D->ColOffs); } break; } D = D->Next; } rowcount ++; R = R->Next; } /* Finally, add up all the widths of the columns and return it */ max_table_width = 0; for (I = 0; I < p->ColSpan; I++) max_table_width += abs(ColOffs[I]); #ifdef TRACE if (tl & (1u<<20)) Printf("tables_width_table: Finished, returning %d (%d OS)\n", max_table_width, max_table_width / 400); #endif _swix(Hourglass_Off, 0); /* Must of course add in whatever we subtracted from available */ /* width to allow for cellspacing to the returned table width, */ /* and allow for the outer table border. */ if (p->ColSpan) max_table_width += cellspacingmp * (p->ColSpan + 1) + tbordmp * 2; return max_table_width; } /*************************************************/ /* tables_width_cell() */ /* */ /* Returns the actual width of a given table */ /* cell after attempting to reformat it to a */ /* given width. */ /* */ /* Parameters: 1 for a top level call, 0 if */ /* being called as part of a nested */ /* table parse; */ /* */ /* Pointer to a browser_data struct */ /* relevant to the table; */ /* */ /* Pointer to the first HStream */ /* structure in the stream that the */ /* cell is to contain; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the table's array of */ /* reformat_cell structures; */ /* */ /* Allowed column width (millipts); */ /* */ /* Row number of the cell; */ /* */ /* Column number of the cell. */ /* */ /* Returns: Actual width after formatting, */ /* in millipoints. */ /*************************************************/ int tables_width_cell(int toplevel, browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int ColWidth, int Row, int Column) { return reformat_format_cell(toplevel, b, streambase, table, cellarray, ColWidth, Row, Column); } /*************************************************/ /* tables_height_table() */ /* */ /* Works out the heights in millipoints for all */ /* cells in a given table. This may be called */ /* from recursively called functions, but does */ /* not enter into recursion itself as the height */ /* information for particular cells is stored */ /* when finding the width - there's no need to */ /* call the reformatter again. */ /* */ /* Parameters: 1 for a top level call, else 0 if */ /* being called as part of a nested */ /* tables reformat session; */ /* */ /* Pointer to a browser_data struct, */ /* which is the parent of the table; */ /* */ /* Pointer to a table_stream struct */ /* relevant to the table; */ /* */ /* Pointer to an array of points to */ /* reformat_cell structures which */ /* will hold the final table layout. */ /* */ /* Returns: Height of the whole table, in */ /* millipoints. */ /*************************************************/ int tables_height_table(int toplevel, browser_data * b, table_stream * p, reformat_cell * cellarray) { int ReturnHeight = 0, Height, NewWidth; int * RowOffs; int * ColOffs; int Row; table_row * R; table_headdata * D; int I, C; int scale_height = 0; int cellmax = p->ColSpan * p->RowSpan; int cellspacingos = p->cellspacing * 2; /* 1 'web pixel' = 2 OS units */ int cellspacingmp; int tbordmp; if (!cellmax) return 1600; convert_to_points(cellspacingos, &cellspacingmp); convert_to_points(TABLE_BORDER(p) * 2, &tbordmp); /* 1 'web pixel' = 2 OS units */ #ifdef TRACE if (tl & (1u<<20)) Printf("tables_height_table entered: 0x%x 0x%x\n",(int) P,(int) cellarray); #endif /* To work out height, must know the width (so that the */ /* reformatter knows what width to wrap to, etc.). So */ /* must only call this *after* tables_width_table. */ RowOffs = p->RowOffs; ColOffs = p->ColOffs; Row = 0; R = p->List; /* Deal with specific table heights - we may need */ /* to scale everything up to these afterwards. */ if (TABLE_HAS_HEIGHT(p)) { int height = 0, dy; switch (TABLE_HEIGHT_UNITS(p)) { default: case UNITS_PIXELS: { convert_to_points(wimpt_dy(), &dy); height = TABLE_HEIGHT(p) * dy; } break; case UNITS_PERCENT: { convert_to_points(redraw_display_height(b, NULL), &height); height = (TABLE_HEIGHT(p) * height) / 100; } break; } scale_height = height; } /* Now find the collective row heights for all of the cells */ while (R) { D = R->List; /* Scan the cells */ while (D) { switch (D->Tag) { case TagTableData: case TagTableHead: { /* Work out the width that this cell uses, for ROWSPAN = 1 cells only */ if (D->ColOffs < p->ColSpan && D->RowOffs < p->RowSpan && D->RowSpan <= 1) { NewWidth = abs(ColOffs[D->ColOffs]); if (D->ColSpan > 1) { int index; for (I = 1; I < D->ColSpan; I++) { index = D->ColOffs + I; if (index < p->ColSpan) NewWidth += abs(ColOffs[index]); } } /* Find the required cell height (this is not a */ /* recursive call - the height is stored in the */ /* relevant place by tables_width_cell called from */ /* tables_width_table above, which is where the */ /* recursion is handled). */ Height = tables_height_cell(toplevel, b, (HStream *) D->List, p, cellarray, NewWidth, D->RowOffs, D->ColOffs); /* Deal with direct height specifiers */ if (TD_HAS_HEIGHT(D)) { int hc = 0; /* Percentages come from the whole visible area size */ if (TD_HEIGHT_UNITS(D) == UNITS_PERCENT) { hc = (b->display_height * TD_HEIGHT(D)) / 100; } /* Pixel specifiers are easier */ else { hc = TD_HEIGHT(D) * 2; /* 2 'web pixels' = 1 OS unit */ } convert_to_points(hc, &hc); if (hc > Height) Height = hc; } /* If this is the greatest height so far for the row, remember it */ if (Height > RowOffs[Row]) RowOffs[Row] = Height; } } break; } D = D->Next; } #ifdef TRACE if (tl & (1u<<20)) Printf("tables_height_table: First pass, row %d, height %d (%d OS)\n", Row, RowOffs[Row], RowOffs[Row] / 400); #endif Row ++; R = R->Next; } /* Another scan, for ROWSPAN this time */ Row = 0; R = p->List; while (R) { D = R->List; while (D) { switch (D->Tag) { case TagTableData: case TagTableHead: { /* Work out the width that this cell uses, for ROWSPAN = 1 cells only */ if (D->ColOffs < p->ColSpan && D->RowOffs < p->RowSpan && D->RowSpan > 1) { NewWidth = abs(ColOffs[D->ColOffs]); if (D->ColSpan > 1) { int index; for (I = 1; I < D->ColSpan; I++) { index = D->ColOffs + I; if (index < p->ColSpan) NewWidth += abs(ColOffs[index]); } } /* Find the required cell height (this is not a */ /* recursive call - the height is stored in the */ /* relevant place by tables_width_cell called from */ /* tables_width_table above, which is where the */ /* recursion is handled). */ Height = tables_height_cell(toplevel, b, (HStream *) D->List, p, cellarray, NewWidth, D->RowOffs, D->ColOffs); /* Deal with direct height specifiers */ if (TD_HAS_HEIGHT(D)) { /* Percentages come from the whole visible area size */ if (TD_HEIGHT_UNITS(D) == UNITS_PERCENT) { int hc = (b->display_height * TD_HEIGHT(D)) / 100; if (hc > Height) Height = hc; } /* Pixel specifiers are easier */ else if (TD_HEIGHT_UNITS(D) == UNITS_PIXELS) { int hc = TD_HEIGHT(D) * 2; /* 2 'web pixels' = 1 OS unit */ if (hc > Height) Height = hc; } } /* The cell spans several rows, so we must count up the height */ /* already present in the rows it spans. Any extra that this */ /* cell requires is added to the last row it touches. */ C = D->RowSpan; if (C + Row >= p->RowSpan) C = p->RowSpan - Row; if (C > 0) { int TotalHeight = 0; for (I = Row; I < Row + C && I < p->RowSpan; I++) { TotalHeight += RowOffs[I]; } /* If the total amount the cell spans is less than this cell */ /* requires, add in the extra to the last cell. */ if (TotalHeight < Height) RowOffs[Row + C - 1] += Height - TotalHeight; } } } break; } D = D->Next; } #ifdef TRACE if (tl & (1u<<20)) Printf("tables_height_table: Second pass, row %d, height %d (%d OS)\n", Row, RowOffs[Row], RowOffs[Row] / 400); #endif Row ++; R = R->Next; } /* Calculate the table height */ for (Row = 0; Row < p->RowSpan; Row ++) ReturnHeight += RowOffs[Row]; /* If this is below the table scale height, do a *linear* scale up */ if (ReturnHeight < scale_height) { int difference = scale_height - ReturnHeight; int basic; int remainder; basic = difference / (p->RowSpan ? p->RowSpan : 1); remainder = difference - (basic * p->RowSpan); for ( Row = 0; Row < p->RowSpan; Row ++ ) { if (remainder < 0) { RowOffs[Row] += basic - 1; remainder ++; } else if (remainder > 0) { RowOffs[Row] += basic + 1; remainder --; } else RowOffs[Row] += basic; } /* Recalculate height (may not be dead on scale_height because */ /* of rounding errors, though it won't be far out because of */ /* the above code) */ ReturnHeight = 0; for (Row = 0; Row < p->RowSpan; Row ++) ReturnHeight += RowOffs[Row]; } /* Add in cell spacings and the table outer border */ if (p->RowSpan) ReturnHeight += cellspacingmp * (p->RowSpan + 1) + tbordmp * 2; /* Return the height */ #ifdef TRACE if (tl & (1u<<20)) Printf("tables_height_table: Finished, returning %d (%d OS)\n\n", ReturnHeight, ReturnHeight / 400); #endif return ReturnHeight; } /*************************************************/ /* tables_height_cell() */ /* */ /* Returns the height of a cell widthed by a */ /* previous call to tables_width_cell. */ /* */ /* Parameters: 1 for a top level call, 0 if */ /* being called as part of a nested */ /* table parse; */ /* */ /* Pointer to a browser_data struct */ /* relevant to the table; */ /* */ /* Pointer to the first HStream */ /* structure in the stream that the */ /* cell is to contain; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the table's array of */ /* reformat_cell structures; */ /* */ /* Allowed column width (millipts - */ /* this is not used at present); */ /* */ /* Row number of the cell; */ /* */ /* Column number of the cell. */ /* */ /* Returns: Cell height in millipoints. */ /*************************************************/ int tables_height_cell(int toplevel, browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int ColWidth, int Row, int Column) { int cellindex = Row * table->ColSpan + Column; int cellpadmp; reformat_cell * c; /* Can't do anything if the cell index is out of range */ if (cellindex >= table->RowSpan * table->ColSpan) return 1600; else c = &cellarray[cellindex]; /* OK, so not used for now, but may be - this is to avoid a compiler warning */ b = b; #ifdef TRACE if (tl & (1u<<20)) { Printf("tables_height_cell: %p %p %d %d %d\n",streambase,table,ColWidth,Row,Column); Printf("tables_height_cell returning %d\n",c->height); } #endif /* Add in the cell padding values to the top and bottom */ cellpadmp = table->cellpadding * 4; /* 1 'web pixel' = 2 OS units, times two for top and bottom */ convert_to_points(cellpadmp, &cellpadmp); c->height += cellpadmp; return c->height; } /*************************************************/ /* tables_fix_table() */ /* */ /* Having found the width and height of the */ /* cells in a given table, and their row/column */ /* offset from the top left, this fixes the */ /* cell positions as x and y coordinates */ /* in millipoints from the top left. */ /* */ /* Parameters: Pointer to a browser_data struct, */ /* which is the parent of the table; */ /* */ /* Pointer to a table_stream struct */ /* relevant to the table; */ /* */ /* Pointer to an array of points to */ /* reformat_cell structures which */ /* will hold the final table layout. */ /*************************************************/ void tables_fix_table(browser_data * b, table_stream * p, reformat_cell * cellarray) { table_row * R; table_headdata * D; int I; int Width, Height; int * ColOffs; int * RowOffs; int CurX, CurY; int cellcount = 0; int cellmax = p->ColSpan * p->RowSpan; int cellspacingos = p->cellspacing * 2; /* 1 'web pixel' = 2 OS units */ int cellspacingmp; int tbordmp; convert_to_points(cellspacingos, &cellspacingmp); convert_to_points(TABLE_BORDER(p) * 2, &tbordmp); /* 1 'web pixel' = 2 OS units */ /* Have ColOffs and RowOffs as the arrays holding millipoint */ /* offsets from the top left of the table. */ R = p->List; ColOffs = p->ColOffs; RowOffs = p->RowOffs; while (R && cellcount < cellmax) { D = R->List; /* In here, D->RowOffs or D->ColOffs will be the number of */ /* rows / columns from the top left that this cell is */ /* offset by. */ while (D && cellcount < cellmax) { if (D->ColOffs < p->ColSpan && D->RowOffs < p->RowSpan) { /* If the cell is offset horizontally, add up the widths of */ /* all the columns before it into CurX. */ CurX = cellspacingmp + tbordmp; if (D->ColOffs) { for (I = 0; I < D->ColOffs; I++) { CurX += abs(ColOffs[I]) + cellspacingmp; } } CurY = -cellspacingmp - tbordmp; /* Similarly, add up the row heights */ if (D->RowOffs) { for (I = 0; I < D->RowOffs; I++) { CurY -= abs(RowOffs[I]) + cellspacingmp; } } /* Set Width to the width of this cell, */ /* taking account of column spanning. */ Width = abs(ColOffs[D->ColOffs]); if (D->ColSpan > 1) { for (I = 1; I < D->ColSpan; I++) { Width += abs(ColOffs[D->ColOffs + I]); } /* Account for cell spacing */ Width += cellspacingmp * (D->ColSpan - 1); } /* Do the same for the height */ Height = abs(RowOffs[D->RowOffs]); if (D->RowSpan > 1) { for (I = 1; I < CorrectRowSpan(D, p); I++) { Height += abs(RowOffs[D->RowOffs + I]); } /* Account for cell spacing */ Height += cellspacingmp * (D->RowSpan - 1); } /* We now know the X and Y millipoint offsets of the cell and */ /* its width and height (again, in millipoints). Now call */ /* tables_fix_cell to fix the positions of the cell inside */ /* the relevant data structure components. */ switch (D->Tag) { case TagTableData: tables_fix_cell(b, (HStream *) D->List, p, cellarray, CurX, CurY, Width, Height, D->RowOffs, D->ColOffs); break; case TagTableHead: tables_fix_cell(b, (HStream *) D->List, p, cellarray, CurX, CurY, Width, Height, D->RowOffs, D->ColOffs); break; } } cellcount++; D = D->Next; } R = R->Next; } } /*************************************************/ /* tables_fix_cell() */ /* */ /* Fixes the position of a given table cell as */ /* an offset from the top left of the table in */ /* millipoints. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* owning the table; */ /* */ /* Pointer to the first HStream */ /* structure in the stream that the */ /* cell is to contain; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the table's array of */ /* reformat_cell structures; */ /* */ /* X offset from top left, in */ /* millipoints; */ /* */ /* Yoffset from top left, in */ /* millipoints; */ /* */ /* Total cell width (millipoints); */ /* */ /* Total cell height (millipoints); */ /* */ /* Row number of the cell; */ /* */ /* Column number of the cell. */ /*************************************************/ void tables_fix_cell(browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int x, int y, int Width, int Height, int Row, int Column) { int cellindex = Row * table->ColSpan + Column; reformat_cell * c; #ifdef TRACE if (tl & (1u<<20)) Printf("tables_fix_cell: %p %p %d %d\n",streambase,table,Row,Column); #endif /* Can't do anything if the cell index is out of range */ if (cellindex >= table->RowSpan * table->ColSpan) return; else c = &cellarray[cellindex]; //the 3200's in here should be replaced by border sizes in due course c->x = x + Column;// * 3200 + 1600; c->y = y - Row;// * 3200 - 1600; c->cellwidth = Width; c->cellheight = Height; /* Post-correct Y for VALIGN and cellpadding considerations */ tables_align_contents(b, streambase, table, cellarray, Row, Column); /* Finished */ #ifdef TRACE if (tl & (1u<<20)) Printf("tables_fix_cell: Successful\n"); #endif } /*************************************************/ /* tables_align_contents() */ /* */ /* Once the cellheight field of a reformat_cell */ /* struct is filled in, it is possible to look */ /* at the VALIGN specification on the table */ /* cell tag represented by the reformat_cell */ /* struct and align the cell contents in an */ /* appropriate fashion. This is done by */ /* increasing the height of the first line as */ /* required, shifting the y coordinate of all */ /* subsequent lines as needed, and increasing */ /* the used height value in the cell to match. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* owning the table; */ /* */ /* Pointer to the first HStream */ /* structure in the stream that the */ /* cell is to contain; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the table's array of */ /* reformat_cell structures; */ /* */ /* Row number of the cell; */ /* */ /* Column number of the cell. */ /*************************************************/ static void tables_align_contents(browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int Row, int Column) { int cellindex = Row * table->ColSpan + Column; int cellpadmp = table->cellpadding * 2; /* 1 'web pixel' = 2 OS units */ int valign_offset_mp, valign_offset_os; table_headdata * head; reformat_cell * c; #ifdef TRACE if (tl & (1u<<20)) Printf("tables_align_contents: %p %p %d %d\n",streambase,table,Row,Column); #endif convert_to_points(cellpadmp, &cellpadmp); /* Can't do anything if the cell index is out of range */ if (cellindex >= table->RowSpan * table->ColSpan) return; else c = &cellarray[cellindex]; /* If the cell has no lines, again, can't do anything */ if (!c->nlines) return; /* OK, so not used for now, but may be - this is to avoid a compiler warning */ b = b; /* Work out the vertical alignment offset value in OS units and millipoints */ head = streambase ? (table_headdata *) streambase->parent : NULL; if (head) { if (TD_VALIGN(head) == ALIGN_BOTTOM) valign_offset_mp = c->cellheight - c->height; else if (TD_VALIGN(head) == ALIGN_MIDDLE) valign_offset_mp = (c->cellheight - c->height) / 2; else valign_offset_mp = 0; valign_offset_mp += cellpadmp; convert_to_os(valign_offset_mp, &valign_offset_os); /* Only proceed if there's a change to be made */ if (valign_offset_mp && valign_offset_os) { int l; /* Shift the y coordinates of the lines */ for (l = 0; l < c->nlines; l++) c->ldata[l].y -= valign_offset_os; /* Increase the used height value */ c->height += valign_offset_mp; } } #ifdef TRACE if (tl & (1u<<20)) Printf("tables_align_contents: Successful\n"); #endif } /*************************************************/ /* tables_free_memory() */ /* */ /* Goes through the lines of a reformat_cell */ /* structure, freeing all allocated memory */ /* relating to any tables in that list; that is, */ /* the lines and chunks allocated in any assoc- */ /* iated cells. Does not free the given cell's */ /* line or chunk arrays, or any HTMLLib */ /* allocated data such as the cell arrays */ /* allocated through HTMLLib or the HStream */ /* lists (HtmlStreamFree should be used for */ /* those). */ /* */ /* Parameters: 1 for top level call, 0 for a */ /* recursive one (only used for the */ /* hourglass); */ /* */ /* Pointer to a browser_data struct */ /* relevant to the tables; */ /* */ /* Pointer to a reformat_cell struct */ /* holding the line array; */ /* */ /* Line number in that line array */ /* from which freeing is to start. */ /*************************************************/ void tables_free_memory(int toplevel, browser_data * b, reformat_cell * d, int line) { int l, c; if (!d->nlines || line >= d->nlines) return; if (line < 0) line = 0; if (toplevel) _swix(Hourglass_Start, _IN(0), Tables_Hourglass_FreeingTablesDelay); /* Scan through the lines from last line to the requested one */ /* (this is so that flex thrashage will hopefully be reduced, */ /* as other cells in lines of higher numbers will on average */ /* appear in memory above cells in lines of lower numbers). */ for (l = d->nlines - 1; l >= line; l--) { /* Scan through all chunks on the line */ for (c = d->ldata[l].chunks + d->ldata[l].n - 1; c >= d->ldata[l].chunks; c--) { /* Does the chunk represent a table? */ if (d->cdata[c].t->tagno == TAG_TABLE) { table_stream * table = (table_stream *) d->cdata[c].t; table_row * row = NULL; table_headdata * head = NULL; reformat_cell * cellarray = table->cells; reformat_cell * cell; int cellindex; int cellcount = 0; int cellmax = table->ColSpan * table->RowSpan; if (cellarray) { row = table->List; while (row && cellcount < cellmax) { head = row->List; while (head && cellcount < cellmax) { switch (head->Tag) { case TagTableData: case TagTableHead: { cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { cell = &cellarray[cellindex]; /* Recursive call to free any nested tables inside this cell */ tables_free_memory(0, b, cell, 0); /* Free the line and chunk lists for the cell */ if (cell->ldata) flex_free((flex_ptr) &cell->ldata); if (cell->cdata) flex_free((flex_ptr) &cell->cdata); cell->nlines = 0; /* Invalidate the min and max width information */ cell->minwid = -1; cell->maxwid = -1; } } break; /* Closure of 'switch (head->Tag)' */ } cellcount++; head = head->Next; /* Closure of 'while (head && ...)' */ } row = row->Next; /* Closure of 'while (row && ...)' */ } /* Closure of 'if (cellarray)' */ } /* Closure of 'if (d->cdata[c].t->tagno == TAG_TABLE)' */ } /* Closure of loop scanning this line's chunks */ } if (toplevel && d->nlines) _swix(Hourglass_Percentage, _IN(0), (100 * (d->nlines - l - 1)) / d->nlines); /* Closure of loop scanning the cell's lines */ } if (toplevel) _swix(Hourglass_Off, 0); }