]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/table.C
move some selection related stuff over to textcursor.C
[lyx.git] / src / tex2lyx / table.C
1 /** The .tex to .lyx converter
2     \author André Pönitz (2003)
3  */
4
5 // {[(
6
7 #include <config.h>
8
9 #include "Lsstream.h"
10 #include "tex2lyx.h"
11
12 #include <cctype>
13 #include <fstream>
14 #include <iostream>
15 #include <vector>
16
17 using std::cerr;
18 using std::endl;
19 using std::istringstream;
20 using std::ostream;
21 using std::ostringstream;
22 using std::vector;
23
24 #include "mathed/math_gridinfo.h"
25
26 namespace {
27
28 int string2int(string const & s, int deflt = 0)
29 {
30         istringstream is(STRCONV(s));
31         int i = deflt;
32         is >> i;
33         return i;
34 }
35
36
37 string read_hlines(Parser & p)
38 {
39         ostringstream os;
40         p.skip_spaces();
41         while (p.good()) {
42                 if (p.next_token().cs() == "hline") {
43                         p.get_token();
44                         os << "\\hline";
45                 } else if (p.next_token().cs() == "cline") {
46                         p.get_token();
47                         os << "\\cline{" << p.verbatim_item() << "}";
48                 } else
49                         break;
50                 p.skip_spaces();
51         };
52         //cerr << "read_hlines(), read: '" << os.str() << "'\n";
53         //cerr << "read_hlines(), next token: " << p.next_token() << "\n";
54         return STRCONV(os.str());
55 }
56
57
58
59 /* rather brutish way to code table structure in a string:
60
61   \begin{tabular}{ccc}
62     1 & 2 & 3\\ \hline
63     \multicolumn{2}{c}{4} & 5 //
64     6 & 7 \\
65   \end{tabular}
66
67  gets "translated" to:
68
69   1 TAB 2 TAB 3 LINE
70   \hline HLINE  TAB 5 LINE
71   5 TAB 7 LINE
72 */
73
74 char const TAB   = '\001';
75 char const LINE  = '\002';
76 char const HLINE = '\004';
77
78
79 void handle_colalign(Parser & p, vector<ColInfo> & colinfo)
80 {
81         if (p.get_token().cat() != catBegin)
82                 cerr << "wrong syntax for table column alignment. '{' expected\n";
83
84         char nextalign = 'b';
85         bool leftline = false;
86         for (Token t=p.get_token(); p.good() && t.cat() != catEnd; t = p.get_token()){
87 #ifdef FILEDEBUG
88                 cerr << "t: " << t << "  c: '" << t.character() << "'\n";
89 #endif
90
91                 switch (t.character()) {
92                         case 'c':
93                         case 'l':
94                         case 'r': {
95                                 ColInfo ci;
96                                 ci.align = t.character();
97                                 if (colinfo.size() && colinfo.back().rightline > 1) {
98                                         ci.leftline = true;
99                                         --colinfo.back().rightline;
100                                 }
101                                 colinfo.push_back(ci);
102                                 break;
103                         }
104                         case 'p':
105                                 colinfo.push_back(ColInfo());
106                                 colinfo.back().align = nextalign;
107                                 colinfo.back().width = p.verbatim_item();
108                                 nextalign = 'b';
109                                 break;
110                         case '|':
111                                 if (colinfo.empty())
112                                         leftline = true;
113                                 else
114                                         ++colinfo.back().rightline;
115                                 break;
116                         case '>': {
117                                 string s = p.verbatim_item();
118                                 if (s == "\\raggedleft ")
119                                         nextalign = 'l';
120                                 else if (s == "\\raggedright ")
121                                         nextalign = 'r';
122                                 else
123                                         cerr << "unknown '>' column '" << s << "'\n";
124                                 break;
125                         }
126                         default:
127                                 cerr << "ignoring special separator '" << t << "'\n";
128                                 break;
129                         }
130         }
131         if (colinfo.size() && leftline)
132                 colinfo[0].leftline = true;
133 }
134
135
136 } // anonymous namespace
137
138
139 void parse_table(Parser & p, ostream & os, unsigned flags)
140 {
141         string hlines;
142
143         while (p.good()) {
144                 Token const & t = p.get_token();
145
146 #ifdef FILEDEBUG
147                 cerr << "t: " << t << " flags: " << flags << "\n";
148 #endif
149
150                 //
151                 // cat codes
152                 //
153                 if (t.cat() == catMath) {
154                                 // we are inside some text mode thingy, so opening new math is allowed
155                         Token const & n = p.get_token();
156                         if (n.cat() == catMath) {
157                                 // TeX's $$...$$ syntax for displayed math
158                                 os << "\\[";
159                                 parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
160                                 os << "\\]";
161                                 p.get_token(); // skip the second '$' token
162                         } else {
163                                 // simple $...$  stuff
164                                 p.putback();
165                                 os << '$';
166                                 parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
167                                 os << '$';
168                         }
169                 }
170
171                 else if (t.cat() == catLetter ||
172                                t.cat() == catSpace ||
173                                t.cat() == catSuper ||
174                                t.cat() == catSub ||
175                                t.cat() == catOther ||
176                                t.cat() == catActive ||
177                                t.cat() == catNewline ||
178                                t.cat() == catParameter)
179                         os << t.character();
180
181                 else if (t.cat() == catBegin) {
182                         os << '{';
183                         parse_table(p, os, FLAG_BRACE_LAST);
184                         os << '}';
185                 }
186
187                 else if (t.cat() == catEnd) {
188                         if (flags & FLAG_BRACE_LAST)
189                                 return;
190                         cerr << "unexpected '}'\n";
191                 }
192
193                 else if (t.cat() == catAlign) {
194                         os << TAB;
195                 }
196
197                 else if (t.cs() == "tabularnewline" || t.cs() == "\\") {
198                         // stuff before the line break
199                         // and look ahead for stuff after the line break
200                         os << HLINE << hlines << HLINE << LINE << read_hlines(p) << HLINE;
201                         hlines.erase();
202                 }
203
204                 else if (t.cs() == "hline")
205                         hlines += "\\hline";
206
207                 else if (t.cs() == "cline")
208                         hlines += "\\cline{" + p.verbatim_item() + '}';
209
210                 else if (t.cat() == catComment)
211                         handle_comment(p);
212
213                 else if (t.cs() == "(") {
214                         os << "\\(";
215                         parse_math(p, os, FLAG_SIMPLE2, MATH_MODE);
216                         os << "\\)";
217                 }
218
219                 else if (t.cs() == "[") {
220                         os << "\\[";
221                         parse_math(p, os, FLAG_EQUATION, MATH_MODE);
222                         os << "\\]";
223                 }
224
225                 else if (t.cs() == "begin") {
226                         string const name = p.getArg('{', '}');
227                         active_environments.push_back(name);
228                         parse_table(p, os, FLAG_END);
229                 }
230
231                 else if (t.cs() == "end") {
232                         if (flags & FLAG_END) {
233                                 // eat environment name
234                                 string const name = p.getArg('{', '}');
235                                 if (name != active_environment())
236                                         p.error("\\end{" + name + "} does not match \\begin{"
237                                                 + active_environment() + "}");
238                                 active_environments.pop_back();
239                                 return;
240                         }
241                         p.error("found 'end' unexpectedly");
242                 }
243
244                 else 
245                         os << t.asInput();
246         }
247 }
248
249
250 void handle_hline_above(RowInfo & ri, vector<CellInfo> & ci)
251 {
252         ri.topline = true;
253         for (size_t col = 0; col < ci.size(); ++col)
254                 ci[col].topline = true;
255 }
256
257
258 void handle_hline_below(RowInfo & ri, vector<CellInfo> & ci)
259 {
260         ri.bottomline = true;
261         for (size_t col = 0; col < ci.size(); ++col)
262                 ci[col].bottomline = true;
263 }
264
265
266 void handle_tabular(Parser & p, ostream & os)
267 {
268         string posopts = p.getOpt();
269         if (posopts.size())
270                 cerr << "vertical tabular positioning '" << posopts << "' ignored\n";
271
272         vector<ColInfo>            colinfo;
273
274         // handle column formatting
275         handle_colalign(p, colinfo);
276
277         // handle initial hlines
278
279         // first scan of cells
280         // use table mode to keep it minimal-invasive
281         // not exactly what's TeX doing...
282         vector<string> lines;
283         ostringstream ss;
284         ss << read_hlines(p) << HLINE; // handle initial hlines
285         parse_table(p, ss, FLAG_END);
286         split(STRCONV(ss.str()), lines, LINE);
287
288         vector< vector<CellInfo> > cellinfo(lines.size());
289         vector<RowInfo> rowinfo(lines.size());
290         
291         // split into rows
292         //cerr << "// split into rows\n";
293         for (size_t row = 0; row < rowinfo.size(); ++row) {
294
295                 // init row
296                 cellinfo[row].resize(colinfo.size());
297
298                 // split row    
299                 vector<string> dummy;
300                 //cerr << "\n########### LINE: " << lines[row] << "########\n";
301                 split(lines[row], dummy, HLINE);
302
303                 // handle horizontal line fragments
304                 if (dummy.size() != 3) {
305                         if (dummy.size() != 1)
306                                 cerr << "unexpected dummy size: " << dummy.size()
307                                         << " content: " << lines[row] << "\n";
308                         dummy.resize(3);
309                 }
310                 lines[row] = dummy[1];
311
312                 //cerr << "line: " << row << " above 0: " << dummy[0] << "\n";
313                 //cerr << "line: " << row << " below 2: " << dummy[2] <<  "\n";
314                 //cerr << "line: " << row << " cells 1: " << dummy[1] <<  "\n";
315
316                 for (int i = 0; i <= 2; i += 2) {       
317                         //cerr << "   reading from line string '" << dummy[i] << "'\n";
318                         Parser p1(dummy[i]);
319                         while (p1.good()) {
320                                 Token t = p1.get_token();
321                                 //cerr << "read token: " << t << "\n";
322                                 if (t.cs() == "hline") {
323                                         if (i == 0) {
324                                                 if (rowinfo[row].topline) {
325                                                         if (row > 0) // extra bottomline above
326                                                                 handle_hline_below(rowinfo[row - 1], cellinfo[row - 1]);
327                                                         else
328                                                                 cerr << "dropping extra hline\n";
329                                                         //cerr << "below row: " << row-1 << endl;
330                                                 } else {
331                                                         handle_hline_above(rowinfo[row], cellinfo[row]);
332                                                         //cerr << "above row: " << row << endl;
333                                                 }
334                                         } else {        
335                                                 //cerr << "below row: " << row << endl;
336                                                 handle_hline_below(rowinfo[row], cellinfo[row]);
337                                         }
338                                 } else if (t.cs() == "cline") {
339                                         string arg = p1.verbatim_item();
340                                         //cerr << "read cline arg: '" << arg << "'\n";
341                                         vector<string> t;
342                                         split(arg, t, '-');
343                                         t.resize(2);
344                                         size_t from = string2int(t[0]) - 1;
345                                         size_t to = string2int(t[1]);
346                                         for (size_t col = from; col < to; ++col) {
347                                                 //cerr << "row: " << row << " col: " << col << " i: " << i << endl;
348                                                 if (i == 0) {
349                                                         rowinfo[row].topline = true;
350                                                         cellinfo[row][col].topline = true;
351                                                 } else {
352                                                         rowinfo[row].bottomline = true;
353                                                         cellinfo[row][col].bottomline = true;
354                                                 }
355                                         }
356                                 } else {
357                                         cerr << "unexpected line token: " << t << endl;
358                                 }
359                         }
360                 }
361
362                 // split into cells
363                 vector<string> cells;
364                 split(lines[row], cells, TAB);
365                 for (size_t col = 0, cell = 0;
366                                 cell < cells.size() && col < colinfo.size(); ++col, ++cell) {
367                         //cerr << "cell content: '" << cells[cell] << "'\n";
368                         Parser p(cells[cell]);
369                         p.skip_spaces();        
370                         //cells[cell] << "'\n";
371                         if (p.next_token().cs() == "multicolumn") {
372                                 // how many cells?
373                                 p.get_token();
374                                 size_t const ncells = string2int(p.verbatim_item());
375
376                                 // special cell properties alignment    
377                                 vector<ColInfo> t;
378                                 handle_colalign(p, t);
379                                 cellinfo[row][col].multi     = 1;
380                                 cellinfo[row][col].align     = t.front().align;
381                                 cellinfo[row][col].content   = parse_text(p, FLAG_ITEM, false);
382                                 cellinfo[row][col].leftline  |= t.front().leftline;
383                                 cellinfo[row][col].rightline |= t.front().rightline;
384
385                                 // add dummy cells for multicol
386                                 for (size_t i = 0; i < ncells - 1 && col < colinfo.size(); ++i) {
387                                         ++col;
388                                         cellinfo[row][col].multi = 2;
389                                         cellinfo[row][col].align = 'c';
390                                 }
391
392                                 // more than one line on the right?
393                                 if (t.front().rightline > 1)
394                                         cellinfo[row][col + 1].leftline = true;
395
396                         } else {        
397                                 // FLAG_END is a hack, we need to read all of it
398                                 cellinfo[row][col].content = parse_text(p, FLAG_END, false);
399                         }
400                 }
401
402                 //cerr << "//  handle almost empty last row what we have\n";
403                 // handle almost empty last row
404                 if (row && lines[row].empty() && row + 1 == rowinfo.size()) {
405                         //cerr << "remove empty last line\n";
406                         if (rowinfo[row].topline)
407                                 rowinfo[row - 1].bottomline = true;
408                         for (size_t col = 0; col < colinfo.size(); ++col)
409                                 if (cellinfo[row][col].topline)
410                                         cellinfo[row - 1][col].bottomline = true;
411                         rowinfo.pop_back();
412                 }
413
414         }
415
416         //cerr << "// output what we have\n";
417         // output what we have
418         os << "<lyxtabular version=\"3\" rows=\"" << rowinfo.size()
419                  << "\" columns=\"" << colinfo.size() << "\">\n"
420                  << "<features>\n";
421
422         //cerr << "// after header\n";
423         for (size_t col = 0; col < colinfo.size(); ++col) {
424                 os << "<column alignment=\"" << colinfo[col].align << "\"";
425                 if (colinfo[col].rightline)
426                         os << " rightline=\"true\"";
427                 if (colinfo[col].leftline)
428                         os << " leftline=\"true\"";
429                 os << " valignment=\"top\"";
430                 os << " width=\"" << colinfo[col].width << "\"";
431                 os << ">\n";
432         }
433         //cerr << "// after cols\n";
434
435         for (size_t row = 0; row < rowinfo.size(); ++row) {
436                 os << "<row";
437                 if (rowinfo[row].topline)
438                         os << " topline=\"true\"";
439                 if (rowinfo[row].bottomline)
440                         os << " bottomline=\"true\"";
441                 os << ">\n";
442                 for (size_t col = 0; col < colinfo.size(); ++col) {
443                         CellInfo const & cell = cellinfo[row][col];
444                         os << "<cell";
445                         if (cell.multi)
446                                 os << " multicolumn=\"" << cell.multi << "\"";
447                         if (cell.leftline)
448                                 os << " leftline=\"true\"";
449                         if (cell.rightline)
450                                 os << " rightline=\"true\"";
451                         if (cell.topline)
452                                 os << " topline=\"true\"";
453                         if (cell.bottomline)
454                                 os << " bottomline=\"true\"";
455                         //cerr << "\nrow: " << row << " col: " << col;
456                         //if (cell.topline)
457                         //      cerr << " topline=\"true\"";
458                         //if (cell.bottomline)
459                         //      cerr << " bottomline=\"true\"";
460                         os << " alignment=\"" << verbose_align(cell.align) << "\""
461                                  << " valignment=\"top\""
462                                  << " usebox=\"none\""
463                                  << ">"
464                            << "\n\\begin_inset Text"
465                            << "\n\n\\layout Standard\n\n"
466                            << cell.content
467                            << "\n\\end_inset\n\n"
468                            << "</cell>\n";
469                 }
470                 os << "</row>\n";
471         }
472                         
473         os << "</lyxtabular>\n";
474 }
475
476
477
478
479 // }])