Back to index

texmacs  1.0.7.15
table.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : table.cpp
00004 * DESCRIPTION: Tables and cells of tables
00005 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "Table/table.hpp"
00013 #include "Boxes/construct.hpp"
00014 #include "Format/format.hpp"
00015 #include "analyze.hpp"
00016 
00017 lazy make_lazy_paragraph (edit_env env, array<box> bs, path ip);
00018 
00019 /******************************************************************************
00020 * Tables
00021 ******************************************************************************/
00022 
00023 table_rep::table_rep (edit_env env2, int status2, int i0b, int j0b):
00024   var (""), env (env2), status (status2), i0 (i0b), j0 (j0b),
00025   T (NULL), nr_rows (0), mw (NULL), lw (NULL), rw (NULL),
00026   width (0), height (0) {}
00027 
00028 table_rep::~table_rep () {
00029   if (T != NULL) {
00030     int i;
00031     for (i=0; i<nr_rows; i++) tm_delete_array (T[i]);
00032     tm_delete_array (T);
00033   }
00034   if (mw != NULL) tm_delete_array (mw);
00035   if (lw != NULL) tm_delete_array (lw);
00036   if (rw != NULL) tm_delete_array (rw);
00037 }
00038 
00039 void
00040 table_rep::display (bool flag) {
00041   int i, j;
00042   if (flag) cout << "---------------------------------------------------------------------------\n";
00043   else cout << "{ ";
00044   for (i=0; i<nr_rows; i++) {
00045     cout << "[ ";
00046     for (j=0; j<nr_cols; j++)
00047       if (!is_nil (T[i][j])) {
00048        cell C= T[i][j];
00049        if (j != 0) cout << ", ";
00050        if (!is_nil (C->b)) cout << C->b;
00051        else if (!is_nil (C->T)) {
00052          cout << "subtable ";
00053          C->T->display (false);
00054        }
00055        else cout << "nil";
00056        if (!is_nil (C->D)) {
00057          cout << " & decoration ";
00058          C->D->display (false);
00059        }
00060       }
00061     cout << " ]";
00062     if (flag) cout << "\n";
00063     else if (i<nr_rows-1) cout << ", ";
00064   }
00065   if (flag) cout << "---------------------------------------------------------------------------\n";
00066   else cout << " }";
00067 }
00068 
00069 void
00070 table_rep::typeset (tree t, path iq) {
00071   ip= iq;
00072   tree old_format= env->local_begin (CELL_FORMAT, tree (TFORMAT));
00073   tree new_format= old_format;
00074   if (!is_func (new_format, TFORMAT)) new_format= tree (TFORMAT);
00075   while (is_func (t, TFORMAT)) {
00076     new_format= new_format * t (0, N(t)-1);
00077     iq        = descend (iq, N(t)-1);
00078     t         = t[N(t)-1];
00079   }
00080   format_table (new_format);
00081   typeset_table (new_format, t, iq);
00082   env->local_end (CELL_FORMAT, old_format);
00083 }
00084 
00085 void
00086 table_rep::typeset_table (tree fm, tree t, path ip) {
00087   int i;
00088   nr_rows= N(t);
00089   nr_cols= 0;
00090   T= tm_new_array<cell*> (nr_rows);
00091   STACK_NEW_ARRAY (subformat, tree, nr_rows);
00092   extract_format (fm, subformat, nr_rows);
00093   for (i=0; i<nr_rows; i++)
00094     typeset_row (i, subformat[i], t[i], descend (ip, i));
00095   STACK_DELETE_ARRAY (subformat);
00096   mw= tm_new_array<SI> (nr_cols);
00097   lw= tm_new_array<SI> (nr_cols);
00098   rw= tm_new_array<SI> (nr_cols);
00099 }
00100 
00101 void
00102 table_rep::typeset_row (int i, tree fm, tree t, path ip) {
00103   int j;
00104   nr_cols= N(t);
00105   T[i]= tm_new_array<cell> (nr_cols);
00106   STACK_NEW_ARRAY (subformat, tree, nr_cols);
00107   extract_format (fm, subformat, nr_cols);
00108   for (j=0; j<nr_cols; j++) {
00109     cell& C= T[i][j];
00110     C= cell (env);
00111     C->typeset (subformat[j], t[j], descend (ip, j));
00112     C->row_span= min (C->row_span, nr_rows- i);
00113     C->col_span= min (C->col_span, nr_cols- j);
00114     if (hyphen == "y") C->row_span= 1;
00115   }
00116   STACK_DELETE_ARRAY (subformat);
00117 }
00118 
00119 /******************************************************************************
00120 * Table formatting variables
00121 ******************************************************************************/
00122 
00123 void
00124 table_rep::format_table (tree fm) {
00125   int i, l= N(fm);
00126   for (i=0; i<l; i++)
00127     format_item (fm[i]);
00128 
00129   if (var->contains (TABLE_WIDTH)) {
00130     width= env->as_length (env->exec (var[TABLE_WIDTH]));
00131     if (var->contains (TABLE_HMODE))
00132       hmode= as_string (env->exec (var[TABLE_HMODE]));
00133     else hmode= "exact";
00134   }
00135   else {
00136     width= 0;
00137     hmode= "auto";
00138   }
00139   if (var->contains (TABLE_HEIGHT)) {
00140     height= env->as_length (env->exec (var[TABLE_HEIGHT]));
00141     if (var->contains (TABLE_VMODE))
00142       vmode= as_string (env->exec (var[TABLE_VMODE]));
00143     else vmode= "exact";
00144   }
00145   else {
00146     height= 0;
00147     vmode = "auto";
00148   }
00149   if (var->contains (TABLE_LSEP))
00150     lsep= env->as_length (env->exec (var[TABLE_LSEP]));
00151   else lsep= 0;
00152   if (var->contains (TABLE_RSEP))
00153     rsep= env->as_length (env->exec (var[TABLE_RSEP]));
00154   else rsep= 0;
00155   if (var->contains (TABLE_BSEP))
00156     bsep= env->as_length (env->exec (var[TABLE_BSEP]));
00157   else bsep= 0;
00158   if (var->contains (TABLE_TSEP))
00159     tsep= env->as_length (env->exec (var[TABLE_TSEP]));
00160   else tsep= 0;
00161   if (var->contains (TABLE_LBORDER))
00162     lborder= env->as_length (env->exec (var[TABLE_LBORDER])) >> 1;
00163   else lborder= 0;
00164   if (var->contains (TABLE_RBORDER))
00165     rborder= env->as_length (env->exec (var[TABLE_RBORDER])) >> 1;
00166   else rborder= 0;
00167   if (var->contains (TABLE_BBORDER))
00168     bborder= env->as_length (env->exec (var[TABLE_BBORDER])) >> 1;
00169   else bborder= 0;
00170   if (var->contains (TABLE_TBORDER))
00171     tborder= env->as_length (env->exec (var[TABLE_TBORDER])) >> 1;
00172   else tborder= 0;
00173   if (var->contains (TABLE_HALIGN))
00174     halign= as_string (env->exec (var[TABLE_HALIGN]));
00175   else halign= "l";
00176   if (var->contains (TABLE_VALIGN))
00177     valign= as_string (env->exec (var[TABLE_VALIGN]));
00178   else valign= "f";
00179   if (var->contains (TABLE_HYPHEN))
00180     hyphen= as_string (env->exec (var[TABLE_HYPHEN]));
00181   else hyphen= "n";
00182   if (var->contains (TABLE_ROW_ORIGIN))
00183     row_origin= as_int (env->exec (var[TABLE_ROW_ORIGIN]));
00184   else row_origin= 1;
00185   if (var->contains (TABLE_COL_ORIGIN))
00186     col_origin= as_int (env->exec (var[TABLE_COL_ORIGIN]));
00187   else col_origin= 1;
00188 }
00189 
00190 void
00191 table_rep::format_item (tree with) {
00192   if (is_func (with, TWITH, 2))
00193     var (as_string (with[0]))= with[1];
00194 }
00195 
00196 /******************************************************************************
00197 * Handling decorations and span
00198 ******************************************************************************/
00199 
00200 void
00201 table_rep::handle_decorations () {
00202   bool flag= true;
00203   int i, j, ii, jj, di, dj;
00204   array<int> ex_i1 (nr_rows), ex_i2 (nr_rows), off_i (nr_rows);
00205   array<int> ex_j1 (nr_cols), ex_j2 (nr_cols), off_j (nr_cols);
00206 
00207   /*** determine amount of decoration there is for each row and column ***/
00208   for (i=0; i<nr_rows; i++)
00209     ex_i1[i]= ex_i2[i]= 0;
00210   for (j=0; j<nr_cols; j++)
00211     ex_j1[j]= ex_j2[j]= 0;
00212   for (i=0; i<nr_rows; i++)
00213     for (j=0; j<nr_cols; j++) {
00214       cell C= T[i][j];
00215       if ((!is_nil (C)) && (!is_nil (C->T))) C->T->handle_decorations ();
00216       if ((!is_nil (C)) && (!is_nil (C->D))) {
00217        C->D->handle_decorations ();
00218        if (C->D->status == 1) {
00219          ii= i+ C->row_span- 1;
00220          jj= j+ C->col_span- 1;
00221          ex_i1[i ]= max (ex_i1[i ], C->D->i0);
00222          ex_j1[j ]= max (ex_j1[j ], C->D->j0);
00223          ex_i2[ii]= max (ex_i2[ii], C->D->nr_rows- 1- C->D->i0);
00224          ex_j2[jj]= max (ex_j2[jj], C->D->nr_cols- 1- C->D->j0);
00225          flag= false;
00226        }
00227       }
00228     }
00229   if (flag) return;
00230   for (i=0, ii=0; i<nr_rows; ii+=ex_i1[i]+ex_i2[i]+1, i++)
00231     off_i[i]= ii;
00232   for (j=0, jj=0; j<nr_cols; jj+=ex_j1[j]+ex_j2[j]+1, j++)
00233     off_j[j]= jj;
00234 
00235   /*** compute decorated table ***/
00236   cell** U;
00237   int new_rows= nr_rows, new_cols= nr_cols;
00238   for (i=0; i<nr_rows; i++) new_rows += ex_i1[i] + ex_i2[i];
00239   for (j=0; j<nr_cols; j++) new_cols += ex_j1[j] + ex_j2[j];
00240   U= tm_new_array<cell*> (new_rows);
00241   for (i=0; i<new_rows; i++)
00242     U[i]= tm_new_array<cell> (new_cols);
00243 
00244   for (i=0; i<nr_rows; i++)
00245     for (j=0; j<nr_cols; j++) {
00246       cell C= T[i][j];
00247       if (!is_nil (C)) {
00248        if ((!is_nil (C->D)) && (C->D->status==1)) {
00249          for (di=0; di<C->D->nr_rows; di++)
00250            for (dj=0; dj<C->D->nr_cols; dj++) {
00251              ii= di+ off_i[i]+ ex_i1[i]- C->D->i0;
00252              jj= dj+ off_j[j]+ ex_j1[j]- C->D->j0;
00253              U[ii][jj]= C->D->T[di][dj];
00254            }
00255          C->D= table ();
00256        }
00257        ii= off_i[i]+ ex_i1[i];
00258        jj= off_j[j]+ ex_j1[j];
00259        U[ii][jj]= C;
00260        ii= i+ C->row_span- 1;
00261        jj= j+ C->col_span- 1;
00262        C->row_span= off_i[ii]+ ex_i1[ii]+ 1- off_i[i]- ex_i1[i];
00263        C->col_span= off_j[jj]+ ex_j1[jj]+ 1- off_j[j]- ex_j1[j];
00264       }
00265     }
00266 
00267   /*** replace old table by new one ***/
00268   for (i=0; i<nr_rows; i++) tm_delete_array (T[i]);
00269   tm_delete_array (T);
00270   T      = U;
00271   nr_rows= new_rows;
00272   nr_cols= new_cols;
00273   i0     = off_i[i0] + ex_i1[i0];
00274   j0     = off_j[j0] + ex_j1[j0];
00275 }
00276 
00277 void
00278 table_rep::handle_span () {
00279   int i, j, ii, jj;
00280   for (i=0; i<nr_rows; i++)
00281     for (j=0; j<nr_cols; j++) {
00282       cell C= T[i][j];
00283       if (!is_nil (C)) {
00284        for (ii=0; ii<C->row_span; ii++)
00285          for (jj=0; jj<C->col_span; jj++)
00286            if ((ii != 0) || (jj != 0))
00287              T[i+ii][j+jj]= cell ();
00288        if (!is_nil (C->T)) C->T->handle_span ();
00289       }
00290     }
00291 }
00292 
00293 /******************************************************************************
00294 * Each border is the maximum of the borders of its adjacent cells
00295 ******************************************************************************/
00296 
00297 void
00298 table_rep::merge_borders () {
00299   int hh= nr_cols + 1, vv= nr_rows + 1, nn= hh * vv;
00300   array<SI> horb (nn), verb (nn);
00301   for (int i=0; i<nn; i++) horb[i]= verb[i]= 0;
00302 
00303   for (int i=0; i<nr_rows; i++)
00304     for (int j=0; j<nr_cols; j++) {
00305       cell C= T[i][j];
00306       if (!is_nil (C)) {
00307         for (int di=0; di < C->row_span; di++) {
00308           int ii= i+di, jj= j, kk= ii*hh + jj;
00309           horb[kk]= max (horb[kk], C->lborder);
00310           jj= j + C->col_span; kk= ii*hh + jj;
00311           horb[kk]= max (horb[kk], C->rborder);
00312         }
00313         for (int dj=0; dj < C->col_span; dj++) {
00314           int ii= i, jj= j+dj, kk= ii*hh + jj;
00315           verb[kk]= max (verb[kk], C->tborder);
00316           ii= i + C->row_span; kk= ii*hh + jj;
00317           verb[kk]= max (verb[kk], C->bborder);
00318         }
00319       }
00320     }
00321 
00322   for (int i=0; i<nr_rows; i++)
00323     for (int j=0; j<nr_cols; j++) {
00324       cell C= T[i][j];
00325       if (!is_nil (C)) {
00326         SI lb= 0, rb= 0, bb= 0, tb= 0;
00327         for (int di=0; di < C->row_span; di++) {
00328           int ii= i+di, jj= j, kk= ii*hh + jj;
00329           lb= max (horb[kk], lb);
00330           jj= j + C->col_span; kk= ii*hh + jj;
00331           rb= max (horb[kk], rb);
00332         }
00333         for (int dj=0; dj < C->col_span; dj++) {
00334           int ii= i, jj= j+dj, kk= ii*hh + jj;
00335           tb= max (verb[kk], tb);
00336           ii= i + C->row_span; kk= ii*hh + jj;
00337           bb= max (verb[kk], bb);
00338         }
00339         C->lborder= lb; C->rborder= rb;
00340         C->bborder= bb; C->tborder= tb;
00341       }
00342     }
00343 }
00344 
00345 /******************************************************************************
00346 * Subroutines for positioning
00347 ******************************************************************************/
00348 
00349 static SI
00350 sum (SI* a, int n) {
00351   int i, s=0;
00352   for (i=0; i<n; i++) s+= a[i];
00353   return s;
00354 }
00355 
00356 static double
00357 sum (double* a, int n) {
00358   int i;
00359   double s=0.0;
00360   for (i=0; i<n; i++) s+= a[i];
00361   return s;
00362 }
00363 
00364 static void
00365 blow_up (SI* w, SI* l, SI* r, SI* W, SI* L, SI* R,
00366         SI room, double* part, int n)
00367 {
00368   int i;
00369   double total= sum (part, n);
00370   if (total <= 0)
00371     for (i=0; i<n; i++) part[i]= 1;
00372   int nr_ext= 0;
00373   for (i=0; i<n; i++)
00374     if (w[i] != W[i] && part[i] > 0)
00375       nr_ext++;
00376   if (nr_ext != 0)
00377     for (i=0; i<n; i++)
00378       if (w[i] == W[i])
00379        part[i]= 0;
00380   total= sum (part, n);
00381 
00382   STACK_NEW_ARRAY (Part, double, n);
00383   for (i=0; i<n; i++) Part[i]= part[i];
00384 
00385   SI old_room= room;
00386   while (true) {
00387     for (i=0; i<n; i++)
00388       if (part[i] > 0) {
00389        SI extra= (SI) ((part[i]/total) * old_room);
00390        if (w[i]+extra >= W[i]) {
00391          room    -= (W[i]-w[i]);
00392          w[i]     = W[i];
00393          l[i]     = L[i];
00394          r[i]     = R[i];
00395          part[i]  = 0;
00396        }
00397       }
00398     total= sum (part, n);
00399     if (room <= 0) {
00400       STACK_DELETE_ARRAY (Part);
00401       return;
00402     }
00403     if ((total <= 0) || (room == old_room) || (room <= 0)) break;
00404     old_room= room;
00405   }
00406 
00407   if (total <= 0) {
00408     for (i=0; i<n; i++) part[i]= Part[i];
00409     total= sum (part, n);
00410   }
00411   for (i=0; i<n; i++) {
00412     SI extra = (SI) ((part[i]/total) * room);
00413     w[i] += extra;
00414     if (L[i] + R[i] >= w[i]) {
00415       SI s= l[i] + r[i];
00416       SI S= L[i] + R[i];
00417       double lambda= ((double) (w[i] - s)) / ((double) (S - s));
00418       l[i] += (SI) (lambda * (L[i] - l[i]));
00419       r[i]  = w[i] - l[i];
00420     }
00421     else {
00422       l[i]= L[i];
00423       r[i]= R[i];
00424     }
00425   }
00426   STACK_DELETE_ARRAY (Part);
00427 }
00428 
00429 /******************************************************************************
00430 * Horizontal positioning
00431 ******************************************************************************/
00432 
00433 void
00434 table_rep::compute_widths (SI* Mw, SI* Lw, SI* Rw, bool large) {
00435   int i, j;
00436   for (j=0; j<nr_cols; j++)
00437     Mw[j]= Lw[j]= Rw[j]= 0;
00438 
00439   for (j=0; j<nr_cols; j++)
00440     for (i=0; i<nr_rows; i++) {
00441       cell C= T[i][j];
00442       if ((!is_nil (C)) && (C->col_span == 1)) {
00443        SI cmw, clw, crw;
00444        C->compute_width (cmw, clw, crw, large);
00445        //cout << i << ", " << j << ": "
00446        //<< (cmw>>8) << "; " << (clw>>8) << ", " << (crw>>8) << "\n";
00447        Mw[j]= max (Mw[j], cmw);
00448        Lw[j]= max (Lw[j], clw);
00449        Rw[j]= max (Rw[j], crw);
00450        Mw[j]= max (Mw[j], Lw[j] + Rw[j]);
00451       }
00452     }
00453 
00454   for (j=0; j<nr_cols; j++)
00455     for (i=0; i<nr_rows; i++) {
00456       cell C= T[i][j];
00457       if ((!is_nil (C)) && (C->col_span != 1)) {
00458        SI cmw, clw, crw;
00459        C->compute_width (cmw, clw, crw, large);
00460        SI tot= sum (Mw+j, C->col_span);
00461        if (cmw > tot) Mw[j] += cmw - tot;
00462       }
00463     }
00464 }
00465 
00466 void
00467 table_rep::compute_horizontal_parts (double* part) {
00468   int i, j;
00469   for (j=0; j<nr_cols; j++) part[j]= 0.0;
00470   for (j=0; j<nr_cols; j++)
00471     for (i=0; i<nr_rows; i++) {
00472       cell C= T[i][j];
00473       if (!is_nil (C))
00474        part[j]= max (part[j], C->hpart);
00475     }
00476 }
00477 
00478 void
00479 table_rep::position_columns () {
00480   //cout << "-------------------------------------\n";
00481   //cout << "Position columns " << (width >> 8) << "\n";
00482   compute_widths (mw, lw, rw, hmode == "auto");
00483   //for (int i=0; i<nr_cols; i++)
00484   //cout << "Column " << i << ": " << (mw[i]>>8) << "; "
00485   //<< (lw[i]>>8) << ", " << (rw[i]>>8) << "\n";
00486   if (hmode != "auto") {
00487     SI min_width= sum (mw, nr_cols);
00488     SI hextra= width - min_width;
00489     //cout << "Extra horizontal " << (hextra >> 8) << "\n";
00490     if (hextra > 0) {
00491       STACK_NEW_ARRAY (part, double, nr_cols);
00492       STACK_NEW_ARRAY (Mw, SI, nr_cols);
00493       STACK_NEW_ARRAY (Lw, SI, nr_cols);
00494       STACK_NEW_ARRAY (Rw, SI, nr_cols);
00495       compute_horizontal_parts (part);
00496       compute_widths (Mw, Lw, Rw, true);
00497       SI max_width= sum (Mw, nr_cols);
00498       SI computed_width= width;
00499       if (hmode == "min") computed_width= min (width, max_width);
00500       if (hmode == "max") computed_width= max (width, min_width);
00501       hextra= max (computed_width - min_width, 0);
00502       //for (int i=0; i<nr_cols; i++)
00503       //cout << "Column " << i << ": " << (Mw[i]>>8) << "; "
00504       //<< (Lw[i]>>8) << ", " << (Rw[i]>>8) << "\n";
00505       blow_up (mw, lw, rw, Mw, Lw, Rw, hextra, part, nr_cols);
00506       //for (int i=0; i<nr_cols; i++)
00507       //cout << "Column " << i << ": " << (mw[i]>>8) << "; "
00508       //<< (lw[i]>>8) << ", " << (rw[i]>>8) << "\n";
00509       STACK_DELETE_ARRAY (part);
00510       STACK_DELETE_ARRAY (Mw);
00511       STACK_DELETE_ARRAY (Lw);
00512       STACK_DELETE_ARRAY (Rw);
00513     }
00514   }
00515 
00516   int i, j;
00517   for (j=0; j<nr_cols; j++)
00518     for (i=0; i<nr_rows; i++) {
00519       cell C= T[i][j];
00520       if (!is_nil (C)) {
00521        if (!is_nil (C->T)) {
00522          C->T->width= mw[j]- C->lborder- C->rborder;
00523          C->T->hmode= "exact";
00524          C->T->position_columns ();
00525        }
00526        else if (C->hyphen != "n")
00527          C->width= mw[j];
00528       }
00529     }
00530 
00531   SI xoff;
00532   if (halign == "l") xoff= 0;
00533   else if (halign == "c") xoff= -sum (mw, nr_cols) >> 1;
00534   else if (halign == "r") xoff= -sum (mw, nr_cols);
00535   else if (halign == "L") xoff= -lw[0];
00536   else if (halign == "C")
00537     xoff= -(sum (mw, nr_cols>>1) + sum (mw, (nr_cols-1)>>1)+
00538            lw[nr_cols>>1] + lw[(nr_cols-1)>>1]) >> 1;
00539   else if (halign == "R") xoff= -sum (mw, nr_cols - 1) - lw[nr_cols - 1];
00540   else if (halign == "O") {
00541     int orig= col_origin;
00542     if (orig < 0) orig += nr_cols;
00543     else orig--;
00544     orig= max (min (orig, nr_cols - 1), 0);
00545     xoff= -sum (mw, orig) - lw[orig];
00546   }
00547   else xoff= -sum (mw, j0) - lw[j0];
00548 
00549   x1= xoff- lborder- lsep;
00550   for (j=0; j<nr_cols; j++) xoff += mw[j];
00551   x2= xoff+ rborder+ rsep;
00552 }
00553 
00554 void
00555 table_rep::compute_width (SI& tmw, SI& tlw, SI& trw) {
00556   position_columns ();
00557   tmw= x2 - x1;
00558   tlw= -x1;
00559   trw= x2;
00560 }
00561 
00562 /******************************************************************************
00563 * Vertical positioning
00564 ******************************************************************************/
00565 
00566 void
00567 table_rep::compute_heights (SI* mh, SI* bh, SI* th) {
00568   int i, j;
00569   for (i=0; i<nr_rows; i++)
00570     mh[i]= bh[i]= th[i]= 0;
00571 
00572   for (i=0; i<nr_rows; i++)
00573     for (j=0; j<nr_cols; j++) {
00574       cell C= T[i][j];
00575       if ((!is_nil (C)) && (C->row_span==1)) {
00576        SI cmh, cbh, cth;
00577        C->compute_height (cmh, cbh, cth);
00578        mh[i]= max (mh[i], cmh);
00579        bh[i]= max (bh[i], cbh);
00580        th[i]= max (th[i], cth);
00581        mh[i]= max (mh[i], bh[i] + th[i]);
00582       }
00583     }
00584 
00585   for (i=0; i<nr_rows; i++)
00586     for (j=0; j<nr_cols; j++) {
00587       cell C= T[i][j];
00588       if ((!is_nil (C)) && (C->row_span!=1)) {
00589        SI cmh, cbh, cth;
00590        C->compute_height (cmh, cbh, cth);
00591        SI tot= sum (mh+i, C->row_span);
00592        if (cmh > tot) mh[i] += cmh - tot;
00593       }
00594     }
00595 }
00596 
00597 void
00598 table_rep::compute_vertical_parts (double* part) {
00599   int i, j;
00600   for (i=0; i<nr_rows; i++) part[i]= 0.0;
00601   for (i=0; i<nr_rows; i++)
00602     for (j=0; j<nr_cols; j++) {
00603       cell C= T[i][j];
00604       if (!is_nil (C))
00605        part[i]= max (part[i], C->vpart);
00606     }
00607 }
00608 
00609 void
00610 table_rep::position_rows () {
00611   STACK_NEW_ARRAY (mh, SI, nr_rows);
00612   STACK_NEW_ARRAY (bh, SI, nr_rows);
00613   STACK_NEW_ARRAY (th, SI, nr_rows);
00614 
00615   compute_heights (mh, bh, th);
00616   if (vmode != "auto") {
00617     SI min_height= sum (mh, nr_rows);
00618     SI vextra= height - min_height;
00619     if (vextra > 0) {
00620       int i;
00621       STACK_NEW_ARRAY (part, double, nr_rows);
00622       STACK_NEW_ARRAY (Mh, SI, nr_rows);
00623       STACK_NEW_ARRAY (Bh, SI, nr_rows);
00624       STACK_NEW_ARRAY (Th, SI, nr_rows);
00625       compute_vertical_parts (part);
00626       for (i=0; i<nr_rows; i++) {
00627        Mh[i]= mh[i];
00628        Bh[i]= bh[i];
00629        Th[i]= th[i]; 
00630       }
00631       SI computed_height= height;
00632       if (vmode == "min") computed_height= min (height, min_height);
00633       if (vmode == "max") computed_height= max (height, min_height);
00634       vextra= max (computed_height - min_height, 0);
00635       blow_up (mh, bh, th, Mh, Bh, Th, vextra, part, nr_rows);
00636       STACK_DELETE_ARRAY (part);
00637       STACK_DELETE_ARRAY (Mh);
00638       STACK_DELETE_ARRAY (Bh);
00639       STACK_DELETE_ARRAY (Th);
00640     }
00641   }
00642 
00643   int i, j;
00644   for (i=0; i<nr_rows; i++) {
00645     for (j=0; j<nr_cols; j++) {
00646       cell C= T[i][j];
00647       if ((!is_nil (C)) && (!is_nil (C->T))) {
00648        C->T->height= mh[j]- C->bborder- C->tborder;
00649        C->T->vmode = "exact";
00650        C->T->position_rows ();
00651       }
00652     }
00653   }
00654 
00655   SI yoff;
00656   if (valign == "t") yoff= 0;
00657   else if (valign == "c") yoff= sum (mh, nr_rows) >> 1;
00658   else if (valign == "f") yoff= (sum (mh, nr_rows) >> 1) + env->fn->yfrac;
00659   else if (valign == "b") yoff= sum (mh, nr_rows);
00660   else if (valign == "T") yoff= th[0];
00661   else if (valign == "C")
00662     yoff= (sum (mh, nr_rows>>1) + sum (mh, (nr_rows-1)>>1) +
00663           th[nr_rows>>1] + th[(nr_rows-1)>>1]) >> 1;
00664   else if (valign == "B") yoff= sum (mh, nr_rows - 1) + th[nr_rows - 1];
00665   else if (valign == "O") {
00666     int orig= row_origin;
00667     if (orig < 0) orig += nr_rows;
00668     else orig--;
00669     orig= max (min (orig, nr_rows - 1), 0);
00670     yoff= sum (mh, orig) + th[orig];
00671   }
00672   else yoff= sum (mh, i0) + th[i0];
00673 
00674   y2= yoff+ tborder+ tsep;
00675   for (i=0; i<nr_rows; i++) {
00676     for (j=0; j<nr_cols; j++) {
00677       cell C= T[i][j];
00678       if (!is_nil (C)) {
00679        SI tot= sum (mh+i, C->row_span);
00680        C->position_vertically (yoff, tot, tot+ bh[i]- mh[i], th[i]);
00681        C->shift= -yoff+ tot- bh[i];
00682       }
00683     }
00684     yoff -= mh[i];
00685   }
00686   y1= yoff- bborder+ bsep;
00687 
00688   STACK_DELETE_ARRAY (mh);
00689   STACK_DELETE_ARRAY (bh);
00690   STACK_DELETE_ARRAY (th);
00691 }
00692 
00693 void
00694 table_rep::compute_height (SI& tmh, SI& tbh, SI& tth) {
00695   position_rows ();
00696   tmh= y2 - y1;
00697   tbh= -y1;
00698   tth= y2;
00699 }
00700 
00701 /******************************************************************************
00702 * Generate table
00703 ******************************************************************************/
00704 
00705 void
00706 table_rep::finish_horizontal () {
00707   int i, j;
00708   SI xoff= x1 + lborder + lsep;
00709   for (j=0; j<nr_cols; j++) {
00710     for (i=0; i<nr_rows; i++) {
00711       cell C= T[i][j];
00712       if (!is_nil (C)) {
00713        if (!is_nil (C->T))
00714          C->T->finish_horizontal ();
00715        else if (C->hyphen != "n")
00716          C->finish_horizontal ();
00717        SI tot= sum (mw+j, C->col_span);
00718        C->position_horizontally (xoff, tot, lw[j], rw[j+C->col_span-1]);
00719       }
00720     }
00721     xoff += mw[j];
00722   }
00723 }
00724 
00725 void
00726 table_rep::finish () {
00727   int i, j;
00728   array<box> bs;
00729   array<SI>  x;
00730   array<SI>  y;
00731   for (i=0; i<nr_rows; i++)
00732     for (j=0; j<nr_cols; j++)
00733       if (!is_nil (T[i][j])) {
00734        cell C = T[i][j];
00735        C->finish ();
00736        bs << C->b;
00737        x  << C->x1;
00738        y  << C->y1;
00739       }
00740 
00741   box   tb = composite_box (ip, bs, x, y, false);
00742   SI    x1= tb->x1;
00743   SI    y1= tb->y1;
00744   SI    x2= tb->x2;
00745   SI    y2= tb->y2;
00746   color fg= env->col;
00747   b= cell_box (tb->ip, tb, 0, 0, x1, y1, x2, y2,
00748               lborder, rborder, bborder, tborder, fg, "", 0);
00749   SI Lsep= lsep+lborder, Rsep= rsep+rborder;
00750   SI Bsep= bsep+bborder, Tsep= tsep+tborder;
00751   if ((Lsep != 0) || (Rsep != 0) || (Bsep != 0) || (Tsep != 0))
00752     b= cell_box (b->ip, b, Lsep, 0, x1, y1-Bsep, x2+Lsep+Rsep, y2+Tsep,
00753                0, 0, 0, 0, fg, "", 0);
00754 }
00755 
00756 array<box>
00757 table_rep::var_finish () {
00758   if (hyphen == "n") {
00759     array<box> bs (1);
00760     finish ();
00761     bs[0]= b;
00762     return bs;
00763   }
00764 
00765   int i, j;
00766   array<box> stack;
00767   for (i=0; i<nr_rows; i++) {
00768     array<box> bs;
00769     array<SI>  x;
00770     array<SI>  y;
00771     for (j=0; j<nr_cols; j++)
00772       if (!is_nil (T[i][j])) {
00773        cell C = T[i][j];
00774        C->finish ();
00775        bs << C->b;
00776        x  << C->x1;
00777        y  << (C->y1 + C->shift);
00778       }
00779     if (N(bs)==0) continue;
00780 
00781     box   tb= composite_box (ip, bs, x, y, false);
00782     SI    BB= (i==(nr_rows-1)? bborder: 0);
00783     SI    TB= (i==0? tborder: 0);
00784     SI    BS= (i==(nr_rows-1)? bsep: 0);
00785     SI    TS= (i==0? tsep: 0);
00786     SI    x1= tb->x1 - lborder;
00787     SI    x2= tb->x2 + rborder;
00788     SI    y1= tb->y1 - BB;
00789     SI    y2= tb->y2 + TB;
00790     color fg= env->col;
00791     b= cell_box (tb->ip, tb, 0, 0, x1, y1, x2, y2,
00792                lborder, rborder, BB, TB, fg, "", 0);
00793     SI Lsep= lsep+lborder, Rsep= rsep+rborder;
00794     SI Bsep= BS+BB, Tsep= TS+TB;
00795     if ((Lsep != 0) || (Rsep != 0) || (Bsep != 0) || (Tsep != 0))
00796       b= cell_box (b->ip, b, Lsep, 0, x1, y1-Bsep, x2+Lsep+Rsep, y2+Tsep,
00797                  0, 0, 0, 0, fg, "", 0);
00798     stack << b;
00799   }
00800   return stack;
00801 }
00802 
00803 /******************************************************************************
00804 * Lazy tables
00805 ******************************************************************************/
00806 
00807 struct lazy_table_rep: public lazy_rep {
00808   table T; // the table
00809   lazy_table_rep (table T2, path ip): lazy_rep (LAZY_TABLE, ip), T (T2) {}
00810   operator tree () { return "Table"; }
00811   lazy produce (lazy_type request, format fm);
00812   format query (lazy_type request, format fm);
00813 };
00814 
00815 struct lazy_table {
00816   EXTEND_NULL(lazy,lazy_table);
00817   inline lazy_table (table T, path ip):
00818     rep (tm_new<lazy_table_rep> (T, ip)) { rep->ref_count= 1; }
00819 };
00820 EXTEND_NULL_CODE(lazy,lazy_table);
00821 
00822 format
00823 lazy_table_rep::query (lazy_type request, format fm) {
00824   //cout << "Query= " << request << "\n";
00825   //cout << "  format= " << fm << "\n";
00826   //cout << "  par= " << T->env->as_length ("1par") << "\n";
00827   if ((request == LAZY_BOX) && (fm->type == QUERY_VSTREAM_WIDTH)) {
00828     SI tmw, tlw, trw;
00829     T->compute_width (tmw, tlw, trw);
00830     return make_format_width (max (tmw, 1));
00831   }
00832   return lazy_rep::query (request, fm);
00833 }
00834 
00835 lazy
00836 lazy_table_rep::produce (lazy_type request, format fm) {
00837   //cout << "produce= " << request << "\n";
00838   //cout << "  format= " << fm << "\n";
00839   //cout << "  par= " << T->env->as_length ("1par") << "\n";
00840   if (request == type) return this;
00841   if (request == LAZY_VSTREAM) {
00842     if (fm->type == FORMAT_VSTREAM) {
00843       format_vstream fs= (format_vstream) fm;
00844       string s= as_string (T->var[TABLE_WIDTH]);
00845       if (ends (s, "par"))
00846        T->width= max ((SI) (as_double (s (0, N(s)-3)) * fs->width), 1);
00847     }
00848     T->position_columns ();
00849     T->finish_horizontal ();
00850     T->position_rows ();
00851     array<box> bs= T->var_finish ();
00852     tree old1= T->env->local_begin (PAR_LEFT, "0tmpt");
00853     tree old2= T->env->local_begin (PAR_RIGHT, "0tmpt");
00854     // FIXME: check whether we should also set the other
00855     // paragraph formatting variables, as in cell_rep::typeset
00856     lazy tmp= make_lazy_paragraph (T->env, bs, ip);
00857     T->env->local_end (PAR_RIGHT, old2);
00858     T->env->local_end (PAR_LEFT, old1);
00859     return tmp->produce (request, fm);
00860   }
00861   return lazy_rep::produce (request, fm);
00862 }
00863 
00864 lazy
00865 make_lazy_table (edit_env env, tree t, path ip) {
00866   table T (env);
00867   T->typeset (t, ip);
00868   T->handle_decorations ();
00869   T->handle_span ();
00870   T->merge_borders (); // FIXME: introduce merge_border variables
00871   return lazy_table (T, ip);
00872 }
00873 
00874 /******************************************************************************
00875 * User interface
00876 ******************************************************************************/
00877 
00878 box
00879 typeset_as_table (edit_env env, tree t, path ip) {
00880   table T (env);
00881   T->typeset (t, ip);
00882   T->handle_decorations ();
00883   T->handle_span ();
00884   T->merge_borders ();
00885   T->position_columns ();
00886   T->finish_horizontal ();
00887   T->position_rows ();
00888   T->finish ();
00889   return T->b;
00890 }
00891 
00892 array<box>
00893 typeset_as_var_table (edit_env env, tree t, path ip) {
00894   table T (env);
00895   T->typeset (t, ip);
00896   T->handle_decorations ();
00897   T->handle_span ();
00898   T->merge_borders ();
00899   T->position_columns ();
00900   T->finish_horizontal ();
00901   T->position_rows ();
00902   return T->var_finish ();
00903 }