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