]> git.lyx.org Git - lyx.git/blob - src/buffer.C
Remove LYX_LAYOUT_DEFAULT - it is very very old, even klyx doesn't use it.
[lyx.git] / src / buffer.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2001 The LyX Team.
8  *
9  *           This file is Copyright 1996-2001
10  *           Lars Gullik Bjønnes
11  *
12  * ======================================================
13  */
14
15 #include <config.h>
16
17 #ifdef __GNUG__
18 #pragma implementation
19 #endif
20
21 #include "buffer.h"
22 #include "bufferlist.h"
23 #include "lyx_main.h"
24 #include "LyXAction.h"
25 #include "lyxrc.h"
26 #include "lyxlex.h"
27 #include "tex-strings.h"
28 #include "layout.h"
29 #include "bufferview_funcs.h"
30 #include "lyxfont.h"
31 #include "version.h"
32 #include "LaTeX.h"
33 #include "Chktex.h"
34 #include "frontends/LyXView.h"
35 #include "debug.h"
36 #include "LaTeXFeatures.h"
37 #include "lyxtext.h"
38 #include "gettext.h"
39 #include "language.h"
40 #include "encoding.h"
41 #include "exporter.h"
42 #include "Lsstream.h"
43 #include "converter.h"
44 #include "BufferView.h"
45 #include "ParagraphParameters.h"
46 #include "iterators.h"
47 #include "lyxtextclasslist.h"
48
49 #include "mathed/formulamacro.h"
50 #include "mathed/formula.h"
51
52 #include "insets/inset.h"
53 #include "insets/inseterror.h"
54 #include "insets/insetlabel.h"
55 #include "insets/insetref.h"
56 #include "insets/inseturl.h"
57 #include "insets/insetnote.h"
58 #include "insets/insetquotes.h"
59 #include "insets/insetlatexaccent.h"
60 #include "insets/insetbib.h"
61 #include "insets/insetcite.h"
62 #include "insets/insetexternal.h"
63 #include "insets/insetindex.h"
64 #include "insets/insetinclude.h"
65 #include "insets/insettoc.h"
66 #include "insets/insetparent.h"
67 #include "insets/insetspecialchar.h"
68 #include "insets/insettext.h"
69 #include "insets/insetert.h"
70 #include "insets/insetgraphics.h"
71 #include "insets/insetfoot.h"
72 #include "insets/insetmarginal.h"
73 #include "insets/insetminipage.h"
74 #include "insets/insetfloat.h"
75 #include "insets/insettabular.h"
76 #if 0
77 #include "insets/insettheorem.h"
78 #include "insets/insetlist.h"
79 #endif
80 #include "insets/insetcaption.h"
81 #include "insets/insetfloatlist.h"
82
83 #include "frontends/Dialogs.h"
84 #include "frontends/Alert.h"
85
86 #include "graphics/Previews.h"
87
88 #include "support/textutils.h"
89 #include "support/filetools.h"
90 #include "support/path.h"
91 #include "support/os.h"
92 #include "support/lyxlib.h"
93 #include "support/FileInfo.h"
94 #include "support/lyxmanip.h"
95 #include "support/lyxalgo.h" // for lyx::count
96
97 #include <fstream>
98 #include <iomanip>
99 #include <map>
100 #include <stack>
101 #include <list>
102 #include <algorithm>
103
104 #include <cstdlib>
105 #include <cmath>
106 #include <unistd.h>
107 #include <sys/types.h>
108 #include <utime.h>
109
110
111 #ifdef HAVE_LOCALE
112 #include <locale>
113 #endif
114
115 #ifndef CXX_GLOBAL_CSTD
116 using std::pow;
117 #endif
118
119 using std::ostream;
120 using std::ofstream;
121 using std::ifstream;
122 using std::fstream;
123 using std::ios;
124 using std::setw;
125 using std::endl;
126 using std::pair;
127 using std::make_pair;
128 using std::vector;
129 using std::map;
130 using std::max;
131 using std::set;
132 using std::stack;
133 using std::list;
134
135 using lyx::pos_type;
136 using lyx::textclass_type;
137
138 // all these externs should eventually be removed.
139 extern BufferList bufferlist;
140
141 extern LyXAction lyxaction;
142
143 namespace {
144
145 const int LYX_FORMAT = 220;
146
147 } // namespace anon
148
149 extern int tex_code_break_column;
150
151
152 Buffer::Buffer(string const & file, bool ronly)
153         : paragraph(0), niceFile(true), lyx_clean(true), bak_clean(true),
154           unnamed(false), dep_clean(0), read_only(ronly),
155           filename_(file), users(0)
156 {
157         lyxerr[Debug::INFO] << "Buffer::Buffer()" << endl;
158 //      filename = file;
159         filepath_ = OnlyPath(file);
160 //      paragraph = 0;
161 //      lyx_clean = true;
162 //      bak_clean = true;
163 //      dep_clean = 0;
164 //      read_only = ronly;
165 //      unnamed = false;
166 //      users = 0;
167         lyxvc.buffer(this);
168         if (read_only || lyxrc.use_tempdir) {
169                 tmppath = CreateBufferTmpDir();
170         } else {
171                 tmppath.erase();
172         }
173 }
174
175
176 Buffer::~Buffer()
177 {
178         lyxerr[Debug::INFO] << "Buffer::~Buffer()" << endl;
179         // here the buffer should take care that it is
180         // saved properly, before it goes into the void.
181
182         // make sure that views using this buffer
183         // forgets it.
184         if (users)
185                 users->buffer(0);
186
187         if (!tmppath.empty()) {
188                 DestroyBufferTmpDir(tmppath);
189         }
190
191         Paragraph * par = paragraph;
192         Paragraph * tmppar;
193         while (par) {
194                 tmppar = par->next();
195                 delete par;
196                 par = tmppar;
197         }
198         paragraph = 0;
199
200         // Remove any previewed LaTeX snippets assocoated with this buffer.
201         grfx::Previews::get().removeLoader(this);
202 }
203
204
205 string const Buffer::getLatexName(bool no_path) const
206 {
207         string const name = ChangeExtension(MakeLatexName(fileName()), ".tex");
208         if (no_path)
209                 return OnlyFilename(name);
210         else
211                 return name;
212 }
213
214
215 pair<Buffer::LogType, string> const Buffer::getLogName(void) const
216 {
217         string const filename = getLatexName(false);
218
219         if (filename.empty())
220                 return make_pair(Buffer::latexlog, string());
221
222         string path = OnlyPath(filename);
223
224         if (lyxrc.use_tempdir || !IsDirWriteable(path))
225                 path = tmppath;
226
227         string const fname = AddName(path,
228                                      OnlyFilename(ChangeExtension(filename,
229                                                                   ".log")));
230         string const bname =
231                 AddName(path, OnlyFilename(
232                         ChangeExtension(filename,
233                                         formats.extension("literate") + ".out")));
234
235         // If no Latex log or Build log is newer, show Build log
236
237         FileInfo const f_fi(fname);
238         FileInfo const b_fi(bname);
239
240         if (b_fi.exist() &&
241             (!f_fi.exist() || f_fi.getModificationTime() < b_fi.getModificationTime())) {
242                 lyxerr[Debug::FILES] << "Log name calculated as : " << bname << endl;
243                 return make_pair(Buffer::buildlog, bname);
244         }
245         lyxerr[Debug::FILES] << "Log name calculated as : " << fname << endl;
246         return make_pair(Buffer::latexlog, fname);
247 }
248
249
250 void Buffer::setReadonly(bool flag)
251 {
252         if (read_only != flag) {
253                 read_only = flag;
254                 updateTitles();
255                 users->owner()->getDialogs()->updateBufferDependent(false);
256         }
257 }
258
259
260 /// Update window titles of all users
261 // Should work on a list
262 void Buffer::updateTitles() const
263 {
264         if (users)
265                 users->owner()->updateWindowTitle();
266 }
267
268
269 /// Reset autosave timer of all users
270 // Should work on a list
271 void Buffer::resetAutosaveTimers() const
272 {
273         if (users)
274                 users->owner()->resetAutosaveTimer();
275 }
276
277
278 void Buffer::setFileName(string const & newfile)
279 {
280         filename_ = MakeAbsPath(newfile);
281         filepath_ = OnlyPath(filename_);
282         setReadonly(IsFileWriteable(filename_) == 0);
283         updateTitles();
284 }
285
286
287 // We'll remove this later. (Lgb)
288 namespace {
289
290 string last_inset_read;
291
292 #ifndef NO_COMPABILITY
293 struct ErtComp
294 {
295         ErtComp() : active(false), fromlayout(false), in_tabular(false) {
296         }
297         string contents;
298         bool active;
299         bool fromlayout;
300         bool in_tabular;
301         LyXFont font;
302 };
303
304 std::stack<ErtComp> ert_stack;
305 ErtComp ert_comp;
306 #endif
307
308 #ifdef WITH_WARNINGS
309 #warning And _why_ is this here? (Lgb)
310 #endif
311 int unknown_layouts;
312 int unknown_tokens;
313
314 } // anon
315
316
317 // candidate for move to BufferView
318 // (at least some parts in the beginning of the func)
319 //
320 // Uwe C. Schroeder
321 // changed to be public and have one parameter
322 // if par = 0 normal behavior
323 // else insert behavior
324 // Returns false if "\the_end" is not read for formats >= 2.13. (Asger)
325 bool Buffer::readLyXformat2(LyXLex & lex, Paragraph * par)
326 {
327         unknown_layouts = 0;
328         unknown_tokens = 0;
329 #ifndef NO_COMPABILITY
330         ert_comp.contents.erase();
331         ert_comp.active = false;
332         ert_comp.fromlayout = false;
333         ert_comp.in_tabular = false;
334 #endif
335         int pos = 0;
336         Paragraph::depth_type depth = 0;
337         bool the_end_read = false;
338
339         Paragraph * first_par = 0;
340         LyXFont font(LyXFont::ALL_INHERIT, params.language);
341
342 #if 0
343         if (file_format < 216 && params.language->lang() == "hebrew")
344                 font.setLanguage(default_language);
345 #endif
346
347         if (!par) {
348                 par = new Paragraph;
349                 par->layout(params.getLyXTextClass().defaultLayout());
350         } else {
351                 // We are inserting into an existing document
352                 users->text->breakParagraph(users);
353                 first_par = users->text->ownerParagraph();
354                 pos = 0;
355                 markDirty();
356                 // We don't want to adopt the parameters from the
357                 // document we insert, so we skip until the text begins:
358                 while (lex.isOK()) {
359                         lex.nextToken();
360                         string const pretoken = lex.getString();
361                         if (pretoken == "\\layout") {
362                                 lex.pushToken(pretoken);
363                                 break;
364                         }
365                 }
366         }
367
368         while (lex.isOK()) {
369                 lex.nextToken();
370                 string const token = lex.getString();
371
372                 if (token.empty()) continue;
373
374                 lyxerr[Debug::PARSER] << "Handling token: `"
375                                       << token << "'" << endl;
376
377                 the_end_read =
378                         parseSingleLyXformat2Token(lex, par, first_par,
379                                                    token, pos, depth,
380                                                    font);
381         }
382
383         if (!first_par)
384                 first_par = par;
385
386         paragraph = first_par;
387
388         if (unknown_layouts > 0) {
389                 string s = _("Couldn't set the layout for ");
390                 if (unknown_layouts == 1) {
391                         s += _("one paragraph");
392                 } else {
393                         s += tostr(unknown_layouts);
394                         s += _(" paragraphs");
395                 }
396                 Alert::alert(_("Textclass Loading Error!"), s,
397                            _("When reading " + fileName()));
398         }
399
400         if (unknown_tokens > 0) {
401                 string s = _("Encountered ");
402                 if (unknown_tokens == 1) {
403                         s += _("one unknown token");
404                 } else {
405                         s += tostr(unknown_tokens);
406                         s += _(" unknown tokens");
407                 }
408                 Alert::alert(_("Textclass Loading Error!"), s,
409                            _("When reading " + fileName()));
410         }
411
412         return the_end_read;
413 }
414
415
416 #ifndef NO_COMPABILITY
417
418 Inset * Buffer::isErtInset(Paragraph * par, int pos) const
419 {
420         Inset * inset;
421         if ((par->getChar(pos) == Paragraph::META_INSET) &&
422                 (inset = par->getInset(pos)) &&
423                 (inset->lyxCode() == Inset::ERT_CODE))
424         {
425                 return inset;
426         }
427         return 0;
428 }
429
430 void Buffer::insertErtContents(Paragraph * par, int & pos, bool set_inactive)
431 {
432         if (ert_comp.contents.find_first_not_of(' ') != string::npos) {
433                 // we only skip completely empty ERT (only spaces) otherwise
434                 // we have to insert it as is.
435                 string str(ert_comp.contents);
436                 lyxerr[Debug::INSETS] << "ERT contents:\n'"
437                         << str << "'" << endl;
438                 // check if we have already an ert inset a position earlier
439                 // if this is the case then we should insert the contents
440                 // inside the other ertinset as it is stupid to have various
441                 // ert in a row.
442                 Inset * inset;
443                 if ((pos > 0) && (inset=isErtInset(par, pos-1))) {
444                         // get the last paragraph from the inset before
445                         Paragraph * last = inset->firstParagraph();
446                         while(last->next())
447                                 last = last->next();
448                         // create the new paragraph after the last one
449                         Paragraph * par = new Paragraph(last);
450                         par->layout(params.getLyXTextClass().defaultLayoutName());
451                         par->setInsetOwner(last->inInset());
452                         // set the contents
453                         LyXFont font(LyXFont::ALL_INHERIT, params.language);
454                         string::const_iterator cit = str.begin();
455                         string::const_iterator end = str.end();
456                         pos_type pos = 0;
457                         for (; cit != end; ++cit) {
458                                 par->insertChar(pos++, *cit, font);
459                         }
460                 } else {
461                         Inset * inset =
462                                 new InsetERT(params, params.language, str, true);
463                         par->insertInset(pos++, inset, ert_comp.font);
464                 }
465         }
466         ert_comp.contents.erase();
467         ert_comp.fromlayout = false;
468         if (set_inactive) {
469                 ert_comp.active = false;
470         }
471 }
472 #endif
473
474
475 bool
476 Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
477                                    Paragraph *& first_par,
478                                    string const & token, int & pos,
479                                    Paragraph::depth_type & depth,
480                                    LyXFont & font
481         )
482 {
483         bool the_end_read = false;
484 #ifndef NO_COMPABILITY
485 #ifndef NO_PEXTRA_REALLY
486         // This is super temporary but is needed to get the compability
487         // mode for minipages work correctly together with new tabulars.
488         static int call_depth;
489         ++call_depth;
490         bool checkminipage = false;
491         static Paragraph * minipar = 0;
492         static Paragraph * parBeforeMinipage;
493 #endif
494 #endif
495
496         // The order of the tags tested may seem unnatural, but this
497         // has been done in order to reduce the number of string
498         // comparisons needed to recognize a given token. This leads
499         // on large documents like UserGuide to a reduction of a
500         // factor 5! (JMarc)
501         if (token[0] != '\\') {
502 #ifndef NO_COMPABILITY
503                 if (ert_comp.active) {
504                         ert_comp.contents += token;
505                 } else {
506 #endif
507                 for (string::const_iterator cit = token.begin();
508                      cit != token.end(); ++cit) {
509                         par->insertChar(pos, (*cit), font);
510                         ++pos;
511                 }
512 #ifndef NO_COMPABILITY
513                 }
514 #endif
515         } else if (token == "\\layout") {
516 #ifndef NO_COMPABILITY
517                 bool old_fromlayout = ert_comp.fromlayout;
518                 bool create_new_par = true;
519                 ert_comp.in_tabular = false;
520                 // Do the insetert.
521                 if (!par->size() && par->previous() &&
522                         (par->previous()->size() == 1) &&
523                         isErtInset(par->previous(), par->previous()->size()-1))
524                 {
525                         int p = par->previous()->size();
526                         insertErtContents(par->previous(), p);
527                         create_new_par = false;
528                 } else {
529                         insertErtContents(par, pos);
530                 }
531 #endif
532                 // reset the font as we start a new layout and if the font is
533                 // not ALL_INHERIT,document_language then it will be set to the
534                 // right values after this tag (Jug 20020420)
535                 font = LyXFont(LyXFont::ALL_INHERIT, params.language);
536
537 #if 0
538                 if (file_format < 216 && params.language->lang() == "hebrew")
539                         font.setLanguage(default_language);
540 #endif
541
542                 lex.eatLine();
543                 string layoutname = lex.getString();
544
545                 LyXTextClass const & tclass = params.getLyXTextClass();
546
547                 if (layoutname.empty()) {
548                         layoutname = tclass.defaultLayoutName();
549                 }
550 #ifndef NO_COMPABILITY
551                 if (compare_ascii_no_case(layoutname, "latex") == 0) {
552                         ert_comp.active = true;
553                         ert_comp.fromlayout = true;
554                         ert_comp.font = font;
555                         if (old_fromlayout)
556                                 create_new_par = false;
557                 }
558 #endif
559                 bool hasLayout = tclass.hasLayout(layoutname);
560                 if (!hasLayout) {
561                         lyxerr << "Layout '" << layoutname << "' does not"
562                                << " exist in textclass '" << tclass.name()
563                                << "'." << endl;
564                         lyxerr << "Trying to use default layout instead."
565                                << endl;
566                         layoutname = tclass.defaultLayoutName();
567                 }
568
569 #ifdef USE_CAPTION
570                 // The is the compability reading of layout caption.
571                 // It can be removed in LyX version 1.3.0. (Lgb)
572                 if (compare_ascii_no_case(layoutname, "caption") == 0) {
573                         // We expect that the par we are now working on is
574                         // really inside a InsetText inside a InsetFloat.
575                         // We also know that captions can only be
576                         // one paragraph. (Lgb)
577
578                         // We should now read until the next "\layout"
579                         // is reached.
580                         // This is probably not good enough, what if the
581                         // caption is the last par in the document (Lgb)
582                         istream & ist = lex.getStream();
583                         stringstream ss;
584                         string line;
585                         int begin = 0;
586                         while (true) {
587                                 getline(ist, line);
588                                 if (prefixIs(line, "\\layout")) {
589                                         lex.pushToken(line);
590                                         break;
591                                 }
592                                 if (prefixIs(line, "\\begin_inset"))
593                                         ++begin;
594                                 if (prefixIs(line, "\\end_inset")) {
595                                         if (begin)
596                                                 --begin;
597                                         else {
598                                                 lex.pushToken(line);
599                                                 break;
600                                         }
601                                 }
602
603                                 ss << line << '\n';
604                         }
605                         // Now we should have the whole layout in ss
606                         // we should now be able to give this to the
607                         // caption inset.
608                         ss << "\\end_inset\n";
609
610                         // This seems like a bug in stringstream.
611                         // We really should be able to use ss
612                         // directly. (Lgb)
613                         istringstream is(ss.str());
614                         LyXLex tmplex(0, 0);
615                         tmplex.setStream(is);
616                         Inset * inset = new InsetCaption;
617                         inset->Read(this, tmplex);
618                         par->InsertInset(pos, inset, font);
619                         ++pos;
620                 } else {
621 #endif
622 #ifndef NO_COMPABILITY
623                 if (create_new_par) {
624 #endif
625                         if (!first_par)
626                                 first_par = par;
627                         else {
628                                 par = new Paragraph(par);
629                                 par->layout(params.getLyXTextClass().defaultLayout());
630                         }
631                         pos = 0;
632                         par->layout(params.getLyXTextClass()[layoutname]);
633                         // Test whether the layout is obsolete.
634                         LyXLayout_ptr const & layout = par->layout();
635                         if (!layout->obsoleted_by().empty())
636                                 par->layout(params.getLyXTextClass()[layout->obsoleted_by()]);
637                         par->params().depth(depth);
638 #ifndef NO_COMPABILITY
639                 } else {
640                         // we duplicate code here because it's easier to remove
641                         // the code then of NO_COMPATIBILITY
642                         par->layout(layoutname);
643                         // Test whether the layout is obsolete.
644                         LyXLayout_ptr const & layout =
645                                 params.getLyXTextClass()[par->layout()];
646                         if (!layout->obsoleted_by().empty())
647                                 par->layout(layout->obsoleted_by());
648                         par->params().depth(depth);
649                 }
650 #endif
651 #if USE_CAPTION
652                 }
653 #endif
654
655         } else if (token == "\\end_inset") {
656                 lyxerr << "Solitary \\end_inset. Missing \\begin_inset?.\n"
657                        << "Last inset read was: " << last_inset_read
658                        << endl;
659                 // Simply ignore this. The insets do not have
660                 // to read this.
661                 // But insets should read it, it is a part of
662                 // the inset isn't it? Lgb.
663         } else if (token == "\\begin_inset") {
664 #ifndef NO_COMPABILITY
665                 insertErtContents(par, pos, false);
666                 ert_stack.push(ert_comp);
667                 ert_comp = ErtComp();
668 #endif
669                 readInset(lex, par, pos, font);
670 #ifndef NO_COMPABILITY
671                 ert_comp = ert_stack.top();
672                 ert_stack.pop();
673                 insertErtContents(par, pos);
674 #endif
675         } else if (token == "\\family") {
676                 lex.next();
677                 font.setLyXFamily(lex.getString());
678         } else if (token == "\\series") {
679                 lex.next();
680                 font.setLyXSeries(lex.getString());
681         } else if (token == "\\shape") {
682                 lex.next();
683                 font.setLyXShape(lex.getString());
684         } else if (token == "\\size") {
685                 lex.next();
686                 font.setLyXSize(lex.getString());
687 #ifndef NO_COMPABILITY
688         } else if (token == "\\latex") {
689                 lex.next();
690                 string const tok = lex.getString();
691                 if (tok == "no_latex") {
692                         // Do the insetert.
693                         insertErtContents(par, pos);
694                 } else if (tok == "latex") {
695                         ert_comp.active = true;
696                         ert_comp.font = font;
697                 } else if (tok == "default") {
698                         // Do the insetert.
699                         insertErtContents(par, pos);
700                 } else {
701                         lex.printError("Unknown LaTeX font flag "
702                                        "`$$Token'");
703                 }
704 #endif
705         } else if (token == "\\lang") {
706                 lex.next();
707                 string const tok = lex.getString();
708                 Language const * lang = languages.getLanguage(tok);
709                 if (lang) {
710                         font.setLanguage(lang);
711                 } else {
712                         font.setLanguage(params.language);
713                         lex.printError("Unknown language `$$Token'");
714                 }
715         } else if (token == "\\numeric") {
716                 lex.next();
717                 font.setNumber(font.setLyXMisc(lex.getString()));
718         } else if (token == "\\emph") {
719                 lex.next();
720                 font.setEmph(font.setLyXMisc(lex.getString()));
721         } else if (token == "\\bar") {
722                 lex.next();
723                 string const tok = lex.getString();
724                 // This is dirty, but gone with LyX3. (Asger)
725                 if (tok == "under")
726                         font.setUnderbar(LyXFont::ON);
727                 else if (tok == "no")
728                         font.setUnderbar(LyXFont::OFF);
729                 else if (tok == "default")
730                         font.setUnderbar(LyXFont::INHERIT);
731                 else
732                         lex.printError("Unknown bar font flag "
733                                        "`$$Token'");
734         } else if (token == "\\noun") {
735                 lex.next();
736                 font.setNoun(font.setLyXMisc(lex.getString()));
737         } else if (token == "\\color") {
738                 lex.next();
739                 font.setLyXColor(lex.getString());
740         } else if (token == "\\SpecialChar") {
741                 LyXLayout_ptr const & layout = par->layout();
742
743                 // Insets don't make sense in a free-spacing context! ---Kayvan
744                 if (layout->free_spacing || par->isFreeSpacing()) {
745                         if (lex.isOK()) {
746                                 lex.next();
747                                 string next_token = lex.getString();
748                                 if (next_token == "\\-") {
749                                         par->insertChar(pos, '-', font);
750                                 } else if (next_token == "\\protected_separator"
751                                         || next_token == "~") {
752                                         par->insertChar(pos, ' ', font);
753                                 } else {
754                                         lex.printError("Token `$$Token' "
755                                                        "is in free space "
756                                                        "paragraph layout!");
757                                         --pos;
758                                 }
759                         }
760                 } else {
761                         Inset * inset = new InsetSpecialChar;
762                         inset->read(this, lex);
763                         par->insertInset(pos, inset, font);
764                 }
765                 ++pos;
766         } else if (token == "\\i") {
767                 Inset * inset = new InsetLatexAccent;
768                 inset->read(this, lex);
769                 par->insertInset(pos, inset, font);
770                 ++pos;
771         } else if (token == "\\backslash") {
772 #ifndef NO_COMPABILITY
773                 if (ert_comp.active) {
774                         ert_comp.contents += "\\";
775                 } else {
776 #endif
777                 par->insertChar(pos, '\\', font);
778                 ++pos;
779 #ifndef NO_COMPABILITY
780                 }
781 #endif
782 #ifndef NO_COMPABILITY
783         } else if (token == "\\begin_float") {
784                 insertErtContents(par, pos);
785                 //insertErtContents(par, pos, false);
786                 //ert_stack.push(ert_comp);
787                 //ert_comp = ErtComp();
788
789                 // This is the compability reader. It can be removed in
790                 // LyX version 1.3.0. (Lgb)
791                 lex.next();
792                 string const tmptok = lex.getString();
793                 //lyxerr << "old float: " << tmptok << endl;
794
795                 Inset * inset = 0;
796                 stringstream old_float;
797
798                 if (tmptok == "footnote") {
799                         inset = new InsetFoot(params);
800                         old_float << "collapsed true\n";
801                 } else if (tmptok == "margin") {
802                         inset = new InsetMarginal(params);
803                         old_float << "collapsed true\n";
804                 } else if (tmptok == "fig") {
805                         inset = new InsetFloat(params, "figure");
806                         old_float << "wide false\n"
807                                   << "collapsed false\n";
808                 } else if (tmptok == "tab") {
809                         inset = new InsetFloat(params, "table");
810                         old_float << "wide false\n"
811                                   << "collapsed false\n";
812                 } else if (tmptok == "alg") {
813                         inset = new InsetFloat(params, "algorithm");
814                         old_float << "wide false\n"
815                                   << "collapsed false\n";
816                 } else if (tmptok == "wide-fig") {
817                         inset = new InsetFloat(params, "figure");
818                         //InsetFloat * tmp = new InsetFloat("figure");
819                         //tmp->wide(true);
820                         //inset = tmp;
821                         old_float << "wide true\n"
822                                   << "collapsed false\n";
823                 } else if (tmptok == "wide-tab") {
824                         inset = new InsetFloat(params, "table");
825                         //InsetFloat * tmp = new InsetFloat("table");
826                         //tmp->wide(true);
827                         //inset = tmp;
828                         old_float << "wide true\n"
829                                   << "collapsed false\n";
830                 }
831
832                 if (!inset) {
833 #ifndef NO_PEXTRA_REALLY
834                         --call_depth;
835 #endif
836                         return false; // no end read yet
837                 }
838
839                 // Here we need to check for \end_deeper and handle that
840                 // before we do the footnote parsing.
841                 // This _is_ a hack! (Lgb)
842                 while (true) {
843                         lex.next();
844                         string const tmp = lex.getString();
845                         if (tmp == "\\end_deeper") {
846                                 //lyxerr << "\\end_deeper caught!" << endl;
847                                 if (!depth) {
848                                         lex.printError("\\end_deeper: "
849                                                        "depth is already null");
850                                 } else
851                                         --depth;
852
853                         } else {
854                                 old_float << tmp << ' ';
855                                 break;
856                         }
857                 }
858
859                 old_float << lex.getLongString("\\end_float")
860                           << "\n\\end_inset\n";
861                 //lyxerr << "Float Body:\n" << old_float.str() << endl;
862                 // That this does not work seems like a bug
863                 // in stringstream. (Lgb)
864                 istringstream istr(old_float.str());
865                 LyXLex nylex(0, 0);
866                 nylex.setStream(istr);
867                 inset->read(this, nylex);
868                 par->insertInset(pos, inset, font);
869                 ++pos;
870                 insertErtContents(par, pos);
871
872                 // we have to reset the font as in the old format after a float
873                 // the font was automatically reset!
874                 font = LyXFont(LyXFont::ALL_INHERIT, params.language);
875 #endif
876         } else if (token == "\\begin_deeper") {
877                 ++depth;
878         } else if (token == "\\end_deeper") {
879                 if (!depth) {
880                         lex.printError("\\end_deeper: "
881                                        "depth is already null");
882                 }
883                 else
884                         --depth;
885         } else if (token == "\\begin_preamble") {
886                 params.readPreamble(lex);
887         } else if (token == "\\textclass") {
888                 lex.eatLine();
889                 pair<bool, textclass_type> pp =
890                         textclasslist.NumberOfClass(lex.getString());
891                 if (pp.first) {
892                         params.textclass = pp.second;
893                 } else {
894                         Alert::alert(string(_("Textclass error")),
895                                 string(_("The document uses an unknown textclass \"")) +
896                                 lex.getString() + string("\"."),
897                                 string(_("LyX will not be able to produce output correctly.")));
898                         params.textclass = 0;
899                 }
900                 if (!params.getLyXTextClass().load()) {
901                         // if the textclass wasn't loaded properly
902                         // we need to either substitute another
903                         // or stop loading the file.
904                         // I can substitute but I don't see how I can
905                         // stop loading... ideas??  ARRae980418
906                         Alert::alert(_("Textclass Loading Error!"),
907                                    string(_("Can't load textclass ")) +
908                                    params.getLyXTextClass().name(),
909                                    _("-- substituting default"));
910                         params.textclass = 0;
911                 }
912         } else if (token == "\\options") {
913                 lex.eatLine();
914                 params.options = lex.getString();
915         } else if (token == "\\language") {
916                 params.readLanguage(lex);
917         } else if (token == "\\fontencoding") {
918                 lex.eatLine();
919         } else if (token == "\\inputencoding") {
920                 lex.eatLine();
921                 params.inputenc = lex.getString();
922         } else if (token == "\\graphics") {
923                 params.readGraphicsDriver(lex);
924         } else if (token == "\\fontscheme") {
925                 lex.eatLine();
926                 params.fonts = lex.getString();
927         } else if (token == "\\noindent") {
928                 par->params().noindent(true);
929         } else if (token == "\\leftindent") {
930                 lex.nextToken();
931                 LyXLength value(lex.getString());
932                 par->params().leftIndent(value);
933         } else if (token == "\\fill_top") {
934                 par->params().spaceTop(VSpace(VSpace::VFILL));
935         } else if (token == "\\fill_bottom") {
936                 par->params().spaceBottom(VSpace(VSpace::VFILL));
937         } else if (token == "\\line_top") {
938                 par->params().lineTop(true);
939         } else if (token == "\\line_bottom") {
940                 par->params().lineBottom(true);
941         } else if (token == "\\pagebreak_top") {
942                 par->params().pagebreakTop(true);
943         } else if (token == "\\pagebreak_bottom") {
944                 par->params().pagebreakBottom(true);
945         } else if (token == "\\start_of_appendix") {
946                 par->params().startOfAppendix(true);
947         } else if (token == "\\paragraph_separation") {
948                 int tmpret = lex.findToken(string_paragraph_separation);
949                 if (tmpret == -1)
950                         ++tmpret;
951                 params.paragraph_separation =
952                         static_cast<BufferParams::PARSEP>(tmpret);
953         } else if (token == "\\defskip") {
954                 lex.nextToken();
955                 params.defskip = VSpace(lex.getString());
956 #ifndef NO_COMPABILITY
957         } else if (token == "\\epsfig") { // obsolete
958                 // Indeed it is obsolete, but we HAVE to be backwards
959                 // compatible until 0.14, because otherwise all figures
960                 // in existing documents are irretrivably lost. (Asger)
961                 params.readGraphicsDriver(lex);
962 #endif
963         } else if (token == "\\quotes_language") {
964                 int tmpret = lex.findToken(string_quotes_language);
965                 if (tmpret == -1)
966                         ++tmpret;
967                 InsetQuotes::quote_language tmpl =
968                         InsetQuotes::EnglishQ;
969                 switch (tmpret) {
970                 case 0:
971                         tmpl = InsetQuotes::EnglishQ;
972                         break;
973                 case 1:
974                         tmpl = InsetQuotes::SwedishQ;
975                         break;
976                 case 2:
977                         tmpl = InsetQuotes::GermanQ;
978                         break;
979                 case 3:
980                         tmpl = InsetQuotes::PolishQ;
981                         break;
982                 case 4:
983                         tmpl = InsetQuotes::FrenchQ;
984                         break;
985                 case 5:
986                         tmpl = InsetQuotes::DanishQ;
987                         break;
988                 }
989                 params.quotes_language = tmpl;
990         } else if (token == "\\quotes_times") {
991                 lex.nextToken();
992                 switch (lex.getInteger()) {
993                 case 1:
994                         params.quotes_times = InsetQuotes::SingleQ;
995                         break;
996                 case 2:
997                         params.quotes_times = InsetQuotes::DoubleQ;
998                         break;
999                 }
1000         } else if (token == "\\papersize") {
1001                 int tmpret = lex.findToken(string_papersize);
1002                 if (tmpret == -1)
1003                         ++tmpret;
1004                 else
1005                         params.papersize2 = tmpret;
1006         } else if (token == "\\paperpackage") {
1007                 int tmpret = lex.findToken(string_paperpackages);
1008                 if (tmpret == -1) {
1009                         ++tmpret;
1010                         params.paperpackage = BufferParams::PACKAGE_NONE;
1011                 } else
1012                         params.paperpackage = tmpret;
1013         } else if (token == "\\use_geometry") {
1014                 lex.nextToken();
1015                 params.use_geometry = lex.getInteger();
1016         } else if (token == "\\use_amsmath") {
1017                 lex.nextToken();
1018                 params.use_amsmath = lex.getInteger();
1019         } else if (token == "\\use_natbib") {
1020                 lex.nextToken();
1021                 params.use_natbib = lex.getInteger();
1022         } else if (token == "\\use_numerical_citations") {
1023                 lex.nextToken();
1024                 params.use_numerical_citations = lex.getInteger();
1025         } else if (token == "\\paperorientation") {
1026                 int tmpret = lex.findToken(string_orientation);
1027                 if (tmpret == -1)
1028                         ++tmpret;
1029                 params.orientation =
1030                         static_cast<BufferParams::PAPER_ORIENTATION>(tmpret);
1031         } else if (token == "\\paperwidth") {
1032                 lex.next();
1033                 params.paperwidth = lex.getString();
1034         } else if (token == "\\paperheight") {
1035                 lex.next();
1036                 params.paperheight = lex.getString();
1037         } else if (token == "\\leftmargin") {
1038                 lex.next();
1039                 params.leftmargin = lex.getString();
1040         } else if (token == "\\topmargin") {
1041                 lex.next();
1042                 params.topmargin = lex.getString();
1043         } else if (token == "\\rightmargin") {
1044                 lex.next();
1045                 params.rightmargin = lex.getString();
1046         } else if (token == "\\bottommargin") {
1047                 lex.next();
1048                 params.bottommargin = lex.getString();
1049         } else if (token == "\\headheight") {
1050                 lex.next();
1051                 params.headheight = lex.getString();
1052         } else if (token == "\\headsep") {
1053                 lex.next();
1054                 params.headsep = lex.getString();
1055         } else if (token == "\\footskip") {
1056                 lex.next();
1057                 params.footskip = lex.getString();
1058         } else if (token == "\\paperfontsize") {
1059                 lex.nextToken();
1060                 params.fontsize = rtrim(lex.getString());
1061         } else if (token == "\\papercolumns") {
1062                 lex.nextToken();
1063                 params.columns = lex.getInteger();
1064         } else if (token == "\\papersides") {
1065                 lex.nextToken();
1066                 switch (lex.getInteger()) {
1067                 default:
1068                 case 1: params.sides = LyXTextClass::OneSide; break;
1069                 case 2: params.sides = LyXTextClass::TwoSides; break;
1070                 }
1071         } else if (token == "\\paperpagestyle") {
1072                 lex.nextToken();
1073                 params.pagestyle = rtrim(lex.getString());
1074         } else if (token == "\\bullet") {
1075                 lex.nextToken();
1076                 int const index = lex.getInteger();
1077                 lex.nextToken();
1078                 int temp_int = lex.getInteger();
1079                 params.user_defined_bullets[index].setFont(temp_int);
1080                 params.temp_bullets[index].setFont(temp_int);
1081                 lex.nextToken();
1082                 temp_int = lex.getInteger();
1083                 params.user_defined_bullets[index].setCharacter(temp_int);
1084                 params.temp_bullets[index].setCharacter(temp_int);
1085                 lex.nextToken();
1086                 temp_int = lex.getInteger();
1087                 params.user_defined_bullets[index].setSize(temp_int);
1088                 params.temp_bullets[index].setSize(temp_int);
1089                 lex.nextToken();
1090                 string const temp_str = lex.getString();
1091                 if (temp_str != "\\end_bullet") {
1092                                 // this element isn't really necessary for
1093                                 // parsing but is easier for humans
1094                                 // to understand bullets. Put it back and
1095                                 // set a debug message?
1096                         lex.printError("\\end_bullet expected, got" + temp_str);
1097                                 //how can I put it back?
1098                 }
1099         } else if (token == "\\bulletLaTeX") {
1100                 // The bullet class should be able to read this.
1101                 lex.nextToken();
1102                 int const index = lex.getInteger();
1103                 lex.next();
1104                 string temp_str = lex.getString();
1105                 string sum_str;
1106                 while (temp_str != "\\end_bullet") {
1107                                 // this loop structure is needed when user
1108                                 // enters an empty string since the first
1109                                 // thing returned will be the \\end_bullet
1110                                 // OR
1111                                 // if the LaTeX entry has spaces. Each element
1112                                 // therefore needs to be read in turn
1113                         sum_str += temp_str;
1114                         lex.next();
1115                         temp_str = lex.getString();
1116                 }
1117
1118                 params.user_defined_bullets[index].setText(sum_str);
1119                 params.temp_bullets[index].setText(sum_str);
1120         } else if (token == "\\secnumdepth") {
1121                 lex.nextToken();
1122                 params.secnumdepth = lex.getInteger();
1123         } else if (token == "\\tocdepth") {
1124                 lex.nextToken();
1125                 params.tocdepth = lex.getInteger();
1126         } else if (token == "\\spacing") {
1127                 lex.next();
1128                 string const tmp = rtrim(lex.getString());
1129                 Spacing::Space tmp_space = Spacing::Default;
1130                 float tmp_val = 0.0;
1131                 if (tmp == "single") {
1132                         tmp_space = Spacing::Single;
1133                 } else if (tmp == "onehalf") {
1134                         tmp_space = Spacing::Onehalf;
1135                 } else if (tmp == "double") {
1136                         tmp_space = Spacing::Double;
1137                 } else if (tmp == "other") {
1138                         lex.next();
1139                         tmp_space = Spacing::Other;
1140                         tmp_val = lex.getFloat();
1141                 } else {
1142                         lex.printError("Unknown spacing token: '$$Token'");
1143                 }
1144                 // Small hack so that files written with klyx will be
1145                 // parsed correctly.
1146                 if (first_par) {
1147                         par->params().spacing(Spacing(tmp_space, tmp_val));
1148                 } else {
1149                         params.spacing.set(tmp_space, tmp_val);
1150                 }
1151         } else if (token == "\\paragraph_spacing") {
1152                 lex.next();
1153                 string const tmp = rtrim(lex.getString());
1154                 if (tmp == "single") {
1155                         par->params().spacing(Spacing(Spacing::Single));
1156                 } else if (tmp == "onehalf") {
1157                         par->params().spacing(Spacing(Spacing::Onehalf));
1158                 } else if (tmp == "double") {
1159                         par->params().spacing(Spacing(Spacing::Double));
1160                 } else if (tmp == "other") {
1161                         lex.next();
1162                         par->params().spacing(Spacing(Spacing::Other,
1163                                          lex.getFloat()));
1164                 } else {
1165                         lex.printError("Unknown spacing token: '$$Token'");
1166                 }
1167         } else if (token == "\\float_placement") {
1168                 lex.nextToken();
1169                 params.float_placement = lex.getString();
1170         } else if (token == "\\align") {
1171                 int tmpret = lex.findToken(string_align);
1172                 if (tmpret == -1) ++tmpret;
1173                 int const tmpret2 = int(pow(2.0, tmpret));
1174                 par->params().align(LyXAlignment(tmpret2));
1175         } else if (token == "\\added_space_top") {
1176                 lex.nextToken();
1177                 VSpace value = VSpace(lex.getString());
1178                 // only add the length when value > 0 or
1179                 // with option keep
1180                 if ((value.length().len().value() != 0) ||
1181                     value.keep() ||
1182                     (value.kind() != VSpace::LENGTH))
1183                         par->params().spaceTop(value);
1184         } else if (token == "\\added_space_bottom") {
1185                 lex.nextToken();
1186                 VSpace value = VSpace(lex.getString());
1187                 // only add the length when value > 0 or
1188                 // with option keep
1189                 if ((value.length().len().value() != 0) ||
1190                    value.keep() ||
1191                     (value.kind() != VSpace::LENGTH))
1192                         par->params().spaceBottom(value);
1193 #ifndef NO_COMPABILITY
1194 #ifndef NO_PEXTRA_REALLY
1195         } else if (token == "\\pextra_type") {
1196                 lex.nextToken();
1197                 par->params().pextraType(lex.getInteger());
1198         } else if (token == "\\pextra_width") {
1199                 lex.nextToken();
1200                 par->params().pextraWidth(lex.getString());
1201         } else if (token == "\\pextra_widthp") {
1202                 lex.nextToken();
1203                 par->params().pextraWidthp(lex.getString());
1204         } else if (token == "\\pextra_alignment") {
1205                 lex.nextToken();
1206                 par->params().pextraAlignment(lex.getInteger());
1207         } else if (token == "\\pextra_hfill") {
1208                 lex.nextToken();
1209                 par->params().pextraHfill(lex.getInteger());
1210         } else if (token == "\\pextra_start_minipage") {
1211                 lex.nextToken();
1212                 par->params().pextraStartMinipage(lex.getInteger());
1213 #endif
1214 #endif
1215         } else if (token == "\\labelwidthstring") {
1216                 lex.eatLine();
1217                 par->params().labelWidthString(lex.getString());
1218                 // do not delete this token, it is still needed!
1219         } else if (token == "\\newline") {
1220 #ifndef NO_COMPABILITY
1221                 if (!ert_comp.in_tabular && ert_comp.active) {
1222                         ert_comp.contents += char(Paragraph::META_NEWLINE);
1223                 } else {
1224                         // Since we cannot know it this is only a regular
1225                         // newline or a tabular cell delimter we have to
1226                         // handle the ERT here.
1227                         insertErtContents(par, pos, false);
1228
1229                         par->insertChar(pos, Paragraph::META_NEWLINE, font);
1230                         ++pos;
1231                 }
1232 #else
1233                 par->insertChar(pos, Paragraph::META_NEWLINE, font);
1234                 ++pos;
1235 #endif
1236         } else if (token == "\\LyXTable") {
1237 #ifndef NO_COMPABILITY
1238                 ert_comp.in_tabular = true;
1239 #endif
1240                 Inset * inset = new InsetTabular(*this);
1241                 inset->read(this, lex);
1242                 par->insertInset(pos, inset, font);
1243                 ++pos;
1244         } else if (token == "\\hfill") {
1245                 par->insertChar(pos, Paragraph::META_HFILL, font);
1246                 ++pos;
1247         } else if (token == "\\protected_separator") { // obsolete
1248                 // This is a backward compability thingie. (Lgb)
1249                 // Remove it later some time...introduced with fileformat
1250                 // 2.16. (Lgb)
1251                 LyXLayout_ptr const & layout = par->layout();
1252
1253                 if (layout->free_spacing || par->isFreeSpacing()) {
1254                         par->insertChar(pos, ' ', font);
1255                 } else {
1256                         Inset * inset = new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
1257                         par->insertInset(pos, inset, font);
1258                 }
1259                 ++pos;
1260         } else if (token == "\\bibitem") {  // ale970302
1261                 if (!par->bibkey) {
1262                         InsetCommandParams p("bibitem", "dummy");
1263                         par->bibkey = new InsetBibKey(p);
1264                 }
1265                 par->bibkey->read(this, lex);
1266         } else if (token == "\\the_end") {
1267 #ifndef NO_COMPABILITY
1268                 // If we still have some ert active here we have to insert
1269                 // it so we don't loose it. (Lgb)
1270                 insertErtContents(par, pos);
1271 #endif
1272                 the_end_read = true;
1273 #ifndef NO_COMPABILITY
1274 #ifndef NO_PEXTRA_REALLY
1275                 if (minipar == par)
1276                         par = 0;
1277                 minipar = parBeforeMinipage = 0;
1278 #endif
1279 #endif
1280         } else {
1281 #ifndef NO_COMPABILITY
1282                 if (ert_comp.active) {
1283                         ert_comp.contents += token;
1284                 } else {
1285 #endif
1286                 // This should be insurance for the future: (Asger)
1287                 ++unknown_tokens;
1288                 lex.eatLine();
1289                 string const s = _("Unknown token: ") + token
1290                         + " " + lex.text()  + "\n";
1291                 // we can do this here this way because we're actually reading
1292                 // the buffer and don't care about LyXText right now.
1293                 InsetError * new_inset = new InsetError(s);
1294                 par->insertInset(pos, new_inset, LyXFont(LyXFont::ALL_INHERIT,
1295                                  params.language));
1296
1297 #ifndef NO_COMPABILITY
1298                 }
1299 #endif
1300         }
1301
1302 #ifndef NO_COMPABILITY
1303 #ifndef NO_PEXTRA_REALLY
1304         // I wonder if we could use this blanket fix for all the
1305         // checkminipage cases...
1306         // don't forget about ert paragraphs and compatibility read for'em
1307         if (par && (par->size() || !ert_comp.contents.empty())) {
1308                 // It is possible that this will check to often,
1309                 // but that should not be an correctness issue.
1310                 // Only a speed issue.
1311                 checkminipage = true;
1312         }
1313
1314         // now check if we have a minipage paragraph as at this
1315         // point we already read all the necessary data!
1316         // this cannot be done in layout because there we did
1317         // not read yet the paragraph PEXTRA-params (Jug)
1318         //
1319         // BEGIN pextra_minipage compability
1320         // This should be removed in 1.3.x (Lgb)
1321         // I don't think we should remove this so fast (Jug)
1322
1323         // This compability code is not perfect. In a couple
1324         // of rand cases it fails. When the minipage par is
1325         // the first par in the document, and when there are
1326         // none or only one regular paragraphs after the
1327         // minipage. Currently I am not investing any effort
1328         // in fixing those cases.
1329
1330 //      lyxerr << "Call depth: " << call_depth << endl;
1331 //      lyxerr << "Checkminipage: " << checkminipage << endl;
1332
1333         if (checkminipage && (call_depth == 1)) {
1334                 checkminipage = false;
1335                 if (minipar && (minipar != par) &&
1336                     (par->params().pextraType() == Paragraph::PEXTRA_MINIPAGE)) {
1337                         lyxerr << "minipages in a row" << endl;
1338                         if (par->params().pextraStartMinipage()) {
1339                                 lyxerr << "start new minipage" << endl;
1340                                 // minipages in a row
1341                                 par->previous()->next(0);
1342                                 par->previous(0);
1343
1344                                 Paragraph * tmp = minipar;
1345                                 while (tmp) {
1346                                         tmp->params().pextraType(0);
1347                                         tmp->params().pextraWidth(string());
1348                                         tmp->params().pextraWidthp(string());
1349                                         tmp->params().pextraAlignment(0);
1350                                         tmp->params().pextraHfill(false);
1351                                         tmp->params().pextraStartMinipage(false);
1352                                         tmp = tmp->next();
1353                                 }
1354                                 // create a new paragraph to insert the
1355                                 // minipages in the following case
1356                                 if (par->params().pextraStartMinipage() &&
1357                                     !par->params().pextraHfill()) {
1358                                         Paragraph * p = new Paragraph;
1359                                         p->layout(params.getLyXTextClass().defaultLayoutName());
1360
1361                                         p->previous(parBeforeMinipage);
1362                                         parBeforeMinipage->next(p);
1363                                         p->next(0);
1364                                         p->params().depth(parBeforeMinipage->params().depth());
1365                                         parBeforeMinipage = p;
1366                                 }
1367                                 InsetMinipage * mini = new InsetMinipage(params);
1368                                 mini->pos(static_cast<InsetMinipage::Position>(par->params().pextraAlignment()));
1369                                 mini->pageWidth(LyXLength(par->params().pextraWidth()));
1370                                 if (!par->params().pextraWidthp().empty()) {
1371                                         lyxerr << "WP:" << mini->pageWidth().asString() << endl;
1372                                         mini->pageWidth(LyXLength((par->params().pextraWidthp())+"col%"));
1373                                 }
1374                                 Paragraph * op = mini->firstParagraph();
1375                                 mini->inset.paragraph(par);
1376                                 //
1377                                 // and free the old ones!
1378                                 //
1379                                 while(op) {
1380                                         Paragraph * pp = op->next();
1381                                         delete op;
1382                                         op = pp;
1383                                 }
1384                                 // Insert the minipage last in the
1385                                 // previous paragraph.
1386                                 if (par->params().pextraHfill()) {
1387                                         parBeforeMinipage->insertChar
1388                                                 (parBeforeMinipage->size(),
1389                                                  Paragraph::META_HFILL, font);
1390                                 }
1391                                 parBeforeMinipage->insertInset
1392                                         (parBeforeMinipage->size(), mini, font);
1393
1394                                 minipar = par;
1395                         } else {
1396                                 lyxerr << "new minipage par" << endl;
1397                                 //nothing to do just continue reading
1398                         }
1399
1400                 } else if (minipar && (minipar != par)) {
1401                         lyxerr << "last minipage par read" << endl;
1402                         // The last paragraph read was not part of a
1403                         // minipage but the par linked list is...
1404                         // So we need to remove the last par from the
1405                         // rest
1406                         if (par->previous())
1407                                 par->previous()->next(0);
1408                         par->previous(parBeforeMinipage);
1409                         parBeforeMinipage->next(par);
1410                         Paragraph * tmp = minipar;
1411                         while (tmp) {
1412                                 tmp->params().pextraType(0);
1413                                 tmp->params().pextraWidth(string());
1414                                 tmp->params().pextraWidthp(string());
1415                                 tmp->params().pextraAlignment(0);
1416                                 tmp->params().pextraHfill(false);
1417                                 tmp->params().pextraStartMinipage(false);
1418                                 tmp = tmp->next();
1419                         }
1420                         depth = parBeforeMinipage->params().depth();
1421                         // and set this depth on the par as it has not been set already
1422                         par->params().depth(depth);
1423                         minipar = parBeforeMinipage = 0;
1424                 } else if (!minipar &&
1425                            (par->params().pextraType() == Paragraph::PEXTRA_MINIPAGE)) {
1426                         // par is the first paragraph in a minipage
1427                         lyxerr << "begin minipage" << endl;
1428                         // To minimize problems for
1429                         // the users we will insert
1430                         // the first minipage in
1431                         // a sequence of minipages
1432                         // in its own paragraph.
1433                         Paragraph * p = new Paragraph;
1434                         p->layout(params.getLyXTextClass().defaultLayoutName());
1435                         p->previous(par->previous());
1436                         p->next(0);
1437                         p->params().depth(depth);
1438                         par->params().depth(0);
1439                         depth = 0;
1440                         if (par->previous())
1441                                 par->previous()->next(p);
1442                         par->previous(0);
1443                         parBeforeMinipage = p;
1444                         minipar = par;
1445                         if (!first_par || (first_par == par))
1446                                 first_par = p;
1447
1448                         InsetMinipage * mini = new InsetMinipage(params);
1449                         mini->pos(static_cast<InsetMinipage::Position>(minipar->params().pextraAlignment()));
1450                         mini->pageWidth(LyXLength(minipar->params().pextraWidth()));
1451                         if (!par->params().pextraWidthp().empty()) {
1452                                 lyxerr << "WP:" << mini->pageWidth().asString() << endl;
1453                                 mini->pageWidth(LyXLength((par->params().pextraWidthp())+"col%"));
1454                         }
1455
1456                         Paragraph * op = mini->firstParagraph();
1457                         mini->inset.paragraph(minipar);
1458                         //
1459                         // and free the old ones!
1460                         //
1461                         while(op) {
1462                                 Paragraph * pp = op->next();
1463                                 delete op;
1464                                 op = pp;
1465                         }
1466
1467                         // Insert the minipage last in the
1468                         // previous paragraph.
1469                         if (minipar->params().pextraHfill()) {
1470                                 parBeforeMinipage->insertChar
1471                                         (parBeforeMinipage->size(),
1472                                          Paragraph::META_HFILL, font);
1473                         }
1474                         parBeforeMinipage->insertInset
1475                                 (parBeforeMinipage->size(), mini, font);
1476                 } else if (par->params().pextraType() == Paragraph::PEXTRA_INDENT) {
1477                         par->params().leftIndent(LyXLength(par->params().pextraWidth()));
1478                         if (!par->params().pextraWidthp().empty()) {
1479                                 par->params().leftIndent(LyXLength((par->params().pextraWidthp())+"col%"));
1480                         }
1481                 }
1482         }
1483         // End of pextra_minipage compability
1484         --call_depth;
1485 #endif
1486 #endif
1487         return the_end_read;
1488 }
1489
1490 // needed to insert the selection
1491 void Buffer::insertStringAsLines(Paragraph *& par, pos_type & pos,
1492                                  LyXFont const & fn,string const & str) const
1493 {
1494         LyXLayout_ptr const & layout = par->layout();
1495
1496         LyXFont font = fn;
1497
1498         par->checkInsertChar(font);
1499         // insert the string, don't insert doublespace
1500         bool space_inserted = true;
1501         bool autobreakrows = !par->inInset() ||
1502                 static_cast<InsetText *>(par->inInset())->getAutoBreakRows();
1503         for(string::const_iterator cit = str.begin();
1504             cit != str.end(); ++cit) {
1505                 if (*cit == '\n') {
1506                         if (autobreakrows && (par->size() || layout->keepempty)) {
1507                                 par->breakParagraph(params, pos,
1508                                                     layout->isEnvironment());
1509                                 par = par->next();
1510                                 pos = 0;
1511                                 space_inserted = true;
1512                         } else {
1513                                 continue;
1514                         }
1515                         // do not insert consecutive spaces if !free_spacing
1516                 } else if ((*cit == ' ' || *cit == '\t') &&
1517                            space_inserted && !layout->free_spacing &&
1518                                    !par->isFreeSpacing())
1519                 {
1520                         continue;
1521                 } else if (*cit == '\t') {
1522                         if (!layout->free_spacing && !par->isFreeSpacing()) {
1523                                 // tabs are like spaces here
1524                                 par->insertChar(pos, ' ', font);
1525                                 ++pos;
1526                                 space_inserted = true;
1527                         } else {
1528                                 const pos_type nb = 8 - pos % 8;
1529                                 for (pos_type a = 0; a < nb ; ++a) {
1530                                         par->insertChar(pos, ' ', font);
1531                                         ++pos;
1532                                 }
1533                                 space_inserted = true;
1534                         }
1535                 } else if (!IsPrintable(*cit)) {
1536                         // Ignore unprintables
1537                         continue;
1538                 } else {
1539                         // just insert the character
1540                         par->insertChar(pos, *cit, font);
1541                         ++pos;
1542                         space_inserted = (*cit == ' ');
1543                 }
1544
1545         }
1546 }
1547
1548
1549 void Buffer::readInset(LyXLex & lex, Paragraph *& par,
1550                        int & pos, LyXFont & font)
1551 {
1552         // consistency check
1553         if (lex.getString() != "\\begin_inset") {
1554                 lyxerr << "Buffer::readInset: Consistency check failed."
1555                        << endl;
1556         }
1557
1558         Inset * inset = 0;
1559
1560         lex.next();
1561         string const tmptok = lex.getString();
1562         last_inset_read = tmptok;
1563
1564         // test the different insets
1565         if (tmptok == "LatexCommand") {
1566                 InsetCommandParams inscmd;
1567                 inscmd.read(lex);
1568
1569                 string const cmdName = inscmd.getCmdName();
1570
1571                 // This strange command allows LyX to recognize "natbib" style
1572                 // citations: citet, citep, Citet etc.
1573                 if (compare_ascii_no_case(cmdName.substr(0,4), "cite") == 0) {
1574                         inset = new InsetCitation(inscmd);
1575                 } else if (cmdName == "bibitem") {
1576                         lex.printError("Wrong place for bibitem");
1577                         inset = new InsetBibKey(inscmd);
1578                 } else if (cmdName == "BibTeX") {
1579                         inset = new InsetBibtex(inscmd);
1580                 } else if (cmdName == "index") {
1581                         inset = new InsetIndex(inscmd);
1582                 } else if (cmdName == "include") {
1583                         inset = new InsetInclude(inscmd, *this);
1584                 } else if (cmdName == "label") {
1585                         inset = new InsetLabel(inscmd);
1586                 } else if (cmdName == "url"
1587                            || cmdName == "htmlurl") {
1588                         inset = new InsetUrl(inscmd);
1589                 } else if (cmdName == "ref"
1590                            || cmdName == "pageref"
1591                            || cmdName == "vref"
1592                            || cmdName == "vpageref"
1593                            || cmdName == "prettyref") {
1594                         if (!inscmd.getOptions().empty()
1595                             || !inscmd.getContents().empty()) {
1596                                 inset = new InsetRef(inscmd, *this);
1597                         }
1598                 } else if (cmdName == "tableofcontents") {
1599                         inset = new InsetTOC(inscmd);
1600                 } else if (cmdName == "listofalgorithms") {
1601                         inset = new InsetFloatList("algorithm");
1602                 } else if (cmdName == "listoffigures") {
1603                         inset = new InsetFloatList("figure");
1604                 } else if (cmdName == "listoftables") {
1605                         inset = new InsetFloatList("table");
1606                 } else if (cmdName == "printindex") {
1607                         inset = new InsetPrintIndex(inscmd);
1608                 } else if (cmdName == "lyxparent") {
1609                         inset = new InsetParent(inscmd, *this);
1610                 }
1611         } else {
1612                 bool alreadyread = false;
1613                 if (tmptok == "Quotes") {
1614                         inset = new InsetQuotes;
1615                 } else if (tmptok == "External") {
1616                         inset = new InsetExternal;
1617                 } else if (tmptok == "FormulaMacro") {
1618                         inset = new InsetFormulaMacro;
1619                 } else if (tmptok == "Formula") {
1620                         inset = new InsetFormula;
1621                 } else if (tmptok == "Figure") { // Backward compatibility
1622 //                      inset = new InsetFig(100, 100, *this);
1623                         inset = new InsetGraphics;
1624                 } else if (tmptok == "Graphics") {
1625                         inset = new InsetGraphics;
1626                 } else if (tmptok == "Info") {// backwards compatibility
1627                         inset = new InsetNote(this,
1628                                               lex.getLongString("\\end_inset"),
1629                                               true);
1630                         alreadyread = true;
1631                 } else if (tmptok == "Note") {
1632                         inset = new InsetNote(params);
1633                 } else if (tmptok == "Include") {
1634                         InsetCommandParams p("Include");
1635                         inset = new InsetInclude(p, *this);
1636                 } else if (tmptok == "ERT") {
1637                         inset = new InsetERT(params);
1638                 } else if (tmptok == "Tabular") {
1639                         inset = new InsetTabular(*this);
1640                 } else if (tmptok == "Text") {
1641                         inset = new InsetText(params);
1642                 } else if (tmptok == "Foot") {
1643                         inset = new InsetFoot(params);
1644                 } else if (tmptok == "Marginal") {
1645                         inset = new InsetMarginal(params);
1646                 } else if (tmptok == "Minipage") {
1647                         inset = new InsetMinipage(params);
1648                 } else if (tmptok == "Float") {
1649                         lex.next();
1650                         string tmptok = lex.getString();
1651                         inset = new InsetFloat(params, tmptok);
1652 #if 0
1653                 } else if (tmptok == "List") {
1654                         inset = new InsetList;
1655                 } else if (tmptok == "Theorem") {
1656                         inset = new InsetList;
1657 #endif
1658                 } else if (tmptok == "Caption") {
1659                         inset = new InsetCaption(params);
1660                 } else if (tmptok == "FloatList") {
1661                         inset = new InsetFloatList;
1662                 }
1663
1664                 if (inset && !alreadyread) inset->read(this, lex);
1665         }
1666
1667         if (inset) {
1668                 par->insertInset(pos, inset, font);
1669                 ++pos;
1670         }
1671 }
1672
1673
1674 bool Buffer::readFile(LyXLex & lex, Paragraph * par)
1675 {
1676         if (lex.isOK()) {
1677                 lex.next();
1678                 string const token(lex.getString());
1679                 if (token == "\\lyxformat") { // the first token _must_ be...
1680                         lex.eatLine();
1681                         string tmp_format = lex.getString();
1682                         //lyxerr << "LyX Format: `" << tmp_format << "'" << endl;
1683                         // if present remove ".," from string.
1684                         string::size_type dot = tmp_format.find_first_of(".,");
1685                         //lyxerr << "           dot found at " << dot << endl;
1686                         if (dot != string::npos)
1687                                 tmp_format.erase(dot, 1);
1688                         file_format = strToInt(tmp_format);
1689                         if (file_format == LYX_FORMAT) {
1690                                 // current format
1691                         } else if (file_format > LYX_FORMAT) {
1692                                 // future format
1693                                 Alert::alert(_("Warning!"),
1694                                            _("LyX file format is newer that what"),
1695                                            _("is supported in this LyX version. Expect some problems."));
1696
1697                         } else if (file_format < LYX_FORMAT) {
1698                                 // old formats
1699                                 if (file_format < 200) {
1700                                         Alert::alert(_("ERROR!"),
1701                                                    _("Old LyX file format found. "
1702                                                      "Use LyX 0.10.x to read this!"));
1703                                         return false;
1704                                 } else if (file_format < 220) {
1705                                         string const command = "lyx2lyx "
1706                                                 + QuoteName(filename_);
1707                                         cmd_ret const ret = RunCommand(command);
1708                                         if (ret.first) {
1709                                                 Alert::alert(_("ERROR!"),
1710                                                      _("An error occured while "
1711                                                        "running the conversion script."));
1712                                                 return false;
1713                                         }
1714                                         istringstream is(ret.second);
1715                                         LyXLex tmplex(0, 0);
1716                                         tmplex.setStream(is);
1717                                         return readFile(tmplex);
1718                                 }
1719                         }
1720                         bool the_end = readLyXformat2(lex, par);
1721                         params.setPaperStuff();
1722
1723 #if 0
1724                         // the_end was added in 213
1725                         if (file_format < 213)
1726                                 the_end = true;
1727 #endif
1728
1729                         if (!the_end) {
1730                                 Alert::alert(_("Warning!"),
1731                                            _("Reading of document is not complete"),
1732                                            _("Maybe the document is truncated"));
1733                         }
1734                         return true;
1735                 } else { // "\\lyxformat" not found
1736                         Alert::alert(_("ERROR!"), _("Not a LyX file!"));
1737                 }
1738         } else
1739                 Alert::alert(_("ERROR!"), _("Unable to read file!"));
1740         return false;
1741 }
1742
1743
1744 // Should probably be moved to somewhere else: BufferView? LyXView?
1745 bool Buffer::save() const
1746 {
1747         // We don't need autosaves in the immediate future. (Asger)
1748         resetAutosaveTimers();
1749
1750         // make a backup
1751         string s;
1752         if (lyxrc.make_backup) {
1753                 s = fileName() + '~';
1754                 if (!lyxrc.backupdir_path.empty())
1755                         s = AddName(lyxrc.backupdir_path,
1756                                     subst(os::slashify_path(s),'/','!'));
1757
1758                 // Rename is the wrong way of making a backup,
1759                 // this is the correct way.
1760                 /* truss cp fil fil2:
1761                    lstat("LyXVC3.lyx", 0xEFFFF898)                 Err#2 ENOENT
1762                    stat("LyXVC.lyx", 0xEFFFF688)                   = 0
1763                    open("LyXVC.lyx", O_RDONLY)                     = 3
1764                    open("LyXVC3.lyx", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 4
1765                    fstat(4, 0xEFFFF508)                            = 0
1766                    fstat(3, 0xEFFFF508)                            = 0
1767                    read(3, " # T h i s   f i l e   w".., 8192)     = 5579
1768                    write(4, " # T h i s   f i l e   w".., 5579)    = 5579
1769                    read(3, 0xEFFFD4A0, 8192)                       = 0
1770                    close(4)                                        = 0
1771                    close(3)                                        = 0
1772                    chmod("LyXVC3.lyx", 0100644)                    = 0
1773                    lseek(0, 0, SEEK_CUR)                           = 46440
1774                    _exit(0)
1775                 */
1776
1777                 // Should probably have some more error checking here.
1778                 // Doing it this way, also makes the inodes stay the same.
1779                 // This is still not a very good solution, in particular we
1780                 // might loose the owner of the backup.
1781                 FileInfo finfo(fileName());
1782                 if (finfo.exist()) {
1783                         mode_t fmode = finfo.getMode();
1784                         struct utimbuf times = {
1785                                 finfo.getAccessTime(),
1786                                 finfo.getModificationTime() };
1787
1788                         ifstream ifs(fileName().c_str());
1789                         ofstream ofs(s.c_str(), ios::out|ios::trunc);
1790                         if (ifs && ofs) {
1791                                 ofs << ifs.rdbuf();
1792                                 ifs.close();
1793                                 ofs.close();
1794                                 ::chmod(s.c_str(), fmode);
1795
1796                                 if (::utime(s.c_str(), &times)) {
1797                                         lyxerr << "utime error." << endl;
1798                                 }
1799                         } else {
1800                                 lyxerr << "LyX was not able to make "
1801                                         "backup copy. Beware." << endl;
1802                         }
1803                 }
1804         }
1805
1806         if (writeFile(fileName())) {
1807                 markClean();
1808                 removeAutosaveFile(fileName());
1809         } else {
1810                 // Saving failed, so backup is not backup
1811                 if (lyxrc.make_backup) {
1812                         lyx::rename(s, fileName());
1813                 }
1814                 return false;
1815         }
1816         return true;
1817 }
1818
1819
1820 bool Buffer::writeFile(string const & fname) const
1821 {
1822         if (read_only && (fname == fileName())) {
1823                 return false;
1824         }
1825
1826         FileInfo finfo(fname);
1827         if (finfo.exist() && !finfo.writable()) {
1828                 return false;
1829         }
1830
1831         ofstream ofs(fname.c_str());
1832         if (!ofs) {
1833                 return false;
1834         }
1835
1836 #ifdef HAVE_LOCALE
1837         // Use the standard "C" locale for file output.
1838         ofs.imbue(std::locale::classic());
1839 #endif
1840
1841         // The top of the file should not be written by params.
1842
1843         // write out a comment in the top of the file
1844         ofs << '#' << lyx_docversion
1845             << " created this file. For more info see http://www.lyx.org/\n"
1846             << "\\lyxformat " << LYX_FORMAT << "\n";
1847
1848         // now write out the buffer paramters.
1849         params.writeFile(ofs);
1850
1851         Paragraph::depth_type depth = 0;
1852
1853         // this will write out all the paragraphs
1854         // using recursive descent.
1855         paragraph->writeFile(this, ofs, params, depth);
1856
1857         // Write marker that shows file is complete
1858         ofs << "\n\\the_end" << endl;
1859
1860         ofs.close();
1861
1862         // how to check if close went ok?
1863         // Following is an attempt... (BE 20001011)
1864
1865         // good() returns false if any error occured, including some
1866         //        formatting error.
1867         // bad()  returns true if something bad happened in the buffer,
1868         //        which should include file system full errors.
1869
1870         bool status = true;
1871         if (!ofs.good()) {
1872                 status = false;
1873 #if 0
1874                 if (ofs.bad()) {
1875                         lyxerr << "Buffer::writeFile: BAD ERROR!" << endl;
1876                 } else {
1877                         lyxerr << "Buffer::writeFile: NOT SO BAD ERROR!"
1878                                << endl;
1879                 }
1880 #endif
1881         }
1882
1883         return status;
1884 }
1885
1886
1887 namespace {
1888
1889 pair<int, string> const addDepth(int depth, int ldepth)
1890 {
1891         int d = depth * 2;
1892         if (ldepth > depth)
1893                 d += (ldepth - depth) * 2;
1894         return make_pair(d, string(d, ' '));
1895 }
1896
1897 }
1898
1899
1900 string const Buffer::asciiParagraph(Paragraph const * par,
1901                                     unsigned int linelen,
1902                                     bool noparbreak) const
1903 {
1904         ostringstream buffer;
1905         Paragraph::depth_type depth = 0;
1906         int ltype = 0;
1907         Paragraph::depth_type ltype_depth = 0;
1908         bool ref_printed = false;
1909 //      if (!par->previous()) {
1910 #if 0
1911         // begins or ends a deeper area ?
1912         if (depth != par->params().depth()) {
1913                 if (par->params().depth() > depth) {
1914                         while (par->params().depth() > depth) {
1915                                 ++depth;
1916                         }
1917                 } else {
1918                         while (par->params().depth() < depth) {
1919                                 --depth;
1920                         }
1921                 }
1922         }
1923 #else
1924         depth = par->params().depth();
1925 #endif
1926
1927         // First write the layout
1928         string const & tmp = par->layout()->name();
1929         if (compare_no_case(tmp, "itemize") == 0) {
1930                 ltype = 1;
1931                 ltype_depth = depth + 1;
1932         } else if (compare_ascii_no_case(tmp, "enumerate") == 0) {
1933                 ltype = 2;
1934                 ltype_depth = depth + 1;
1935         } else if (contains(ascii_lowercase(tmp), "ection")) {
1936                 ltype = 3;
1937                 ltype_depth = depth + 1;
1938         } else if (contains(ascii_lowercase(tmp), "aragraph")) {
1939                 ltype = 4;
1940                 ltype_depth = depth + 1;
1941         } else if (compare_ascii_no_case(tmp, "description") == 0) {
1942                 ltype = 5;
1943                 ltype_depth = depth + 1;
1944         } else if (compare_ascii_no_case(tmp, "abstract") == 0) {
1945                 ltype = 6;
1946                 ltype_depth = 0;
1947         } else if (compare_ascii_no_case(tmp, "bibliography") == 0) {
1948                 ltype = 7;
1949                 ltype_depth = 0;
1950         } else {
1951                 ltype = 0;
1952                 ltype_depth = 0;
1953         }
1954
1955         /* maybe some vertical spaces */
1956
1957         /* the labelwidthstring used in lists */
1958
1959         /* some lines? */
1960
1961         /* some pagebreaks? */
1962
1963         /* noindent ? */
1964
1965         /* what about the alignment */
1966 //      } else {
1967 //              lyxerr << "Should this ever happen?" << endl;
1968 //      }
1969
1970         // linelen <= 0 is special and means we don't have pargraph breaks
1971
1972         string::size_type currlinelen = 0;
1973
1974         if (!noparbreak) {
1975                 if (linelen > 0)
1976                         buffer << "\n\n";
1977
1978                 buffer << string(depth * 2, ' ');
1979                 currlinelen += depth * 2;
1980
1981                 //--
1982                 // we should probably change to the paragraph language in the
1983                 // gettext here (if possible) so that strings are outputted in
1984                 // the correct language! (20012712 Jug)
1985                 //--
1986                 switch (ltype) {
1987                 case 0: // Standard
1988                 case 4: // (Sub)Paragraph
1989                 case 5: // Description
1990                         break;
1991                 case 6: // Abstract
1992                         if (linelen > 0) {
1993                                 buffer << _("Abstract") << "\n\n";
1994                                 currlinelen = 0;
1995                         } else {
1996                                 string const abst = _("Abstract: ");
1997                                 buffer << abst;
1998                                 currlinelen += abst.length();
1999                         }
2000                         break;
2001                 case 7: // Bibliography
2002                         if (!ref_printed) {
2003                                 if (linelen > 0) {
2004                                         buffer << _("References") << "\n\n";
2005                                         currlinelen = 0;
2006                                 } else {
2007                                         string const refs = _("References: ");
2008                                         buffer << refs;
2009                                         currlinelen += refs.length();
2010                                 }
2011
2012                                 ref_printed = true;
2013                         }
2014                         break;
2015                 default:
2016                 {
2017                         string const parlab = par->params().labelString();
2018                         buffer << parlab << " ";
2019                         currlinelen += parlab.length() + 1;
2020                 }
2021                 break;
2022
2023                 }
2024         }
2025
2026         if (!currlinelen) {
2027                 pair<int, string> p = addDepth(depth, ltype_depth);
2028                 buffer << p.second;
2029                 currlinelen += p.first;
2030         }
2031
2032         // this is to change the linebreak to do it by word a bit more
2033         // intelligent hopefully! (only in the case where we have a
2034         // max linelenght!) (Jug)
2035
2036         string word;
2037
2038         for (pos_type i = 0; i < par->size(); ++i) {
2039                 char c = par->getUChar(params, i);
2040                 switch (c) {
2041                 case Paragraph::META_INSET:
2042                 {
2043                         Inset const * inset = par->getInset(i);
2044                         if (inset) {
2045                                 if (linelen > 0) {
2046                                         buffer << word;
2047                                         currlinelen += word.length();
2048                                         word.erase();
2049                                 }
2050                                 if (inset->ascii(this, buffer, linelen)) {
2051                                         // to be sure it breaks paragraph
2052                                         currlinelen += linelen;
2053                                 }
2054                         }
2055                 }
2056                 break;
2057
2058                 case Paragraph::META_NEWLINE:
2059                         if (linelen > 0) {
2060                                 buffer << word << "\n";
2061                                 word.erase();
2062
2063                                 pair<int, string> p = addDepth(depth,
2064                                                                ltype_depth);
2065                                 buffer << p.second;
2066                                 currlinelen = p.first;
2067                         }
2068                         break;
2069
2070                 case Paragraph::META_HFILL:
2071                         buffer << word << "\t";
2072                         currlinelen += word.length() + 1;
2073                         word.erase();
2074                         break;
2075
2076                 default:
2077                         if (c == ' ') {
2078                                 if (linelen > 0 &&
2079                                     currlinelen + word.length() > linelen - 10) {
2080                                         buffer << "\n";
2081                                         pair<int, string> p =
2082                                                 addDepth(depth, ltype_depth);
2083                                         buffer << p.second;
2084                                         currlinelen = p.first;
2085                                 }
2086
2087                                 buffer << word << ' ';
2088                                 currlinelen += word.length() + 1;
2089                                 word.erase();
2090
2091                         } else {
2092                                 if (c != '\0') {
2093                                         word += c;
2094                                 } else {
2095                                         lyxerr[Debug::INFO] <<
2096                                                 "writeAsciiFile: NULL char in structure." << endl;
2097                                 }
2098                                 if ((linelen > 0) &&
2099                                         (currlinelen + word.length()) > linelen)
2100                                 {
2101                                         buffer << "\n";
2102
2103                                         pair<int, string> p =
2104                                                 addDepth(depth, ltype_depth);
2105                                         buffer << p.second;
2106                                         currlinelen = p.first;
2107                                 }
2108                         }
2109                         break;
2110                 }
2111         }
2112         buffer << word;
2113         return buffer.str().c_str();
2114 }
2115
2116
2117 void Buffer::writeFileAscii(string const & fname, int linelen)
2118 {
2119         ofstream ofs(fname.c_str());
2120         if (!ofs) {
2121                 Alert::err_alert(_("Error: Cannot write file:"), fname);
2122                 return;
2123         }
2124         writeFileAscii(ofs, linelen);
2125 }
2126
2127
2128 void Buffer::writeFileAscii(ostream & ofs, int linelen)
2129 {
2130         Paragraph * par = paragraph;
2131         while (par) {
2132                 ofs << asciiParagraph(par, linelen, par->previous() == 0);
2133                 par = par->next();
2134         }
2135         ofs << "\n";
2136 }
2137
2138
2139 bool use_babel;
2140
2141
2142 void Buffer::makeLaTeXFile(string const & fname,
2143                            string const & original_path,
2144                            bool nice, bool only_body, bool only_preamble)
2145 {
2146         lyxerr[Debug::LATEX] << "makeLaTeXFile..." << endl;
2147
2148         ofstream ofs(fname.c_str());
2149         if (!ofs) {
2150                 Alert::err_alert(_("Error: Cannot open file: "), fname);
2151                 return;
2152         }
2153
2154         makeLaTeXFile(ofs, original_path, nice, only_body, only_preamble);
2155
2156         ofs.close();
2157         if (ofs.fail()) {
2158                 lyxerr << "File was not closed properly." << endl;
2159         }
2160 }
2161
2162
2163 void Buffer::makeLaTeXFile(ostream & os,
2164                            string const & original_path,
2165                            bool nice, bool only_body, bool only_preamble)
2166 {
2167         niceFile = nice; // this will be used by Insetincludes.
2168
2169         tex_code_break_column = lyxrc.ascii_linelen;
2170
2171         // validate the buffer.
2172         lyxerr[Debug::LATEX] << "  Validating buffer..." << endl;
2173         LaTeXFeatures features(params);
2174         validate(features);
2175         lyxerr[Debug::LATEX] << "  Buffer validation done." << endl;
2176
2177         texrow.reset();
2178         // The starting paragraph of the coming rows is the
2179         // first paragraph of the document. (Asger)
2180         texrow.start(paragraph, 0);
2181
2182         if (!only_body && nice) {
2183                 os << "%% " << lyx_docversion << " created this file.  "
2184                         "For more info, see http://www.lyx.org/.\n"
2185                         "%% Do not edit unless you really know what "
2186                         "you are doing.\n";
2187                 texrow.newline();
2188                 texrow.newline();
2189         }
2190         lyxerr[Debug::INFO] << "lyx header finished" << endl;
2191         // There are a few differences between nice LaTeX and usual files:
2192         // usual is \batchmode and has a
2193         // special input@path to allow the including of figures
2194         // with either \input or \includegraphics (what figinsets do).
2195         // batchmode is not set if there is a tex_code_break_column.
2196         // In this case somebody is interested in the generated LaTeX,
2197         // so this is OK. input@path is set when the actual parameter
2198         // original_path is set. This is done for usual tex-file, but not
2199         // for nice-latex-file. (Matthias 250696)
2200         if (!only_body) {
2201                 if (!nice) {
2202                         // code for usual, NOT nice-latex-file
2203                         os << "\\batchmode\n"; // changed
2204                         // from \nonstopmode
2205                         texrow.newline();
2206                 }
2207                 if (!original_path.empty()) {
2208                         string inputpath = os::external_path(original_path);
2209                         subst(inputpath, "~", "\\string~");
2210                         os << "\\makeatletter\n"
2211                             << "\\def\\input@path{{"
2212                             << inputpath << "/}}\n"
2213                             << "\\makeatother\n";
2214                         texrow.newline();
2215                         texrow.newline();
2216                         texrow.newline();
2217                 }
2218
2219                 os << "\\documentclass";
2220
2221                 LyXTextClass const & tclass = params.getLyXTextClass();
2222
2223                 ostringstream options; // the document class options.
2224
2225                 if (tokenPos(tclass.opt_fontsize(),
2226                              '|', params.fontsize) >= 0) {
2227                         // only write if existing in list (and not default)
2228                         options << params.fontsize << "pt,";
2229                 }
2230
2231
2232                 if (!params.use_geometry &&
2233                     (params.paperpackage == BufferParams::PACKAGE_NONE)) {
2234                         switch (params.papersize) {
2235                         case BufferParams::PAPER_A4PAPER:
2236                                 options << "a4paper,";
2237                                 break;
2238                         case BufferParams::PAPER_USLETTER:
2239                                 options << "letterpaper,";
2240                                 break;
2241                         case BufferParams::PAPER_A5PAPER:
2242                                 options << "a5paper,";
2243                                 break;
2244                         case BufferParams::PAPER_B5PAPER:
2245                                 options << "b5paper,";
2246                                 break;
2247                         case BufferParams::PAPER_EXECUTIVEPAPER:
2248                                 options << "executivepaper,";
2249                                 break;
2250                         case BufferParams::PAPER_LEGALPAPER:
2251                                 options << "legalpaper,";
2252                                 break;
2253                         }
2254                 }
2255
2256                 // if needed
2257                 if (params.sides != tclass.sides()) {
2258                         switch (params.sides) {
2259                         case LyXTextClass::OneSide:
2260                                 options << "oneside,";
2261                                 break;
2262                         case LyXTextClass::TwoSides:
2263                                 options << "twoside,";
2264                                 break;
2265                         }
2266                 }
2267
2268                 // if needed
2269                 if (params.columns != tclass.columns()) {
2270                         if (params.columns == 2)
2271                                 options << "twocolumn,";
2272                         else
2273                                 options << "onecolumn,";
2274                 }
2275
2276                 if (!params.use_geometry
2277                     && params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
2278                         options << "landscape,";
2279
2280                 // language should be a parameter to \documentclass
2281                 use_babel = false;
2282                 ostringstream language_options;
2283                 if (params.language->babel() == "hebrew"
2284                     && default_language->babel() != "hebrew")
2285                          // This seems necessary
2286                         features.useLanguage(default_language);
2287
2288                 if (lyxrc.language_use_babel ||
2289                     params.language->lang() != lyxrc.default_language ||
2290                     features.hasLanguages()) {
2291                         use_babel = true;
2292                         language_options << features.getLanguages();
2293                         language_options << params.language->babel();
2294                         if (lyxrc.language_global_options)
2295                                 options << language_options.str() << ',';
2296                 }
2297
2298                 // the user-defined options
2299                 if (!params.options.empty()) {
2300                         options << params.options << ',';
2301                 }
2302
2303                 string strOptions(options.str().c_str());
2304                 if (!strOptions.empty()) {
2305                         strOptions = rtrim(strOptions, ",");
2306                         os << '[' << strOptions << ']';
2307                 }
2308
2309                 os << '{' << tclass.latexname() << "}\n";
2310                 texrow.newline();
2311                 // end of \documentclass defs
2312
2313                 // font selection must be done before loading fontenc.sty
2314                 // The ae package is not needed when using OT1 font encoding.
2315                 if (params.fonts != "default" &&
2316                     (params.fonts != "ae" || lyxrc.fontenc != "default")) {
2317                         os << "\\usepackage{" << params.fonts << "}\n";
2318                         texrow.newline();
2319                         if (params.fonts == "ae") {
2320                                 os << "\\usepackage{aecompl}\n";
2321                                 texrow.newline();
2322                         }
2323                 }
2324                 // this one is not per buffer
2325                 if (lyxrc.fontenc != "default") {
2326                         os << "\\usepackage[" << lyxrc.fontenc
2327                             << "]{fontenc}\n";
2328                         texrow.newline();
2329                 }
2330
2331                 if (params.inputenc == "auto") {
2332                         string const doc_encoding =
2333                                 params.language->encoding()->LatexName();
2334
2335                         // Create a list with all the input encodings used
2336                         // in the document
2337                         set<string> encodings = features.getEncodingSet(doc_encoding);
2338
2339                         os << "\\usepackage[";
2340                         std::copy(encodings.begin(), encodings.end(),
2341                                   std::ostream_iterator<string>(os, ","));
2342                         os << doc_encoding << "]{inputenc}\n";
2343                         texrow.newline();
2344                 } else if (params.inputenc != "default") {
2345                         os << "\\usepackage[" << params.inputenc
2346                             << "]{inputenc}\n";
2347                         texrow.newline();
2348                 }
2349
2350                 // At the very beginning the text parameters.
2351                 if (params.paperpackage != BufferParams::PACKAGE_NONE) {
2352                         switch (params.paperpackage) {
2353                         case BufferParams::PACKAGE_A4:
2354                                 os << "\\usepackage{a4}\n";
2355                                 texrow.newline();
2356                                 break;
2357                         case BufferParams::PACKAGE_A4WIDE:
2358                                 os << "\\usepackage{a4wide}\n";
2359                                 texrow.newline();
2360                                 break;
2361                         case BufferParams::PACKAGE_WIDEMARGINSA4:
2362                                 os << "\\usepackage[widemargins]{a4}\n";
2363                                 texrow.newline();
2364                                 break;
2365                         }
2366                 }
2367                 if (params.use_geometry) {
2368                         os << "\\usepackage{geometry}\n";
2369                         texrow.newline();
2370                         os << "\\geometry{verbose";
2371                         if (params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
2372                                 os << ",landscape";
2373                         switch (params.papersize2) {
2374                         case BufferParams::VM_PAPER_CUSTOM:
2375                                 if (!params.paperwidth.empty())
2376                                         os << ",paperwidth="
2377                                             << params.paperwidth;
2378                                 if (!params.paperheight.empty())
2379                                         os << ",paperheight="
2380                                             << params.paperheight;
2381                                 break;
2382                         case BufferParams::VM_PAPER_USLETTER:
2383                                 os << ",letterpaper";
2384                                 break;
2385                         case BufferParams::VM_PAPER_USLEGAL:
2386                                 os << ",legalpaper";
2387                                 break;
2388                         case BufferParams::VM_PAPER_USEXECUTIVE:
2389                                 os << ",executivepaper";
2390                                 break;
2391                         case BufferParams::VM_PAPER_A3:
2392                                 os << ",a3paper";
2393                                 break;
2394                         case BufferParams::VM_PAPER_A4:
2395                                 os << ",a4paper";
2396                                 break;
2397                         case BufferParams::VM_PAPER_A5:
2398                                 os << ",a5paper";
2399                                 break;
2400                         case BufferParams::VM_PAPER_B3:
2401                                 os << ",b3paper";
2402                                 break;
2403                         case BufferParams::VM_PAPER_B4:
2404                                 os << ",b4paper";
2405                                 break;
2406                         case BufferParams::VM_PAPER_B5:
2407                                 os << ",b5paper";
2408                                 break;
2409                         default:
2410                                 // default papersize ie BufferParams::VM_PAPER_DEFAULT
2411                                 switch (lyxrc.default_papersize) {
2412                                 case BufferParams::PAPER_DEFAULT: // keep compiler happy
2413                                 case BufferParams::PAPER_USLETTER:
2414                                         os << ",letterpaper";
2415                                         break;
2416                                 case BufferParams::PAPER_LEGALPAPER:
2417                                         os << ",legalpaper";
2418                                         break;
2419                                 case BufferParams::PAPER_EXECUTIVEPAPER:
2420                                         os << ",executivepaper";
2421                                         break;
2422                                 case BufferParams::PAPER_A3PAPER:
2423                                         os << ",a3paper";
2424                                         break;
2425                                 case BufferParams::PAPER_A4PAPER:
2426                                         os << ",a4paper";
2427                                         break;
2428                                 case BufferParams::PAPER_A5PAPER:
2429                                         os << ",a5paper";
2430                                         break;
2431                                 case BufferParams::PAPER_B5PAPER:
2432                                         os << ",b5paper";
2433                                         break;
2434                                 }
2435                         }
2436                         if (!params.topmargin.empty())
2437                                 os << ",tmargin=" << params.topmargin;
2438                         if (!params.bottommargin.empty())
2439                                 os << ",bmargin=" << params.bottommargin;
2440                         if (!params.leftmargin.empty())
2441                                 os << ",lmargin=" << params.leftmargin;
2442                         if (!params.rightmargin.empty())
2443                                 os << ",rmargin=" << params.rightmargin;
2444                         if (!params.headheight.empty())
2445                                 os << ",headheight=" << params.headheight;
2446                         if (!params.headsep.empty())
2447                                 os << ",headsep=" << params.headsep;
2448                         if (!params.footskip.empty())
2449                                 os << ",footskip=" << params.footskip;
2450                         os << "}\n";
2451                         texrow.newline();
2452                 }
2453
2454                 if (tokenPos(tclass.opt_pagestyle(),
2455                              '|', params.pagestyle) >= 0) {
2456                         if (params.pagestyle == "fancy") {
2457                                 os << "\\usepackage{fancyhdr}\n";
2458                                 texrow.newline();
2459                         }
2460                         os << "\\pagestyle{" << params.pagestyle << "}\n";
2461                         texrow.newline();
2462                 }
2463
2464                 if (params.secnumdepth != tclass.secnumdepth()) {
2465                         os << "\\setcounter{secnumdepth}{"
2466                             << params.secnumdepth
2467                             << "}\n";
2468                         texrow.newline();
2469                 }
2470                 if (params.tocdepth != tclass.tocdepth()) {
2471                         os << "\\setcounter{tocdepth}{"
2472                             << params.tocdepth
2473                             << "}\n";
2474                         texrow.newline();
2475                 }
2476
2477                 if (params.paragraph_separation) {
2478                         switch (params.defskip.kind()) {
2479                         case VSpace::SMALLSKIP:
2480                                 os << "\\setlength\\parskip{\\smallskipamount}\n";
2481                                 break;
2482                         case VSpace::MEDSKIP:
2483                                 os << "\\setlength\\parskip{\\medskipamount}\n";
2484                                 break;
2485                         case VSpace::BIGSKIP:
2486                                 os << "\\setlength\\parskip{\\bigskipamount}\n";
2487                                 break;
2488                         case VSpace::LENGTH:
2489                                 os << "\\setlength\\parskip{"
2490                                     << params.defskip.length().asLatexString()
2491                                     << "}\n";
2492                                 break;
2493                         default: // should never happen // Then delete it.
2494                                 os << "\\setlength\\parskip{\\medskipamount}\n";
2495                                 break;
2496                         }
2497                         texrow.newline();
2498
2499                         os << "\\setlength\\parindent{0pt}\n";
2500                         texrow.newline();
2501                 }
2502
2503                 // Now insert the LyX specific LaTeX commands...
2504
2505                 // The optional packages;
2506                 string preamble(features.getPackages());
2507
2508                 // this might be useful...
2509                 preamble += "\n\\makeatletter\n";
2510
2511                 // Some macros LyX will need
2512                 string tmppreamble(features.getMacros());
2513
2514                 if (!tmppreamble.empty()) {
2515                         preamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2516                                 "LyX specific LaTeX commands.\n"
2517                                 + tmppreamble + '\n';
2518                 }
2519
2520                 // the text class specific preamble
2521                 tmppreamble = features.getTClassPreamble();
2522                 if (!tmppreamble.empty()) {
2523                         preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2524                                 "Textclass specific LaTeX commands.\n"
2525                                 + tmppreamble + '\n';
2526                 }
2527
2528                 /* the user-defined preamble */
2529                 if (!params.preamble.empty()) {
2530                         preamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2531                                 "User specified LaTeX commands.\n"
2532                                 + params.preamble + '\n';
2533                 }
2534
2535                 // Itemize bullet settings need to be last in case the user
2536                 // defines their own bullets that use a package included
2537                 // in the user-defined preamble -- ARRae
2538                 // Actually it has to be done much later than that
2539                 // since some packages like frenchb make modifications
2540                 // at \begin{document} time -- JMarc
2541                 string bullets_def;
2542                 for (int i = 0; i < 4; ++i) {
2543                         if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
2544                                 if (bullets_def.empty())
2545                                         bullets_def="\\AtBeginDocument{\n";
2546                                 bullets_def += "  \\renewcommand{\\labelitemi";
2547                                 switch (i) {
2548                                 // `i' is one less than the item to modify
2549                                 case 0:
2550                                         break;
2551                                 case 1:
2552                                         bullets_def += 'i';
2553                                         break;
2554                                 case 2:
2555                                         bullets_def += "ii";
2556                                         break;
2557                                 case 3:
2558                                         bullets_def += 'v';
2559                                         break;
2560                                 }
2561                                 bullets_def += "}{" +
2562                                   params.user_defined_bullets[i].getText()
2563                                   + "}\n";
2564                         }
2565                 }
2566
2567                 if (!bullets_def.empty())
2568                   preamble += bullets_def + "}\n\n";
2569
2570                 int const nlines =
2571                         int(lyx::count(preamble.begin(), preamble.end(), '\n'));
2572                 for (int j = 0; j != nlines; ++j) {
2573                         texrow.newline();
2574                 }
2575
2576                 // We try to load babel late, in case it interferes
2577                 // with other packages.
2578                 if (use_babel) {
2579                         string tmp = lyxrc.language_package;
2580                         if (!lyxrc.language_global_options
2581                             && tmp == "\\usepackage{babel}")
2582                                 tmp = string("\\usepackage[") +
2583                                         language_options.str().c_str() +
2584                                         "]{babel}";
2585                         preamble += tmp + "\n";
2586                         preamble += features.getBabelOptions();
2587                 }
2588
2589                 preamble += "\\makeatother\n";
2590
2591                 os << preamble;
2592
2593                 if (only_preamble)
2594                         return;
2595
2596                 // make the body.
2597                 os << "\\begin{document}\n";
2598                 texrow.newline();
2599         } // only_body
2600         lyxerr[Debug::INFO] << "preamble finished, now the body." << endl;
2601
2602         if (!lyxrc.language_auto_begin) {
2603                 os << subst(lyxrc.language_command_begin, "$$lang",
2604                              params.language->babel())
2605                     << endl;
2606                 texrow.newline();
2607         }
2608
2609         latexParagraphs(os, paragraph, 0, texrow);
2610
2611         // add this just in case after all the paragraphs
2612         os << endl;
2613         texrow.newline();
2614
2615         if (!lyxrc.language_auto_end) {
2616                 os << subst(lyxrc.language_command_end, "$$lang",
2617                              params.language->babel())
2618                     << endl;
2619                 texrow.newline();
2620         }
2621
2622         if (!only_body) {
2623                 os << "\\end{document}\n";
2624                 texrow.newline();
2625
2626                 lyxerr[Debug::LATEX] << "makeLaTeXFile...done" << endl;
2627         } else {
2628                 lyxerr[Debug::LATEX] << "LaTeXFile for inclusion made."
2629                                      << endl;
2630         }
2631
2632         // Just to be sure. (Asger)
2633         texrow.newline();
2634
2635         // tex_code_break_column's value is used to decide
2636         // if we are in batchmode or not (within mathed_write()
2637         // in math_write.C) so we must set it to a non-zero
2638         // value when we leave otherwise we save incorrect .lyx files.
2639         tex_code_break_column = lyxrc.ascii_linelen;
2640
2641         lyxerr[Debug::INFO] << "Finished making latex file." << endl;
2642         lyxerr[Debug::INFO] << "Row count was " << texrow.rows()-1 << "." << endl;
2643
2644         // we want this to be true outside previews (for insetexternal)
2645         niceFile = true;
2646 }
2647
2648
2649 //
2650 // LaTeX all paragraphs from par to endpar, if endpar == 0 then to the end
2651 //
2652 void Buffer::latexParagraphs(ostream & ofs, Paragraph * par,
2653                              Paragraph * endpar, TexRow & texrow,
2654                              bool moving_arg) const
2655 {
2656         bool was_title = false;
2657         bool already_title = false;
2658
2659         // if only_body
2660         while (par != endpar) {
2661                 Inset * in = par->inInset();
2662                 // well we have to check if we are in an inset with unlimited
2663                 // lenght (all in one row) if that is true then we don't allow
2664                 // any special options in the paragraph and also we don't allow
2665                 // any environment other then "Standard" to be valid!
2666                 if ((in == 0) || !in->forceDefaultParagraphs(in)) {
2667                         LyXLayout_ptr const & layout = par->layout();
2668
2669                         if (layout->intitle) {
2670                                 if (already_title) {
2671                                         lyxerr <<"Error in latexParagraphs: You"
2672                                                 " should not mix title layouts"
2673                                                 " with normal ones." << endl;
2674                                 } else
2675                                         was_title = true;
2676                         } else if (was_title && !already_title) {
2677                                 ofs << "\\maketitle\n";
2678                                 texrow.newline();
2679                                 already_title = true;
2680                                 was_title = false;
2681                         }
2682
2683                         if (layout->isEnvironment() ||
2684                                 !par->params().leftIndent().zero())
2685                         {
2686                                 par = par->TeXEnvironment(this, params, ofs, texrow);
2687                         } else {
2688                                 par = par->TeXOnePar(this, params, ofs, texrow, moving_arg);
2689                         }
2690                 } else {
2691                         par = par->TeXOnePar(this, params, ofs, texrow, moving_arg);
2692                 }
2693         }
2694         // It might be that we only have a title in this document
2695         if (was_title && !already_title) {
2696                 ofs << "\\maketitle\n";
2697                 texrow.newline();
2698         }
2699 }
2700
2701
2702 bool Buffer::isLatex() const
2703 {
2704         return params.getLyXTextClass().outputType() == LATEX;
2705 }
2706
2707
2708 bool Buffer::isLinuxDoc() const
2709 {
2710         return params.getLyXTextClass().outputType() == LINUXDOC;
2711 }
2712
2713
2714 bool Buffer::isLiterate() const
2715 {
2716         return params.getLyXTextClass().outputType() == LITERATE;
2717 }
2718
2719
2720 bool Buffer::isDocBook() const
2721 {
2722         return params.getLyXTextClass().outputType() == DOCBOOK;
2723 }
2724
2725
2726 bool Buffer::isSGML() const
2727 {
2728         LyXTextClass const & tclass = params.getLyXTextClass();
2729
2730         return tclass.outputType() == LINUXDOC ||
2731                tclass.outputType() == DOCBOOK;
2732 }
2733
2734
2735 int Buffer::sgmlOpenTag(ostream & os, Paragraph::depth_type depth, bool mixcont,
2736                          string const & latexname) const
2737 {
2738         if (!latexname.empty() && latexname != "!-- --") {
2739                 if (!mixcont)
2740                         os << string(" ",depth);
2741                 os << "<" << latexname << ">";
2742         }
2743
2744         if (!mixcont)
2745                 os << endl;
2746
2747         return mixcont?0:1;
2748 }
2749
2750
2751 int Buffer::sgmlCloseTag(ostream & os, Paragraph::depth_type depth, bool mixcont,
2752                           string const & latexname) const
2753 {
2754         if (!latexname.empty() && latexname != "!-- --") {
2755                 if (!mixcont)
2756                         os << endl << string(" ",depth);
2757                 os << "</" << latexname << ">";
2758         }
2759
2760         if (!mixcont)
2761                 os << endl;
2762
2763         return mixcont?0:1;
2764 }
2765
2766
2767 void Buffer::makeLinuxDocFile(string const & fname, bool nice, bool body_only)
2768 {
2769         ofstream ofs(fname.c_str());
2770
2771         if (!ofs) {
2772                 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
2773                 return;
2774         }
2775
2776         niceFile = nice; // this will be used by included files.
2777
2778         LaTeXFeatures features(params);
2779
2780         validate(features);
2781
2782         texrow.reset();
2783
2784         LyXTextClass const & tclass = params.getLyXTextClass();
2785
2786         string top_element = tclass.latexname();
2787
2788         if (!body_only) {
2789                 ofs << "<!doctype linuxdoc system";
2790
2791                 string preamble = params.preamble;
2792                 const string name = nice ? ChangeExtension(filename_, ".sgml")
2793                          : fname;
2794                 preamble += features.getIncludedFiles(name);
2795                 preamble += features.getLyXSGMLEntities();
2796
2797                 if (!preamble.empty()) {
2798                         ofs << " [ " << preamble << " ]";
2799                 }
2800                 ofs << ">\n\n";
2801
2802                 if (params.options.empty())
2803                         sgmlOpenTag(ofs, 0, false, top_element);
2804                 else {
2805                         string top = top_element;
2806                         top += " ";
2807                         top += params.options;
2808                         sgmlOpenTag(ofs, 0, false, top);
2809                 }
2810         }
2811
2812         ofs << "<!-- "  << lyx_docversion
2813             << " created this file. For more info see http://www.lyx.org/"
2814             << " -->\n";
2815
2816         Paragraph::depth_type depth = 0; // paragraph depth
2817         Paragraph * par = paragraph;
2818         string item_name;
2819         vector<string> environment_stack(5);
2820
2821         while (par) {
2822                 LyXLayout_ptr const & style = par->layout();
2823                 // treat <toc> as a special case for compatibility with old code
2824                 if (par->isInset(0)) {
2825                         Inset * inset = par->getInset(0);
2826                         Inset::Code lyx_code = inset->lyxCode();
2827                         if (lyx_code == Inset::TOC_CODE) {
2828                                 string const temp = "toc";
2829                                 sgmlOpenTag(ofs, depth, false, temp);
2830
2831                                 par = par->next();
2832                                 continue;
2833                         }
2834                 }
2835
2836                 // environment tag closing
2837                 for (; depth > par->params().depth(); --depth) {
2838                         sgmlCloseTag(ofs, depth, false, environment_stack[depth]);
2839                         environment_stack[depth].erase();
2840                 }
2841
2842                 // write opening SGML tags
2843                 switch (style->latextype) {
2844                 case LATEX_PARAGRAPH:
2845                         if (depth == par->params().depth()
2846                            && !environment_stack[depth].empty()) {
2847                                 sgmlCloseTag(ofs, depth, false, environment_stack[depth]);
2848                                 environment_stack[depth].erase();
2849                                 if (depth)
2850                                         --depth;
2851                                 else
2852                                         ofs << "</p>";
2853                         }
2854                         sgmlOpenTag(ofs, depth, false, style->latexname());
2855                         break;
2856
2857                 case LATEX_COMMAND:
2858                         if (depth!= 0)
2859                                 sgmlError(par, 0,
2860                                           _("Error : Wrong depth for"
2861                                             " LatexType Command.\n"));
2862
2863                         if (!environment_stack[depth].empty()) {
2864                                 sgmlCloseTag(ofs, depth, false, environment_stack[depth]);
2865                                 ofs << "</p>";
2866                         }
2867
2868                         environment_stack[depth].erase();
2869                         sgmlOpenTag(ofs, depth, false, style->latexname());
2870                         break;
2871
2872                 case LATEX_ENVIRONMENT:
2873                 case LATEX_ITEM_ENVIRONMENT:
2874                 {
2875                         string const & latexname = style->latexname();
2876
2877                         if (depth == par->params().depth()
2878                             && environment_stack[depth] != latexname) {
2879                                 sgmlCloseTag(ofs, depth, false,
2880                                              environment_stack[depth]);
2881                                 environment_stack[depth].erase();
2882                         }
2883                         if (depth < par->params().depth()) {
2884                                depth = par->params().depth();
2885                                environment_stack[depth].erase();
2886                         }
2887                         if (environment_stack[depth] != latexname) {
2888                                 if (depth == 0) {
2889                                         sgmlOpenTag(ofs, depth, false, "p");
2890                                 }
2891                                 sgmlOpenTag(ofs, depth, false, latexname);
2892
2893                                 if (environment_stack.size() == depth + 1)
2894                                         environment_stack.push_back("!-- --");
2895                                 environment_stack[depth] = latexname;
2896                         }
2897
2898                         if (style->latexparam() == "CDATA")
2899                                 ofs << "<![CDATA[";
2900
2901                         if (style->latextype == LATEX_ENVIRONMENT) break;
2902
2903                         if (style->labeltype == LABEL_MANUAL)
2904                                 item_name = "tag";
2905                         else
2906                                 item_name = "item";
2907
2908                         sgmlOpenTag(ofs, depth + 1, false, item_name);
2909                 }
2910                 break;
2911
2912                 default:
2913                         sgmlOpenTag(ofs, depth, false, style->latexname());
2914                         break;
2915                 }
2916
2917                 simpleLinuxDocOnePar(ofs, par, depth);
2918
2919                 par = par->next();
2920
2921                 ofs << "\n";
2922                 // write closing SGML tags
2923                 switch (style->latextype) {
2924                 case LATEX_COMMAND:
2925                         break;
2926                 case LATEX_ENVIRONMENT:
2927                 case LATEX_ITEM_ENVIRONMENT:
2928                         if (style->latexparam() == "CDATA")
2929                                 ofs << "]]>";
2930                         break;
2931                 default:
2932                         sgmlCloseTag(ofs, depth, false, style->latexname());
2933                         break;
2934                 }
2935         }
2936
2937         // Close open tags
2938         for (int i = depth; i >= 0; --i)
2939                 sgmlCloseTag(ofs, depth, false, environment_stack[i]);
2940
2941         if (!body_only) {
2942                 ofs << "\n\n";
2943                 sgmlCloseTag(ofs, 0, false, top_element);
2944         }
2945
2946         ofs.close();
2947         // How to check for successful close
2948
2949         // we want this to be true outside previews (for insetexternal)
2950         niceFile = true;
2951 }
2952
2953
2954 // checks, if newcol chars should be put into this line
2955 // writes newline, if necessary.
2956 namespace {
2957
2958 void sgmlLineBreak(ostream & os, string::size_type & colcount,
2959                           string::size_type newcol)
2960 {
2961         colcount += newcol;
2962         if (colcount > lyxrc.ascii_linelen) {
2963                 os << "\n";
2964                 colcount = newcol; // assume write after this call
2965         }
2966 }
2967
2968 enum PAR_TAG {
2969         NONE=0,
2970         TT = 1,
2971         SF = 2,
2972         BF = 4,
2973         IT = 8,
2974         SL = 16,
2975         EM = 32
2976 };
2977
2978
2979 string tag_name(PAR_TAG const & pt) {
2980         switch (pt) {
2981         case NONE: return "!-- --";
2982         case TT: return "tt";
2983         case SF: return "sf";
2984         case BF: return "bf";
2985         case IT: return "it";
2986         case SL: return "sl";
2987         case EM: return "em";
2988         }
2989         return "";
2990 }
2991
2992
2993 inline
2994 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
2995 {
2996         p1 = static_cast<PAR_TAG>(p1 | p2);
2997 }
2998
2999
3000 inline
3001 void reset(PAR_TAG & p1, PAR_TAG const & p2)
3002 {
3003         p1 = static_cast<PAR_TAG>(p1 & ~p2);
3004 }
3005
3006 } // anon
3007
3008
3009 // Handle internal paragraph parsing -- layout already processed.
3010 void Buffer::simpleLinuxDocOnePar(ostream & os,
3011         Paragraph * par,
3012         Paragraph::depth_type /*depth*/)
3013 {
3014         LyXLayout_ptr const & style = par->layout();
3015
3016         string::size_type char_line_count = 5;     // Heuristic choice ;-)
3017
3018         // gets paragraph main font
3019         LyXFont font_old;
3020         bool desc_on;
3021         if (style->labeltype == LABEL_MANUAL) {
3022                 font_old = style->labelfont;
3023                 desc_on = true;
3024         } else {
3025                 font_old = style->font;
3026                 desc_on = false;
3027         }
3028
3029         LyXFont::FONT_FAMILY family_type = LyXFont::ROMAN_FAMILY;
3030         LyXFont::FONT_SERIES series_type = LyXFont::MEDIUM_SERIES;
3031         LyXFont::FONT_SHAPE  shape_type  = LyXFont::UP_SHAPE;
3032         bool is_em = false;
3033
3034         stack<PAR_TAG> tag_state;
3035         // parsing main loop
3036         for (pos_type i = 0; i < par->size(); ++i) {
3037
3038                 PAR_TAG tag_close = NONE;
3039                 list < PAR_TAG > tag_open;
3040
3041                 LyXFont const font = par->getFont(params, i);
3042
3043                 if (font_old.family() != font.family()) {
3044                         switch (family_type) {
3045                         case LyXFont::SANS_FAMILY:
3046                                 tag_close |= SF;
3047                                 break;
3048                         case LyXFont::TYPEWRITER_FAMILY:
3049                                 tag_close |= TT;
3050                                 break;
3051                         default:
3052                                 break;
3053                         }
3054
3055                         family_type = font.family();
3056
3057                         switch (family_type) {
3058                         case LyXFont::SANS_FAMILY:
3059                                 tag_open.push_back(SF);
3060                                 break;
3061                         case LyXFont::TYPEWRITER_FAMILY:
3062                                 tag_open.push_back(TT);
3063                                 break;
3064                         default:
3065                                 break;
3066                         }
3067                 }
3068
3069                 if (font_old.series() != font.series()) {
3070                         switch (series_type) {
3071                         case LyXFont::BOLD_SERIES:
3072                                 tag_close |= BF;
3073                                 break;
3074                         default:
3075                                 break;
3076                         }
3077
3078                         series_type = font.series();
3079
3080                         switch (series_type) {
3081                         case LyXFont::BOLD_SERIES:
3082                                 tag_open.push_back(BF);
3083                                 break;
3084                         default:
3085                                 break;
3086                         }
3087
3088                 }
3089
3090                 if (font_old.shape() != font.shape()) {
3091                         switch (shape_type) {
3092                         case LyXFont::ITALIC_SHAPE:
3093                                 tag_close |= IT;
3094                                 break;
3095                         case LyXFont::SLANTED_SHAPE:
3096                                 tag_close |= SL;
3097                                 break;
3098                         default:
3099                                 break;
3100                         }
3101
3102                         shape_type = font.shape();
3103
3104                         switch (shape_type) {
3105                         case LyXFont::ITALIC_SHAPE:
3106                                 tag_open.push_back(IT);
3107                                 break;
3108                         case LyXFont::SLANTED_SHAPE:
3109                                 tag_open.push_back(SL);
3110                                 break;
3111                         default:
3112                                 break;
3113                         }
3114                 }
3115                 // handle <em> tag
3116                 if (font_old.emph() != font.emph()) {
3117                         if (font.emph() == LyXFont::ON) {
3118                                 tag_open.push_back(EM);
3119                                 is_em = true;
3120                         }
3121                         else if (is_em) {
3122                                 tag_close |= EM;
3123                                 is_em = false;
3124                         }
3125                 }
3126
3127                 list < PAR_TAG > temp;
3128                 while (!tag_state.empty() && tag_close) {
3129                         PAR_TAG k =  tag_state.top();
3130                         tag_state.pop();
3131                         os << "</" << tag_name(k) << ">";
3132                         if (tag_close & k)
3133                                 reset(tag_close,k);
3134                         else
3135                                 temp.push_back(k);
3136                 }
3137
3138                 for(list< PAR_TAG >::const_iterator j = temp.begin();
3139                     j != temp.end(); ++j) {
3140                         tag_state.push(*j);
3141                         os << "<" << tag_name(*j) << ">";
3142                 }
3143
3144                 for(list< PAR_TAG >::const_iterator j = tag_open.begin();
3145                     j != tag_open.end(); ++j) {
3146                         tag_state.push(*j);
3147                         os << "<" << tag_name(*j) << ">";
3148                 }
3149
3150                 char c = par->getChar(i);
3151
3152                 if (c == Paragraph::META_INSET) {
3153                         Inset * inset = par->getInset(i);
3154                         inset->linuxdoc(this, os);
3155                         font_old = font;
3156                         continue;
3157                 }
3158
3159                 if (style->latexparam() == "CDATA") {
3160                         // "TeX"-Mode on == > SGML-Mode on.
3161                         if (c != '\0')
3162                                 os << c;
3163                         ++char_line_count;
3164                 } else {
3165                         string sgml_string;
3166                         if (par->sgmlConvertChar(c, sgml_string)
3167                             && !style->free_spacing && !par->isFreeSpacing())
3168                         {
3169                                 // in freespacing mode, spaces are
3170                                 // non-breaking characters
3171                                 if (desc_on) {// if char is ' ' then...
3172
3173                                         ++char_line_count;
3174                                         sgmlLineBreak(os, char_line_count, 6);
3175                                         os << "</tag>";
3176                                         desc_on = false;
3177                                 } else  {
3178                                         sgmlLineBreak(os, char_line_count, 1);
3179                                         os << c;
3180                                 }
3181                         } else {
3182                                 os << sgml_string;
3183                                 char_line_count += sgml_string.length();
3184                         }
3185                 }
3186                 font_old = font;
3187         }
3188
3189         while (!tag_state.empty()) {
3190                 os << "</" << tag_name(tag_state.top()) << ">";
3191                 tag_state.pop();
3192         }
3193
3194         // resets description flag correctly
3195         if (desc_on) {
3196                 // <tag> not closed...
3197                 sgmlLineBreak(os, char_line_count, 6);
3198                 os << "</tag>";
3199         }
3200 }
3201
3202
3203 // Print an error message.
3204 void Buffer::sgmlError(Paragraph * /*par*/, int /*pos*/,
3205         string const & /*message*/) const
3206 {
3207 #ifdef WITH_WARNINGS
3208 #warning This is wrong we cannot insert an inset like this!!!
3209         // I guess this was Jose' so I explain you more or less why this
3210         // is wrong. This way you insert something in the paragraph and
3211         // don't tell it to LyXText (row rebreaking and undo handling!!!)
3212         // I deactivate this code, have a look at BufferView::insertErrors
3213         // how you should do this correctly! (Jug 20020315)
3214 #endif
3215 #if 0
3216         // insert an error marker in text
3217         InsetError * new_inset = new InsetError(message);
3218         par->insertInset(pos, new_inset, LyXFont(LyXFont::ALL_INHERIT,
3219                          params.language));
3220 #endif
3221 }
3222
3223
3224 void Buffer::makeDocBookFile(string const & fname, bool nice, bool only_body)
3225 {
3226         ofstream ofs(fname.c_str());
3227         if (!ofs) {
3228                 Alert::alert(_("LYX_ERROR:"), _("Cannot write file"), fname);
3229                 return;
3230         }
3231
3232         Paragraph * par = paragraph;
3233
3234         niceFile = nice; // this will be used by Insetincludes.
3235
3236         LaTeXFeatures features(params);
3237         validate(features);
3238
3239         texrow.reset();
3240
3241         LyXTextClass const & tclass = params.getLyXTextClass();
3242         string top_element = tclass.latexname();
3243
3244         if (!only_body) {
3245                 ofs << "<!DOCTYPE " << top_element
3246                     << "  PUBLIC \"-//OASIS//DTD DocBook V4.1//EN\"";
3247
3248                 string preamble = params.preamble;
3249                 const string name = nice ? ChangeExtension(filename_, ".sgml")
3250                          : fname;
3251                 preamble += features.getIncludedFiles(name);
3252                 preamble += features.getLyXSGMLEntities();
3253
3254                 if (!preamble.empty()) {
3255                         ofs << "\n [ " << preamble << " ]";
3256                 }
3257                 ofs << ">\n\n";
3258         }
3259
3260         string top = top_element;
3261         top += " lang=\"";
3262         top += params.language->code();
3263         top += "\"";
3264
3265         if (!params.options.empty()) {
3266                 top += " ";
3267                 top += params.options;
3268         }
3269         sgmlOpenTag(ofs, 0, false, top);
3270
3271         ofs << "<!-- DocBook file was created by " << lyx_docversion
3272             << "\n  See http://www.lyx.org/ for more information -->\n";
3273
3274         vector<string> environment_stack(10);
3275         vector<string> environment_inner(10);
3276         vector<string> command_stack(10);
3277
3278         bool command_flag = false;
3279         Paragraph::depth_type command_depth = 0;
3280         Paragraph::depth_type command_base = 0;
3281         Paragraph::depth_type cmd_depth = 0;
3282         Paragraph::depth_type depth = 0; // paragraph depth
3283
3284         string item_name;
3285         string command_name;
3286
3287         while (par) {
3288                 string sgmlparam;
3289                 string c_depth;
3290                 string c_params;
3291                 int desc_on = 0; // description mode
3292
3293                 LyXLayout_ptr const & style = par->layout();
3294
3295                 // environment tag closing
3296                 for (; depth > par->params().depth(); --depth) {
3297                         if (environment_inner[depth] != "!-- --") {
3298                                 item_name = "listitem";
3299                                 sgmlCloseTag(ofs, command_depth + depth, false, item_name);
3300                                 if (environment_inner[depth] == "varlistentry")
3301                                         sgmlCloseTag(ofs, depth+command_depth, false, environment_inner[depth]);
3302                         }
3303                         sgmlCloseTag(ofs, depth + command_depth, false, environment_stack[depth]);
3304                         environment_stack[depth].erase();
3305                         environment_inner[depth].erase();
3306                 }
3307
3308                 if (depth == par->params().depth()
3309                    && environment_stack[depth] != style->latexname()
3310                    && !environment_stack[depth].empty()) {
3311                         if (environment_inner[depth] != "!-- --") {
3312                                 item_name= "listitem";
3313                                 sgmlCloseTag(ofs, command_depth+depth, false, item_name);
3314                                 if (environment_inner[depth] == "varlistentry")
3315                                         sgmlCloseTag(ofs, depth + command_depth, false, environment_inner[depth]);
3316                         }
3317
3318                         sgmlCloseTag(ofs, depth + command_depth, false, environment_stack[depth]);
3319
3320                         environment_stack[depth].erase();
3321                         environment_inner[depth].erase();
3322                 }
3323
3324                 // Write opening SGML tags.
3325                 switch (style->latextype) {
3326                 case LATEX_PARAGRAPH:
3327                         sgmlOpenTag(ofs, depth + command_depth,
3328                                     false, style->latexname());
3329                         break;
3330
3331                 case LATEX_COMMAND:
3332                         if (depth != 0)
3333                                 sgmlError(par, 0,
3334                                           _("Error : Wrong depth for "
3335                                             "LatexType Command.\n"));
3336
3337                         command_name = style->latexname();
3338
3339                         sgmlparam = style->latexparam();
3340                         c_params = split(sgmlparam, c_depth,'|');
3341
3342                         cmd_depth = lyx::atoi(c_depth);
3343
3344                         if (command_flag) {
3345                                 if (cmd_depth < command_base) {
3346                                         for (Paragraph::depth_type j = command_depth;
3347                                              j >= command_base; --j) {
3348                                                 sgmlCloseTag(ofs, j, false, command_stack[j]);
3349                                                 ofs << endl;
3350                                         }
3351                                         command_depth = command_base = cmd_depth;
3352                                 } else if (cmd_depth <= command_depth) {
3353                                         for (int j = command_depth;
3354                                              j >= int(cmd_depth); --j) {
3355                                                 sgmlCloseTag(ofs, j, false, command_stack[j]);
3356                                                 ofs << endl;
3357                                         }
3358                                         command_depth = cmd_depth;
3359                                 } else
3360                                         command_depth = cmd_depth;
3361                         } else {
3362                                 command_depth = command_base = cmd_depth;
3363                                 command_flag = true;
3364                         }
3365                         if (command_stack.size() == command_depth + 1)
3366                                 command_stack.push_back(string());
3367                         command_stack[command_depth] = command_name;
3368
3369                         // treat label as a special case for
3370                         // more WYSIWYM handling.
3371                         // This is a hack while paragraphs can't have
3372                         // attributes, like id in this case.
3373                         if (par->isInset(0)) {
3374                                 Inset * inset = par->getInset(0);
3375                                 Inset::Code lyx_code = inset->lyxCode();
3376                                 if (lyx_code == Inset::LABEL_CODE) {
3377                                         command_name += " id=\"";
3378                                         command_name += (static_cast<InsetCommand *>(inset))->getContents();
3379                                         command_name += "\"";
3380                                         desc_on = 3;
3381                                 }
3382                         }
3383
3384                         sgmlOpenTag(ofs, depth + command_depth, false, command_name);
3385
3386                         item_name = c_params.empty()?"title":c_params;
3387                         sgmlOpenTag(ofs, depth + 1 + command_depth, false, item_name);
3388                         break;
3389
3390                 case LATEX_ENVIRONMENT:
3391                 case LATEX_ITEM_ENVIRONMENT:
3392                         if (depth < par->params().depth()) {
3393                                 depth = par->params().depth();
3394                                 environment_stack[depth].erase();
3395                         }
3396
3397                         if (environment_stack[depth] != style->latexname()) {
3398                                 if (environment_stack.size() == depth + 1) {
3399                                         environment_stack.push_back("!-- --");
3400                                         environment_inner.push_back("!-- --");
3401                                 }
3402                                 environment_stack[depth] = style->latexname();
3403                                 environment_inner[depth] = "!-- --";
3404                                 sgmlOpenTag(ofs, depth + command_depth, false, environment_stack[depth]);
3405                         } else {
3406                                 if (environment_inner[depth] != "!-- --") {
3407                                         item_name= "listitem";
3408                                         sgmlCloseTag(ofs, command_depth + depth, false, item_name);
3409                                         if (environment_inner[depth] == "varlistentry")
3410                                                 sgmlCloseTag(ofs, depth + command_depth, false, environment_inner[depth]);
3411                                 }
3412                         }
3413
3414                         if (style->latextype == LATEX_ENVIRONMENT) {
3415                                 if (!style->latexparam().empty()) {
3416                                         if (style->latexparam() == "CDATA")
3417                                                 ofs << "<![CDATA[";
3418                                         else
3419                                                 sgmlOpenTag(ofs, depth + command_depth, false, style->latexparam());
3420                                 }
3421                                 break;
3422                         }
3423
3424                         desc_on = (style->labeltype == LABEL_MANUAL);
3425
3426                         environment_inner[depth] = desc_on ? "varlistentry" : "listitem";
3427                         sgmlOpenTag(ofs, depth + 1 + command_depth,
3428                                     false, environment_inner[depth]);
3429
3430                         item_name = desc_on ? "term" : "para";
3431                         sgmlOpenTag(ofs, depth + 1 + command_depth,
3432                                     false, item_name);
3433                         break;
3434                 default:
3435                         sgmlOpenTag(ofs, depth + command_depth,
3436                                     false, style->latexname());
3437                         break;
3438                 }
3439
3440                 simpleDocBookOnePar(ofs, par, desc_on,
3441                                     depth + 1 + command_depth);
3442                 par = par->next();
3443
3444                 string end_tag;
3445                 // write closing SGML tags
3446                 switch (style->latextype) {
3447                 case LATEX_COMMAND:
3448                         end_tag = c_params.empty() ? "title" : c_params;
3449                         sgmlCloseTag(ofs, depth + command_depth,
3450                                      false, end_tag);
3451                         break;
3452                 case LATEX_ENVIRONMENT:
3453                         if (!style->latexparam().empty()) {
3454                                 if (style->latexparam() == "CDATA")
3455                                         ofs << "]]>";
3456                                 else
3457                                         sgmlCloseTag(ofs, depth + command_depth, false, style->latexparam());
3458                         }
3459                         break;
3460                 case LATEX_ITEM_ENVIRONMENT:
3461                         if (desc_on == 1) break;
3462                         end_tag= "para";
3463                         sgmlCloseTag(ofs, depth + 1 + command_depth, false, end_tag);
3464                         break;
3465                 case LATEX_PARAGRAPH:
3466                         sgmlCloseTag(ofs, depth + command_depth, false, style->latexname());
3467                         break;
3468                 default:
3469                         sgmlCloseTag(ofs, depth + command_depth, false, style->latexname());
3470                         break;
3471                 }
3472         }
3473
3474         // Close open tags
3475         for (int d = depth; d >= 0; --d) {
3476                 if (!environment_stack[depth].empty()) {
3477                         if (environment_inner[depth] != "!-- --") {
3478                                 item_name = "listitem";
3479                                 sgmlCloseTag(ofs, command_depth + depth, false, item_name);
3480                                if (environment_inner[depth] == "varlistentry")
3481                                        sgmlCloseTag(ofs, depth + command_depth, false, environment_inner[depth]);
3482                         }
3483
3484                         sgmlCloseTag(ofs, depth + command_depth, false, environment_stack[depth]);
3485                 }
3486         }
3487
3488         for (int j = command_depth; j >= 0 ; --j)
3489                 if (!command_stack[j].empty()) {
3490                         sgmlCloseTag(ofs, j, false, command_stack[j]);
3491                         ofs << endl;
3492                 }
3493
3494         ofs << "\n\n";
3495         sgmlCloseTag(ofs, 0, false, top_element);
3496
3497         ofs.close();
3498         // How to check for successful close
3499
3500         // we want this to be true outside previews (for insetexternal)
3501         niceFile = true;
3502 }
3503
3504
3505 void Buffer::simpleDocBookOnePar(ostream & os,
3506                                  Paragraph * par, int & desc_on,
3507                                  Paragraph::depth_type depth) const
3508 {
3509         bool emph_flag = false;
3510
3511         LyXLayout_ptr const & style = par->layout();
3512
3513         LyXFont font_old = (style->labeltype == LABEL_MANUAL ? style->labelfont : style->font);
3514
3515         int char_line_count = depth;
3516         //if (!style.free_spacing)
3517         //      os << string(depth,' ');
3518
3519         // parsing main loop
3520         for (pos_type i = 0; i < par->size(); ++i) {
3521                 LyXFont font = par->getFont(params, i);
3522
3523                 // handle <emphasis> tag
3524                 if (font_old.emph() != font.emph()) {
3525                         if (font.emph() == LyXFont::ON) {
3526                                 if (style->latexparam() == "CDATA")
3527                                         os << "]]>";
3528                                 os << "<emphasis>";
3529                                 if (style->latexparam() == "CDATA")
3530                                         os << "<![CDATA[";
3531                                 emph_flag = true;
3532                         } else if (i) {
3533                                 if (style->latexparam() == "CDATA")
3534                                         os << "]]>";
3535                                 os << "</emphasis>";
3536                                 if (style->latexparam() == "CDATA")
3537                                         os << "<![CDATA[";
3538                                 emph_flag = false;
3539                         }
3540                 }
3541
3542
3543                 if (par->isInset(i)) {
3544                         Inset * inset = par->getInset(i);
3545                         // don't print the inset in position 0 if desc_on == 3 (label)
3546                         if (i || desc_on != 3) {
3547                                 if (style->latexparam() == "CDATA")
3548                                         os << "]]>";
3549                                 inset->docbook(this, os, false);
3550                                 if (style->latexparam() == "CDATA")
3551                                         os << "<![CDATA[";
3552                         }
3553                 } else {
3554                         char c = par->getChar(i);
3555                         string sgml_string;
3556                         par->sgmlConvertChar(c, sgml_string);
3557
3558                         if (style->pass_thru) {
3559                                 os << c;
3560                         } else if (style->free_spacing || par->isFreeSpacing() || c != ' ') {
3561                                         os << sgml_string;
3562                         } else if (desc_on ==1) {
3563                                 ++char_line_count;
3564                                 os << "\n</term><listitem><para>";
3565                                 desc_on = 2;
3566                         } else {
3567                                 os << ' ';
3568                         }
3569                 }
3570                 font_old = font;
3571         }
3572
3573         if (emph_flag) {
3574                 if (style->latexparam() == "CDATA")
3575                         os << "]]>";
3576                 os << "</emphasis>";
3577                 if (style->latexparam() == "CDATA")
3578                         os << "<![CDATA[";
3579         }
3580
3581         // resets description flag correctly
3582         if (desc_on == 1) {
3583                 // <term> not closed...
3584                 os << "</term>\n<listitem><para>&nbsp;</para>";
3585         }
3586         if (style->free_spacing)
3587                 os << '\n';
3588 }
3589
3590
3591 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
3592 // Other flags: -wall -v0 -x
3593 int Buffer::runChktex()
3594 {
3595         if (!users->text) return 0;
3596
3597         users->owner()->prohibitInput();
3598
3599         // get LaTeX-Filename
3600         string const name = getLatexName();
3601         string path = filePath();
3602
3603         string const org_path = path;
3604         if (lyxrc.use_tempdir || !IsDirWriteable(path)) {
3605                 path = tmppath;
3606         }
3607
3608         Path p(path); // path to LaTeX file
3609         users->owner()->message(_("Running chktex..."));
3610
3611         // Remove all error insets
3612         bool const removedErrorInsets = users->removeAutoInsets();
3613
3614         // Generate the LaTeX file if neccessary
3615         makeLaTeXFile(name, org_path, false);
3616
3617         TeXErrors terr;
3618         Chktex chktex(lyxrc.chktex_command, name, filePath());
3619         int res = chktex.run(terr); // run chktex
3620
3621         if (res == -1) {
3622                 Alert::alert(_("chktex did not work!"),
3623                            _("Could not run with file:"), name);
3624         } else if (res > 0) {
3625                 // Insert all errors as errors boxes
3626                 users->insertErrors(terr);
3627         }
3628
3629         // if we removed error insets before we ran chktex or if we inserted
3630         // error insets after we ran chktex, this must be run:
3631         if (removedErrorInsets || res) {
3632 #warning repaint needed here, or do you mean update() ?
3633                 users->repaint();
3634                 users->fitCursor();
3635         }
3636         users->owner()->allowInput();
3637
3638         return res;
3639 }
3640
3641
3642 void Buffer::validate(LaTeXFeatures & features) const
3643 {
3644         Paragraph * par = paragraph;
3645         LyXTextClass const & tclass = params.getLyXTextClass();
3646
3647         // AMS Style is at document level
3648         if (params.use_amsmath || tclass.provides(LyXTextClass::amsmath))
3649                 features.require("amsmath");
3650
3651         while (par) {
3652                 // We don't use "lyxerr.debug" because of speed. (Asger)
3653                 if (lyxerr.debugging(Debug::LATEX))
3654                         lyxerr << "Paragraph: " <<  par << endl;
3655
3656                 // Now just follow the list of paragraphs and run
3657                 // validate on each of them.
3658                 par->validate(features);
3659
3660                 // and then the next paragraph
3661                 par = par->next();
3662         }
3663
3664         // the bullet shapes are buffer level not paragraph level
3665         // so they are tested here
3666         for (int i = 0; i < 4; ++i) {
3667                 if (params.user_defined_bullets[i] != ITEMIZE_DEFAULTS[i]) {
3668                         int const font = params.user_defined_bullets[i].getFont();
3669                         if (font == 0) {
3670                                 int const c = params
3671                                         .user_defined_bullets[i]
3672                                         .getCharacter();
3673                                 if (c == 16
3674                                    || c == 17
3675                                    || c == 25
3676                                    || c == 26
3677                                    || c == 31) {
3678                                         features.require("latexsym");
3679                                 }
3680                         } else if (font == 1) {
3681                                 features.require("amssymb");
3682                         } else if ((font >= 2 && font <= 5)) {
3683                                 features.require("pifont");
3684                         }
3685                 }
3686         }
3687
3688         if (lyxerr.debugging(Debug::LATEX)) {
3689                 features.showStruct();
3690         }
3691 }
3692
3693
3694 // This function should be in Buffer because it's a buffer's property (ale)
3695 string const Buffer::getIncludeonlyList(char delim)
3696 {
3697         string lst;
3698         for (inset_iterator it = inset_iterator_begin();
3699             it != inset_iterator_end(); ++it) {
3700                 if ((*it)->lyxCode() == Inset::INCLUDE_CODE) {
3701                         InsetInclude * insetinc =
3702                                 static_cast<InsetInclude *>(*it);
3703                         if (insetinc->isIncludeOnly()) {
3704                                 if (!lst.empty())
3705                                         lst += delim;
3706                                 lst += insetinc->getRelFileBaseName();
3707                         }
3708                 }
3709         }
3710         lyxerr[Debug::INFO] << "Includeonly(" << lst << ')' << endl;
3711         return lst;
3712 }
3713
3714
3715 vector<string> const Buffer::getLabelList()
3716 {
3717         /// if this is a child document and the parent is already loaded
3718         /// Use the parent's list instead  [ale990407]
3719         if (!params.parentname.empty()
3720             && bufferlist.exists(params.parentname)) {
3721                 Buffer * tmp = bufferlist.getBuffer(params.parentname);
3722                 if (tmp)
3723                         return tmp->getLabelList();
3724         }
3725
3726         vector<string> label_list;
3727         for (inset_iterator it = inset_iterator_begin();
3728              it != inset_iterator_end(); ++it) {
3729                 vector<string> const l = (*it)->getLabelList();
3730                 label_list.insert(label_list.end(), l.begin(), l.end());
3731         }
3732         return label_list;
3733 }
3734
3735
3736 // This is also a buffer property (ale)
3737 vector<pair<string, string> > const Buffer::getBibkeyList() const
3738 {
3739         typedef pair<string, string> StringPair;
3740         /// if this is a child document and the parent is already loaded
3741         /// Use the parent's list instead  [ale990412]
3742         if (!params.parentname.empty() && bufferlist.exists(params.parentname)) {
3743                 Buffer const * tmp = bufferlist.getBuffer(params.parentname);
3744                 if (tmp)
3745                         return tmp->getBibkeyList();
3746         }
3747
3748         vector<StringPair> keys;
3749         Paragraph * par = paragraph;
3750         while (par) {
3751                 if (par->bibkey) {
3752                         string const key = par->bibkey->getContents();
3753                         string const opt = par->bibkey->getOptions();
3754                         string const ref = par->asString(this, false);
3755                         string const info = opt + "TheBibliographyRef" + ref;
3756
3757                         keys.push_back(StringPair(key, info));
3758                 }
3759                 par = par->next();
3760         }
3761
3762         // Might be either using bibtex or a child has bibliography
3763         if (keys.empty()) {
3764                 for (inset_iterator it = inset_const_iterator_begin();
3765                         it != inset_const_iterator_end(); ++it) {
3766                         // Search for Bibtex or Include inset
3767                         if ((*it)->lyxCode() == Inset::BIBTEX_CODE) {
3768                                 vector<StringPair> tmp =
3769                                         static_cast<InsetBibtex*>(*it)->getKeys(this);
3770                                 keys.insert(keys.end(), tmp.begin(), tmp.end());
3771                         } else if ((*it)->lyxCode() == Inset::INCLUDE_CODE) {
3772                                 vector<StringPair> const tmp =
3773                                         static_cast<InsetInclude*>(*it)->getKeys();
3774                                 keys.insert(keys.end(), tmp.begin(), tmp.end());
3775                         }
3776                 }
3777         }
3778
3779         return keys;
3780 }
3781
3782
3783 bool Buffer::isDepClean(string const & name) const
3784 {
3785         DEPCLEAN * item = dep_clean;
3786         while (item && item->master != name)
3787                 item = item->next;
3788         if (!item) return true;
3789         return item->clean;
3790 }
3791
3792
3793 void Buffer::markDepClean(string const & name)
3794 {
3795         if (!dep_clean) {
3796                 dep_clean = new DEPCLEAN;
3797                 dep_clean->clean = true;
3798                 dep_clean->master = name;
3799                 dep_clean->next = 0;
3800         } else {
3801                 DEPCLEAN * item = dep_clean;
3802                 while (item && item->master != name)
3803                         item = item->next;
3804                 if (item) {
3805                         item->clean = true;
3806                 } else {
3807                         item = new DEPCLEAN;
3808                         item->clean = true;
3809                         item->master = name;
3810                         item->next = 0;
3811                 }
3812         }
3813 }
3814
3815
3816 bool Buffer::dispatch(string const & command, bool * result)
3817 {
3818         // Split command string into command and argument
3819         string cmd;
3820         string line = ltrim(command);
3821         string const arg = trim(split(line, cmd, ' '));
3822
3823         return dispatch(lyxaction.LookupFunc(cmd), arg, result);
3824 }
3825
3826
3827 bool Buffer::dispatch(int action, string const & argument, bool * result)
3828 {
3829         bool dispatched = true;
3830
3831         switch (action) {
3832                 case LFUN_EXPORT: {
3833                         bool const tmp = Exporter::Export(this, argument, false);
3834                         if (result)
3835                                 *result = tmp;
3836                         break;
3837                 }
3838
3839                 default:
3840                         dispatched = false;
3841         }
3842         return dispatched;
3843 }
3844
3845
3846 void Buffer::resizeInsets(BufferView * bv)
3847 {
3848         /// then remove all LyXText in text-insets
3849         Paragraph * par = paragraph;
3850         for (; par; par = par->next()) {
3851             par->resizeInsetsLyXText(bv);
3852         }
3853 }
3854
3855
3856 void Buffer::redraw()
3857 {
3858 #warning repaint needed here, or do you mean update() ?
3859         users->repaint();
3860         users->fitCursor();
3861 }
3862
3863
3864 void Buffer::changeLanguage(Language const * from, Language const * to)
3865 {
3866
3867         ParIterator end = par_iterator_end();
3868         for (ParIterator it = par_iterator_begin(); it != end; ++it)
3869                 (*it)->changeLanguage(params, from, to);
3870 }
3871
3872
3873 bool Buffer::isMultiLingual()
3874 {
3875         ParIterator end = par_iterator_end();
3876         for (ParIterator it = par_iterator_begin(); it != end; ++it)
3877                 if ((*it)->isMultiLingual(params))
3878                         return true;
3879
3880         return false;
3881 }
3882
3883
3884 Buffer::inset_iterator::inset_iterator(Paragraph * paragraph, pos_type pos)
3885         : par(paragraph)
3886 {
3887         it = par->InsetIterator(pos);
3888         if (it == par->inset_iterator_end()) {
3889                 par = par->next();
3890                 setParagraph();
3891         }
3892 }
3893
3894
3895 void Buffer::inset_iterator::setParagraph()
3896 {
3897         while (par) {
3898                 it = par->inset_iterator_begin();
3899                 if (it != par->inset_iterator_end())
3900                         return;
3901                 par = par->next();
3902         }
3903         //it = 0;
3904         // We maintain an invariant that whenever par = 0 then it = 0
3905 }
3906
3907
3908 Inset * Buffer::getInsetFromID(int id_arg) const
3909 {
3910         for (inset_iterator it = inset_const_iterator_begin();
3911                  it != inset_const_iterator_end(); ++it)
3912         {
3913                 if ((*it)->id() == id_arg)
3914                         return *it;
3915                 Inset * in = (*it)->getInsetFromID(id_arg);
3916                 if (in)
3917                         return in;
3918         }
3919         return 0;
3920 }
3921
3922
3923 Paragraph * Buffer::getParFromID(int id) const
3924 {
3925         if (id < 0) return 0;
3926         Paragraph * par = paragraph;
3927         while (par) {
3928                 if (par->id() == id) {
3929                         return par;
3930                 }
3931                 Paragraph * tmp = par->getParFromID(id);
3932                 if (tmp) {
3933                         return tmp;
3934                 }
3935                 par = par->next();
3936         }
3937         return 0;
3938 }
3939
3940
3941 ParIterator Buffer::par_iterator_begin()
3942 {
3943         return ParIterator(paragraph);
3944 }
3945
3946
3947 ParIterator Buffer::par_iterator_end()
3948 {
3949         return ParIterator();
3950 }